最新教程下載下傳: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 總結
- 學習定時器外設推薦從硬體框圖開始了解基本的功能特性,然後逐漸深入了解各種特性,這種方式友善記憶和以後查閱。
- 特别注意STM32F4的TIM1,8,15,16,17才有RCR重複計數器,其它都沒用的。
- STM32的單個定時器中不同通道可以配置不同頻率PWM。http://www.armbbs.cn/forum.php?mod=viewthread&tid=89008 。
- 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
注,不同定時支援的功能略有差別,基礎定時器功能較少,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=2
0010: fSAMPLING=fCK_INT, N=4
0011: fSAMPLING=fCK_INT, N=8
0100: fSAMPLING=fDTS/2, N=6
0101: fSAMPLING=fDTS/2, N=8
0110: fSAMPLING=fDTS/4, N=6
0111: 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()
定時器常用的功能,通過上面這幾步即可實作。
此檔案涉及到的函數非常多,這裡把幾個常用的函數做個說明:
- 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表示時間溢出。
注意事項:
- 從中心對齊計數器模式切換到遞增/遞減計數器模式需要重置計時器以避免意外的計數方向,因為在中心對齊模式下DIR位為隻讀。解決辦法,在HAL_TIM_Base_Init之前調用HAL_TIM_Base_DeInit。
- 函數HAL_TIM_Base_MspInit用于初始化定時器的底層時鐘、引腳等功能。需要使用者自己在此函數裡面實作具體的功能,由于這個函數是弱定義的,允許使用者在工程其它源檔案裡面重新實作此函數。當然,不限制一定要在此函數裡面實作,也可以像早期的标準庫那樣,使用者自己初始化即可,更靈活些。
- 如果形參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方式。
- 函數HAL_TIM_PWM_MspInit用于初始化定時器的底層時鐘、引腳等功能。需要使用者自己在此函數裡面實作具體的功能,由于這個函數是弱定義的,允許使用者在工程其它源檔案裡面重新實作此函數。當然,不限制一定要在此函數裡面實作,也可以像早期的标準庫那樣,使用者自己初始化即可,更靈活些。
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
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。
- 第2個參數是通道設定,支援以下參數:
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;
}
此函數用于定時器輸入捕獲初始化。
- 函數HAL_TIM_IC_MspInit用于初始化定時器的底層時鐘、引腳等功能。需要使用者自己在此函數裡面實作具體的功能,由于這個函數是弱定義的,允許使用者在工程其它源檔案裡面重新實作此函數。當然,不限制一定要在此函數裡面實作,也可以像早期的标準庫那樣,使用者自己初始化即可,更靈活些。
對于局部變量來說,這個參數就是一個随機值,如果是全局變量還好,一般MDK和IAR都會将全部變量初始化為0,而恰好這個HAL_TIM_STATE_RESET = 0x00U。
方法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類型結構體指定變量,用于定時器輸出比較參數配置。
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;
}
此函數用于啟動定時器輸入捕獲模式,采用定時器方式。
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;
}
- 函數HAL_TIM_OC_MspInit用于初始化定時器的底層時鐘、引腳等功能。需要使用者自己在此函數裡面實作具體的功能,由于這個函數是弱定義的,允許使用者在工程其它源檔案裡面重新實作此函數。當然,不限制一定要在此函數裡面實作,也可以像早期的标準庫那樣,使用者自己初始化即可,更靈活些。
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;
}
此函數用于初始化序列槽的基礎特性和進階特性。
TIM_CHANNEL_5
TIM_CHANNEL_6
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;
}
此函數用于啟動定時器輸出比較模式。
本章節就為大家講解這麼多,建議大家将定時器的驅動源碼結合參考手冊中的寄存器通讀一遍,對于我們後面章節的學習大有裨益。
微信公衆号:armfly_com
安富萊論壇:www.armbbs.cn
安富萊淘寶:https://armfly.taobao.com