1 定時器

1.1 定時器分類

對於STM32來說,定時器可分為基本定時器、通用定時器、高階定時器三類,後者包括前者的全部功能。以stm32f1系列為例,TIM6和TIM7為基本定時器,TIM2~TIM5為通用定時器,TIM和TIM8為高階控制定時器。

基本定時器(TIM6/TIM7)【精簡型】

● 16位自動重灌載累加計數器
● 16位可程式設計(可實時修改)預分頻器,用於對輸入的時鐘按係數為1~65536之間的任意數值分頻
● 觸發DAC的同步電路,TIM6/7獨有功能
● 在更新事件(計數器溢位)時產生中斷/DMA請求

通用定時器(TIM2、TIM3、TIM4和TIM5)【通用型】

● 16位向上、向下、向上/向下自動裝載計數器
● 16位可程式設計(可以實時修改)預分頻器,計數器時鐘頻率的分頻係數為1~65536之間的任意數值
● 4個獨立通道:
─ 輸入捕獲
─ 輸出比較
─ PWM生成(邊緣或中間對齊模式)
─ 單脈衝模式輸出
● 使用外部訊號控制定時器和定時器互連的同步電路
● 如下事件發生時產生中斷/DMA:
─ 更新:計數器向上溢位/向下溢位,計數器初始化(通過軟體或者內部/外部觸發)
─ 觸發事件(計數器啟動、停止、初始化或者由內部/外部觸發計數)
─ 輸入捕獲
─ 輸出比較
● 支援針對定位的增量(正交)編碼器和霍爾感測器電路
● 觸發輸入作為外部時鐘或者按週期的電流管理

高階定時器(TIM1和TIM8)【增強型】

● 16位向上、向下、向上/下自動裝載計數器
● 16位可程式設計(可以實時修改)預分頻器,計數器時鐘頻率的分頻係數為1~65535之間的任意數值
● 4個獨立通道:
─ 輸入捕獲
─ 輸出比較
─ PWM生成(邊緣或中間對齊模式)
─ 單脈衝模式輸出

● 使用外部訊號控制定時器和定時器互聯的同步電路
● 如下事件發生時產生中斷/DMA:
─ 更新:計數器向上溢位/向下溢位,計數器初始化(通過軟體或者內部/外部觸發)
─ 觸發事件(計數器啟動、停止、初始化或者由內部/外部觸發計數)
─ 輸入捕獲
─ 輸出比較
─ 剎車訊號輸入
● 支援針對定位的增量(正交)編碼器和霍爾感測器電路
● 觸發輸入作為外部時鐘或者按週期的電流管理
● 死區時間可程式設計的互補輸出
● 允許在指定數目的計數器週期之後更新定時器暫存器的重複計數器
● 剎車輸入訊號可以將定時器輸出訊號置於復位狀態或者一個已知狀態

通用定時器掛載在APB1匯流排,高階定時器掛載在APB2匯流排。

1.2 計數模式

(1)向上計數模式
計數器從0計數到自動載入值(TIMx_ARR計數器的內容),然後重新從0開始計數並且產生一個計數器向上溢位時間,每次溢位時可以產生更新事件。
(2)向下計數模式
計數器從自動載入值(TIMx_ARR計數器的內容)向下計數到0,然後從自動裝載值重新開始並且產生一個計數器向下溢位時間,每次溢位時可以產生更新事件。
(3)中央對齊模式(向上/向下計數)
計數器從0開始計數到自動載入值(TIMx_ARR暫存器)-1,產生一個計數器向上溢位事件,最後向下計數到1併產生一個計數器向下溢位時間,最後再從0開始重新計數。

1.3 相關結構及函式

本節基於stm32f1系列基本定時器進行相關講解,如下圖所示為基本定時器結構框圖

查閱參考手冊RCC章節的時鐘樹可以知道,RCC的定時器時鐘TIMxCLK,即內部時鐘CK_INT是由APB1預分頻器分頻後提供。如下圖所示,如果APB1預分頻係數為1,則頻率不變,否則頻率為2倍。即此時用於分頻的APB1的預分頻係數為2,所以TIMxCLK = 36 * 2 = 72MHz。

看第一個圓圈內容,APB1的時鐘,最大是36M,由分頻係數決定,當分頻係數是2的時候,APB1的時鐘就是36MHz。

看第二個圓圈內容,當APB1的分頻係數不為1的時候,TIMXCLK的時鐘就是APB1的時鐘乘以2,所以TIM2的時鐘就是72MHz了。為什麼可以乘以2?答:手冊上就是這麼說的,至於為什麼,你得去問STM32晶片廠商的IC工程師了。

system_stm32f10x.c檔案的SetSysClockTo72()函式,預設就是配置APB1位2分頻,如下圖所示:

(1)TIM_TimeBaseInitTypeDef結構體

TIM_Prescaler:指定定時器預分頻器數值,由TIMx_PSC暫存器配置,可設定範圍為0x0000~0xFFFF,即0~65535;
TIM_CounterMode:計數模式,可分為向上計數、向下計數以及三種中心對齊模式。而基本定時器只能向上計數;
TIM_Period:計數器週期,即自動重灌載暫存器TIMx_ARR的值,在事件生成時更新到影子暫存器,由TIMx_CR1暫存器的ARPE位配置是否使能緩衝;
TIM_ClockDivision:時鐘分頻,配置定時器時鐘CK_INT頻率與數字濾波器取樣時鐘頻率分頻比,基本定時器沒有這個功能,不用設定;
TIM_RepetitionCounter:重複計數器,屬於高階控制暫存器專用暫存器位,利用它可以很容易控制輸出PWM個數,這裡不用設定。

計一個數的時間是1/CK_CNT,產生一次中斷的時間為(ARR+1)/CK_CNT。如果在中斷服務程式裡設定一個變數time用於記錄中斷次數,則time定時時間為:(ARR+1)/CK_CNT*time。

(2)定時1s實驗

例如,需要做一個1s的定時,CK_PSC=72MHz,則PSC=71,那麼CK_CNT=1MHz,

計一個數時間:1/CK_CNT = 1/1MHz = 1us,
中斷一次的時間:(ARR+1)/CK_CNT = (999+1)/1MHz = 1ms,
則定時時間:(ARR+1)/CK_CNT*time = 1ms*1000 = 1s

初始化TIM_TimeBaseInitTypeDef

 1 /**
2 * @brief 基本定時器配置
3 * @param 無
4 * @retval 無
5 */
6 static void BASIC_TIM_Mode_Config(void)
7 {
8 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
9
10 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE); // 內部時鐘72MHz
11
12 TIM_TimeBaseStructure.TIM_Period = 999; // 自動重灌載暫存器的值
13 TIM_TimeBaseStructure.TIM_Prescaler= 71; // 預分頻器數值
14 TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
15
16 TIM_ClearFlag(TIM6, TIM_FLAG_Update); // 清除計數器中斷標誌位
17 TIM_ITConfig(TIM6, TIM_IT_Update, ENABLE);
18
19 TIM_Cmd(TIM6, ENABLE);
20 }

基本定時器配置

中斷優先順序配置

/**
* @brief 中斷優先順序配置
* @param 無
* @retval 無
*/
static void BASIC_TIM_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}

中斷優先順序配置

中斷函式

 1 extern volatile uint32_t time;            // 該變數定義在main()函式裡
2
3 void TIM6_IRQHandler(void)
4 {
5 if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
6 {
7 time++; // 每中斷一次,time值加1,中斷一次時間為1ms,需要中斷1000次才可定時1s,即time值為1000
8 TIM_ClearITPendingBit(TIM6, TIM_FLAG_Update);
9 }
10 }

中斷函式

在main()函式裡呼叫led和定時器的初始化配置函式,在一個迴圈裡判斷time變數的值是否為1000,如果已經達到1000,則led燈狀態變化(亮或滅)一次,並且time變數值重賦為0,以便繼續判斷及定時。

2 PWM

脈衝寬度調製(PWM),是英文“Pulse Width Modulation”的縮寫,簡稱脈寬調製,是利用微處理器的數字輸出來對類比電路進行控制的一種非常有效的技術,廣泛應用在從測量、通訊到功率控制與變換的許多領域中。

2.1 PWM工作過程

將暫存器值和計數器值比較,通過比較結果輸出高低電平,實現PWM訊號

如圖為向上計數:
定時器重灌載值為ARR,比較值CCRx
t時刻對計數器值和比較值進行比較
如果計數器值小於CCRx值,輸出低電平
如果計數器值大於CCRx值,輸出高電平
PWM的一個週期
定時器從0開始向上計數
0-t1段,定時器計數器TIMx_CNT值小於CCRx值,輸出低電平
t1-t2段,定時器計數器TIMx_CNT值大於CCRx值,輸出高電平
當TIMx_CNT值達到ARR時,定時器溢位,重新向上計數...迴圈此過程
至此一個PWM週期完成
影響因素
ARR:決定PWM週期(在時鐘頻率一定的情況下,當前為預設內部時鐘CK_INT)
CCRx:決定PWM佔空比(高低電平所佔整個週期比例)

1 TIMx_CCMR1暫存器的OC1M[2:0]位,設定輸出模式控制器

110:TIM_OCMode_PWM1(向上計數時,CNT<CCR為有效電平,oc1ref=1,否則為無效電平;向下計數時,CNT>CCR為無效電平,oc1ref=1,否則為有效電平)
111:TIM_OCMode_PWM2(向上計數時,CNT<CCR為無效電平,oc1ref=1,否則為有效電平;向下計數時,CNT>CCR為有效電平,oc1ref=1,否則為無效電平)

兩種模式的有效無效正好相反。

2 計數器值TIMx_CNT與通道1捕獲比較暫存器CCR1進行比較,通過比較結果輸出有效電平和無效電平
OC1REF=0 無效電平
OC1REF=1 有效電平

3 通過輸出模式控制器產生的訊號
TIMx_CCER暫存器的CC1P位,設定輸入/捕獲通道1輸出極性
0:高電平有效
1:低電平有效

4 TIMx_CCER:CC1E位控制輸出使能電路,訊號由此輸出到對應引腳
0:關閉
1:開啟

計數器值TIMx_CNT與捕獲比較暫存器值CCRx比較後,由TIMx_CCMR1:OC1M位和TIMx_CCER:CC1P位共同決定最終的輸出結果。

下圖中,通過配置TIMx_CCMR1可以配置相應通道為輸入(捕獲模式)還是輸出(比較模式),OCxx描述了輸出模式下的含義,ICxx描述了輸入模式下的含義。

通過設定模式1或模式2,決定了比較結果輸出為有效電平(OC1REF=1高電平)或無效電平(OC1REF=0低電平)

CC1P設定輸入/捕獲1極性,確定最終輸出為高電平還是低電平,CC1P=0,則在OC1REF為高電平時輸出高電平,CC1P=1,則在OC1REF為低電平時輸出高電平,從圖2中可以看出在CC1P=1時會有一個反相器,將OC1REF輸入進行反相。

CC1E設定輸入/捕獲1輸出使能,0:關閉-OC1禁止輸出;1:開啟-OC1訊號輸出到對應的輸出引腳。

2.2 PWM相關庫函式

在本人使用的板子上TIM3_CH1對應的GPIO是PB4,以此為例進行說明,以下為PWM相關的主要函式

1 使能定時器3和相關IO時鐘(LED-PB4)
使能定時器3時鐘:RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
使能GPIOB時鐘:RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

2 初始化IO口為複用功能輸出GPIO_Init();

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
GPIO_Init(GPIOB, &GPIO_InitStructure);

3 PB4輸出PWM(定時器3通道1),需要對PB4進行對映
GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_TIM3);


4 初始化定時器 (重灌載值ARR,與分頻係數PSC等)

PrescalerValue = (uint16_t) (SystemCoreClock  / 1000000) - 1; // 100Mhz->1Mhz
TIM_TimeBaseStructure.TIM_Period = 100-1;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
TIM_PrescalerConfig(TIM3, PrescalerValue, TIM_PSCReloadMode_Immediate);

5 初始化輸出比較引數:

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);

6 使能預裝載暫存器
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);

7 使能定時器
TIM_Cmd(TIM3, ENABLE);

8 不斷改變比較值CCRx,達到不同的佔空比效果
TIM_SetCompare1(TIM3, pule);

在main函式中實現如下

while (1)
{
  Delay(10);
  if(i)
    pule++;
  else
    pule--;
  if(pule==0)
    i=1;
  if(pule>100)
    i=0;
  TIM_SetCompare1(TIM3, pule);
}