最新教程下載: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 初學者重要提示
- 學習定時器外設推薦從硬件框圖開始了解基本的功能特性,然后逐步深入了解各種特性,這種方式方便記憶和以后查閱。
- 特別注意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
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=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()
定時器常用的功能,通過上面這幾步即可實現。
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表示時間溢出。
注意事項:
- 從中心對齊計數器模式切換到遞增/遞減計數器模式需要重置計時器以避免意外的計數方向,因為在中心對齊模式下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方式。
函數參數:
- 第1個參數是TIM_HandleTypeDef類型結構體指針變量,用於配置要初始化的參數。
- 返回值,返回HAL_ERROR表示配置失敗,HAL_OK表示配置成功,HAL_BUSY表示忙(操作中),HAL_TIMEOUT表示時間溢出。
注意事項:
- 函數HAL_TIM_PWM_MspInit用於初始化定時器的底層時鍾、引腳等功能。需要用戶自己在此函數里面實現具體的功能,由於這個函數是弱定義的,允許用戶在工程其它源文件里面重新實現此函數。當然,不限制一定要在此函數里面實現,也可以像早期的標准庫那樣,用戶自己初始化即可,更靈活些。
- 如果形參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表示時間溢出。
注意事項:
- 函數HAL_TIM_IC_MspInit用於初始化定時器的底層時鍾、引腳等功能。需要用戶自己在此函數里面實現具體的功能,由於這個函數是弱定義的,允許用戶在工程其它源文件里面重新實現此函數。當然,不限制一定要在此函數里面實現,也可以像早期的標准庫那樣,用戶自己初始化即可,更靈活些。
- 如果形參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表示時間溢出。
注意事項:
- 函數HAL_TIM_OC_MspInit用於初始化定時器的底層時鍾、引腳等功能。需要用戶自己在此函數里面實現具體的功能,由於這個函數是弱定義的,允許用戶在工程其它源文件里面重新實現此函數。當然,不限制一定要在此函數里面實現,也可以像早期的標准庫那樣,用戶自己初始化即可,更靈活些。
- 如果形參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 總結
本章節就為大家講解這么多,建議大家將定時器的驅動源碼結合參考手冊中的寄存器通讀一遍,對於我們后面章節的學習大有裨益。