思路:
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); 43 else pwm->period = pwm->pulse_tmp + (pwm->fall_timer_count-1)*pwm->timer_max_cnt +(pwm->timer_max_cnt-pwm->fall_start_time) + current_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; 60 else pwm->pulse_tmp = current_time + (pwm->rise_timer_count-1)*pwm->timer_max_cnt + (pwm->timer_max_cnt-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(¤t_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; 14 uint32_t curr_cnt = htim3.Instance->CNT; //在此使用定时器三,回去计数值16 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(¤t_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(¤t_pwm); //更新定时器计数值 38 } 39 }
获取输入PWM信号的占空比程序采用了面向对象的封装方法,可以使得程序调用简单。
原文:https://www.cnblogs.com/BlogsOfLei/p/12637663.html