【STM32H7教程】第32章 STM32H7的TIM定時器基礎知識和HAL庫API


完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第32章       STM32H7的TIM定時器基礎知識和HAL庫API

本章節為大家講解TIM1 – TIM17(STM32H7沒有TIM9,TIM10和TIM11)共計14個定時器的基礎知識和對應的HAL庫API。

32.1 初學者重要提示

32.2 定時器基礎知識

32.3 定時器的HAL庫用戶

32.4 源文件stm32h7xx_hal_tim.c

32.5 總結

 

 

32.1 初學者重要提示

  1.   學習定時器外設推薦從硬件框圖開始了解基本的功能特性,然后逐步深入了解各種特性,這種方式方便記憶和以后查閱。
  2.   STM32H7的定時器輸出100MHz方波是完全沒問題。http://www.armbbs.cn/forum.php?mod=viewthread&tid=86434
  3.   STM32H7定時器進出中斷的速度能跑到12.5MHz,所有程序在TCM和Flash運行沒差別。http://www.armbbs.cn/forum.php?mod=viewthread&tid=90836
  4.   STM32H7的定時器輸入捕獲可以實現12MHz方波的雙邊沿捕獲,單邊沿可以做到24MHz。http://www.armbbs.cn/forum.php?mod=viewthread&tid=91283
  5.   特別注意STM32H7的TIM1,8,15,16,17才有RCR重復計數器,其它都沒用的。
  6.   STM32H7的單個定時器中不同通道可以配置不同頻率PWM。http://www.armbbs.cn/forum.php?mod=viewthread&tid=89008
  7.   STM32H7的TIM1-TIM17中斷入口函數名使用時要注意,別搞錯了:
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          
TIM15_IRQHandler 
TIM16_IRQHandler 
TIM17_IRQHandler

 

32.2 定時器基礎知識

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

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

– 輸入捕獲。

– 輸出比較。

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

– 單脈沖模式輸出。

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

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

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

– 輸入捕獲

– 輸出比較

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

32.2.1 定時器TIM1-TIM17的區別

STM32H7支持的定時器有點多,要簡單的區分下。STM32H7支持TIM1-TIM8,TIM12-TIM17共14個定時器,而中間的TIM9,TIM10,TIM11是不存在的,這點要注意。

粗略的比較如下:

 

 

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

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

32.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的定時器觸發。

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

OC1到OC4有對應的輸出引腳,而OC5和OC6沒有對應的輸出引腳,主要用於內部控制。

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

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

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

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

32.2.3 定時器的時基單元

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

  •   預分頻器寄存器 (TIMx_PSC)

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

注:預分頻器有個緩沖功能,可以讓用戶實時更改,新的預分頻值將在下一個更新事件發生時被采用(以遞增計數模式為例,就是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有此寄存器。

 

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

32.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的波形效果:

 

32.2.5 定時器輸入捕獲

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

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

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

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

32.3 定時器的HAL庫用法

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

32.3.1 定時器寄存器結構體TIM_TypeDef

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

typedef struct
{
  __IO uint16_t CR1;         /*!< TIM control register 1,                   Address offset: 0x00 */
  uint16_t      RESERVED0;   /*!< Reserved, 0x02                                                 */
  __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 uint16_t PSC;         /*!< TIM prescaler,                            Address offset: 0x28 */
  uint16_t      RESERVED9;   /*!< Reserved, 0x2A                                                 */
  __IO uint32_t ARR;         /*!< TIM auto-reload register,                 Address offset: 0x2C */
  __IO uint16_t RCR;         /*!< TIM repetition counter register,          Address offset: 0x30 */
  uint16_t      RESERVED10;  /*!< Reserved, 0x32                                                 */
  __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 uint16_t DCR;         /*!< TIM DMA control register,                 Address offset: 0x48 */
  uint16_t      RESERVED12;  /*!< Reserved, 0x4A                                                 */
  __IO uint16_t DMAR;        /*!< TIM DMA address for full transfer,        Address offset: 0x4C */
  uint16_t      RESERVED13;  /*!< Reserved, 0x4E                                                 */
  uint16_t      RESERVED14;  /*!< Reserved, 0x50                                                 */
  __IO uint32_t CCMR3;       /*!< TIM capture/compare mode register 3,      Address offset: 0x54 */
  __IO uint32_t CCR5;        /*!< TIM capture/compare register5,            Address offset: 0x58 */
  __IO uint32_t CCR6;        /*!< TIM capture/compare register6,            Address offset: 0x5C */
  __IO uint32_t AF1;         /*!< TIM alternate function option register 1, Address offset: 0x60 */
  __IO uint32_t AF2;         /*!< TIM alternate function option register 2, Address offset: 0x64 */
  __IO uint32_t TISEL;       /*!< TIM Input Selection register,             Address offset: 0x68 */
} TIM_TypeDef;

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

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

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

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

#define PERIPH_BASE         ((uint32_t)0x40000000)
#define D2_APB1PERIPH_BASE   PERIPH_BASE
#define D2_APB2PERIPH_BASE   (PERIPH_BASE + 0x00010000)

/*!< D2_APB1PERIPH 外設 */
#define TIM2_BASE             (D2_APB1PERIPH_BASE + 0x0000) <----- 展開這個宏,(TIM_TypeDef *) 0x40000000
#define TIM3_BASE             (D2_APB1PERIPH_BASE + 0x0400)
#define TIM4_BASE             (D2_APB1PERIPH_BASE + 0x0800)
#define TIM5_BASE             (D2_APB1PERIPH_BASE + 0x0C00)
#define TIM6_BASE             (D2_APB1PERIPH_BASE + 0x1000)
#define TIM7_BASE             (D2_APB1PERIPH_BASE + 0x1400)
#define TIM12_BASE            (D2_APB1PERIPH_BASE + 0x1800)
#define TIM13_BASE            (D2_APB1PERIPH_BASE + 0x1C00)
#define TIM14_BASE            (D2_APB1PERIPH_BASE + 0x2000)

/*!< D2_APB1PERIPH 外設 */
#define TIM1_BASE             (D2_APB2PERIPH_BASE + 0x0000)
#define TIM8_BASE             (D2_APB2PERIPH_BASE + 0x0400)
#define TIM15_BASE            (D2_APB2PERIPH_BASE + 0x4000)
#define TIM16_BASE            (D2_APB2PERIPH_BASE + 0x4400)
#define TIM17_BASE            (D2_APB2PERIPH_BASE + 0x4800)

#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 TIM12               ((TIM_TypeDef *) TIM12_BASE)
#define TIM13               ((TIM_TypeDef *) TIM13_BASE)
#define TIM14               ((TIM_TypeDef *) TIM14_BASE)
#define TIM15               ((TIM_TypeDef *) TIM15_BASE)
#define TIM16               ((TIM_TypeDef *) TIM16_BASE)
#define TIM17               ((TIM_TypeDef *) TIM17_BASE)

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

32.3.2 定時器句柄結構體TIM_HandleTypeDef

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

typedef struct
{
  TIM_TypeDef              *Instance;     /*!< Register base address             */ 
  TIM_Base_InitTypeDef     Init;          /*!< TIM Time Base required parameters */
  HAL_TIM_ActiveChannel    Channel;       /*!< Active channel                    */ 

/*!< DMA Handlers array This array is accessed by a @ref DMA_Handle_index */
  DMA_HandleTypeDef        *hdma[7];      
  HAL_LockTypeDef          Lock;          /*!< Locking object                    */
  __IO HAL_TIM_StateTypeDef   State;      /*!< TIM operation state               */  
}TIM_HandleTypeDef;

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

  TIM_TypeDef  *Instance

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

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

  TIM_Base_InitTypeDef  Init

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

TIM_Base_InitTypeDef結構體的定義如下:

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                ((uint32_t)0x0000U)   /*!< Up counting mode */
#define TIM_COUNTERMODE_DOWN               TIM_CR1_DIR          /*!< Down counting mode */
#define TIM_COUNTERMODE_CENTERALIGNED1     TIM_CR1_CMS_0        /*!< Center-aligned counting mode 1 */
#define TIM_COUNTERMODE_CENTERALIGNED2     TIM_CR1_CMS_1        /*!< Center-aligned counting mode 2 */
#define TIM_COUNTERMODE_CENTERALIGNED3     TIM_CR1_CMS          /*!< Center-aligned counting mode 3 */
  •   成員Period

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

  •   成員ClockDivision

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

#define TIM_CLOCKDIVISION_DIV1       ((uint32_t)0x0000U)        /*!< Clock Division DIV1 */
#define TIM_CLOCKDIVISION_DIV2       (TIM_CR1_CKD_0)            /*!< Clock Division DIV2 */
#define TIM_CLOCKDIVISION_DIV4       (TIM_CR1_CKD_1)             /*!< Clock Division DIV4 */
  •   成員RepetitionCounter

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

  •   成員AutoReloadPreload

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

#define TIM_AUTORELOAD_PRELOAD_DISABLE   ((uint32_t)0x0000U)   /*!< 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都是6個通道。

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_5        = 0x10U,    /*!< The active channel is 5     */
  HAL_TIM_ACTIVE_CHANNEL_6        = 0x20U,    /*!< The active channel is 6     */
  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__);
}

 

32.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

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

/*!< TIM Output timing mode */
#define TIM_OCMODE_TIMING                ((uint32_t)0x0000U)       

/*!< TIM Output Active mode */                          
#define TIM_OCMODE_ACTIVE                ((uint32_t)TIM_CCMR1_OC1M_0) 

 /*!< TIM Output Inactive mode */                       
#define TIM_OCMODE_INACTIVE              ((uint32_t)TIM_CCMR1_OC1M_1)           
              
 /*!< TIM Output Toggle mode */               
#define TIM_OCMODE_TOGGLE                 ((uint32_t)TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0)         

 /*!< TIM PWM mode 1 */
#define TIM_OCMODE_PWM1                   ((uint32_t)TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1) 

/*!< TIM PWM mode 2 */                    
#define TIM_OCMODE_PWM2                    ((uint32_t)TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_0)   

 /*!< TIM Forced Active mode */   
#define TIM_OCMODE_FORCED_ACTIVE           ((uint32_t)TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1M_0)         

 /*!< TIM Forced Inactive mode */             
#define TIM_OCMODE_FORCED_INACTIVE         ((uint32_t)TIM_CCMR1_OC1M_2)                                        

/*!< TIM Rettrigerrable OPM mode 1 */  
#define TIM_OCMODE_RETRIGERRABLE_OPM1      ((uint32_t)TIM_CCMR1_OC1M_3)   

/*!< TIM Rettrigerrable OPM mode 2 */                                        
#define TIM_OCMODE_RETRIGERRABLE_OPM2      ((uint32_t)TIM_CCMR1_OC1M_3 | TIM_CCMR1_OC1M_0)    

/*!< TIM Combined PWM mode 1 */                     
#define TIM_OCMODE_COMBINED_PWM1           ((uint32_t)TIM_CCMR1_OC1M_3 | TIM_CCMR1_OC1M_2)     

/*!< TIM Combined PWM mode 2 */                  
#define TIM_OCMODE_COMBINED_PWM2           ((uint32_t)TIM_CCMR1_OC1M_3 | TIM_CCMR1_OC1M_0 | TIM_CCMR1_OC1M_2)   

/*!< TIM Asymetruc PWM mode 1 */  
#define TIM_OCMODE_ASSYMETRIC_PWM1         ((uint32_t)TIM_CCMR1_OC1M_3 | TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2)  

 /*!< TIM Asymetruc PWM mode 2 */    
#define TIM_OCMODE_ASSYMETRIC_PWM2         ((uint32_t)TIM_CCMR1_OC1M_3 | TIM_CCMR1_OC1M)  

 

  •   Pulse

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

  •   OCPolarity

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

#define TIM_OCPOLARITY_HIGH                ((uint32_t)0x0000U)
#define TIM_OCPOLARITY_LOW                 (TIM_CCER_CC1P)

 

  •   OCNPolarity

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

#define TIM_OCNPOLARITY_HIGH               ((uint32_t)0x0000U)
#define TIM_OCNPOLARITY_LOW                (TIM_CCER_CC1NP)

 

  •   OCFastMode

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

#define TIM_OCFAST_DISABLE                ((uint32_t)0x0000U)
#define TIM_OCFAST_ENABLE                 (TIM_CCMR1_OC1FE)

 

  •   OCIdleState

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

#define TIM_OCIDLESTATE_SET                (TIM_CR2_OIS1)
#define TIM_OCIDLESTATE_RESET              ((uint32_t)0x0000U)

 

  •   OCNIdleState

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

#define TIM_OCNIDLESTATE_SET               (TIM_CR2_OIS1N)
#define TIM_OCNIDLESTATE_RESET             ((uint32_t)0x0000U)

 

32.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       ((uint32_t)0x0000U)                 
#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

 

32.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。

 

關於這個底層配置有以下幾點要着重說明下:

  •   定時器所使用引腳的復用模式選擇已經被HAL庫定義好,放在了stm32h7xx_hal_gpio_ex.h文件里面。比如TIM1有一個復用,
#define GPIO_AF1_TIM1      ((uint8_t)0x01)  /* TIM1 Alternate Function mapping */

但是卻有4個輸出通道,每個通道都有幾個支持的輸出引腳:

TIM1_CH1,  PA8   PE9    PK1
TIM1_CH2,  PA9   PE11
TIM1_CH3,  PA10  PE13   PJ9
TIM1_CH4,  PA11  PE14   PJ11

具體使用哪個,配置對應引腳的復用即可:

 

32.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操作即可。

32.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()

 

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

32.4 源文件stm32h7xx_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

32.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));
  
  if(htim->State == HAL_TIM_STATE_RESET)
  { 
    /* 默認取消鎖 */
    htim->Lock = HAL_UNLOCKED;
    /* 初始化底層硬件 : GPIO, CLOCK, NVIC */
    HAL_TIM_Base_MspInit(htim);
  }
  
  /* 設置TIM狀態 */
  htim->State= HAL_TIM_STATE_BUSY;
  
  /* 基本參數配置 */
  TIM_Base_SetConfig(htim->Instance, &htim->Init); 
  
  /* 設置TIM就緒 */
  htim->State= HAL_TIM_STATE_READY;
  
  return HAL_OK;
}

 

函數描述:

此函數用於初始化定時器用於PWM。

函數參數:

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

注意事項:

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

 

32.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;
}

 

函數描述:

此函數比較簡單,調用函數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__);
}

 

32.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));

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

    /* 初始底層 : GPIO, CLOCK, NVIC 和 DMA */
    HAL_TIM_PWM_MspInit(htim);
  }

  /* 設置定時狀態 */
  htim->State= HAL_TIM_STATE_BUSY;
 
  /* 配置定時器用於PWM */  
  TIM_Base_SetConfig(htim->Instance, &htim->Init); 
   
  /* 設置定時器狀態 */
  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的結構體成員gState沒有做初始狀態,這個地方就是個坑。特別是用戶搞了一個局部變量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__);
}

 

32.4.4 函數HAL_TIM_PWM_ConfigChannel

函數原型:

HAL_StatusTypeDef HAL_TIM_PWM_ConfigChannel(TIM_HandleTypeDef *htim,
                                            TIM_OC_InitTypeDef* sConfig,
                                            uint32_t Channel)
{
   /* 省略 */

  /* 開鎖 */
  __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);

      /*預裝載使能,更新事件產生時寫入有效 */
      htim->Instance->CCMR1 |= TIM_CCMR1_OC1PE;

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

    case TIM_CHANNEL_2:
    {
       /* 省略 */
    }
    break;

    case TIM_CHANNEL_3:
    {
       /* 省略 */
    }
    break;

    case TIM_CHANNEL_4:
    {
      /* 省略 */
    }
    break;

    case TIM_CHANNEL_5:
    {
       /* 省略 */
    }
    break;

    case TIM_CHANNEL_6:
    {
        /* 省略 */
    }
    break;

    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_CHANNEL_5

TIM_CHANNEL_6

  •   返回值,返回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__);
}

 

32.4.5 函數HAL_TIM_PWM_Start

函數原型:

HAL_StatusTypeDef HAL_TIM_PWM_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
{
  /* 檢測參數 */
  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);
  }
    
  /* 使能定時器 */
  __HAL_TIM_ENABLE(htim);
  
  /* 返回狀態*/
  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__);
}

 

32.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)); 

  if(htim->State == HAL_TIM_STATE_RESET)
  {
/* 默認取消鎖 */
    htim->Lock = HAL_UNLOCKED;
    
     /* 初始底層 : GPIO, CLOCK, NVIC 和 DMA */
    HAL_TIM_IC_MspInit(htim);
  }
  
  /* 設置定時器狀態 */
  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的結構體成員gState沒有做初始狀態,這個地方就是個坑。特別是用戶搞了一個局部變量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();
}

 

32.4.7 函數HAL_TIM_IC_ConfigChannel

函數原型:

HAL_StatusTypeDef HAL_TIM_IC_ConfigChannel(TIM_HandleTypeDef *htim, TIM_IC_InitTypeDef* sConfig, uint32_t Channel)
{
  /* 檢查參數 */
  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));

  /* 開鎖 */  
  __HAL_LOCK(htim);
  
  htim->State = HAL_TIM_STATE_BUSY;
  
  if (Channel == TIM_CHANNEL_1)
  {
    /* 配置輸入通道1 */
    TIM_TI1_SetConfig(htim->Instance,
               sConfig->ICPolarity,
               sConfig->ICSelection,
               sConfig->ICFilter);
               
    /* 清零IC1PSC位 */
    htim->Instance->CCMR1 &= ~TIM_CCMR1_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表示時間溢出。

32.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;  
}

 

函數描述:

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

函數參數:

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

TIM_CHANNEL_1

TIM_CHANNEL_2

TIM_CHANNEL_3

TIM_CHANNEL_4

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

32.4.9 函數HAL_TIM_OC_Init

函數原型:

HAL_StatusTypeDef HAL_TIM_OC_Init(TIM_HandleTypeDef* htim)
{
  /* 檢測形參是否有效 Check */
  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));
 
  if(htim->State == HAL_TIM_STATE_RESET)
  {
/* 默認取消鎖 */
    htim->Lock = HAL_UNLOCKED;
    
/* 初始底層 : GPIO, CLOCK, NVIC 和 DMA */
    HAL_TIM_OC_MspInit(htim);
  }
  
  /* 設置定時器狀態Set the TIM state */
  htim->State= HAL_TIM_STATE_BUSY;

  /* 配置定時器為輸出比較模式Init the base time for the Output Compare */  
  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的結構體成員gState沒有做初始狀態,這個地方就是個坑。特別是用戶搞了一個局部變量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();
}

 

32.4.10   函數HAL_TIM_OC_ConfigChannel

函數原型:

HAL_StatusTypeDef HAL_TIM_OC_ConfigChannel(TIM_HandleTypeDef *htim,
                                           TIM_OC_InitTypeDef* sConfig,
                                           uint32_t Channel)
{  
  /* Check the parameters */
  assert_param(IS_TIM_CHANNELS(Channel)); 
  assert_param(IS_TIM_OC_MODE(sConfig->OCMode));
  assert_param(IS_TIM_OC_POLARITY(sConfig->OCPolarity));
  
  /* Process Locked */
  __HAL_LOCK(htim); 
  
  htim->State = HAL_TIM_STATE_BUSY;
  
  switch (Channel)
  {
    case TIM_CHANNEL_1:
    {
      /* 檢測參數Check the parameters */
      assert_param(IS_TIM_CC1_INSTANCE(htim->Instance)); 
      
     /* 配置定時器輸出比較通道1 */
      TIM_OC1_SetConfig(htim->Instance, sConfig);
    }
    break;
    
    case TIM_CHANNEL_2:
    {
      /* 省略 */
    }
    break;
    
    case TIM_CHANNEL_3:
    {
      /* 省略 */
    }
    break;
    
    case TIM_CHANNEL_4:
    {
      /* 省略 */
    }
    break;
    
    case TIM_CHANNEL_5:
    {
      /* 省略 */
    }
    break;
    
    case TIM_CHANNEL_6:
    {
      /* 省略 */
    }
    break;
        
    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表示時間溢出。

32.4.11   函數HAL_TIM_OC_Start

函數原型:

HAL_StatusTypeDef HAL_TIM_OC_Start(TIM_HandleTypeDef *htim, uint32_t Channel)
{
  /* 檢查參數 */
  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);
  }
  
  /* 使能定時器 */
  __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表示時間溢出。

32.5 總結

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

 


免責聲明!

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



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