一、什么是定時器
關于什么是定時器,簡單來講,就是是用來定時的。STM32F103ZET6有兩個基本定時器TIM6和TIM7,四個通用定時器TIM2~TIM5和兩個高級定時器TIM1,TIM8。每一個定時器都是完全獨立的,不共享任何資源。
根據(jù)中文參考手冊介紹,基本定時器最為簡單,類似于51單片機的定時器。通用定時器在基本定時器的基礎上增加了輸入捕獲和輸出比較功能。高級定時器相比通用定時器,又增加了可編程死區(qū)互補輸出,重復計數(shù)器等功能。
STM32F103ZET6的通用定時器是一個通過可編程預分頻器驅動的16位自動裝載計數(shù)器構成。使用定時器預分頻器和RCC時鐘控制器預分頻器,脈沖長度和波形周期可以在幾個微秒到幾個毫秒間調(diào)整。
這里介紹一下對于定時器的個人理解。定時器的定時原理實際可以理解為按照固定的頻率數(shù)數(shù)。按照固定頻率就說明定時器一定要有輸入時鐘。比如輸入為一個1KHz的時鐘,那么數(shù)一個數(shù)的時間就是1ms。另外,數(shù)數(shù)也不是無限地數(shù),數(shù)值有一個上限。可以規(guī)定是從0開始數(shù)到上限值,還是從上限值數(shù)到0。而且每次數(shù)到頭,需要重新開始。比如,需要控制燈亮200ms。那么只需要在點亮LED之后,等到數(shù)到200時熄滅即可。當數(shù)到上限值或者數(shù)到0時,重新開始數(shù)。
二、定時器有什么用
定時器有許多用途,以通用定時器為例。它可以測量輸入信號的脈沖寬度,產(chǎn)生PWM波。此外定時器也可以用于觸發(fā)ADC采集,按鍵檢測等方面。
中文參考手冊介紹如下
中文參考手冊對于通用定時器功能的描述
三、通用定時器詳細介紹
速成選手可以線跳過這一部分,直接看下面,后來再返回來仔細看。
3.1 時鐘來源
根據(jù)中文參考手冊,通用定時器的時鐘來源有四個。
- ? 內(nèi)部CK_INT
- ? 外部觸發(fā)時鐘輸入TIMx_ETR(外部時鐘模式2)
- ? 內(nèi)部觸發(fā)輸入(ITRx):使用一個定時器作為另一個定時器的預分頻器
- ? 外部引腳輸入(外部時鐘模式1)
通用定時器時鐘來源
通過配置TIMx_SMCR寄存器來選擇,關于寄存器這里就不再詳細介紹了,大家可以去看中文參考手冊。
根據(jù)中文參考手冊關于時鐘的介紹,通用定時器掛接在APB1總線。對于APB1總線的時鐘如下
APB1時鐘介紹
如果APB1的預分頻系數(shù)為1,那么通用定時器的輸入時鐘頻率為36MHz,否則為72MHz。但是通常APB1總線的預分頻系數(shù)我們不會設置成1,所以通用定時器的時鐘頻率為72MHz。
3.2 預分頻器,計數(shù)器,自動重裝載寄存器
預分頻器,計數(shù)器和自動重裝載寄存器
3.2.1 預分頻器
預分頻器是對時鐘進行分頻,范圍是1~65536。比如通用定時器輸入時鐘頻率為72MHz,此時,將預分頻值設置為72,那么最終計數(shù)時的時鐘頻率為72MHz / 72 = 1MHz。
3.2.2 計數(shù)器
計數(shù)器就是用來計數(shù)的,計數(shù)值取值范圍是0~65535。有三種計數(shù)方式:向上計數(shù),向下計數(shù),中央對齊模式(向上/向下計數(shù))。
在向上計數(shù)模式中,計數(shù)器從0計數(shù)到自動加載值(TIMx_ARR計數(shù)器的內(nèi)容),然后重新從0開始計數(shù)并且產(chǎn)生一個計數(shù)器溢出事件。
在向下計數(shù)模式中,計數(shù)器從自動裝入的值(TIMx_ARR計數(shù)器的值)開始向下計數(shù)到0,然后從自動裝入的值重新開始并且產(chǎn)生一個計數(shù)器向下溢出事件。
在 中央對齊模式 ,計數(shù)器從0開始計數(shù)到自動加載的值(TIMx_ARR寄存器)?1,產(chǎn)生一個計數(shù)器溢出事件,然后向下計數(shù)到1并且產(chǎn)生一個計數(shù)器下溢事件;然后再從0開始重新計數(shù)。
3.2.3 自動重裝載寄存器
比如,選擇向下計數(shù)模式,初始值為2000。當計數(shù)到0時,會再次從2000開始向下計數(shù)。這就叫重裝載。但是實際起作用的并不是這里的自動重裝載寄存器,而是影子寄存器。關于影子寄存器這里就不再做介紹了,大家可以自行了解。
3.3 觸發(fā)控制器
從圖中的右上角可以注意到,有一個觸發(fā)控制器。它可以用來觸發(fā)一些外設,比如觸發(fā)ADC采集,也可以用來給其他定時器提供時鐘。
四、PWM
4.1 什么是PWM
PWM(脈沖寬度調(diào)制),它是一種利用微處理器的數(shù)字輸出來對模擬電路進行控制的技術,也可以理解為是對模擬信號電平進行數(shù)字編碼的方法。PWM可被應用于電機驅動,調(diào)光,通信等方面。
4.2 什么是占空比
一個PWM是有固定頻率的,也就意味著周期一定,一個周期內(nèi)有效電平持續(xù)時間占整個周期的比例可以稱為占空比。比如一個周期100ms,其中50ms持續(xù)為有效電平,那么占空比就是50%。正是通過調(diào)節(jié)占空比,來調(diào)節(jié)電機轉速,或者用不同占空比代表不同信號,用于通信。
4.3 STM32F1 PWM介紹
STM32F1系列單片機,除了基本定時器TIM6和TIM7外,都可以產(chǎn)生PWM輸出。其中高級定時器TIM1和TIM8可以同時產(chǎn)生高達7路PWM輸出。PWM輸出其實就是對外輸出占空比可調(diào)的方波,信號頻率由自動重裝載寄存器ARR的值決定,占空比由比較寄存器CCR的值決定。假設高電平為有效電平,見下圖。ARR決定了周期(頻率),CCR調(diào)節(jié)占空比。
PWM示意圖
根據(jù)中文參考手冊介紹,STM32F1的PWM比較輸出模式共有8種。脈沖寬度調(diào)制模式可以產(chǎn)生一個由TIMx_ARR寄存器確定頻率、由TIMx_CCRx寄存器確定占空比的信號。在TIMx_CCMRx寄存器中的OCxM位寫入’110’(PWM模式1)或’111’(PWM模式2),能夠獨立地設置每個OCx輸出通道產(chǎn)生一路PWM。有關寄存器的內(nèi)容,這里就不不再做詳細介紹。
這里介紹一下8種輸出模式中比較常用的兩種PWM輸出模式,PWM1和PWM2。其實這兩種輸出模式差別不大,只不過輸出電平的極性不同。
4.4 PWM頻率計算
頻率 = (主時鐘頻率(72MHz) / (分頻系數(shù) + 1)) / 自動重裝載值(單位為Hz)
五、通用定時器輸出引腳
其他幾個定時器如下
TIM2的PWM引腳
TIM4的PWM引腳
TIM5的PWM引腳
六、實戰(zhàn)項目
這里以一個經(jīng)典項目——呼吸燈,來一起熟悉一下定時器的配置和使用,要求滅—>亮—>滅,時間為2.5s。
6.1 呼吸燈
呼吸燈是指燈能夠像人的呼吸一樣,實現(xiàn)由暗到亮或由亮到暗的變化,通常用于消息提示功能,或者作為系統(tǒng)正在運行的提示。之前一篇博文介紹過三種呼吸燈的實現(xiàn)方式,這里針對普中核心板,來介紹一下如果實現(xiàn)呼吸燈。
6.2 實現(xiàn)思路
這里用兩種方法來實現(xiàn)一下呼吸燈。分別是定時器的溢出中斷和PWM。其實第一種和PWM類似,我非就是控制LED點亮時間。
? 定時器中斷實現(xiàn) 配置好預分頻系數(shù)和重裝載值,使每0.25ms進入一次定時器中斷,記錄進入中斷次數(shù)(count)。當進入次數(shù)滿100次之后(2.5ms),控制LED點亮的變量(t)值加1。主函數(shù)的while(1)輪詢中,如果t小于等于count的時候,LED點亮,否則LED熄滅。t的值累計100加次后(2.5s),開始遞減,LED由亮到滅。控制t是遞增還是遞減的是一個標志位(flag),初始值為0,具體可以看程序設計。
? PWM 利用PWM實現(xiàn)呼吸燈就更加簡單了,只需要不斷調(diào)節(jié)占空比即可。
66.3 定時器配置
配置通用定時器,有以下步驟
? 使能定時器時鐘
? 初始化定時器參數(shù),包括自動重裝載值,分頻系數(shù),計數(shù)方式等
? 設置中斷類型,并使能
? 設置中斷優(yōu)先級,使能定時器中斷通道
? 開啟定時器
? 編寫定時器中斷服務函數(shù)
需要注意的是,配置預分頻系數(shù)時,比如設置為6,實際是6 + 1。
定時時間T = 自動重裝載值 * ((預分頻系數(shù) + 1) / 主時鐘頻率)。主時鐘頻率為72MHz。(為了避免誤導,這里寫的主時鐘頻率為72MHz是APB1總線分頻系數(shù)不是1的前提下。)
6.4 定時器中斷實現(xiàn)呼吸燈
定時器配置程序如下,使用定時器2,控制LED1實現(xiàn)呼吸燈,由滅—>亮—>滅,時間為5秒。
/*
*==============================================================================
*函數(shù)名稱:TIM2_Iint
*函數(shù)功能:初始化定時器2
*輸入?yún)?shù):per:自動重裝載值;psc:預分頻系數(shù)
*返回值:無
*備 注:無
*==============================================================================
*/
void TIM2_Iint (u16 per,u16 psc)
{
// 結構體定義
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); // 使能TIM2時鐘
TIM_TimeBaseInitStructure.TIM_Period = per; // 自動裝載值
TIM_TimeBaseInitStructure.TIM_Prescaler = psc; // 分頻系數(shù)
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 不分頻
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; // 設置向上計數(shù)模式
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); // 開啟定時器中斷
TIM_ClearITPendingBit(TIM2,TIM_IT_Update); // 使能更新中斷
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; // 定時器中斷通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; // 搶占優(yōu)先級
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; // 子優(yōu)先級
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2,ENABLE); // 使能定時器
}
初始化時定時器的程序如下
TIM2_Iint(250,71); // TIM2初始化
預分頻系數(shù)為71 + 1 = 72,計數(shù)到250進入一次中斷,也就是0.25ms進入一次中斷。累計進入100次(25ms)中斷開始調(diào)節(jié)一點LED的亮度。由滅到亮,累計調(diào)節(jié)100次(2.5s)。主函數(shù)和中斷服務函數(shù)如下
u8 gTimIrqCunt = 0; // 進入中斷次數(shù)計數(shù)變量
u8 gLedLightCtrl = 0; // LED亮度控制變量
u8 gLedFlag = 0; // LED亮滅控制標志位,0:滅— >亮;1:亮— >滅
int main(void)
{
Med_Mcu_Iint(); // 系統(tǒng)初始化
while(1)
{
if (gLedLightCtrl <= gTimIrqCunt)
{
Med_Led_StateCtrl (LED1,LED_OFF); // 熄滅LED1
}
if (gLedLightCtrl > gTimIrqCunt)
{
Med_Led_StateCtrl (LED1,LED_ON); // 熄滅LED1
}
}
}
// TIM2中斷服務函數(shù)
void TIM2_IRQHandler(void) // TIM2中斷
{
// 產(chǎn)生更新中斷
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
gTimIrqCunt = gTimIrqCunt + 1; // 進入中斷次數(shù)加1
// 累計進入100次中斷,且是由滅到亮
if (gTimIrqCunt >= 100 && gLedFlag < 100)
{
gTimIrqCunt = 0; // 清零進入中斷計數(shù)變量
gLedLightCtrl = gLedLightCtrl + 1; // LED亮度控制變量加1
gLedFlag = gLedFlag + 1; // LED亮滅控制標志位加1
}
// 累計進入100次中斷,且是由亮到滅
if (gTimIrqCunt >= 100 && gLedFlag >= 100)
{
gTimIrqCunt = 0; // 清零進入中斷計數(shù)變量
gLedLightCtrl = gLedLightCtrl - 1; // LED亮度控制變量加1
gLedFlag = gLedFlag + 1; // LED亮滅控制標志位加1
}
// 一個亮滅周期結束
if (gLedFlag >= 200)
{
gLedFlag = 0; // 清零LED亮滅控制標志位
}
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除TIM2更新中斷標志
}
6.5 使用PWM實現(xiàn)呼吸燈
PWM配置步驟
- ? 使能定時器以及GPIO時鐘,設置引腳復用映射
- ? 初始化定時器參數(shù),包括自動重裝載值,分頻系數(shù),計數(shù)方式等
- ? 初始化PWM輸出參數(shù),包括PWM模式,輸出極性,使能等
- ? 開啟定時器
- ? 修改CCRx的值來修改占空比
- ? 使能TIMx在CCRx上的預裝載寄存器
- ? 使能TIMx在ARR上的預裝載寄存器允許位
TIM3的通道1配置程序如下這里對引腳進行了重映射。
/*
*==============================================================================
*函數(shù)名稱:TIM3_CH1_PWM_Init
*函數(shù)功能:初始化定時器3的PWM通道1
*輸入?yún)?shù):per:自動重裝載值;psc:預分頻系數(shù)
*返回值:無
*備 注:無
*==============================================================================
*/
void TIM3_CH1_PWM_Init (u16 per,u16 psc)
{
// 結構體定義
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
// 開啟時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
// 初始化GPIO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 復用推挽輸出
GPIO_Init(GPIOC,&GPIO_InitStructure);
GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE); // 改變指定管腳的映射
// 初始化定時器參數(shù)
TIM_TimeBaseInitStructure.TIM_Period = per; // 自動裝載值
TIM_TimeBaseInitStructure.TIM_Prescaler = psc; // 分頻系數(shù)
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 設置向上計數(shù)模式
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
// 初始化PWM參數(shù)
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // 比較輸出模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; // 輸出極性
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 輸出使能
TIM_OC1Init(TIM3,&TIM_OCInitStructure); // 輸出比較通道1初始化
TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable); // 使能TIMx在 CCR1 上的預裝載寄存器
TIM_ARRPreloadConfig(TIM3,ENABLE); // 使能預裝載寄存器
TIM_Cmd(TIM3,ENABLE); // 使能定時器
}
實現(xiàn)呼吸燈時,只需要在main函數(shù)中不斷調(diào)整占空比即可,調(diào)整占空比的函數(shù)為
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1)
這里main函數(shù)就不在列出來了。需要注意的是,設置的CCR的值,不能超過自動重裝載值 - 1。
七、拓展
之前介紹按鍵檢測時,介紹過檢測按鍵長短按的方法。當時比較簡單粗暴,這里介紹另一種,使用定時器來判斷按鍵WK UP的長短按。假設規(guī)定,按下時間在10ms~500ms之間為短按,按下時間大于等于1s,為長按。短按LED1點亮,長按LED1熄滅。之前是利用delay來實現(xiàn)時間控制,現(xiàn)在改用定時器實現(xiàn)時間控制,但是基本思路都是相同的。
關于按鍵部分的程序這里就不再做介紹了。首先配置定時器,10ms進入一次更新中斷,預分頻系數(shù)為72,自動重裝載值為10000。使用TIM2,定時器配置程序和上面一樣,初始化程序如下填寫
TIM2_Iint(10000,71); // TIM2初始化
main函數(shù)以及中斷服務函數(shù)如下
u32 gKeyDownTimeCunt = 0; // 按鍵按下時間計數(shù)變量
u8 gKeyLongFlag = 0; // 按鍵長按標志位
u8 gKeyShotFlag = 0; // 按鍵短按標志位
int main(void)
{
Med_Mcu_Iint(); // 系統(tǒng)初始化
while(1)
{
// 短按
if (gKeyShotFlag == 1)
{
Med_Led_StateCtrl (LED1,LED_ON); // 點亮LED1
gKeyShotFlag = 0; // 清除短按標志位
}
// 長按
if (gKeyLongFlag == 1)
{
Med_Led_StateCtrl (LED1,LED_OFF); // 熄滅LED1
gKeyLongFlag = 0; // 清除長按標志位
}
}
}
// TIM2中斷服務函數(shù)
void TIM2_IRQHandler(void) // TIM2中斷
{
// 產(chǎn)生更新中斷
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
if (KEY_UP == 1)
{
gKeyDownTimeCunt = gKeyDownTimeCunt + 1; // 時間計數(shù)變量加1
}
// 松開后
else
{
// 短按
if (1 <= gKeyDownTimeCunt && gKeyDownTimeCunt <= 50)
{
gKeyDownTimeCunt = 0; // 清除時間計數(shù)變量
gKeyShotFlag = 1; // 短按標志位置1
}
// 長按
if (gKeyDownTimeCunt >= 100)
{
gKeyDownTimeCunt = 0; // 清除時間計數(shù)變量
gKeyLongFlag = 1; // 長按標志位置1
}
}
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除TIM2更新中斷標志
}
-
定時器
+關注
關注
23文章
3289瀏覽量
117386 -
RCC
+關注
關注
0文章
95瀏覽量
27427 -
預分頻器
+關注
關注
0文章
19瀏覽量
8273 -
ADC采樣
+關注
關注
0文章
134瀏覽量
13143 -
STM32F103ZET6
+關注
關注
9文章
67瀏覽量
21632
發(fā)布評論請先 登錄
stm32定時器中斷程序
STM32-通用定時器-定時器中斷

STM32---定時器知多少

STM32定時器學習---基本定時器

STM32CubeMX學習筆記(5)——基本定時器接口使用

STM32 HAL庫學習筆記之 定時器中斷

評論