【STM32F407開發板用戶手冊】第25章 STM32F407的TIM定時器基礎知識和HAL庫API


最新教程下載:http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255

第25章       STM32F407的TIM定時器基礎知識和HAL庫API

本章節為大家講解TIM1 – TIM14共計14個定時器的基礎知識和對應的HAL庫API。

25.1 初學者重要提示

25.2 定時器基礎知識

25.3 定時器的HAL庫用戶

25.4 源文件stm32f4xx_hal_tim.c

25.5 總結

 

 

25.1 初學者重要提示

  1.   學習定時器外設推薦從硬件框圖開始了解基本的功能特性,然后逐步深入了解各種特性,這種方式方便記憶和以后查閱。
  2.   特別注意STM32F4的TIM1,8,15,16,17才有RCR重復計數器,其它都沒用的。
  3.   STM32的單個定時器中不同通道可以配置不同頻率PWM。http://www.armbbs.cn/forum.php?mod=viewthread&tid=89008
  4.   STM32f4的TIM1-TIM14中斷入口函數名使用時要注意,別搞錯了:
TIM1_BRK_IRQHandler             
TIM1_UP_IRQHandler              
TIM1_TRG_COM_IRQHandler        
TIM1_CC_IRQHandler                                                    
TIM2_IRQHandler                                           
TIM3_IRQHandler                                                 
TIM4_IRQHandler                 
TIM5_IRQHandler            
TIM6_DAC_IRQHandler           <------------------要注意            
TIM7_IRQHandler 
TIM8_BRK_TIM12_IRQHandler      <------------------要注意,定時器12也是用的這個
TIM8_UP_TIM13_IRQHandler       <------------------要注意,定時器13也是用的這個
TIM8_TRG_COM_TIM14_IRQHandler  <------------------要注意,定時器14也是用的這個
TIM8_CC_IRQHandler

25.2 定時器基礎知識

注,不同定時支持的功能略有區別,基礎定時器功能較少,TIM1和TIM8高級定時器功能多些。

  •   TIM2和TIM5是32位定時器,其它定時器都是16位定時器。16位和32位的區別是CNT計數器范圍不同,32位的范圍是0 到2^32 – 1,而16位的是0到65535;它們支持的分頻是范圍是一樣的,都是1到65535。
  •   計數器支持遞增、遞減和遞增/遞減二合一。
  •   多個獨立通道,可用於:

– 輸入捕獲。

– 輸出比較。

– PWM 生成(邊沿和中心對齊模式)。

– 單脈沖模式輸出。

  •   帶死區插入,斷路功能和PWM互補輸出,效果可看此貼:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=88997

  •   發生如下事件時生成中斷/DMA 請求:

– 更新:計數器上溢/下溢、計數器初始化(通過軟件或內部/外部觸發)

– 觸發事件(計數器啟動、停止、初始化或通過內部/外部觸發計數)

– 輸入捕獲

– 輸出比較

  •   支持增量式編碼器和霍爾傳感器。

25.2.1 定時器TIM1-TIM14的區別

STM32F4支持的定時器有點多,要簡單的區分下。粗略的比較如下:

 

通過上面的表格,至少要了解到以下兩點:

  •   STM32F4的定時器主要分為高級定時器,通用定時器,基礎定時器和低功耗定時器。
  •   TIM2和TIM5是32位定時器,其它都是16位定時器。

25.2.2 定時器的硬件框圖

認識一個外設,最好的方式就是看他的框圖,方便我們快速的了解定時器的基本功能,然后再看手冊了解細節。

下面我們直接看最復雜的高級定時器TIM1&TIM8框圖:

 

通過這個框圖,我們可以得到如下信息:

  •   TIMx_ETR接口

外部觸發輸入接口。ETR支持多種輸入源:輸入引腳(默認配置)、比較器輸出和模擬看門狗。

  •   截圖左側的TIMx_CH1,TIMx_CH2,TIMx_CH3和TIMx_CH4接口

這四個通道主要用於輸入捕獲,可以計算波形頻率和脈寬。                                                                                                                             

  •   TIMx_BKIN和TIMx_BKIN2接口

斷路功能,主要用於保護由 TIM1 和 TIM8 定時器產生的 PWM 信號所驅動的功率開關

  •   TRGO內部輸出通道

主要用於定時器級聯,ADC和DAC的定時器觸發。

  •   4組輸出比較單元OC1到OC6

OC1到OC4有對應的輸出引腳。

  •   截圖右側的輸出比較通道TIMx_CH1,TIMx_CH1N,TIMx_CH2,TIMx_CH2N,TIMx_CH3,TIMx_CH3N和TIMx_CH4

主要用於PWM輸出,注意CH1到CH3有互補輸出,而CH4沒有互補輸出。

  •   其它框圖里面未展示出來功能

定時器TIM1&TIM8還支持的其它功能在用到的時候再做說明。

25.2.3 定時器的時基單元

定時器要工作就需要一個基本時基單元,而基本的時基單元是由下面幾個寄存器組成的:

  •   預分頻器寄存器 (TIMx_PSC)

用於設置定時器的分頻,比如定時器的主頻是168MHz,通過此寄存器可以將其設置為168MHz,84MHz,42MHz等分頻值。

注:預分頻器有個緩沖功能,可以讓用戶實時更改,新的預分頻值將在下一個更新事件發生時被采用(以遞增計數模式為例,就是CNT計數值達到ARR自動重裝寄存器的數值時會產生更新事件)。

  •   計數器寄存器 (TIMx_CNT)

計數器是最基本的計數單元,計數值是建立在分頻的基礎上面,比如通過TIMx_PSC設置分頻后的頻率為100MHz,那么計數寄存器計一次數就是10ns。

  •   自動重載寄存器 (TIMx_ARR)

自動重裝寄存器是CNT計數寄存器能達到的最大計數值,以遞增計數模式為例,就是CNT計數器達到ARR寄存器數值時,重新從0開始計數。

注,自動重載寄存器是預裝載的。對自動重載寄存器執行寫入或讀取操作時會訪問預裝載寄存器。預裝載寄存器的內容既可以立即傳送到影子寄存器(讓設置立即起到效果的寄存器),也可以在每次發生更新事件時傳送到影子寄存器。簡單的說就是讓ARR寄存器的數值立即更新還是更新事件發送的時候更新。

  •   重復計數器寄存器 (TIMx_RCR)

以遞增計數模式為例,當CNT計數器數值達到ARR自動重載數值時,重復計數器的數值加1,重復次數達到TIMx_RCR+ 1后就,將生成更新事件。

注,只有TIM1,TIM8,TIM15,TIM16,TIM17有此寄存器。

 

比如我們要配置定時器實現周期性的中斷,主要使用這幾個寄存器即可。

25.2.4 定時器輸出比較(PWM)

使用定時器時基單元的那幾個寄存器僅僅能設置周期,還不能設置占空比。針對這個問題,還需要比較捕獲寄存CCR的參與,這樣就可以設置占空比了。

為了方便大家理解,以PWM 邊沿對齊模式,遞增計數配置為例:

  •   當計數器TIMx_CNT < 比較捕獲寄存器TIMx_CCRx期間,PWM參考信號OCxREF輸出高電平。
  •   當計數器TIMx_CNT >= 比較捕獲寄存器TIMx_CCRx期間, PWM參考信號OCxREF輸出低電平。
  •   當比較捕獲寄存器TIMx_CCRx > 自動重載寄存器TIMx_ARR,OCxREF保持為1。
  •   當比較捕獲寄存器TIMx_CCRx = 0,則OCxRef保持為0。

下面是TIMx_ARR=8的波形效果:

 

25.2.5 定時器輸入捕獲

與PWM一樣,使用定時器實現輸入捕獲,僅靠時基單元的那幾個寄存器是不行的,我們需要一個寄存器來記錄發生捕獲時的具體時間,這個寄存器依然由比較捕獲寄存器TIMx_CCRx來實現。

比如我們要測量一路方波的周期:

  •   配置定時器為輸入捕獲模式,上升沿觸發,設置分頻,自動重裝等寄存器,比如設置的CNT計數器計數1次是1微秒。
  •   當有上升沿觸發的時候,TIMx_CCRx寄存器就會自動記錄當前的CNT數值,然后用戶就可以通過CC中斷,在中斷復位程序里面保存當前的TIMx_CCRx寄存器數值。等下次再檢測到上升沿觸發,兩次時間求差就可以得到方波的周期。

不過這里要特別注意一點,如果CNT發生溢出(比如16位定時器,計數到65535就溢出了)就需要特別處理下,將CNT計數溢出考慮進來。

25.3 定時器的HAL庫用法

定時器的HAL庫用法其實就是幾個結構體變量成員的配置和使用,然后配置GPIO、時鍾,並根據需要配置NVIC、中斷和DMA。下面我們逐一展開為大家做個說明。

25.3.1 定時器寄存器結構體TIM_TypeDef

定時器相關的寄存器是通過HAL庫中的結構體TIM_TypeDef定義的,在stm32f4xx.h中可以找到這個類型定義:

typedef struct
{
  __IO uint32_t CR1;         /*!< TIM control register 1,              Address offset: 0x00 */
  __IO uint32_t CR2;         /*!< TIM control register 2,              Address offset: 0x04 */
  __IO uint32_t SMCR;        /*!< TIM slave mode control register,     Address offset: 0x08 */
  __IO uint32_t DIER;        /*!< TIM DMA/interrupt enable register,   Address offset: 0x0C */
  __IO uint32_t SR;          /*!< TIM status register,                 Address offset: 0x10 */
  __IO uint32_t EGR;         /*!< TIM event generation register,       Address offset: 0x14 */
  __IO uint32_t CCMR1;       /*!< TIM capture/compare mode register 1, Address offset: 0x18 */
  __IO uint32_t CCMR2;       /*!< TIM capture/compare mode register 2, Address offset: 0x1C */
  __IO uint32_t CCER;        /*!< TIM capture/compare enable register, Address offset: 0x20 */
  __IO uint32_t CNT;         /*!< TIM counter register,                Address offset: 0x24 */
  __IO uint32_t PSC;         /*!< TIM prescaler,                       Address offset: 0x28 */
  __IO uint32_t ARR;         /*!< TIM auto-reload register,            Address offset: 0x2C */
  __IO uint32_t RCR;         /*!< TIM repetition counter register,     Address offset: 0x30 */
  __IO uint32_t CCR1;        /*!< TIM capture/compare register 1,      Address offset: 0x34 */
  __IO uint32_t CCR2;        /*!< TIM capture/compare register 2,      Address offset: 0x38 */
  __IO uint32_t CCR3;        /*!< TIM capture/compare register 3,      Address offset: 0x3C */
  __IO uint32_t CCR4;        /*!< TIM capture/compare register 4,      Address offset: 0x40 */
  __IO uint32_t BDTR;        /*!< TIM break and dead-time register,    Address offset: 0x44 */
  __IO uint32_t DCR;         /*!< TIM DMA control register,            Address offset: 0x48 */
  __IO uint32_t DMAR;        /*!< TIM DMA address for full transfer,   Address offset: 0x4C */
  __IO uint32_t OR;          /*!< TIM option register,                 Address offset: 0x50 */
} TIM_TypeDef;

這個結構體的成員名稱和排列次序和CPU的定時器寄存器是一 一對應的。

__IO表示volatile, 這是標准C語言中的一個修飾字,表示這個變量是非易失性的,編譯器不要將其優化掉。core_m4.h 文件定義了這個宏:

#define     __O     volatile             /*!< Defines 'write only' permissions */
#define     __IO    volatile             /*!< Defines 'read / write' permissions */

下面我們看下定時器的定義,在stm32f4xx.h文件。

#define PERIPH_BASE           0x40000000UL 
#define APB1PERIPH_BASE       PERIPH_BASE
#define APB2PERIPH_BASE       (PERIPH_BASE + 0x00010000UL)

/*!< APB2 peripherals */
#define TIM1_BASE             (APB2PERIPH_BASE + 0x0000UL) <----- 展開這個宏,(TIM_TypeDef *) 0x40000000
#define TIM8_BASE             (APB2PERIPH_BASE + 0x0400UL)
#define TIM9_BASE             (APB2PERIPH_BASE + 0x4000UL)
#define TIM10_BASE            (APB2PERIPH_BASE + 0x4400UL)
#define TIM11_BASE            (APB2PERIPH_BASE + 0x4800UL)

/*!< APB1 peripherals */
#define TIM2_BASE             (APB1PERIPH_BASE + 0x0000UL)
#define TIM3_BASE             (APB1PERIPH_BASE + 0x0400UL)
#define TIM4_BASE             (APB1PERIPH_BASE + 0x0800UL)
#define TIM5_BASE             (APB1PERIPH_BASE + 0x0C00UL)
#define TIM6_BASE             (APB1PERIPH_BASE + 0x1000UL)
#define TIM7_BASE             (APB1PERIPH_BASE + 0x1400UL)
#define TIM12_BASE            (APB1PERIPH_BASE + 0x1800UL)
#define TIM13_BASE            (APB1PERIPH_BASE + 0x1C00UL)
#define TIM14_BASE            (APB1PERIPH_BASE + 0x2000UL)

#define TIM1                ((TIM_TypeDef *) TIM1_BASE)
#define TIM2                ((TIM_TypeDef *) TIM2_BASE)
#define TIM3                ((TIM_TypeDef *) TIM3_BASE)
#define TIM4                ((TIM_TypeDef *) TIM4_BASE)
#define TIM5                ((TIM_TypeDef *) TIM5_BASE)
#define TIM6                ((TIM_TypeDef *) TIM6_BASE)
#define TIM7                ((TIM_TypeDef *) TIM7_BASE)
#define TIM8                ((TIM_TypeDef *) TIM8_BASE)
#define TIM9                ((TIM_TypeDef *) TIM9_BASE)
#define TIM10               ((TIM_TypeDef *) TIM10_BASE)
#define TIM11               ((TIM_TypeDef *) TIM11_BASE)
#define TIM12               ((TIM_TypeDef *) TIM12_BASE)
#define TIM13               ((TIM_TypeDef *) TIM13_BASE)
#define TIM14               ((TIM_TypeDef *) TIM14_BASE)

我們訪問TIM2的CR1寄存器可以采用這種形式:TIM2->CR1 = 0;

25.3.2 定時器句柄結構體TIM_HandleTypeDef

HAL庫在TIM_TypeDef的基礎上封裝了一個結構體TIM_HandleTypeDef,定義如下:

#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
typedef struct __TIM_HandleTypeDef
#else
typedef struct
#endif 
{
  TIM_TypeDef                 *Instance;     /*!< Register base address             */
  TIM_Base_InitTypeDef        Init;          /*!< TIM Time Base required parameters */
  HAL_TIM_ActiveChannel       Channel;       /*!< Active channel                    */
  DMA_HandleTypeDef           *hdma[7];      /*!< DMA Handlers array
                                                  This array is accessed by a @ref DMA_Handle_index */
  HAL_LockTypeDef             Lock;          /*!< Locking object                    */
  __IO HAL_TIM_StateTypeDef   State;         /*!< TIM operation state               */

#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
  void (* Base_MspInitCallback)(struct __TIM_HandleTypeDef *htim);  /*!< TIM Base Msp Init Callback    */
  void (* Base_MspDeInitCallback)(struct __TIM_HandleTypeDef *htim); /*!< TIM Base Msp DeInit Callback */
  /* 省略 */

#endif 
} TIM_HandleTypeDef;

通過條件編譯USE_HAL_TIM_REGISTER_CALLBACKS,每個定時器可以有獨立的注冊回調,不用多個定時公用一個回調函數。

這里重點介紹前四個參數,其它參數主要是HAL庫內部使用的。

  •   TIM_TypeDef  *Instance

這個參數是寄存器的例化,方便操作寄存器,比如使能定時器的計數器。

SET_BIT(huart->Instance->CR1,  TIM_CR1_CEN)。

  •   TIM_Base_InitTypeDef  Init

這個參數是用戶接觸最多的,用於配置定時器的基本參數。

typedef struct
{
  uint32_t Prescaler;      
  uint32_t CounterMode;    
  uint32_t Period;         
  uint32_t ClockDivision;    
  uint32_t RepetitionCounter; 
  uint32_t AutoReloadPreload;  
} TIM_Base_InitTypeDef;

  成員Prescaler

用於設置定時器分頻,對於32位的TIM2和TIM5范圍是0到0xFFFFFFFF,其它定時器是0到0xFFFF。

 成員CounterMode

用於設置計數模式,向上計數模式、向下計數模式和中心對齊模式。

#define TIM_COUNTERMODE_UP                 0x00000000U    /*!< Counter used as up-counter   */
#define TIM_COUNTERMODE_DOWN               TIM_CR1_DIR    /*!< Counter used as down-counter */
#define TIM_COUNTERMODE_CENTERALIGNED1     TIM_CR1_CMS_0  /*!< Center-aligned mode 1        */
#define TIM_COUNTERMODE_CENTERALIGNED2     TIM_CR1_CMS_1  /*!< Center-aligned mode 2        */
#define TIM_COUNTERMODE_CENTERALIGNED3     TIM_CR1_CMS    /*!< Center-aligned mode 3        */

  成員Period

用於設置定時器周期,對於32位的TIM2和TIM5范圍是0到0xFFFFFFFF,其它定時器是0到0xFFFF。

  成員ClockDivision

用於指示定時器時鍾 (CK_INT) 頻率與死區發生器以及數字濾波器(ETR、TIx)所使用的死區及采樣時鍾 (tDTS) 之間的分頻比。

#define TIM_CLOCKDIVISION_DIV1             0x00000000U      /*!< Clock division: tDTS=tCK_INT   */
#define TIM_CLOCKDIVISION_DIV2             TIM_CR1_CKD_0    /*!< Clock division: tDTS=2*tCK_INT */
#define TIM_CLOCKDIVISION_DIV4             TIM_CR1_CKD_1    /*!< Clock division: tDTS=4*tCK_INT */

  成員RepetitionCounter

用於設置重復計數器,僅TIM1和TIM8有,其它定時器沒有。作用是每當計數器上溢/下溢時,重復計數器減1,當減到零時,才會生成更新事件,這個在生成PWM時比較有用。

  成員AutoReloadPreload

用於設置定時器的ARR自動重裝寄存器是更新事件產生時寫入有效還是立即寫入有效。如果使能了表示更新事件產生時寫入有效,否則反之。

#define TIM_AUTORELOAD_PRELOAD_DISABLE    0x00000000U               /*!< TIMx_ARR register is not buffered */
#define TIM_AUTORELOAD_PRELOAD_ENABLE     TIM_CR1_ARPE              /*!< TIMx_ARR register is buffered */
  •   HAL_TIM_ActiveChannel    Channel;

用於設置定時器通道,比如TIM1和TIM8都是4個通道。

typedef enum
{
  HAL_TIM_ACTIVE_CHANNEL_1        = 0x01U,    /*!< The active channel is 1     */
  HAL_TIM_ACTIVE_CHANNEL_2        = 0x02U,    /*!< The active channel is 2     */
  HAL_TIM_ACTIVE_CHANNEL_3        = 0x04U,    /*!< The active channel is 3     */
  HAL_TIM_ACTIVE_CHANNEL_4        = 0x08U,    /*!< The active channel is 4     */
  HAL_TIM_ACTIVE_CHANNEL_CLEARED  = 0x00U     /*!< All active channels cleared */   
}HAL_TIM_ActiveChannel;
  •   DMA_HandleTypeDef        *hdma[7];

用於關聯DMA。

 

此結構體使用舉例:配置定時器參數,其實就是配置結構體TIM_HandleTypeDef的成員。

TIM_HandleTypeDef   TimHandle = {0};

/* 
    定時器中斷更新周期 = TIMxCLK / usPrescaler + 1)/usPeriod + 1)
*/
TimHandle.Instance = TIMx;
TimHandle.Init.Prescaler         = usPrescaler;
TimHandle.Init.Period            = usPeriod;    
TimHandle.Init.ClockDivision     = 0;
TimHandle.Init.CounterMode       = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

25.3.3 定時器輸出比較結構體TIM_OC_InitTypeDef

此結構體主要用於定時器的輸出比較,定義如下:

typedef struct
{                                 
  uint32_t OCMode;       
  uint32_t Pulse;       
  uint32_t OCPolarity;    
  uint32_t OCNPolarity;   
  uint32_t OCFastMode;  
  uint32_t OCIdleState;   
  uint32_t OCNIdleState;  
} TIM_OC_InitTypeDef;  

下面將這幾個參數一 一做個說明。

  •   OCMode

用於配置輸出比較模式,支持的模式較多:

#define TIM_OCMODE_TIMING                   0x00000000U                                              #define TIM_OCMODE_ACTIVE                   TIM_CCMR1_OC1M_0                                        
#define TIM_OCMODE_INACTIVE                 TIM_CCMR1_OC1M_1                                       
#define TIM_OCMODE_TOGGLE                   (TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0)                  
#define TIM_OCMODE_PWM1                     (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1)                   
#define TIM_OCMODE_PWM2                     (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0) 
#define TIM_OCMODE_FORCED_ACTIVE            (TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0)                   
#define TIM_OCMODE_FORCED_INACTIVE          TIM_CCMR1_OC1M_2   
  •   Pulse

可用於設置占空比,對應定時器的CCR寄存器,32位的TIM2和TIM5范圍是0到0xFFFFFFFF。

  •   OCPolarity

設置輸出極性,可選高電平或低電平有效。

#define TIM_OCPOLARITY_HIGH                0x00000000U                         
#define TIM_OCPOLARITY_LOW                 TIM_CCER_CC1P                        /
  •   OCNPolarity

互補輸出極性設置,可選高電平或者低電平有效。

#define TIM_OCNPOLARITY_HIGH               0x00000000U                       
#define TIM_OCNPOLARITY_LOW                TIM_CCER_CC1NP
  •   OCFastMode

快速輸出模式使能,僅OCMode配置為PWM1或者PWM2模式時才有意義。

#define TIM_OCFAST_DISABLE                 0x00000000U                         
#define TIM_OCFAST_ENABLE                  TIM_CCMR1_OC1FE   
  •   OCIdleState

空閑狀態時,設置輸出比較引腳的電平狀態。

#define TIM_OCIDLESTATE_SET                TIM_CR2_OIS1                        
#define TIM_OCIDLESTATE_RESET              0x00000000U
  •   OCNIdleState

空閑狀態時,設置互補輸出引腳的電平狀態。

#define TIM_OCNIDLESTATE_SET               TIM_CR2_OIS1N                       
#define TIM_OCNIDLESTATE_RESET             0x00000000U     

25.3.4 定時器輸入捕獲結構體TIM_IC_InitTypeDef

此結構體主要用於定時器的輸入捕獲,定義如下:

typedef struct
{                                  
  uint32_t ICPolarity;   
  uint32_t ICSelection; 
  uint32_t ICPrescaler; 
  uint32_t ICFilter;    
} TIM_IC_InitTypeDef;

下面將這幾個參數一 一做個說明。

  •   ICPolarity

輸入觸發極性,可以選擇上升沿,下降沿或者雙沿觸發。

#define  TIM_ICPOLARITY_RISING             TIM_INPUTCHANNELPOLARITY_RISING      
#define  TIM_ICPOLARITY_FALLING            TIM_INPUTCHANNELPOLARITY_FALLING    
#define  TIM_ICPOLARITY_BOTHEDGE           TIM_INPUTCHANNELPOLARITY_BOTHEDGE
  •   ICSelection

輸入捕獲通道選擇,可以選擇直接輸入(即CC1選擇TI1,CC2選擇TI2等),間接輸入(CC1選擇TI2,CC3選擇TI4等)或者TRC。

#define TIM_ICSELECTION_DIRECTTI       (TIM_CCMR1_CC1S_0)   
#define TIM_ICSELECTION_INDIRECTTI     (TIM_CCMR1_CC1S_1)  
#define TIM_ICSELECTION_TRC            (TIM_CCMR1_CC1S)     
  •   ICPrescaler

輸入捕獲分頻,表示每捕獲1,2,4或8個事件后表示一次捕獲。

#define TIM_ICPSC_DIV1       0x00000000U                 
#define TIM_ICPSC_DIV2       (TIM_CCMR1_IC1PSC_0)    
#define TIM_ICPSC_DIV4       (TIM_CCMR1_IC1PSC_1)    
#define TIM_ICPSC_DIV8       (TIM_CCMR1_IC1PSC)  
  •   ICFilter

輸入捕獲濾波器,可以定義采樣頻率和多少個連續事件才視為有效的觸發,參數范圍0到15。具體定義如下,其中fCK_INT表示定時器時鍾,fDTS表示死區時間采樣率,N表示這么多個事件代表一次有效邊沿。

0000:無濾波器,按 fDTS 頻率進行采樣
0001: fSAMPLING=fCK_INT, N=20010: fSAMPLING=fCK_INT, N=40011: fSAMPLING=fCK_INT, N=80100: fSAMPLING=fDTS/2, N=60101: fSAMPLING=fDTS/2, N=80110: fSAMPLING=fDTS/4, N=60111: fSAMPLING=fDTS/4, N=8
1000: fSAMPLING=fDTS/8, N=6
1001: fSAMPLING=fDTS/8, N=8
1010: fSAMPLING=fDTS/16, N=5
1011: fSAMPLING=fDTS/16, N=6
1100: fSAMPLING=fDTS/16, N=8
1101: fSAMPLING=fDTS/32, N=5
1110: fSAMPLING=fDTS/32, N=6

25.3.5 定時器的底層配置(GPIO,時鍾,中斷等)

HAL庫有個自己的底層初始化回調函數,比如調用函數HAL_TIM_Base_Init就會調用HAL_TIM_Base_MspInit,此函數是弱定義的。

__weak void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(htim);
  /* NOTE : This function Should not be modified, when the callback is needed,
            the HAL_TIM_Base_MspDeInit could be implemented in the user file
   */
}

用戶可以在其它的C文件重定向,並將相對的底層初始化在里面實現。對應的底層復位函數HAL_TIM_Base_DeInit是在函數HAL_TIM_Base_MspDeInit里面被調用的,也是弱定義的。

當然,用戶也可以自己初始化,不限制必須在兩個函數里面實現。

定時器外設的基本參數配置完畢后還不能使用,還需要配置GPIO、時鍾、中斷等參數,比如下面配置TIM1使用PA8做PWM輸出。

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
  GPIO_InitTypeDef   GPIO_InitStruct;

  /* 使能TIM1時鍾 */
  __HAL_RCC_TIM1_CLK_ENABLE ();

  /* 使能GPIOA時鍾 */
  __HAL_RCC_GPIOA_CLK_ENABLE ();

  /* 設置TIM1使用PA8做PWM輸出引腳,將其配置為輸出,推挽,復用模式 */
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

  GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
  GPIO_InitStruct.Pin = GPIO_PIN_8;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

}

總結下來就是以下幾點:

  •   配置TIM時鍾。
  •   配置TIM所用到引腳和對應的GPIO時鍾。
  •   如果用到定時器中斷,還需要通過NVIC配置中斷。
  •   如果用到DMA,還要配置DMA。

25.3.6 定時器的狀態標志清除問題

下面我們介紹__HAL_TIM_GET_FLAG函數。這個函數用來檢查定時器標志位是否被設置。

/** @brief  Check whether the specified TIM interrupt flag is set or not.
  * @param  __HANDLE__: specifies the TIM Handle.
  * @param  __FLAG__: specifies the TIM interrupt flag to check.
  *        This parameter can be one of the following values:
  *            @arg TIM_FLAG_UPDATE: Update interrupt flag
  *            @arg TIM_FLAG_CC1: Capture/Compare 1 interrupt flag
  *            @arg TIM_FLAG_CC2: Capture/Compare 2 interrupt flag
  *            @arg TIM_FLAG_CC3: Capture/Compare 3 interrupt flag
  *            @arg TIM_FLAG_CC4: Capture/Compare 4 interrupt flag
  *            @arg TIM_FLAG_CC5: Compare 5 interrupt flag
  *            @arg TIM_FLAG_CC6: Compare 6 interrupt flag
  *            @arg TIM_FLAG_COM:  Commutation interrupt flag
  *            @arg TIM_FLAG_TRIGGER: Trigger interrupt flag
  *            @arg TIM_FLAG_BREAK: Break interrupt flag   
  *            @arg TIM_FLAG_BREAK2: Break 2 interrupt flag                     
  *            @arg TIM_FLAG_SYSTEM_BREAK: System Break interrupt flag
  *            @arg TIM_FLAG_CC1OF: Capture/Compare 1 overcapture flag
  *            @arg TIM_FLAG_CC2OF: Capture/Compare 2 overcapture flag
  *            @arg TIM_FLAG_CC3OF: Capture/Compare 3 overcapture flag
  *            @arg TIM_FLAG_CC4OF: Capture/Compare 4 overcapture flag
  * @retval The new state of __FLAG__ (TRUE or FALSE).
  */
#define __HAL_TIM_GET_FLAG(__HANDLE__, __FLAG__)   (((__HANDLE__)->Instance->SR &(__FLAG__)) == (__FLAG__))

前5個是比較常用的中斷標志。

  •   TIM_FLAG_UPDATE

定時器更新標准,配置一個周期性的定時器中斷要用到。

  •   TIM_FLAG_CC1

TIM_FLAG_CC2

TIM_FLAG_CC3

TIM_FLAG_CC4

捕獲/比較標志,配置了捕獲/比較中斷要用到。

 

與標志獲取函數__HAL_TIM_GET_FLAG對應的清除函數是__HAL_TIM_CLEAR_FLAG:

/** @brief  Clear the specified TIM interrupt flag.
  * @param  __HANDLE__: specifies the TIM Handle.
  * @param  __FLAG__: specifies the TIM interrupt flag to clear.
  *        This parameter can be one of the following values:
  *            @arg TIM_FLAG_UPDATE: Update interrupt flag
  *            @arg TIM_FLAG_CC1: Capture/Compare 1 interrupt flag
  *            @arg TIM_FLAG_CC2: Capture/Compare 2 interrupt flag
  *            @arg TIM_FLAG_CC3: Capture/Compare 3 interrupt flag
  *            @arg TIM_FLAG_CC4: Capture/Compare 4 interrupt flag
  *            @arg TIM_FLAG_CC5: Compare 5 interrupt flag
  *            @arg TIM_FLAG_CC6: Compare 6 interrupt flag
  *            @arg TIM_FLAG_COM:  Commutation interrupt flag
  *            @arg TIM_FLAG_TRIGGER: Trigger interrupt flag
  *            @arg TIM_FLAG_BREAK: Break interrupt flag   
  *            @arg TIM_FLAG_BREAK2: Break 2 interrupt flag                     
  *            @arg TIM_FLAG_SYSTEM_BREAK: System Break interrupt flag
  *            @arg TIM_FLAG_CC1OF: Capture/Compare 1 overcapture flag
  *            @arg TIM_FLAG_CC2OF: Capture/Compare 2 overcapture flag
  *            @arg TIM_FLAG_CC3OF: Capture/Compare 3 overcapture flag
  *            @arg TIM_FLAG_CC4OF: Capture/Compare 4 overcapture flag
  * @retval The new state of __FLAG__ (TRUE or FALSE).
  */
#define __HAL_TIM_CLEAR_FLAG(__HANDLE__, __FLAG__)       ((__HANDLE__)->Instance->SR = ~(__FLAG__))

清除標志函數所支持的參數跟獲取函數是一 一對應的。除了這兩個函數,還是定時器的中斷開啟和中斷關閉函數用的也比較多。                                                                                                                                                   

/** @brief  Enable the specified TIM interrupt.
* @param  __HANDLE__: specifies the TIM Handle.
* @param  __INTERRUPT__: specifies the TIM interrupt source to enable.
*          This parameter can be one of the following values:
*            @arg TIM_IT_UPDATE: Update interrupt
*            @arg TIM_IT_CC1:   Capture/Compare 1 interrupt
*            @arg TIM_IT_CC2:  Capture/Compare 2 interrupt
*            @arg TIM_IT_CC3:  Capture/Compare 3 interrupt
*            @arg TIM_IT_CC4:  Capture/Compare 4 interrupt
*            @arg TIM_IT_COM:   Commutation interrupt
*            @arg TIM_IT_TRIGGER: Trigger interrupt
*            @arg TIM_IT_BREAK: Break interrupt
* @retval None
*/
#define __HAL_TIM_ENABLE_IT(__HANDLE__, __INTERRUPT__)  ((__HANDLE__)->Instance->DIER |= (__INTERRUPT__))

  /** @brief  Disable the specified TIM interrupt.
  * @param  __HANDLE__: specifies the TIM Handle.
  * @param  __INTERRUPT__: specifies the TIM interrupt source to disable.
  *          This parameter can be one of the following values:
  *            @arg TIM_IT_UPDATE: Update interrupt
  *            @arg TIM_IT_CC1:   Capture/Compare 1 interrupt
  *            @arg TIM_IT_CC2:  Capture/Compare 2 interrupt
  *            @arg TIM_IT_CC3:  Capture/Compare 3 interrupt
  *            @arg TIM_IT_CC4:  Capture/Compare 4 interrupt
  *            @arg TIM_IT_COM:   Commutation interrupt
  *            @arg TIM_IT_TRIGGER: Trigger interrupt
  *            @arg TIM_IT_BREAK: Break interrupt
  * @retval None
  */
#define __HAL_TIM_DISABLE_IT(__HANDLE__, __INTERRUPT__)   ((__HANDLE__)->Instance->DIER &= ~(__INTERRUPT__))

常用的也是前五個參數,1個定時器更新中斷以及4個CC中斷 。

 

注意:操作定時器的寄存器不限制必須要用HAL庫提供的API,比如要操作寄存器CR1,直接調用TIM1->CR1操作即可。

25.3.7 定時器初始化流程總結

使用方法由HAL庫提供:

  第1步:通過下面幾個函數配置定時器工作在相應的模式

  •   HAL_TIM_Base_Init

簡單的定時器時基礎功能

  •   HAL_TIM_OC_Init 和 HAL_TIM_OC_ConfigChannel

配置定時器產生輸出比較信號

  •   HAL_TIM_PWM_Init 和 HAL_TIM_PWM_ConfigChannel

配置定時器產生PWM信號

  •   HAL_TIM_IC_Init 和 HAL_TIM_IC_ConfigChannel

配置定時器測量外部信號

  •   HAL_TIM_OnePulse_Init 和 HAL_TIM_OnePulse_ConfigChannel

配置定時器工作在單脈沖模式

  •   HAL_TIM_Encoder_Init

配置定時器使用編碼器接口

  第2步:定時器幾個常用功能的底層初始化API,這個里面需要用戶自己填第1步里面的幾個函數會調用下面的API。

  •   定時器基本功能 : HAL_TIM_Base_MspInit()
  •   輸入捕獲 : HAL_TIM_IC_MspInit()
  •   輸出比較 : HAL_TIM_OC_MspInit()
  •   PWM輸出 : HAL_TIM_PWM_MspInit()
  •   單脈沖輸出模式: HAL_TIM_OnePulse_MspInit()
  •   編碼器模式 : HAL_TIM_Encoder_MspInit()

  第3步:底層初始化具體實現第2步中函數的具體實現。

  •   使用函數__HAL_RCC_TIMx_CLK_ENABLE()使能定時器時鍾。
  •   使用函數__HAL_RCC_GPIOx_CLK_ENABLE()使能定時器使用到的引腳時鍾。
  •   使用函數HAL_GPIO_Init()配置GPIO的復用功能。
  •   如果使能了定時器中斷,調用函數HAL_NVIC_SetPriority和HAL_NVIC_EnableIRQ配置。
  •   如果使能了DMA,還需要做DMA的配置。
  •   定時器默認使用APB時鍾,如果使用外部時鍾,調用函數HAL_TIM_ConfigClockSource可以配置。

  第4步:啟動定時器外設

  •   定時器基礎功能:

HAL_TIM_Base_Start()

HAL_TIM_Base_Start_DMA()

HAL_TIM_Base_Start_IT()

  •   輸入捕獲 :

HAL_TIM_IC_Start()

HAL_TIM_IC_Start_DMA()

HAL_TIM_IC_Start_IT()

  •   輸出比較 :

HAL_TIM_OC_Start()

HAL_TIM_OC_Start_DMA()

HAL_TIM_OC_Start_IT()

  •   PWM輸出:

HAL_TIM_PWM_Start()

HAL_TIM_PWM_Start_DMA()

HAL_TIM_PWM_Start_IT()

  •   單脈沖模式:

HAL_TIM_OnePulse_Start()

HAL_TIM_OnePulse_Start_IT().

  •   編碼器模式:

HAL_TIM_Encoder_Start()

HAL_TIM_Encoder_Start_DMA()

HAL_TIM_Encoder_Start_IT().

  第5步:定時器的DMA突發使用下面兩個函數

  •   HAL_TIM_DMABurst_WriteStart()
  •   HAL_TIM_DMABurst_ReadStart()

 

定時器常用的功能,通過上面這幾步即可實現。

25.4 源文件stm32f4xx_hal_tim.c

此文件涉及到的函數非常多,這里把幾個常用的函數做個說明:

  •   HAL_TIM_Base_Init
  •   HAL_TIM_Base_Start
  •   HAL_TIM_PWM_Init
  •   HAL_TIM_PWM_ConfigChannel
  •   HAL_TIM_PWM_Start
  •   HAL_TIM_IC_Init
  •   HAL_TIM_IC_ConfigChannel
  •   HAL_TIM_IC_Start_IT
  •   HAL_TIM_OC_Init
  •   HAL_TIM_OC_ConfigChannel
  •   HAL_TIM_OC_Start

25.4.1 函數HAL_TIM_Base_Init

函數原型:

HAL_StatusTypeDef HAL_TIM_Base_Init(TIM_HandleTypeDef *htim)
{
  /* 檢測是否是有效句柄 */
  if (htim == NULL)
  {
    return HAL_ERROR;
  }

  /* 檢測參數 */
  assert_param(IS_TIM_INSTANCE(htim->Instance));
  assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
  assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
  assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload));

  if (htim->State == HAL_TIM_STATE_RESET)
  {
    /* 默認取消鎖 */
    htim->Lock = HAL_UNLOCKED;

    /* 使用注冊回調,這樣每個定時器可以有獨立的回調,無需多個定時器共用 */
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
    /* 復位中斷回調為兼容的弱回調 */
    TIM_ResetCallback(htim);

    if (htim->Base_MspInitCallback == NULL)
    {
      htim->Base_MspInitCallback = HAL_TIM_Base_MspInit;
    }
    /* 初始化底層硬件 : GPIO, CLOCK, NVIC */
    htim->Base_MspInitCallback(htim);
#else
    /* 初始化底層硬件 : GPIO, CLOCK, NVIC */
    HAL_TIM_Base_MspInit(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
  }

  /* 設置定時器狀態忙 */
  htim->State = HAL_TIM_STATE_BUSY;

  /* 定時器基礎配置 */
  TIM_Base_SetConfig(htim->Instance, &htim->Init);

  /* 設置定時器就緒 */
  htim->State = HAL_TIM_STATE_READY;

  return HAL_OK;
}

函數描述:

此函數用於初始化定時器基礎配置。

函數參數:

  •   第1個參數是TIM_HandleTypeDef類型結構體指針變量,用於配置要初始化的參數。
  •   返回值,返回HAL_ERROR表示配置失敗,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示時間溢出。

注意事項:

  1. 從中心對齊計數器模式切換到遞增/遞減計數器模式需要重置計時器以避免意外的計數方向,因為在中心對齊模式下DIR位為只讀。解決辦法,在HAL_TIM_Base_Init之前調用HAL_TIM_Base_DeInit。
  2. 函數HAL_TIM_Base_MspInit用於初始化定時器的底層時鍾、引腳等功能。需要用戶自己在此函數里面實現具體的功能,由於這個函數是弱定義的,允許用戶在工程其它源文件里面重新實現此函數。當然,不限制一定要在此函數里面實現,也可以像早期的標准庫那樣,用戶自己初始化即可,更靈活些。
  3. 如果形參htim的結構體成員State沒有做初始狀態,這個地方就是個坑。特別是用戶搞了一個局部變量TIM_HandleTypeDef TimHandle。

對於局部變量來說,這個參數就是一個隨機值,如果是全局變量還好,一般MDK和IAR都會將全部變量初始化為0,而恰好這個 HAL_TIM_STATE_RESET  = 0x00U。

解決辦法有三:

方法1:用戶自己初始定時器和涉及到的GPIO等。

方法2:定義TIM_HandleTypeDef TimHandle為全局變量。

方法3:下面的方法

if(HAL_TIM_Base_DeInit(&TimHandle) != HAL_OK)
{
    Error_Handler();
}  
if(HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
{
    Error_Handler();
}

使用舉例:

TIM_HandleTypeDef   TimHandle = {0};

/* 
    定時器中斷更新周期 = TIMxCLK / usPrescaler + 1)/usPeriod + 1)
*/
TimHandle.Instance = TIMx;
TimHandle.Init.Prescaler         = usPrescaler;
TimHandle.Init.Period            = usPeriod;    
TimHandle.Init.ClockDivision     = 0;
TimHandle.Init.CounterMode       = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

25.4.2 函數HAL_TIM_Base_Start

函數原型:

HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim)
{
  
  assert_param(IS_TIM_INSTANCE(htim->Instance));
  
  /* 設置定時器狀態 */
  htim->State= HAL_TIM_STATE_BUSY;
  
  /* 使能定時器 */
  __HAL_TIM_ENABLE(htim);
  
  /* 設置定時器狀態 */
  htim->State= HAL_TIM_STATE_READY;
  
  /* 返回HAL_OK */
  return HAL_OK;
uint32_t tmpsmcr;

/* 檢測參數狀態 */
  assert_param(IS_TIM_INSTANCE(htim->Instance));

  /* 設置定時器狀態忙 */
  htim->State = HAL_TIM_STATE_BUSY;

  /* 使能外設,除了觸發模式,這個模式會自動使能 */
  tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
  if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
  {
    __HAL_TIM_ENABLE(htim);
  }

  /* 設置定時器就緒 */
  htim->State = HAL_TIM_STATE_READY;

  /* 返回函數狀態 */
  return HAL_OK;
}

函數描述:

此函數比較簡單,調用函數HAL_TIM_Base_Init配置了基礎功能后,啟動定時器。

函數參數:

  •   第1個參數是TIM_HandleTypeDef類型結構體指針變量。
  •   返回值,固定返回HAL_OK,表示初始化成功。

使用舉例:

TIM_HandleTypeDef   TimHandle = {0};

/* 
    定時器中斷更新周期 = TIMxCLK / usPrescaler + 1)/usPeriod + 1)
*/
TimHandle.Instance = TIMx;
TimHandle.Init.Prescaler         = usPrescaler;
TimHandle.Init.Period            = usPeriod;    
TimHandle.Init.ClockDivision     = 0;
TimHandle.Init.CounterMode       = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

/* 啟動定時器 */
if (HAL_TIM_Base_Start(&TimHandle) != HAL_OK)
{
Error_Handler(__FILE__, __LINE__);
}

25.4.3 函數HAL_TIM_PWM_Init

函數原型:

HAL_StatusTypeDef HAL_TIM_PWM_Init(TIM_HandleTypeDef *htim)
{
  /* 檢查句柄是否有效 */
  if (htim == NULL)
  {
    return HAL_ERROR;
  }

  /* 檢測參數 */
  assert_param(IS_TIM_INSTANCE(htim->Instance));
  assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
  assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
  assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload));

  if (htim->State == HAL_TIM_STATE_RESET)
  {
    /* 默認取消鎖 */
    htim->Lock = HAL_UNLOCKED;
/* 使用注冊回調,這樣每個定時器可以有獨立的回調,無需多個定時器共用 */
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
    /* 復位中斷回調為兼容的弱回調 */
    TIM_ResetCallback(htim);

    if (htim->PWM_MspInitCallback == NULL)
    {
      htim->PWM_MspInitCallback = HAL_TIM_PWM_MspInit;
    }
    /* 初始化底層硬件 : GPIO, CLOCK, NVIC */
    htim->PWM_MspInitCallback(htim);
#else
    /* 初始化底層硬件 : GPIO, CLOCK, NVIC */
    HAL_TIM_PWM_MspInit(htim);
#endif 
  }

  /* 設置定時器狀態 */
  htim->State = HAL_TIM_STATE_BUSY;

  /* Init the base time for the PWM */
  TIM_Base_SetConfig(htim->Instance, &htim->Init);

  /* Initialize the TIM state*/
  htim->State = HAL_TIM_STATE_READY;

  return HAL_OK;
}

函數描述:

此函數用於初始化定時為PWM方式。

函數參數:

  •   第1個參數是TIM_HandleTypeDef類型結構體指針變量,用於配置要初始化的參數。
  •   返回值,返回HAL_ERROR表示配置失敗,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示時間溢出。

注意事項:

  1. 函數HAL_TIM_PWM_MspInit用於初始化定時器的底層時鍾、引腳等功能。需要用戶自己在此函數里面實現具體的功能,由於這個函數是弱定義的,允許用戶在工程其它源文件里面重新實現此函數。當然,不限制一定要在此函數里面實現,也可以像早期的標准庫那樣,用戶自己初始化即可,更靈活些。
  2. 如果形參htim的結構體成員State沒有做初始狀態,這個地方就是個坑。特別是用戶搞了一個局部變量TIM_HandleTypeDef TimHandle。

對於局部變量來說,這個參數就是一個隨機值,如果是全局變量還好,一般MDK和IAR都會將全部變量初始化為0,而恰好這個 HAL_TIM_STATE_RESET  = 0x00U。

解決辦法有三:

方法1:用戶自己初始定時器和涉及到的GPIO等。

方法2:定義TIM_HandleTypeDef TimHandle為全局變量。

方法3:下面的方法

if(HAL_TIM_PWM_DeInit(&TimHandle) != HAL_OK)
{
    Error_Handler();
}  
if(HAL_TIM_PWM_Init(&TimHandle) != HAL_OK)
{
    Error_Handler();
}

使用舉例:

TIM_HandleTypeDef  TimHandle = {0};

/*  PWM頻率 = TIMxCLK / usPrescaler + 1)/usPeriod + 1)*/
TimHandle.Instance = TIM1;
TimHandle.Init.Prescaler         = usPrescaler;
TimHandle.Init.Period            = usPeriod;
TimHandle.Init.ClockDivision     = 0;
TimHandle.Init.CounterMode       = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = 0;
if (HAL_TIM_PWM_Init(&TimHandle) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

25.4.4 函數HAL_TIM_PWM_ConfigChannel

函數原型:

HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim,
                                            TIM_OC_InitTypeDef *sConfig,
                                            uint32_t Channel)
{
  /* 檢測參數 */
  assert_param(IS_TIM_CHANNELS(Channel));
  assert_param(IS_TIM_PWM_MODE(sConfig->OCMode));
  assert_param(IS_TIM_OC_POLARITY(sConfig->OCPolarity));
  assert_param(IS_TIM_FAST_STATE(sConfig->OCFastMode));

  /* 上鎖 */
  __HAL_LOCK(htim);

  htim->State = HAL_TIM_STATE_BUSY;

  switch (Channel)
  {
    case TIM_CHANNEL_1:
    {
      /* 檢測參數 */
      assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));

      /* 配置通道1為PWM模式 */
      TIM_OC1_SetConfig(htim->Instance, sConfig);

      /* 使能重載bit */
      htim->Instance->CCMR1 |= TIM_CCMR1_OC1PE;

      /* 配置快速輸出模式 */
      htim->Instance->CCMR1 &= ~TIM_CCMR1_OC1FE;
      htim->Instance->CCMR1 |= sConfig->OCFastMode;
      break;
    }

    case TIM_CHANNEL_2:
    {
  
    }

    case TIM_CHANNEL_3:
    {
   
    }

    case TIM_CHANNEL_4:
    {
    }

    default:
      break;
  }

  htim->State = HAL_TIM_STATE_READY;

  __HAL_UNLOCK(htim);

  return HAL_OK;
}

函數描述:

此函數用於配置定時器的PWM通道。

函數參數:

  •   第1個參數是TIM_HandleTypeDef類型結構體指針變量,用於定時器基本參數配置。
  •   第2個參數是TIM_OC_InitTypeDef類型結構體指定變量,用於定時器輸出比較參數配置。
  •   第3個參數是通道設置,支持以下參數:

TIM_CHANNEL_1

TIM_CHANNEL_2

TIM_CHANNEL_3

TIM_CHANNEL_4

  •   返回值,返回HAL_ERROR表示配置失敗,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示時間溢出。

使用舉例:

TIM_HandleTypeDef  TimHandle = {0};

/*  PWM頻率 = TIMxCLK / usPrescaler + 1)/usPeriod + 1)*/
TimHandle.Instance = TIM1;
TimHandle.Init.Prescaler         = usPrescaler;
TimHandle.Init.Period            = usPeriod;
TimHandle.Init.ClockDivision     = 0;
TimHandle.Init.CounterMode       = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = 0;
if (HAL_TIM_PWM_Init(&TimHandle) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

/* 配置定時器PWM輸出通道 */
sConfig.OCMode       = TIM_OCMODE_PWM1;
sConfig.OCPolarity   = TIM_OCPOLARITY_HIGH;
sConfig.OCFastMode   = TIM_OCFAST_DISABLE;
sConfig.OCNPolarity  = TIM_OCNPOLARITY_HIGH;
sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
sConfig.OCIdleState  = TIM_OCIDLESTATE_RESET;

/* 占空比 */
sConfig.Pulse = pulse;
if (HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TimChannel[_ucChannel]) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

25.4.5 函數HAL_TIM_PWM_Start

函數原型:

HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
{
  uint32_t tmpsmcr;

  /* 檢測參數 */
  assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));

  /* 使能捕獲比較通道 */
  TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);

  if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
  {
    /* 使能主輸出 */
    __HAL_TIM_MOE_ENABLE(htim);
  }

  /* 觸發模式會自動使能 */
  tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
  if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
  {
    __HAL_TIM_ENABLE(htim);
  }

  /* 返回OK */
  return HAL_OK;
}

函數描述:

此函數用於啟動PWM。

函數參數:

  •   第1個參數是TIM_HandleTypeDef類型結構體指針變量,用於配置要初始化的參數。
  •   第2個參數是通道設置,支持以下參數:

TIM_CHANNEL_1

TIM_CHANNEL_2

TIM_CHANNEL_3

TIM_CHANNEL_4

  •   返回值,返回HAL_ERROR表示配置失敗,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示時間溢出。

使用舉例:

TIM_HandleTypeDef  TimHandle = {0};

/*  PWM頻率 = TIMxCLK / usPrescaler + 1)/usPeriod + 1)*/
TimHandle.Instance = TIM1;
TimHandle.Init.Prescaler         = usPrescaler;
TimHandle.Init.Period            = usPeriod;
TimHandle.Init.ClockDivision     = 0;
TimHandle.Init.CounterMode       = TIM_COUNTERMODE_UP;
TimHandle.Init.RepetitionCounter = 0;
TimHandle.Init.AutoReloadPreload = 0;
if (HAL_TIM_PWM_Init(&TimHandle) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

/* 配置定時器PWM輸出通道 */
sConfig.OCMode       = TIM_OCMODE_PWM1;
sConfig.OCPolarity   = TIM_OCPOLARITY_HIGH;
sConfig.OCFastMode   = TIM_OCFAST_DISABLE;
sConfig.OCNPolarity  = TIM_OCNPOLARITY_HIGH;
sConfig.OCNIdleState = TIM_OCNIDLESTATE_RESET;
sConfig.OCIdleState  = TIM_OCIDLESTATE_RESET;

/* 占空比 */
sConfig.Pulse = pulse;
if (HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfig, TimChannel[_ucChannel]) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

/* 啟動PWM輸出 */
if (HAL_TIM_PWM_Start(&TimHandle, TimChannel[_ucChannel]) != HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);
}

25.4.6 函數HAL_TIM_IC_Init

函數原型:

HAL_StatusTypeDef HAL_TIM_IC_Init(TIM_HandleTypeDef *htim)
{
  /* 檢測句柄是否有效 */
  if (htim == NULL)
  {
    return HAL_ERROR;
  }

  /* 檢測參數 */
  assert_param(IS_TIM_INSTANCE(htim->Instance));
  assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
  assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
  assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload));

  if (htim->State == HAL_TIM_STATE_RESET)
  {
    /* 默認取消鎖 */
    htim->Lock = HAL_UNLOCKED;

/* 使用注冊回調,這樣每個定時器可以有獨立的回調,無需多個定時器共用 */
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
    /* 復位中斷回調為兼容的弱回調 */
    TIM_ResetCallback(htim);

    if (htim->IC_MspInitCallback == NULL)
    {
      htim->IC_MspInitCallback = HAL_TIM_IC_MspInit;
    }
/* 初始化底層硬件 : GPIO, CLOCK, NVIC */
    htim->IC_MspInitCallback(htim);
#else
    /* 初始化底層硬件 : GPIO, CLOCK, NVIC */
    HAL_TIM_IC_MspInit(htim);
#endif 
  }

  /* 設置定時忙Set the TIM state */
  htim->State = HAL_TIM_STATE_BUSY;

  /* 設置定時器輸入捕獲 */
  TIM_Base_SetConfig(htim->Instance, &htim->Init);

  /* 設置定時就緒 */
  htim->State = HAL_TIM_STATE_READY;

  return HAL_OK;
}

函數描述:

此函數用於定時器輸入捕獲初始化。

函數參數:

  •   第1個參數是TIM_HandleTypeDef類型結構體指針變量,用於配置要初始化的參數。
  •   返回值,返回HAL_ERROR表示配置失敗,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示時間溢出。

注意事項:

  1. 函數HAL_TIM_IC_MspInit用於初始化定時器的底層時鍾、引腳等功能。需要用戶自己在此函數里面實現具體的功能,由於這個函數是弱定義的,允許用戶在工程其它源文件里面重新實現此函數。當然,不限制一定要在此函數里面實現,也可以像早期的標准庫那樣,用戶自己初始化即可,更靈活些。
  2. 如果形參htim的結構體成員State沒有做初始狀態,這個地方就是個坑。特別是用戶搞了一個局部變量TIM_HandleTypeDef TimHandle。

對於局部變量來說,這個參數就是一個隨機值,如果是全局變量還好,一般MDK和IAR都會將全部變量初始化為0,而恰好這個HAL_TIM_STATE_RESET  = 0x00U。

解決辦法有三:

方法1:用戶自己初始定時器和涉及到的GPIO等。

方法2:定義TIM_HandleTypeDef TimHandle為全局變量。

方法3;下面的方法

if(HAL_TIM_IC_DeInit(&UartHandle) != HAL_OK)
{
    Error_Handler();
}  
if(HAL_TIM_IC_Init(&UartHandle) != HAL_OK)
{
    Error_Handler();
}

25.4.7 函數HAL_TIM_IC_ConfigChannel

函數原型:

HAL_StatusTypeDef HAL_TIM_IC_ConfigChannel(TIM_HandleTypeDef *htim, TIM_IC_InitTypeDef *sConfig, uint32_t Channel)
{
  /* 檢測參數Check the parameters */
  assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));
  assert_param(IS_TIM_IC_POLARITY(sConfig->ICPolarity));
  assert_param(IS_TIM_IC_SELECTION(sConfig->ICSelection));
  assert_param(IS_TIM_IC_PRESCALER(sConfig->ICPrescaler));
  assert_param(IS_TIM_IC_FILTER(sConfig->ICFilter));

  /* 上鎖 Process Locked */
  __HAL_LOCK(htim);

  htim->State = HAL_TIM_STATE_BUSY;

  if (Channel == TIM_CHANNEL_1)
  {
    /* TI1配置 */
    TIM_TI1_SetConfig(htim->Instance,
                      sConfig->ICPolarity,
                      sConfig->ICSelection,
                      sConfig->ICFilter);

    /* 復位IC1PSC*/
    htim->Instance->CCMR1 &= ~TIM_CCMR1_IC1PSC;

    /* 設置IC1PSC分頻值 */
    htim->Instance->CCMR1 |= sConfig->ICPrescaler;
  }
  else if (Channel == TIM_CHANNEL_2)
  {
    /* 省略 */
  }
  else if (Channel == TIM_CHANNEL_3)
  {
    /* 省略 */
  }
  else
  {
  

  }

  htim->State = HAL_TIM_STATE_READY;

  __HAL_UNLOCK(htim);

  return HAL_OK;
}

函數描述:

此函數用於配置定時器的輸入捕獲通道。

函數參數:

  •   第1個參數是TIM_HandleTypeDef類型結構體指針變量,用於定時器基本參數配置
  •   第2個參數是TIM_IC_InitTypeDef類型結構體指定變量,用於定時器輸出比較參數配置。
  •   第3個參數是通道設置,支持以下參數:

TIM_CHANNEL_1

TIM_CHANNEL_2

TIM_CHANNEL_3

TIM_CHANNEL_4

  •   返回值,返回HAL_ERROR表示配置失敗,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示時間溢出。

25.4.8 函數HAL_TIM_IC_Start_IT

函數原型:

HAL_StatusTypeDef HAL_TIM_IC_Start_IT (TIM_HandleTypeDef *htim, uint32_t Channel)
{
  /* 檢查參數 */
  assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));
  
  switch (Channel)
  {
    case TIM_CHANNEL_1:
    {       
      /* 使能CC1(Capture/Compare 1)中斷 */
      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);
    }
    break;
    
    case TIM_CHANNEL_2:
    {
   /* 省略 */
    }
    break;
    
    case TIM_CHANNEL_3:
    {
  /* 省略 */
    }
    break;
    
    case TIM_CHANNEL_4:
    {
  /* 省略 */
    }
    break;
    
    default:
    break;
  }  
  /* 使能輸入捕獲通道 */
  TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);
    
  /* 使能定時器 */
  __HAL_TIM_ENABLE(htim);  

  /* 返回狀態 */
  return HAL_OK;  
}
HAL_StatusTypeDef HAL_TIM_IC_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel)
{
  uint32_t tmpsmcr;

  /* 檢測參數 */
  assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));

  switch (Channel)
  {
    case TIM_CHANNEL_1:
    {
      /* 使能捕獲比較通道1 */
      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC1);
      break;
    }

    case TIM_CHANNEL_2:
    {
      /* 使能捕獲比較通道2 */
      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC2);
      break;
    }

    case TIM_CHANNEL_3:
    {
      /* 使能捕獲比較通道3 */
      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC3);
      break;
    }

    case TIM_CHANNEL_4:
    {
      /* 使能捕獲比較通道4 */
      __HAL_TIM_ENABLE_IT(htim, TIM_IT_CC4);
      break;
    }

    default:
      break;
  }
  /* 使能輸入捕獲通道 */
  TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);

  /* 觸發模式已經自動使能 */
  tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
  if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
  {
    __HAL_TIM_ENABLE(htim);
  }

  /* 返回函數狀態 */
  return HAL_OK;
}

函數描述:

此函數用於啟動定時器輸入捕獲模式,采用定時器方式。

函數參數:

  •   第1個參數是TIM_HandleTypeDef類型結構體指針變量,用於配置要初始化的參數。
  •   第2個參數是通道設置,支持以下參數:

TIM_CHANNEL_1

TIM_CHANNEL_2

TIM_CHANNEL_3

TIM_CHANNEL_4

  •   返回值,返回HAL_ERROR表示配置失敗,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示時間溢出。

25.4.9 函數HAL_TIM_OC_Init

函數原型:

HAL_StatusTypeDef HAL_TIM_OC_Init(TIM_HandleTypeDef *htim)
{
  /* 檢測句柄是否有效 */
  if (htim == NULL)
  {
    return HAL_ERROR;
  }

  /* 檢測參數 */
  assert_param(IS_TIM_INSTANCE(htim->Instance));
  assert_param(IS_TIM_COUNTER_MODE(htim->Init.CounterMode));
  assert_param(IS_TIM_CLOCKDIVISION_DIV(htim->Init.ClockDivision));
  assert_param(IS_TIM_AUTORELOAD_PRELOAD(htim->Init.AutoReloadPreload));

  if (htim->State == HAL_TIM_STATE_RESET)
  {
     /* 默認取消鎖 */
    htim->Lock = HAL_UNLOCKED;

/* 使用注冊回調,這樣每個定時器可以有獨立的回調,無需多個定時器共用 */
#if (USE_HAL_TIM_REGISTER_CALLBACKS == 1)
     /* 復位中斷回調為兼容的弱回調 *
    TIM_ResetCallback(htim);

    if (htim->OC_MspInitCallback == NULL)
    {
      htim->OC_MspInitCallback = HAL_TIM_OC_MspInit;
    }
     /* 初始化底層硬件 : GPIO, CLOCK, NVIC */
    htim->OC_MspInitCallback(htim);
#else
     /* 初始化底層硬件 : GPIO, CLOCK, NVIC */
    HAL_TIM_OC_MspInit(htim);
#endif /* USE_HAL_TIM_REGISTER_CALLBACKS */
  }

  /* 設置定時忙 */
  htim->State = HAL_TIM_STATE_BUSY;

  /* 設置輸出比較 */
  TIM_Base_SetConfig(htim->Instance,  &htim->Init);

  /* 設置定時器就緒 */
  htim->State = HAL_TIM_STATE_READY;

  return HAL_OK;
}

函數描述:

此函數用於定時器輸入捕獲初始化。

函數參數:

  •   第1個參數是TIM_HandleTypeDef類型結構體指針變量,用於配置要初始化的參數。
  •   返回值,返回HAL_ERROR表示配置失敗,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示時間溢出。

注意事項:

  1. 函數HAL_TIM_OC_MspInit用於初始化定時器的底層時鍾、引腳等功能。需要用戶自己在此函數里面實現具體的功能,由於這個函數是弱定義的,允許用戶在工程其它源文件里面重新實現此函數。當然,不限制一定要在此函數里面實現,也可以像早期的標准庫那樣,用戶自己初始化即可,更靈活些。
  2. 如果形參htim的結構體成員State沒有做初始狀態,這個地方就是個坑。特別是用戶搞了一個局部變量TIM_HandleTypeDef TimHandle。

對於局部變量來說,這個參數就是一個隨機值,如果是全局變量還好,一般MDK和IAR都會將全部變量初始化為0,而恰好這個HAL_TIM_STATE_RESET  = 0x00U。

解決辦法有三:

方法1:用戶自己初始定時器和涉及到的GPIO等。

方法2:定義TIM_HandleTypeDef TimHandle為全局變量。

方法3;下面的方法

if(HAL_TIM_OC_DeInit(&UartHandle) != HAL_OK)
{
    Error_Handler();
}  
if(HAL_TIM_OC_Init(&UartHandle) != HAL_OK)
{
    Error_Handler();
}

25.4.10   函數HAL_TIM_OC_ConfigChannel

函數原型:

HAL_StatusTypeDef HAL_TIM_OC_ConfigChannel(TIM_HandleTypeDef *htim,
                                           TIM_OC_InitTypeDef *sConfig,
                                           uint32_t Channel)
{
  /* 檢測參數 */
  assert_param(IS_TIM_CHANNELS(Channel));
  assert_param(IS_TIM_OC_MODE(sConfig->OCMode));
  assert_param(IS_TIM_OC_POLARITY(sConfig->OCPolarity));

  /* 上鎖 */
  __HAL_LOCK(htim);

  htim->State = HAL_TIM_STATE_BUSY;

  switch (Channel)
  {
    case TIM_CHANNEL_1:
    {
      /* 檢測參數 */
      assert_param(IS_TIM_CC1_INSTANCE(htim->Instance));

      /* 配置通道1的輸出比較 */
      TIM_OC1_SetConfig(htim->Instance, sConfig);
      break;
    }

    case TIM_CHANNEL_2:
    {
    
    }

    case TIM_CHANNEL_3:
    {
    
    }

    case TIM_CHANNEL_4:
    {
    
    }

    default:
      break;
  }

  htim->State = HAL_TIM_STATE_READY;

  __HAL_UNLOCK(htim);

  return HAL_OK;
}

函數描述:

此函數用於初始化串口的基礎特性和高級特性。

函數參數:

  •   第1個參數是TIM_HandleTypeDef類型結構體指針變量,用於定時器基本參數配置。
  •   第2個參數是TIM_OC_InitTypeDef類型結構體指定變量,用於定時器輸出比較參數配置。
  •   第3個參數是通道設置,支持以下參數:

TIM_CHANNEL_1

TIM_CHANNEL_2

TIM_CHANNEL_3

TIM_CHANNEL_4

TIM_CHANNEL_5

TIM_CHANNEL_6

  •   返回值,返回HAL_ERROR表示配置失敗,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示時間溢出。

25.4.11   函數HAL_TIM_OC_Start

函數原型:

HAL_StatusTypeDef HAL_TIM_OC_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
{
  uint32_t tmpsmcr;

  /* 檢測參數 Check the parameters */
  assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));

  /* 使能輸出比較通道 */
  TIM_CCxChannelCmd(htim->Instance, Channel, TIM_CCx_ENABLE);

  if (IS_TIM_BREAK_INSTANCE(htim->Instance) != RESET)
  {
    /* 使能主輸出 */
    __HAL_TIM_MOE_ENABLE(htim);
  }

  /* 觸摸模式會自動使能 */
  tmpsmcr = htim->Instance->SMCR & TIM_SMCR_SMS;
  if (!IS_TIM_SLAVEMODE_TRIGGER_ENABLED(tmpsmcr))
  {
    __HAL_TIM_ENABLE(htim);
  }

  /* 返回OK */
  return HAL_OK;
}

函數描述:

此函數用於啟動定時器輸出比較模式。

函數參數:

  •   第1個參數是TIM_HandleTypeDef類型結構體指針變量,用於配置要初始化的參數。
  •   第2個參數是通道設置,支持以下參數:

TIM_CHANNEL_1

TIM_CHANNEL_2

TIM_CHANNEL_3

TIM_CHANNEL_4

  •   返回值,返回HAL_ERROR表示配置失敗,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示時間溢出。

25.5 總結

本章節就為大家講解這么多,建議大家將定時器的驅動源碼結合參考手冊中的寄存器通讀一遍,對於我們后面章節的學習大有裨益。

 


免責聲明!

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



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