第一部分:定時器基本定時的功能;
第二部分:定時器的輸出比較功能;
第三部分:定時器輸入捕獲的功能;
第四部分:定時器的編碼接口。
1?TIM簡介
- TIM(Timer)定時器;
- 定時器可以對輸入的時鐘進行計數,并在計數值達到設定值時觸發中斷(定時觸發中斷);
- 16位計數器、預分頻器、自動重裝寄存器的時基單元,在72MHz計數時鐘下可以實現最大59.65s的定時(72M/65536/65536,再取倒數);
- 不僅具備基本的定時中斷功能,而且還包含內外時鐘源選擇、輸入捕獲、輸出比較、編碼器接口、主從觸發模式等多種功能;
- 根據復雜度和應用場景分為了高級定時器、通用定時器、基本定時器三種類型。
1.1?定時器類型
類型 | 編號 | 總線 | 功能 |
高級定時器 | TIM1、TIM8 | APB2 | 擁有通用定時器全部功能,并額外具有重復計數器、死區生成、互補輸出、剎車輸入等功能 |
通用定時器 | TIM2、TIM3、TIM4、TIM5 | APB1 | 擁有基本定時器全部功能,并額外具有內外時鐘源選擇、輸入捕獲、輸出比較、編碼器接口、主從觸發模式等功能 |
基本定時器 | TIM6、TIM7 | APB1 | 擁有定時中斷、主模式觸發DAC的功能 |
STM32F103C8T6定時器資源:TIM1、TIM2、TIM3、TIM4
1.1.1 基本定時器
基本定時器框圖:擁有定時中斷、主模式觸發DAC的功能
下面三部分預分頻器、計數器、自動重裝載寄存器構成最基本的計數計時電路,因此叫時基單元。
預分頻器之前連接的是基準計數時鐘的輸入,可以認為連接到了輸入端,也就是CK_INT(72MHz)
預分頻器對72MHz的計數時鐘進行分頻,預分頻器寫0就是不分頻,寫1就是二分頻36MHz......,所以預分頻器的值和實際的分頻系數相差1,即實際分頻系數=預分頻器的值 + 1。這個預分頻器是16位的,所以最大值是65535,也就是65536分頻。
然后是計數器,計數器可以對預分頻器后的計數時鐘進行計數,計數時鐘每來一個上升沿,計數器就加1,這個計數器也是16位的,所以里面的值可以從0一直加到65535,再加的話,計數器就會從0開始。所以計數器的值在計時過程中會不斷的自增運行,當自增運行到目標值時,產生中斷,那就完成了定時的任務,所以還需要一個存儲目標值的寄存器,那就是自動重裝載寄存器。
自動重裝載寄存器也是16位的,它存儲的就是我們寫入的計數目標,在運行過程中,計數器值不斷自增,自動重裝載寄存器的值是固定的,當計數值等于自動重裝值時,也就是計時時間到了,此時會產生一個中斷信號,并且清零計數器,計數器自動開始下一次的計數計時。
UI那里向上的折線,代表這里會產生中斷信號,像這種計數值等于重裝值產生的中斷,稱為"更新中斷",這個更新中斷會通往NVIC,再配置好NVIC的定時器通道,那定時器的更新中斷就能夠得到CPU的響應了。
U向下的折線,代表這里會產生一個事件,這里對應的事件稱為"更新事件",更新事件不會觸發中斷,但是可以觸發內部其它電路的工作。
總結:從基準時鐘,到預分頻器,再到計數器,計數器自增,同時不斷與自動重裝寄存器進行比較,它倆值相等時,即計時時間到,這時會產生一個更新中斷和更新事件,CPU響應更新中斷,就完成定時中斷的任務了。
主模式觸發DAC(數字/模擬轉換模塊)功能:它能讓內部的硬件在不受程序的控制下實現自動運行。
用途:在使用DAC的時候,可能會用DAC輸出一段波形,那就需要每隔一段時間來觸發一次DAC,讓它輸出下一個電壓點。如果用正常的思路實現,就是先設置一個定時器產生中斷,每隔一段時間在中斷函數中調用代碼手動觸發一次DAC轉換,然后DAC輸出,這樣也是沒問題的,但是會使主程序處于頻繁被中斷的狀態,會影響主程序的運行和其它中斷的響應,所以定時器設置了一個主模式,使用這個主模式可以把定時器的更新事件映射到觸發輸出(Trigger Out, TRFO)的位置,然后TRGO直接接到DAC的觸發轉換引腳上,這樣定時器的更新就不需要通過中斷來觸發DAC轉換了,僅需要把更新事件通過主模式映射到TRGO,然后TRGO就會直接去觸發DAC了,整個過程不需要軟件的參與,實現了硬件的自動化,這就是主模式的作用。
1.1.2 通用定時器
復雜很多,中間部分還是時基單元
通用計數器那種模式是向上計數,即自增;通用定時器和高級定時器還支持向下計數模式(向下自減,減到0,再回到重裝值申請中斷)和中央對齊模式(先向上自增到重裝值,申請中斷,再向下自減,減到0,申請中斷)
這部分是內外時鐘源選擇和主從觸發模式的結構了。對于基本定時器而言,定時只能選擇內部時鐘,也就是系統頻率72MHz;到了通用定時器這里,時鐘源不僅可以選擇內部的72MHz時鐘,還可以選擇外部時鐘。第一個外部時鐘來自TIMx_ETR引腳上的外部時鐘,這個ETR(External)引腳的位置,參考引腳定義表。
TIM2_CH1_ETR的意思是TIM2的CH1和ETR都是復用在這個位置,即PA0引腳。
這里就可以在TIM2的ETR引腳,也就是PA0上接一個外部方波時鐘,然后配置一下內部的極性選擇、邊沿檢測和預分頻器電路,再配置一下輸入濾波電路(對輸入波形進行濾波),濾波后的信號,兵分兩路,上面一路ETRF進入觸發控制器,緊跟著就可以選擇作為時基單元的時鐘了,這一路電路稱為"外部時鐘模式2"。
除了外部ETR引腳可以提供時鐘外,下面還有一路可以提供時鐘,就是TRGI(Trigger In),它主要的作用是用作觸發輸入來使用的,這個觸發輸入可以觸發定時器的從模式。暫時可以把TRGI看作外部時鐘的輸入來看,這一路稱為"外部時鐘模式1"。通過這一路的外部時鐘有ETR引腳的信號、ITR信號(ITR信號來自其他定時的TRGO處,連接方式見下圖:TIM2的ITR0連到TIM1的TRGO上,TIM2的ITR1連到TIM8的TRGO上,TIM2的ITR2連到TIM1的TRG3上,TIM2的ITR3連到TIM4的TRGO上),可以實現定時器級聯的功能。
通過這一路的外部時鐘有TI1F_ED,這里連接的是輸入捕獲單元的CH1引腳,也就是從CH1引腳獲得時鐘,后面加ED,是邊沿的(Edge)意思,也就是通過這一路輸入的時鐘,上升沿和下降沿均有效。
最后還能通過TI1FP1和TI2FP2獲得。其中TI1FP1連接到CH1引腳的時鐘,其中TI2FP2連接到CH2引腳的時鐘。
總結:外部時鐘模式1的輸入可以是ETR引腳、其他定時器、CH1引腳的邊沿、CH1引腳和CH2引腳;一般情況外部時鐘通過ETR引腳就可以了。
如果使用外部時鐘,首選ETR引腳外部時鐘模式2的輸入。
定時的編碼器接口,可以讀取正交編碼器的輸出波形。
再看下面的電路,主要是兩部分:
輸出比較電路:總共有4個通道,分別對應CH1-CH4的引腳,可以用于輸出PWM波形,驅動電機
輸入捕獲電路:總共有4個通道,分別對應CH1-CH4的引腳,可以用來測量輸入方波的頻率等
中間寄存器是捕獲/比較寄存器,是輸入捕獲和輸出比較共用的電路。輸入捕獲和輸出比較不能同時使用。
1.1.3?高級定時器
和通用定時器相比多的地方,第一個是申請中斷的地方,增加了重復次數計數器,實現每個幾個計數周期才發生一次更新事件和更新中斷(原來是每個計數周期都會發生更新),相當于對輸出的更新信號又做了一次分頻(59s*65536)
下面的是高級定時器對輸出比較模塊的升級了
DTG(Dead Time Generate)死區生成電路,右邊的輸出引腳由原來的一個變為兩個互補的輸出,可以輸出一對互補的PWM波,這些電路是為了驅動三相無刷電機的(四軸飛行器、電動車的后輪、電鉆等)
最后一部分就是剎車輸入功能,為了給電機驅動提供安全保障的。如果外部引腳TIMx_BKIN(break in)產生剎車信號或者內部時鐘失效,產生了故障,那么控制電路就會自動切斷電機的輸出,防止意外的發生。
1.2?定時中斷基本結構
本小結的兩任務-定時中斷和內部時鐘源選擇。
左邊是為時基單元提供時鐘的部分,可以選擇RCC內部時鐘,也可以選擇ETR引腳提供的外部時鐘模式2。
第一個案例定時中斷使用的是RCC內部時鐘;
第二個定時器外部時鐘使用的是外部時鐘模式2這一路。
中斷輸出控制就是一個中斷輸出的允許位。
1.2.1?預分頻器時序
預分頻器可以將計數器的時鐘頻率按1到65536之間的任意值分頻。它是基于一個(在TIMx_PSC
寄存器中的)16位寄存器控制的16位計數器。這個控制寄存器帶有緩沖器,它能夠在工作時被改
變。新的預分頻器參數在下一次更新事件到來時被采用。
第一行CK_PSC,預分頻器的輸入時鐘,選內部時鐘的話,一般是72MHz
第二行CNT_EN,計數器使能,高電平計數器正常運行,低電平計數器停止運行。
第三行CK_CNT,計數器時鐘,它既是預分頻器的時鐘輸出,也是計數器的時鐘輸入;開始時計數器未使能,計數器時鐘不運行;然后使能后,前半段,預分頻器系數為1,計數器的時鐘等于預分頻器前的時鐘;后半段,預分頻器系數變為2,計數器的時鐘也就變為預分頻器前時鐘的一半了。
第四行計數器寄存器,在計數器時鐘的驅動下,計數器寄存器也跟著時鐘的上升沿不斷自增。在中間位置FC之后,計數值變為0,可以推斷出ARR(自動重裝值)就是FC,當計數值和重裝值相等,并且下一個時鐘來臨時,計數值才清零,同時第五行產生一個更新事件(UEV)。這就是一個計數周期的工作流程。
下面還有三行時序。
這里描述的是預分頻器的一種緩沖機制,也就是預分頻器實際是有兩個,一個是供我們讀寫使用的,它并不直接決定分頻系數;另外還有一個緩沖寄存器(影子寄存器),它才是真正起作用的。為了防止在一個周期中間修改了分頻系數,這時這個變化不會立即生效,而是等到本次計數周期結束時產生了跟新事件,預分頻器的值才會被傳遞到緩沖寄存器里面,這時才會生效。
最后預分頻器內部實際也是靠計數來分頻的。當預分頻值是0時,計數器就一直為0,直接輸出原頻率;當預分頻值為1時,計數器就一直0、1、0、1、0、1......這樣計數,在回到0的時候輸出一個脈沖。這樣輸出頻率就是輸入頻率的二分頻。預分頻器的值和實際的分頻系數之間有一個數的偏移。
計數器時鐘:CK_CNT
預分頻器的輸入時鐘:CK_PSC
計數器計數頻率:CK_CNT = CK_PSC / (PSC + 1)
PSC就是0分頻、1分頻、2分頻......
1.2.2?計數器時序
第一行CK_INT是內部時鐘72MHz;
第二行是時鐘使能,高電平正常運行,低電平停止運行。
第三行是計數器時鐘,因為分頻系數位2,所以這個分頻是上面的1/2;然后第四行計數器寄存器的值在上升沿自增,增到0036時,發生溢出,此時再來一個上升沿,計數器清零,計數器溢出(第五行),產生一個更新事件脈沖(第六行),另外還會更新中斷標志(UIF),這個標志位只要置1了,就會去申請中斷,然后中斷響應后,需要在中斷程序中手動清零。
計數器溢出(中斷)頻率:CK_CNT_OV = CK_CNT / (ARR + 1)
????????????????????????????????????????????????????= CK_PSC / (PSC + 1) / (ARR + 1)
ARR自動重裝值
計數器計數頻率:CK_CNT = CK_PSC / (PSC + 1)
寄存器圖中有陰影的都有影子寄存器(緩沖寄存器)
1.2.3?計數器無預裝時序
計數器時序圖,當ARPE=0時的更新事件(TIMx_ARR沒有預裝入),即沒有影子寄存器(緩沖寄存器)
計數器正在自增計數,突然更改自動加載寄存器,就是自動重裝寄存器,由FF變成36,所以上面計到36就直接更新,開始下一輪計數。
1.2.4?計數器有預裝時序
計數器時序圖,當ARPE=1時的更新事件(預裝入了TIMx_ARR),即有影子寄存器(緩沖寄存器)
在計數的中途,把計數目標由F5改成36,下面有個影子寄存器,這個影子寄存器才是真正起作用的,它還是F5,等計數到F5時,產生更新事件;同時要更改的36才被傳到影子寄存器,在下一個計數周期更改的36才有效。引入影子寄存器實際為了同步,就是讓值的變化和更新事件同步發生,防止在運行中途更改造成錯誤。圖中改成36,而F1大于36,如果沒有影子寄存器,計數就會一直加,加到FF,再從0開始加到36,才能產生更新,這就造成一些小問題。
1.2.5?RCC時鐘樹
這個時鐘樹就是STM32中用來產生和配置時鐘,并且把配置好的時鐘發送到各個外設的系統。時鐘是所有外設運行的基礎,所以時鐘也是最先需要配置的東西。主函數之前的SystemInit中配置時鐘。
在時鐘產生電路有四個振蕩源,分別是:
(1)內部的8MHz高速RC振蕩器(提供系統時鐘);
(2)外部的4-16MHz高速石英晶體振蕩器,也就是晶振,一般接8MHz(提供系統時鐘);
(3)外部的32.768KHz低速晶振,一般給RTC提供時鐘;
(4)最后是內部的40KHz低速RC振蕩器,可以給看門狗提供時鐘。
AHB、APB1、APB2的時鐘都是來自前兩個高速晶振。外部的石英晶振要比內部的RC振蕩器更加穩定,所以一般使用外部晶振。
SystemInit配置的流程是:首先會啟動內部時鐘,選擇內部8MHz為系統時鐘,暫時以內部8MHz的時鐘運行;然后再啟動外部時鐘,進入PLL鎖相環進行倍頻,8MHz的9倍是72MHz,等到鎖相環輸出穩定后,選擇鎖相環輸出為系統時鐘,這樣就把系統時鐘由8MHz切換成72MHz。
鎖相環:
為什么是72MHz:
如果外部晶振出問題了,程序時鐘大概慢10倍(1s到10s),會以內部的8MHz運行(不是9倍嗎)
CSS(Clock Security System)時鐘安全系統:負責切換時鐘,可以檢測外部時鐘的運行狀態,一旦外部時鐘失效,它就會自動把外部時鐘切換回內部時鐘,保證系統時鐘的允許,防止程序卡死造成事故。
看右邊的時鐘分配電路。系統時鐘72MHz進入AHB總線,AHB總線有個預分頻器,在SystemInit中配置的分配系數是1,那AHB的時鐘就是72MHz;然后進入APB1總線,這里的配置的分配系數是2,所以APB1總線的時鐘是36MHz。
之前一直說無論高級定時器、通用定時器、基本定時器時鐘都是72MHz,可以看下面的分支,APB1的預分頻系數如果是1,則頻率不變,否則頻率*2,即36 * 2 = 72MHz。
APB2的分頻系數是1,所以APB2的時鐘和AHB的時鐘一樣,都是72MHz。
外部時鐘使能對應代碼
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
手冊
2?TIM定時器之定時器中斷
2.1?接線圖
2.2?模塊化
按照這個圖進行初始化
步驟:
(1)RCC開啟時鐘;
(2)選擇時基單元的時鐘源(內部時鐘);
(3)配置時基單元(預分頻器、自動重裝器、計數模式等);
(4)配置輸出中斷控制,允許更新中斷輸出到NVIC;
(5)配置NVIC,在NVIC中打開定時器中斷通道,并分配一個優先級;
(6)啟動計數器。
定時器的庫函數
// 恢復缺省配置
void TIM_DeInit(TIM_TypeDef* TIMx);
// 時基單元初始化
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
// 給結構體變量賦默認值
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
// 使能計數器
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
// 使能中斷輸出信號
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);// 選擇內部時鐘
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
// 選擇ITRx其他定時器的時鐘
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
// 選擇TIx捕獲通道的時鐘
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,uint16_t TIM_ICPolarity, uint16_t ICFilter);
// 外部時鐘模式1
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
// ETR外部時鐘2
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
// 配置參數
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter); // 單獨寫預分頻值
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
// 改變計數器計數模式
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);
// 自動重裝器預裝功能配置
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
// 計數器寫值
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
// 給自動重裝器寫值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload); // 獲取計數器的值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
// 獲取預分頻器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);
// 在主程序中獲得/清除標志位
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
// 在中斷函數中獲得/清除標志位
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
Timer.c
#include "stm32f10x.h" // Device header// 定時器初始化
void Timer_Init(void)
{// 第一步RCC開啟時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);// 第二步選擇時基單元的時鐘源(內部時鐘)TIM_InternalClockConfig(TIM2);// 第三步配置時基單元(預分頻器、自動重裝器、計數模式等)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 時鐘分頻,影響不大TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 計數模式,向上計數TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重復計數器的值// 關鍵參數,如果想定時1S
// CK_CNT_OV = CK_CNT / (ARR + 1)
// = CK_PSC / (PSC + 1) / (ARR + 1)
// 1 = 72000000 / (PSC + 1) / (ARR + 1)
// 1 = 72000000 / 7200 / 10000TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; // ARR自動重裝器的值 兩個合起來計數1秒TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; // PSC預分頻器的值,7200分頻,得到10k計數TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 防止復位從1開始// 第四步配置輸出中斷控制,允許更新中斷輸出到NVICTIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);// 第五步配置NVIC,在NVIC中打開定時器中斷通道,并分配一個優先級NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 分組NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn; // 定時器2的通道NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; // 搶占優先級NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; // 響應優先級NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; // 使能NVIC_Init(&NVIC_InitStruct);// 第六步啟動計數器TIM_Cmd(TIM2, ENABLE);
}// 中斷函數
//void TIM2_IRQHandler(void)
//{
// // 檢測中斷標志位,確保是設置的中斷源觸發的這個函數
// if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
// {
//
// // 清除中斷標志位
// TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// }
//}
2.3?主函數
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"// 中斷函數
void TIM2_IRQHandler(void);uint16_t num;int main()
{OLED_Init(); // 初始化OLEDTimer_Init(); // 初始化定時器OLED_ShowString(1, 1, "Num:"); // 顯示字符串while (1){OLED_ShowNum(1, 5, num, 5); // 顯示計數OLED_ShowNum(2, 5, TIM_GetCounter(TIM2), 5); // 顯示CNT計數器,最大值9999}
}// 中斷函數
void TIM2_IRQHandler(void)
{// 檢測中斷標志位,確保是設置的中斷源觸發的這個函數if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){// 中斷處理num++;// 清除中斷標志位TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}
現象:定時1s在計數
重點在這里
// 第三步配置時基單元(預分頻器、自動重裝器、計數模式等)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 時鐘分頻,影響不大TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 計數模式,向上計數TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重復計數器的值// 關鍵參數,如果想定時1S
// CK_CNT_OV = CK_CNT / (ARR + 1)
// = CK_PSC / (PSC + 1) / (ARR + 1)
// 1 = 72000000 / (PSC + 1) / (ARR + 1)
// 1 = 72000000 / 7200 / 10000TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; // ARR自動重裝器的值 兩個合起來計數1秒TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; // PSC預分頻器的值,7200分頻,得到10k計數TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2, TIM_FLAG_Update);
3?TIM定時器之定時器外部時鐘
3.1?接線圖
3.2?模塊化
任務依然是定時中斷,但是時鐘部分,不使用內部時鐘了,
Timer
#include "stm32f10x.h" // Device header// 定時器初始化
void Timer_Init(void)
{// 第一步RCC開啟時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉輸入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);// 第二步選擇時基單元的時鐘源(外部時鐘)// 不分頻,不反向(高電平/上升沿有效),外部觸發濾波器:不用TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);// 第三步配置時基單元(預分頻器、自動重裝器、計數模式等)TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 時鐘分頻,影響不大TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 計數模式,向上計數TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 重復計數器的值// 關鍵參數,如果想定時1S
// CK_CNT_OV = CK_CNT / (ARR + 1)
// = CK_PSC / (PSC + 1) / (ARR + 1)TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; // ARR自動重裝器的值,0-9TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; // PSC預分頻器的值,不分頻TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 防止復位從1開始// 第四步配置輸出中斷控制,允許更新中斷輸出到NVICTIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);// 第五步配置NVIC,在NVIC中打開定時器中斷通道,并分配一個優先級NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 分組NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn; // 定時器2的通道NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; // 搶占優先級NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; // 響應優先級NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; // 使能NVIC_Init(&NVIC_InitStruct);// 第六步啟動計數器TIM_Cmd(TIM2, ENABLE);
}// 獲取計數器的值
uint16_t Timer_GetCounter(void)
{return TIM_GetCounter(TIM2);
}// 中斷函數
//void TIM2_IRQHandler(void)
//{
// // 檢測中斷標志位,確保是設置的中斷源觸發的這個函數
// if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
// {
//
// // 清除中斷標志位
// TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// }
//}
3.3?主函數
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"// 中斷函數
void TIM2_IRQHandler(void);uint16_t num;int main()
{OLED_Init(); // 初始化OLEDTimer_Init(); // 初始化定時器OLED_ShowString(1, 1, "Num:"); // 顯示字符串OLED_ShowString(2, 1, "Cnt:"); // 顯示字符串while (1){OLED_ShowNum(1, 5, num, 5); // 顯示計數OLED_ShowNum(2, 5, TIM_GetCounter(TIM2), 5); // 顯示CNT計數器,最大值9999}
}// 中斷函數
void TIM2_IRQHandler(void)
{// 檢測中斷標志位,確保是設置的中斷源觸發的這個函數if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){// 中斷處理num++;// 清除中斷標志位TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}
現象:擋一下對射式紅外傳感器,計數器加1,加到9時,觸發定時中斷,定時中斷加1。