一、引言

AS32系列MCU芯片集成4個高級定時器,每個定時器包含一個32位自動重載計數器,該計數器由可編程預分頻器驅動,支持遞增、遞減、中心計數、編碼器模式等計數方式。

高級定時器具有6個獨立通道,可實現測量輸入信號的脈衝寬度、可編程PWM輸出、帶死區插入的互補PWM等功能。

二、PWM簡介

PWM,全稱脈衝寬度調製。它是一種用數字信號來模擬模擬電壓的技術。簡單來説,就是快速地在“開”(高電平)和“關”(低電平)之間切換,通過改變一個週期內“開”的時間比例,來控制平均電壓。

2.1輸出比較模式

定時器被配置為PWM模式時,會用到比較寄存器。

週期: 由自動重載寄存器決定。計數器從0計數到這個值,然後歸零,這個過程就是一個PWM週期。

佔空比: 由比較寄存器決定。它設定了電平翻轉的閾值。

工作流程:

1.計數器從0開始向上計數。

2.當計數器的值 小於 比較寄存器的值時,輸出高電平(例如)。

3.當計數器的值 達到或超過 比較寄存器的值時,輸出翻轉為低電平。

4.計數器到達自動重載值後歸零,輸出重新變為高電平,開始下一個週期。

通過修改比較寄存器的值,就改變了高電平在一個週期內持續的時間,從而改變了佔空比

輸出比較可用於:

控制LED亮度: 佔空比越大,LED越亮。

驅動舵機: 舵機的角度由PWM脈衝的寬度精確控制。

控制電機速度: 通過改變平均電壓來調節直流電機轉速。

音頻輸出: 通過極高頻率的PWM,經過濾波後可以生成簡單的音頻信號。

2.2輸入捕獲

輸入捕獲功能就像一個“高速抓拍機”。當外部引腳上發生一個特定事件(如上升沿)時,它立刻“抓拍”下當前計數器的值,並保存起來。通過分析兩次“抓拍”的值,我們就能計算出這個事件的時間參
定時器配置為輸入捕獲模式時,會用到捕獲寄存器

工作流程:

1.定時器的計數器一直在自由運行。

2.當輸入引腳上出現第一個上升沿時,硬件會立即將計數器當前的值複製到捕獲寄存器中,併產生一箇中斷。

3.在中斷服務程序裏,程序讀取這個捕獲值(記為t1),並同時將捕獲邊沿設置為下降沿

4.當引腳出現下降沿時,硬件再次將計數器的當前值捕獲(記為t2)。

5.程序計算 t2 - t1,這個差值就是高電平期間計數器計數的次數,再乘以計數週期,就得到了高電平脈衝的精確寬度。

6.同理,可以再捕獲下一個上升沿,計算出整個信號的週期。

輸入捕獲可用於:

測量脈衝寬度和頻率: 例如解碼紅外遙控信號(NEC協議)、測量超聲波測距模塊返回的脈衝寬度。

解碼編碼器信號: 讀取旋轉編碼器的位置和速度。

測量數字信號的佔空比。

三、軟件設計

本文同時啓用定時器HTIM1與HTIM5的通道1與通道2。其中,兩個定時器的通道1均配置為PWM輸出模式,以生成PWM信號;相應的通道2則工作在輸入捕獲模式,並採用中斷驅動方式,以精確測量PWM信號的頻率與佔空比。最終,測量數據將通過串口打印輸出。引腳連線如下:PD4->PH9, PC9->PD5

STM32-一文搞懂通用定時器捕獲/比較通道_捕獲比較寄存器_#架構

3.1軟件分析

HTIM1初始化函數: void User_TIM1_Config(uint32_t arr, uint32_t psc, uint32_t rcr);

硬件使能與準備

1.GPIOD_CLK_ENABLE();      // 使能GPIOD時鐘  
2.HTIM1_CLK_ENABLE();      // 使能HTIM1時鐘

定時器時基配置

TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上計數

TIM_TimeBaseInitStructure.TIM_Period = arr; // 設定週期

TIM_TimeBaseInitStructure.TIM_Prescaler = psc; // 設定預分頻

TIM_TimeBaseInitStructure.TIM_RepetitionCounter = rcr; // 重複計數
輸入捕獲配置(通道2)
1. TIM_IC_InitStructure.TIM_Channel = TIM_Channel_2; // 使用通道2
2. TIM_IC_InitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿捕獲
3. TIM_IC_InitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; // 直接輸入
4. TIM_IC_InitStructure.TIM_ICFilter = 0x0; // 無濾波器
5. TIM_IC_InitStructure.TIM_ICPrescaler = 0x0; // 每個事件都捕獲
中斷與PWM輸出配置
中斷使能:
1. TIM_IT_Update://定時器溢出更新中斷
2. TIM_IT_CC2://通道2捕獲/比較中斷

PWM輸出配置(通道1):

    1. TIM_OC_InitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM模式1
    2. TIM_OC_InitStructure.TIM_Pulse = arr/2; // 初始佔空比50%
    3. TIM_OC_InitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 高電平有效

    注:HTIM5配置除GPIO引腳外其餘配置通HTIM1,此外HTIM5和HTIM1掛在不同總線下,讀者使用時需自行計算外設時鐘

    在輸入捕獲模式下,當相應的 ICx 信號檢測到跳變沿後,將使用捕獲/比較寄存器(TIMx_CCRx)來鎖存計數器的值。簡單的説就是通過檢測 TIMx_CHx 上的邊沿信號,在邊沿信號發生跳變(比如上升沿/下降沿)的時候,將當前定時器的值(TIMx_CNT)存放到對應的通道的捕獲/比較寄存(TIMx_CCRx)裏面,完成一次捕獲。

    STM32-一文搞懂通用定時器捕獲/比較通道_捕獲比較寄存器_#架構_02

    從上圖可以看出,t1-t2 時間就是需要測量的高電平時間,假如定時器工作在向上計數模式,測量方法是:首先設置定時器通道 x 為上升沿捕獲,這樣在 t1 時刻,就會捕獲到當前的 CNT 值,然後立即清零 CNT,並設置通道 x 為下降沿捕獲,這樣到 t2 時刻,又會發生捕獲事件,得到此時的 CNT 值,記為 CCRx2。根據定時器的計數頻率,就可以算出 t1-t2 的時間,從而得到高電平脈寬。在 t1-t2 時間內可能會出現 N 次定時器溢出,因此還需要對定時器溢出進行處理,防止因高電平時間過長髮生溢出導致測量數據不準。CNT計數的次數等於:N*ARR+CCRx2,有了這個計數次數,再乘以 CNT 的計數週期,即可得到 t2-t1 的時間長度,即高電平持續時間。部分邏輯在中斷函數中實現:

    void TIM1_IRQ_Handler()
    
    {
    
    static uint32_t TIM1_counter=0;
    
    /* Get the value of TIM_CNT*/
    
    if(TIM1_GetComplete==0)
    
    {
    
    if(TIM_GetITStatus(TIM1, TIM_IT_Update)!= RESET)
    
    {
    
    TIM1_Update_counter++;
    
    TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
    
    }
    
    
    
    if(TIM_GetITStatus(TIM1, TIM_IT_CC2)!= RESET)
    
    {
    
    TIM1_counter++;
    
    if(TIM1_counter==1)
    
    {
    
    TIM1_Update_counter=0;
    
    TIM1_Value1=TIM_GetCounter(TIM1);
    
    }
    
    if(TIM1_counter==2)
    
    {
    
    TIM1_Value2=TIM_GetCounter(TIM1);
    
    TIM1_counter=0;
    
    TIM1_GetComplete=1;
    
    }
    
    TIM_ClearITPendingBit(TIM1, TIM_IT_CC2);
    
    }
    
    }
    
    else
    
    {
    
    TIM_ClearITPendingBit(TIM1, TIM_IT_Update | TIM_IT_CC2);
    
    }
    
    }

    計算並輸出PWM信號週期和頻率的函數

    /* Calculate the input frequency and period */
    
    TIM1_Input_Poriod=(10000*TIM1_Update_counter-TIM1_Value1+TIM1_Value2);
    
    TIM1_Input_Poriod = TIM1_Input_Poriod/20;
    
    Printf("TIM1 Input_Poriod: %d us\r\n", (uint32_t)TIM1_Input_Poriod);
    
    Printf("TIM1 Frequence: %d hz\r\n", (uint32_t)(1000000/TIM1_Input_Poriod));
    
    TIM1_Update_counter=0;
    
    TIM1_GetComplete=0;
    
    ClearCache();

    其中第三行的20 為 HTIM1的時鐘頻率為20M。

    四、開發板驗證:

    STM32-一文搞懂通用定時器捕獲/比較通道_捕獲比較寄存器_#單片機_03