基於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中斷優先級要高於或者等於定時器的優先級。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM