基于STM32获取外部PWM信号的占空比和周期

思路:

    1、采用外部输入中断的方式获取PWM波形高低电平跳变(上升沿和下降沿),所以外部输入中断引脚配置为上升沿和下降沿中断有效;

    2、定时器定时时钟计数,可按照项目需求的精度确定定时器时钟大小,示例中精度选择为1us,所以定时器时钟设置为1us或者1Mhz。

 

    实现代码如下:(代码实现了简单的封装,调用简单,可以免费使用)

 pwm_input.h

 1 /************************************************************
 2 **   捕获PWM信号占空比
 3 **   编写:Awesome    QQ: 2281280195
 4 **   代码可以免费移植使用
 5 *************************************************************/
 6 
 7 #ifndef PWM_INPUT_H
 8 #define PWM_INPUT_H
 9 
10 #define us_to_ns(x)     ((x)*1000)
11 #define ns_to_us(x)     ((uint32_t)((x)/1000.0))
12 
13 typedef unsigned int uint32_t;
14 
15 typedef enum PWM_Status{
16     PWM_STATUS_NONE = 0,
17     PWM_STATUS_RAISE,    //出现上升沿,处于高电平期间
18     PWM_STATUS_FALLING,  //出现下降沿,处于低电平期间
19     PWM_STATUS_SUCCESS,
20 }PWM_Status;
21 
22 typedef enum io_status{
23     IO_STATUS_LOW,   //低电平
24     IO_STATUS_HIGH,  //高电平
25 }IO_STATUS;
26 
27 typedef struct _pwm{
28     volatile PWM_Status pwm_status;       //电平标志
29     volatile uint32_t timer_uint_ns;      //定时器计数器单位时间
30     volatile uint32_t timer_max_cnt;      //定时器计数寄存器最大定时值
31     volatile uint32_t rise_start_time;    //上升沿定时器CNT值
32     volatile uint32_t fall_start_time;    //下降沿定时器CNT值
33     volatile uint32_t rise_timer_count;   //高电平期间,定时器更新次数
34     volatile uint32_t fall_timer_count;   //低电平期间,定时器更新次数
35     volatile uint32_t pulse_tmp;          //中间值,用户不需要直接访问
36     volatile uint32_t period_tmp;         //中间值,用户不需要直接访问
37     volatile uint32_t pulse;              //占空比高电平时间,单位ns
38     volatile uint32_t period;             //周期,单位ns      
39     PWM_Status (*pwm_input_io_changed)(struct _pwm* pwm, IO_STATUS io_status, uint32_t current_time);  //IO电平变化调用函数
40     void (*pwm_input_timer_count)(struct _pwm* pwm);       //定时器更新调用
41     uint32_t (*get_pwm_input_pulse)(struct _pwm* pwm);     //获取高电平时间,ns
42     uint32_t (*get_pwm_input_period)(struct _pwm* pwm);    //获取周期 ns
43 }PWM;
44 
45 void pwm_input_init(PWM* pwm, uint32_t uint_ns, uint32_t max_cnt);     //初始化PWM对象
46 
47 PWM_Status pwm_input_io_changed(PWM* pwm, IO_STATUS io_status, uint32_t current_time);  //外部触发IO中断调用函数
48 
49 void pwm_input_timer_count(PWM* pwm);              //定时器中断调用该函数
50 
51 uint32_t get_pwm_input_pulse(struct _pwm* pwm);    //获取高电平时间,单位ns
52 
53 uint32_t get_pwm_input_period(struct _pwm* pwm);   //获取周期,单位ns
54 
55 #endif

 

pwm_input.c

 1 /************************************************************
 2 **   捕获PWM信号占空比
 3 **   编写:Awesome    QQ: 2281280195
 4 **   代码可以免费移植使用
 5 *************************************************************/
 6 
 7 #include "pwm_input.h"
 8 
 9 /* 初始化pwm对象,传入两个参数: 定时器单位时间,定时器最大计数值 */
10 void pwm_input_init(PWM* pwm, uint32_t uint_ns, uint32_t max_cnt){
11     if((void*)0==pwm) return;
12     pwm->pwm_status = PWM_STATUS_NONE;
13     pwm->timer_uint_ns = uint_ns;
14     pwm->timer_max_cnt = max_cnt;
15     pwm->fall_start_time = 0;
16     pwm->fall_timer_count = 0;
17     pwm->pulse_tmp = 0;
18     pwm->period_tmp = 0;
19     pwm->period = 0;
20     pwm->pulse = 0;
21     pwm->rise_timer_count = 0;
22     pwm->rise_start_time = 0;
23     pwm->pwm_input_io_changed = pwm_input_io_changed;
24     pwm->pwm_input_timer_count = pwm_input_timer_count;
25     pwm->get_pwm_input_pulse = get_pwm_input_pulse;
26     pwm->get_pwm_input_period = get_pwm_input_period;
27 }
28 
29 /* IO输入上升沿中断或者下降沿中断,一个周期包括一个上升沿,一个下降沿,
30 ** 在接下来的上升沿触发时计算PWM周期
31 */
32 PWM_Status pwm_input_io_changed(PWM* pwm, IO_STATUS io_status, uint32_t current_time){
33     if((void*)0==pwm) return PWM_STATUS_NONE;
34     if(IO_STATUS_HIGH==io_status){ 
35             if(pwm->pwm_status==PWM_STATUS_NONE){  //第一个为上升沿
36                 pwm->pwm_status = PWM_STATUS_RAISE;  //上升沿开始
37                 //记录第一个周期上升开始时间
38                 pwm->rise_timer_count = 0;
39                 pwm->rise_start_time = current_time;
40             }else if(pwm->pwm_status==PWM_STATUS_FALLING){  //已经采集到下降沿,整个周期采集完成
41                 //计算周期
42                 if(pwm->fall_timer_count==0) {
                    //定时计数寄存器后台一直更新,所以需编写下面逻辑代码,保证数据有效性
                    pwm->period_tmp=pwm->pulse_tmp+(current_time-pwm->fall_start_time);
                 }
44 45 pwm->rise_timer_count = 0; 46 pwm->rise_start_time = current_time; 47 pwm->pwm_status = PWM_STATUS_RAISE; 48 49 pwm->period = pwm->period_tmp*pwm->timer_uint_ns; 50 pwm->pulse = pwm->pulse_tmp*pwm->timer_uint_ns; 51 52 return PWM_STATUS_SUCCESS; //成功计算一个PWM周期 53 54 } 55 }else { 56 if(pwm->pwm_status==PWM_STATUS_RAISE){ //已经采集到上升沿,在进入下降沿中断的时候计算占空比时间 57 58 pwm->fall_start_time = current_time;
           //定时计数寄存器后台一直更新,所以需编写下面逻辑代码,保证数据有效性
59 if( pwm->rise_timer_count==0 ) {
                     pwm->pulse_tmp = current_time - pwm->rise_start_time;
                 }
61 62 pwm->fall_timer_count = 0; 63 pwm->pwm_status = PWM_STATUS_FALLING; 64 65 } 66 } 67 return pwm->pwm_status; 68 } 69 70 /* 定时器更新调用 */ 71 void pwm_input_timer_count(PWM* pwm){ 72 if((void*)0==pwm) return; 73 if(pwm->pwm_status==PWM_STATUS_NONE){ 74 pwm->rise_timer_count = 0; 75 pwm->fall_timer_count = 0; 76 }else if(pwm->pwm_status==PWM_STATUS_RAISE){ 77 ++pwm->rise_timer_count; 78 }else if(pwm->pwm_status==PWM_STATUS_FALLING){ 79 ++pwm->fall_timer_count; 80 } 81 } 82 83 /* 获取高电平时间,单位ns */ 84 uint32_t get_pwm_input_pulse(struct _pwm* pwm){ 85 if((void*)0==pwm) return 0; 86 87 return pwm->pulse; 88 } 89 90 /* 获取周期,单位ns */ 91 uint32_t get_pwm_input_period(struct _pwm* pwm){ 92 if((void*)0==pwm) return 0; 93 94 return pwm->period; 95 }

 

主代码调用

 1 /* 简单调用实例 */
 2 
 3 uint32_t pulse, period; //定义PWM占空比时间、周期结果变量,单位采用us
 4 
 5 PWM current_pwm;
 6 
 7 //初始化PWM对象,定时器单位时间为1us,最大定时值为0x10000
 8 pwm_input_init(&current_pwm, us_to_ns(1), 0x10000); 
 9 
10 //外部IO中断覆盖调用,基于cubeMX开发
11 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
12 {
13     IO_STATUS flag = IO_STATUS_LOW;
    uint32_t curr_cnt; 

///********** 此处很重要 ***********/
//如果刚好遇上定时器更新时间,但定时器中断优先级低于外部IO中断,所以,此处做特别处理,防止时间出错,因为定时器计数器是不断更新的
     if (__HAL_TIM_GET_FLAG(&htim3, TIM_FLAG_UPDATE) != RESET){
       if (__HAL_TIM_GET_IT_SOURCE(&htim3, TIM_IT_UPDATE) != RESET){
current_pwm.pwm_input_timer_count(&current_pwm);
            curr_cnt = htim3.Instance->CNT;
            __HAL_TIM_CLEAR_IT(&htim3, TIM_IT_UPDATE);
        }
     }else curr_cnt = htim3.Instance->CNT;
17 if(GPIO_Pin==GPIO_PIN_6){ 18 if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6)==GPIO_PIN_SET){ //高电平,上升沿 19 flag = IO_STATUS_HIGH; 20 }else flag = IO_STATUS_LOW; //低电平,下降沿 21 if( PWM_STATUS_SUCCESS==current_pwm.pwm_input_io_changed(&current_pwm, flag, curr_cnt) ){ //判断是否完整获取过一个周期 22 pulse = ns_to_us(current_pwm.pulse); //获取占空比时间,单位us 23 period = ns_to_us(current_pwm.period); //获取周期,单位us29 } 30 } 31 } 32 33 //定时器中断调用覆盖,基于cubeMX开发 34 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) 35 { 36 if(htim==&htim3){ //使用定时器三 37 current_pwm.pwm_input_timer_count(&current_pwm); //更新定时器计数值 38 } 39 }

获取输入PWM信号的占空比程序采用了面向对象的封装方法,可以使得程序调用简单。注意外部IO中断优先级要高于或者等于定时器的优先级。

 

posted @ 2020-04-05 16:10  前端人生  阅读(2709)  评论(0编辑  收藏  举报