STM32的定時器分了好幾個類别,各個類别針對功能作用都不大相同。
分别有: 1.進階定時器 、2.通用定時器、 3.基本定時器、 4.看門狗定時器 、 5.SysTick定時器
其中看門狗定時器和SysTick定時器本篇筆記闡述,這裡主要記下對平時使用定時器作用的計時計數器的一些自己的了解。
按照參考手冊中的定義 進階定時器 通用定時器 基本定時器,這三個定時器成上下級的關系,即基本定時器有的功能通用定時器都有,而且還增加了向下、向上/向下計數器、PWM生成、輸出比較、輸入捕獲等等功能;而進階定時器又包含了通用定時器的所有功能,另外還增加了死區互補輸出、刹車信号、加入重複計數器等等。(這裡等等功能請參考《STM32參考手冊》)
是以學習STM32 定時器實際就是學習一下進階定時器,然後适當的删減後就是後面的兩種定時器了。
假若不涉及輸出輸入,定時器的最基本用法就是計數定時作用了本篇筆記主要針對這部分的了解所寫下的。
進階定時器中一共有20個寄存器:
TIMx_CR1、TIMx_CR2、TIMx_SMCR、TIMx_DIER、TIMx_SR、TIMx_EGR、TIMx_CCMR1、TIMx_CCMR2、
TIMx_CCER、TIMx_CNT、TIMx_PSC、TIMx_ARR、TIMx_RCR、TIMx_CCR1、TIMx_CCR2、TIMx_CCR3、
TIMx_CCR4、TIMx_BDTR、TIMx_DCR、TIMx_DMAR
好吧一堆寄存器光看都看到眼花缭亂了,當然不是所有寄存器都涉及到才能讓定時器工作的,例如最基本的定時功能所涉及的隻有幾個與時基功能相關的寄存器,TIMx_CNT(計數器寄存器)、TIMx_PSC(預分頻器寄存器)、TIMx_ARR(自動裝載寄存器)、TIMx_RCR(重複次數寄存器)。參考手冊中有那麼 衣服定時器的框圖。這幾個寄存器的關系如圖所示的:
CK_PSC這根時鐘線上的時鐘源的選擇,即給定時器計數計時的時鐘源的輸入方式,有四種方式,分别是内部時鐘,外部時鐘模式1,外部時鐘模式2,内部觸發。這部分日後再說,這裡暫且使用最常用的内部時鐘方式,既是當内部時鐘為72MHz 的内部時鐘源。
如圖所示的,時鐘源首先進入預分頻器,然後再進入預先裝入自動重裝載寄存器的計數器中,當計數器溢出時産生一次中斷和一次事件更新。除了多了一個PSC,其他的基本和51單片機很相似,初次看參考手冊中的功能描述中出現了好多次“更新事件(UEV)”。這究竟是怎麼的一樣東西呢? 在這裡有個新概念叫“影子寄存器”,在上圖中,可以看到PSC、ARR、REP(重複計數器中的低八位)這三個寄存器框框下都有個黑影,每次這三個寄存器就是影子寄存器,如果看到參考手冊全圖中還可以看到另外還有幾個框框下也有陰影部分的,這幾個寄存器也是影子寄存器。何謂影子寄存器呢,例如PSC寄存器可以了解為有兩個,一個是使用者可以通路到的寄存器,可讀可寫,另一部分就是客戶通路不到的但其裝載值和實際寄存器是密切關聯的,當程式在運作中改寫PSC 這時候影子寄存器的作用就展現了,因為立刻寫入的值可能會大于或小于目前正在運作的寄存器中的數值,而真實在運作時候的正是這個影子寄存器中的值,而程式寫入的是可通路的寄存器,隻有當産生一個更新事件的時候影子寄存器才會讀入通路寄存器中的值,這樣就可以防止突然修改而産生的非正常中斷或不會中斷等異常問題。當然在控制器CR1中控制這個影子寄存器是否起作用,不起作用的話就是立即寫入這個數值到寄存器中。
另外進階定時器中還有RCR重複次數寄存器這個,也是比較簡單的事件更新(UEV) 都是在RCR為0的情況下産生計數器溢出而産生的,當RCR中不為0的時候計數器溢出隻會使得重複次數寄存器遞減而不會産生UEV,這樣就可以使得定時器的定時情況得以延長,而相當于有16位的分頻器,16位的計數器,再加入16位的重複次數,一共48位的計數定時器。詳細看參考手冊,這個很好了解。
基本的基時單元就是上面提及的這幾個,下面看看3.0庫是如何實習的基本使用
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- TIM_DeInit(TIM2); //重新将Timer設定為預設值
- TIM_InternalClockConfig(TIM2); //采用内部時鐘給TIM2提供時鐘源
- TIM_TimeBaseStructure.TIM_Prescaler = 36000 - 1; //預分頻系數為36000-1,這樣計數器時鐘為72MHz/36000 = 2kHz
- TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設定時鐘分割
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //設定計數器模式為向上計數模式
- TIM_TimeBaseStructure.TIM_Period = 2000 - 1; //設定計數溢出大小,每計2000個數就産生一個更新事件
- TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure); //将配置應用到TIM2中
- TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除溢出中斷标志
- TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //開啟TIM2的中斷
- 以上是一個最基本的定時器配置的代碼,載自網上被轉載無數次的地方……
- 中斷函數自己按照需求寫,這裡不多說。
- 在庫中的初始化函數和初始化資料類型有3類,TIM_TimeBaseInitTypeDef、TIM_OCInitTypeDef、TIM_ICInitTypeDef
- 與基時參數相關的資料類型是TIM_TimeBaseInitTypeDef
- typedef struct
- {
- uint16_t TIM_Prescaler;
- uint16_t TIM_CounterMode;
- uint16_t TIM_Period;
- uint16_t TIM_ClockDivision;
- uint8_t TIM_RepetitionCounter;
- } TIM_TimeBaseInitTypeDef;
以上是從庫stm32f10x_tim.h中 截取的代碼,整體的資料結構可以中這段注釋中得知,不懂E文的要麼翻字典要麼翻庫函數中文翻譯
版本(當然這個是2.0的庫,有部分會和3.0後的版本很不相同),這部分的資料類型還是很一樣的,不多說。
接着就是TIM_TimeBaseInit()這個函數了,在stm32f10x_tim.c的224行中
- void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
- {
- uint16_t tmpcr1 = 0;
- assert_param(IS_TIM_ALL_PERIPH(TIMx));
- assert_param(IS_TIM_COUNTER_MODE(TIM_TimeBaseInitStruct->TIM_CounterMode));
- assert_param(IS_TIM_CKD_DIV(TIM_TimeBaseInitStruct->TIM_ClockDivision));
- tmpcr1 = TIMx->CR1;
- if((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM2) || (TIMx == TIM3)||
- (TIMx == TIM4) || (TIMx == TIM5))
- {
- tmpcr1 &= (uint16_t)(~((uint16_t)(TIM_CR1_DIR | TIM_CR1_CMS)));
- tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_CounterMode;
- }
- if((TIMx != TIM6) && (TIMx != TIM7))
- {
- tmpcr1 &= (uint16_t)(~((uint16_t)TIM_CR1_CKD));
- tmpcr1 |= (uint32_t)TIM_TimeBaseInitStruct->TIM_ClockDivision;
- }
- TIMx->CR1 = tmpcr1;
- TIMx->ARR = TIM_TimeBaseInitStruct->TIM_Period ;
- TIMx->PSC = TIM_TimeBaseInitStruct->TIM_Prescaler;
- if ((TIMx == TIM1) || (TIMx == TIM8)|| (TIMx == TIM15)|| (TIMx == TIM16) || (TIMx == TIM17))
- {
- TIMx->RCR = TIM_TimeBaseInitStruct->TIM_RepetitionCounter;
- }
- TIMx->EGR = TIM_PSCReloadMode_Immediate;
- }
可以看3.0後的函數裡把所有的TIMx都加入一個函數裡面做判斷了,不需要和2.0的區分TIM1和TIM 兩類函數,比較其基本操作都一樣無非就是多了一個兩個寄存器而已。
轉自晶片之家