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);
}