1、定時器的分類
STM32F103ZET6總共有8個定時器,它們是:TIM1~TIM8。STM32的定時器分為基本定時器、通用定時器和高等定時器。
TIM6、TIM7是基本定時器。基本定時器是只能向上計數的16位定時器,基本定時器只能有定時的功能,沒有外部IO口,所以沒有捕獲和比較通道。
TIM2、TIM3、TIM4、TIM5是通用定時器。通用定時器是可以向上計數,也可以向下計數的16位定時器。通用定時器可以定時、輸出比較、輸入捕捉,每個通用定時器具有4個外部IO口。
TIM1、TIM8是高等定時器。高等定時器是是可以向上計數,也可以向下計數的16位定時器。高等定時器可以定時、輸出比較、輸入捕捉、還可以輸出三相電機互補信號,每個高等定時器有8個外部IO口。
定時器分類圖如下:
2、基本定時器
基本定時器沒有外部IO口,所以它只有定時的功能。
基本定時器只能向上計數,也就是說基本定時器只能遞增計數。
基本定時器功能框圖如下:
從功能圖的1中可以看到,基本定時器的時鍾TIMxCLK來自內部時鍾,該內部時鍾為經過APB1預分頻器分頻后提供的。基本定時器跟APB1總線時鍾的關系如下:
-
- 如果APB1預分頻系數為1,則基本定時器的時鍾等於APB1總線時鍾。
- 如果APB1預分頻系數不為1,則基本定時器的時鍾等於APB1總線時鍾經過分頻后的2倍。
比如APB1總線經過2分頻后的時鍾為36MHZ,那么基本定時器的時鍾就是72MHZ3(36*2)。
功能圖中的2是一個預分頻器,來自內部的時鍾經過預分器分頻后的時鍾,用來驅動基本定時器的計數器計數。基本定時器的預分頻器是一個16位的預分頻器,預分頻器可以對定時器時鍾進行1~65536之間的任何一個數進行分頻。計算方式如下:
定時器工作時鍾 = 來自APB1的時鍾/(預分頻系數+1)
功能圖中的3是一個16位的計數器,該計數器能能向上計數,最大計數值位65535。基本定時器的計數器從0開始向上計數,當計數器的值與自動重裝載寄存器相等時產生更新事件,並清零從頭開始計數。
功能圖中的4是一個16位的自動重裝載寄存器。該寄存器裝着計數器能計數的最大數值。當基本定時器的計數器計數到這個值的時候,如果使能了中斷。定時器就會產生溢出中斷。
在定時器的參考資料中,很多地方都提到更新事件,其實更新事件就是計數器溢出。
3、定時器的時間計算
基本定時器每計數一次所經過的時間為:
Time = (PSC + 1)/ TIMxCLK(us)
PSC是定時器的分頻系數,TIMxCLK是內部時鍾。
基本定時器的計數次數由自動重裝載寄存器決定的,基本定時器的計數器從0開始向上計數,當計數器的值與自動重裝載寄存器相等時,產生溢出。所以基本定時器的溢出時間計算公式如下:
Time = (PSC+1)*(ARR)/ TIMxCLK(us)
ARR是自動重裝載寄存器的值。
假設基本定時器TIMxCLK = 72MHZ,PSC = 71,ARR = 1000,那么定時器的溢出時間為:
Time = (71+1)*1000/72 = 1000(us) = 1(ms)
4、定時器的寄存器
TIMx_CR1控制寄存器1
Bit0位CEN是定時器的使能位,CEN=0時,計數器不工作;CEN=1是計數器開始計數。想要定時器工作,就要將CEN置1。
Bit1位UDIS是禁止更新位,當UDIS=0時,定時器溢出后會把TIMx_SR寄存器的UIF置1;當UDIS=1時,定時器溢出后並不會置位UIF位,即不會產生中斷。
Bit2位URS是更新請求源,當URS=0時,計數器溢出、設置UG位、通過從模式控制器產生的更新都會產生中斷;當URS=1時,只有在計數器溢出的情況下才會產生中斷。
Bit3位OPM可以設置定時器是工作一次還是重復工作,當OPM=0時,定時器產生中斷后繼續工作;當OPM=1時,定時器溢出后會清除CEN位,即定時器停止計數。
Bit7位ARPE是自動重裝載預裝載使能,當ARPE=0時,TIMx_ARR寄存器沒有緩沖;當ARPE=1時,TIMx_ARR寄存器具有緩沖。
TIMx_DIER中斷使能寄存器
Bit0位UIE是更新中斷使能位,當UIE=0時,定時器禁止溢出中斷;當UIE=1時,定時器可以產生溢出中斷。
Bit8位UDE是更新DMA請求使能位,當UDE=0時,禁止更新DMA請求;當UDE=1時,使能更新DMA請求。
TIMx_SR狀態寄存器
Bit1位UIF是溢出中斷標志。當TIMX_CR1的UDIS=0,如果定時器產生溢出,UIF就會被置1,UIF不會被硬件自動清除,只能軟件清除。當產生中斷時,可以由該位判斷是否是定時器的溢出中斷。
TIMx_EGR事件產生寄存器
Bit0位UG是產生更新事件,UG位由軟件置1,但由硬件自動清除。當UG寫0時無作用;當UG寫1時,如果TIMx_CR1寄存器的UDIS = 0,則會重新初始化定時器的計數器並產生對寄存器的更新,需要注意的是預分頻器也被清除(但預分頻系數不變)。
TIMx_CNT計數器寄存器
TIMx_CNT只有低16位有效,可以從0到65535之間計數。
自動重裝載寄存器TIMx_ARR
如果TIMx_ARR的數值為0,則TIMx則會停止工作。
自動重裝載寄存器是預加載的,每次讀寫TIMx_ARR寄存器時,實際上是通過讀寫預加載寄存器實現的。TIMx_ARR寄存器根據TIMx_CR1控制寄存器的bit7位ARPE來決定是帶緩沖的或是不帶緩沖的。
當ARPE=0時,TIMx_ARR寄存器是不具有緩沖功能的,當改變TIMx_ARR寄存器的值時,立馬就更新定時器的自動重裝載寄存器,如下圖。
從上圖可以看到,在沒改變TIMx_ARR寄存器的值之前自動重裝載的寄存器的是FF,當在計數器跑到32的時候突然改變TIMx_ARR寄存器的值為36,那么自動重裝載的寄存器的值也立馬從FF變為了36,改變TIMx_ARR寄存器的值並不會影響計數器寄存器的值,計數器寄存器繼續從32計數到36后溢出並產生更新中斷。
當ARPE=1時,TIMx_ARR寄存器是具有緩沖功能的,當改變TIMx_ARR寄存器的值時,並不會馬上更新定時器的自動重裝載寄存器,而是要等到下一次更新事件發生時,才會更新定時器的自動重裝載寄存器的值,如下圖。
從上圖可以看到,在沒改變TIMx_ARR寄存器的值之前自動重裝載的寄存器的是F5,當在計數器跑到F1的時候突然改變TIMx_ARR寄存器的值為36時,雖然自動重裝載的寄存器的值也變為了36,但是自動重載影子寄存器的值還是F5,並沒有被改變,而且計數器寄存器的值是跟自動重載影子寄存器的值比較,只有當產生更新事件的時候,自動重載影子寄存器的值才變為了36。
TIMx_PSC預分頻寄存器
TIMx_PSC只有低16位有效,用來設置定時器時鍾的分頻系數。
TIMx_PSC預分頻寄存器具有緩沖功能,如果在定時器運行的過程中更改TIMx_PSC寄存器的值,定時器的分頻系數並不會立馬發生更改,而是會等到下一個計數器溢出時新的預分頻數值才會更改。如下圖。
從圖中可以看到,當計數器寄存器遞增到F8的時候,TIMx_PSC的值被改寫成了1,但是實際的預分頻系數還沒有被改變,還是0,只有當計數器寄存器溢出變為0時,實際的預分頻系數才變為1。從圖中還可以看出,當預分頻系數為0的時候,CK_PSC = CK_CNT,當預分頻系數為1的時候,CK_CNT = 2CK_PSC,也就是通過波形可以看出計數頻率變了。
5、基本定時器的配置流程
使能基本定時的時鍾,不然無法使用基本定時器。
設置基本定時器的預分頻系數和自動重裝載寄存器值,通用定時器和高等定時器還要設置計數方向,因為基本定時器只能向上計數,所以不用設置。
開啟基本定時器。
通過NVIC配置基本定時器的中斷。
編寫基本定時器的中斷服務函數,在中斷服務函數中,可以通過基本定時器的狀態寄存器的值來判斷此次產生的中斷屬於什么類型。
6、HAL庫操作基本定時器
在使用HAL操作基本定時器之前,需要將stm32f1xx_hal_tim.c和stm32f1xx_hal_tim_ex.c文件添加到功能目錄,還要在stm32f1xx_hal_conf.h文件中使能HAL_TIM_MODULE_ENABLED宏。如下圖。
以操作基本定時器TIM6為例,初始化代碼如下:
1 TIM_HandleTypeDef TIM6_Handler_Init; 2
3 void TIM6_Init(void) 4 { 5 __HAL_RCC_TIM6_CLK_ENABLE(); 6
7 TIM6_Handler_Init.Instance = TIM6; 8 TIM6_Handler_Init.Init.Prescaler = 36000-1; 9 TIM6_Handler_Init.Init.Period = 1000; 10 HAL_TIM_Base_Init(&TIM6_Handler_Init); 11
12 HAL_TIM_Base_Start_IT(&TIM6_Handler_Init); 13
14 HAL_NVIC_SetPriority(TIM6_IRQn,2,1); 15 HAL_NVIC_EnableIRQ(TIM6_IRQn); 16
17 }
第1行首先定義一個全局變量TIM6_Handler_Init,這是一個句柄變量,用來操作TIM6。
第5行通過__HAL_RCC_TIM6_CLK_ENABLE()宏使能TIM6的時鍾,如果不是能時鍾,是無法操作TIM6的。
第7行是將TIM6_Handler_Init句柄變量指向TIM6。
第8行是將TIM6的分頻系數設為36000,如果TIM6的時鍾為72M,那么分頻后的值為72000000/36000 = 2000HZ。
第9行是將TIM6的自動重載在的值設為1000,即TIM6的計數器計數1000次后溢出,根據分頻系數可以得出溢出時間為1000 * 36000 /72000000 = 1000 / 2000 = 0.5s,即TIM6的計數器每0.5秒溢出一次。
第10行是通過HAL_TIM_Base_Init函數配置TIM6定時器。
第12行是通過HAL_TIM_Base_Start_IT函數使能溢出中斷,並啟動定時器從0開始向上計數。
第14行是通過HAL_NVIC_SetPriority函數設置TIM6的中斷優先級。
第15行是通過HAL_NVIC_EnableIRQ函數使能TIM6中斷,只有使能了中斷,才能進入中斷服務函數。
初始化完TIM6后,編寫中斷服務函數,代碼如下:
1 void TIM6_IRQHandler(void) 2 { 3 HAL_TIM_IRQHandler(&TIM6_Handler_Init); 4 } 5
6 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) 7 { 8 if(htim == &TIM6_Handler_Init) 9 { 10 HAL_GPIO_TogglePin(GPIOF,GPIO_PIN_6); 11 } 12 }
第1行是TIM6的中斷服務函數。
第2行是調用HAL庫編寫的定時器中斷處理函數,將定時器的句柄變量通過參數傳入其中。
第6行是定時器的中斷回調函數,通過這個函數實現定時器的中斷處理。
第8行是通過句柄判斷需要處理的是什么類型的中斷。