往期文章推薦:
STM32CUBEMX 使用教程5 — DMA配置 & 串口結合DMA實現數據搬運
STM32CUBEMX 使用教程4 — 串口 (USART) 配置、重定向 printf 輸出
STM32CUBEMX 使用教程3 — 外部中斷(EXTI)的使用
STM32CUBEMX 使用教程2 — GPIO的使用、輸入/輸出
STM32CUBEMX 使用教程 1 — 配置環境、新建工程
STM32CUBEMX 詳細安裝教程
一、了解什么是定時器
什么是定時器?定時器實現的基本原理是什么?定時器可以做什么?
為了能搞懂上面的問題,文章直接用STM32F103ZET6作講解說明定時器的原理和基本用途。
STM32F103的定時器屬于MCU內部的硬件資源,數量有很多,分類上大致分為:
1)基本定時器
2)通用定時器
3)高級定時器
這里用基本定時器說明工作原理,如下的基本定時器的框圖:
首先需要明確的事情:定時器的本質就是一個計數器,通過在一定的計數頻率下計量一定的裝載值實現計數定時的功能。
如上圖中:基本定時器中有一個 CNT計數器(1)用來計數,這個計數器的計數值來自于重裝載寄存器。基本定時的時鐘(2)來源于RCC的時鐘(實際為APB2),通過控制器(4)復位、使能定時器功能,并通過PSC預分頻器(3)設置時鐘的分頻提供給CNT計數器。從而實現計數器在某個頻率下計數,達到裝載值寄存器中的設定數時完成計數,實現時間定時。
二、STM32F103 的定時器其他特性
(1)計數模式
STM32F103中的定時器除了基本定時器(僅有向上計數),其他的定時器都有向上計數、向下計數、中央對齊模式(向上/向下計數)。
向上計數模式:計數器從0計數到自動加載值(TIMx_ARR計數器的內容),然后重新從0開始計數并且產生一個計數器溢出事件。
向下模式:計數器從自動裝入的值(TIMx_ARR計數器的值)開始向下計數到0,然后從自動裝入的值重新開始并且產生一個計數器向下溢出事件。
中央對齊模式:計數器從0開始計數到自動加載的值(TIMx_ARR寄存器)?1,產生一個計數器溢出事件,然后向下計數到1并且產生一個計數器下溢事件;然后再從0開始重新計數。
(2)更新中斷
所有的定時器都可以產生更新中斷事件,在定時器計數到裝載值時溢出,會產生更新事件 UEV ,如果設置了中斷允許標志位,還會生成更新中斷 UIF并跳轉至中斷入口地址處(中斷函數)。
如下是向上模式下溢出后生成更新事件和更新中斷的時序圖:
(3)輸入捕獲模式
在輸入捕獲模式下,當檢測到ICx信號上相應的邊沿后,計數器的當前值被鎖存到捕獲/比較寄存器(TIMx_CCRx)中。當發生捕獲事件時,相應的CCxIF標志(TIMx_SR寄存器)被置1,如果開放了中斷或者DMA操作,則將產生中斷或者DMA請求。
(4)PWM 模式
脈沖寬度調制模式可以產生一個由TIMx_ARR寄存器確定頻率、由TIMx_CCRx寄存器確定占空比的信號。在TIMx_CCMRx寄存器中的OCxM位寫入‘110’(PWM模式1) 或‘111’(PWM模式2),能夠獨立地設置每個OCx輸出通道產生一路PWM。
基本定時器、通用定時器、高級定時器的比較:
三、STM32CUBEMX 配置定時器
配置定時器 TIM2 定時100ms,超時溢出后產生更新中斷。
需要先介紹一下定時器怎么實現固定時間的定時:
1)在STM32F103 中 TIM2 的時鐘來源于 APB1 的 2 倍,即 72 MHz;
2)TIM2的預分頻系數 1~65535 可選;
3)定時器計數器 CNT 為 16bit,最大值 65535;
所以定時器的超時時間計算公式如下:
Tout = ((Psc + 1) x (arr + 1))/ 72000000 s
其中:
Tout:定時器超時溢出時間
Psc:預分頻系數
arr:裝載值。也就是計數器的計數值
上面的方式計算出來的是單位是秒!!!
要定時100ms的時間,按照上面的公式,Psc = 7199,arr = 999,得到 Tout = 0.1s = 1000ms。
相關配置如下:
(1)因為只需要作為定時使用,所以比較簡單,開始定時器,并配置相關的定時參數即可。如下:
(2)開啟定時器中斷功能,只有開啟了才能觸發溢出中斷,如下:
(3)配置中斷的優先級,包括搶占優先級,子優先級。如下:
生成代碼后,在文件 tim.c 中 TIM2 的配置代碼如下:
TIM_ClockConfigTypeDef sClockSourceConfig = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};/* USER CODE BEGIN TIM2_Init 1 *//* USER CODE END TIM2_Init 1 */htim2.Instance = TIM2;htim2.Init.Prescaler = 7199;htim2.Init.CounterMode = TIM_COUNTERMODE_UP;htim2.Init.Period = 999;htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;if (HAL_TIM_Base_Init(&htim2) != HAL_OK){Error_Handler();}sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK){Error_Handler();}
定時器使能和中斷配置如下:
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{if(tim_baseHandle->Instance==TIM2){/* USER CODE BEGIN TIM2_MspInit 0 *//* USER CODE END TIM2_MspInit 0 *//* TIM2 clock enable */__HAL_RCC_TIM2_CLK_ENABLE();/* TIM2 interrupt Init */HAL_NVIC_SetPriority(TIM2_IRQn, 3, 0);HAL_NVIC_EnableIRQ(TIM2_IRQn);/* USER CODE BEGIN TIM2_MspInit 1 *//* USER CODE END TIM2_MspInit 1 */}
}
相應的中斷入口函數在 stm32f1xx_it.c 中,如下:
void TIM2_IRQHandler(void)
{/* USER CODE BEGIN TIM2_IRQn 0 *//* USER CODE END TIM2_IRQn 0 */HAL_TIM_IRQHandler(&htim2);/* USER CODE BEGIN TIM2_IRQn 1 *//* USER CODE END TIM2_IRQn 1 */
}