STM32快速入門(定時器之輸出PWM波形)
前言
本節主要講解STM32利用通用定時器,利用CCR和CNT寄存器,輸出指定占空比和頻率的PWM波形。其功能的應用有:實現LED呼吸燈的效果、控制步進電機、控制直流電機轉速等。
導航
圖98 通用定時器框圖:
圖片引自STM32 F1XX系列的中文參考手冊。在通用定時器章節的定時器架構圖中,本章講解的定時器輸出功能位于右下角的紅色矩形中。
定時器實現PWM輸出的實現細節
參考中文手冊,實現細節圖125如下:
它內部實現是:輸出模式控制器通過比較TIMx_CCR1(比較捕獲寄存器)和TIMx_CNT(計數器)的值,由輸出模式控制器來確定輸出高(有效)電平,還是低(無效)電平,用戶可以通過改變TIMx_CCR1寄存器的值來改變PWM的占空比。這通常會將輸出模式控制器配置成PWM模式1或PWM模式2,兩種模式就是互為取反的關系,同時這兩種模式也是輸出模式控制器最常用的配置。
對于原理圖125左側的輸出模式控制器:
該部分作用是:控制輸出模式控制器的輸出行為。這里將輸出模式控制器的輸出標記為OC1REF。
輸出模式控制器有7種配置,這些配置是通過操作 TIMx_CCMR1.OC1M[6:4]
實現,7種配置在中文手冊中的描述如下,手冊中的描述可能太晦澀,配合江科的表格會更友好:
對于原理圖125右側:
右側包括一個極性選擇器和輸出使能電路。
該部分作用是:1、對輸出電壓的極性進行控制。2、控制輸出電路的使能。
我們可以通過配置 TIMx_CCER.CC1P[1]
控制選擇器是直接選擇OC1REF波形(輸出模式控制器的輸出)還是選擇OC1REF的反相波形。也就是說,輸出模式控制器配置成PWM1/WPM2可以實現對OC1REF的反相,配置 TIMx_CCER.CC1P[1]
間接配置選擇器也能實現對OC1REF的反相。通過配置 TIMx_CCER.CC1E[0]
可以實現對輸出電路的使能。
由定時器輸出PWM的原理可以得出調節占空比的公式,PWM頻率就是定時器溢出的周期、占空比就是TIMx_CCR1的值,其計算公式如下:
關于定時器的PWM模塊還需提一句的是,有三個寄存器存在緩沖寄存器/影子寄存器的概念的,這三個寄存器分別是:ARR、PSC、CCRx。影子寄存器的存在延續舊值的生命周期,這樣讓舊值繼續該時期的使命。如果用戶提供的新值立馬生效,系統就會出于一種未定義的狀態。有了緩沖寄存器/影子寄存器的概念,在一個更新周期中真正起作用的是影子寄存器,而用戶想要修改預分頻控制寄存器,會先將值寫到緩沖器中,待這個更新周期過去,才會將緩沖器的值給到影子寄存器
下面中文手冊的的兩張時序圖可以很好的說明了:
緩沖寄存器 ----> 預分頻控制寄存器
影子寄存器 ----> 預分頻緩沖器
還需注意的是:TIMx_CR1.ARPE[7]
、TIMx_CCMR1.OC1PE[3]
寄存器可以讓用戶選擇ARR、CCRx是否啟用影子寄存器的功能,而PSC寄存器默認必須使用影子寄存器的功能,但是用戶可以通過TIM_PrescalerConfig函數動態配置計數器的預分頻系數,它的第三個參數可以選擇TIM_PSCReloadMode_Immediate,這會讓定時器立即產生一個更新事件,間接實現了立即更新的效果。
定時器實現PWM輸出的步驟
綜上,可以總結出配置定時器輸出部分的套路:
-
我們需要把
TIMx_CCMR1.CC1S[1:0]
配置為00,這樣CC1通道就被配置為輸出 -
通過配置
TIMx_CCMR1.OC1M[6:4]
,這里將輸出模式控制寄存器配置成PWM1模式。即110。 -
配置原理圖右部分是否開啟反相
TIMx_CCER.CC1P[1]
,這里配置為0不反相。 -
最后使能
TIMx_CCER.CC1E[0]
位,來使能原理圖右邊的輸出使能電路。
一般的話,我門還會配置 TIMx_CCMR1.OC1PE[3]
、 TIMx_CR1.ARPE[7]
,分別啟用TIMx_CCR1、TIMx_ARR寄存器的影子功能。
定時器實現PWM輸出的庫函數實現
因為我的開發板LED0被焊在了PB5所以,所以需要將定時器的PWM波形輸出到PB5上。經過查表需要對TIM3_CH2輸出進行一個重映射;此外還要將PB5配置成復用推挽輸出的狀態。
定時器PWM輸出配置,實例代碼如下:
void LunarInitTIM3() {TIM_TimeBaseInitTypeDef TIM3_Cfg;GPIO_InitTypeDef GPIOB5_Cfg;TIM_OCInitTypeDef TIM3_OCCfg;// 定時器時基配置 BEGIN// 打開TIM3所需要的時鐘 APB1RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);TIM_TimeBaseStructInit(&TIM3_Cfg);// 配置使用內部時鐘 72M HzTIM_InternalClockConfig(TIM3);// 這里配置定時器更新頻率是1000HZTIM3_Cfg.TIM_CounterMode = TIM_CounterMode_Up;TIM3_Cfg.TIM_Period = 100 - 1;TIM3_Cfg.TIM_Prescaler = 720 - 1;TIM_TimeBaseInit(TIM3, &TIM3_Cfg);// 因為TIM_TimeBaseInit會置TIMx_EGR.UG[0]為1,手動產生一個更新事件,// 同時會同步影子寄存器的值,而該更新事件又會產生一個多余的中斷,所以,// 我們需要在開啟中斷之前,手動清楚更新事件標志位TIM_ClearFlag(TIM3, TIM_FLAG_Update);// 定時器時基配置 END// 配置GPIO BEGIN// 開啟復用時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);// 部分重映射GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);// 初始化GPIOB5為推挽復用輸出RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIOB5_Cfg.GPIO_Mode = GPIO_Mode_AF_PP;GPIOB5_Cfg.GPIO_Pin = GPIO_Pin_5;GPIOB5_Cfg.GPIO_Speed = GPIO_Speed_2MHz;GPIO_Init(GPIOB, &GPIOB5_Cfg);// 配置GPIO END// 配置TIM3的PWM輸出 BEGINTIM_OCStructInit(&TIM3_OCCfg);TIM3_OCCfg.TIM_OCMode = TIM_OCMode_PWM1;TIM3_OCCfg.TIM_OCPolarity = TIM_OCPolarity_High;TIM3_OCCfg.TIM_OutputState = TIM_OutputState_Enable;TIM3_OCCfg.TIM_Pulse = 0;TIM_OC2Init(TIM3, &TIM3_OCCfg);// 配置TIM3的PWM輸出 END// 使能arr和ccr寄存器的影子功能TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);TIM_ARRPreloadConfig(TIM3, ENABLE);// 使能更新中斷// TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);// 開啟定時器TIM_Cmd(TIM3, ENABLE);
}int main() {// 初始化定時器LunarInitTIM3();// 初始化系統定時器SYSTick_Init();int dir = 0, cr = 0;while(1) {TIM_SetCompare2(TIM3, cr);Delay_Ms(20);if (dir == 0) {cr++;if (cr > 99) {dir = 1;cr = 99;}} else {cr--;if (cr < 0) {dir = 0;cr = 0;}}}return 0;
}
實驗結果就是PB5處的LED燈實現了呼吸的效果。
本章完結