將通用定時器分為四個部分:
1,選擇時鍾
2,時基電路
3,輸入捕獲
4,輸出比較
本節定時器PWM輸出主要涉及到定時器框圖右下方部分,即輸出比較部分
和上一講相同,時基時鍾來源於內部默認時鍾
對此有疑問請參考 : 定時器中斷實驗 中 定時器時鍾選擇部分 和 定時器時鍾來源部分
什么是PWM
脈沖寬度調制(PWM),是英文“Pulse Width Modulation”的縮寫,簡稱脈寬調制,是利用微處理器的數字輸出來對模擬電路進行控制的一種非常有效的技術,廣泛應用在從測量、通信到功率控制與變換的許多領域中。
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占空比(高低電平所占整個周期比例)
PWM工作過程(以通道1為例)
1,TIMx_CCMR1寄存器的OC1M[2:0]位,設置輸出模式控制器
110:PWM模式1
111:PWM模式2
2,計數器值TIMx_CNT與通道1捕獲比較寄存器CCR1進行比較,通過比較結果輸出有效電平和無效電平
OC1REF=0 無效電平
OC1REF=1 無效電平
3,通過輸出模式控制器產生的信號
TIMx_CCER寄存器的CC1P位,設置輸入/捕獲通道1輸出極性
0:高電平有效
1:低電平有效
4,TIMx_CCER:CC1E位控制輸出使能電路,信號由此輸出到對應引腳
0:關閉
1:開啟
PWM如何輸出高低電平
計數器值TIMx_CNT與捕獲比較寄存器值CCRx比較后,最終輸出高電平還是低電平,
由TIMx_CCMR1:OC1M位和TIMx_CCER:CC1P位共同決定
1,TIMx_CCMR1寄存器的OC1M[2:0]位,設置PWM模式1或模式2
通過設置模式1或模式2,決定了比較結果輸出有效或無效電平
2,TIMx_CCER寄存器的CC1P位,設置輸入/捕獲通道1輸出極性
通過設置輸出極性,確定有效或無效電平為最終輸出的高電平或低電平
總結:
模式1:
CNT<CCR為有效電平 //(OC1REF = 1)
CNT>CCR為無效電平 //(OC1REF = 0)
模式2:
CNT<CCR為無效電平 //(OC1REF = 0)
CNT>CCR為有效電平 //(OC1REF = 1)
CC1P:
0:高電平有效
1:低電平有效
PWM模式配置
TIM_OC1PreloadConfig函數:
作用:TIM_CCMRx寄存器OCxPE位使能相應的預裝在寄存器
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
TIM_ARRPreloadConfig函數:
作用:操作TIMx_CR1寄存器ARPE位,使能自動重裝載的預裝載寄存器
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
ARPE的使能-ARR變更生效配置
ARPE=1,ARR立即生效
ARPE=0,ARR下周期生效
定時器3輸出通道引腳
定時器3的4個通道對應的引腳及重映射
PWM輸出庫函數
1,定時器通道初始化-TIM_OC1Init
經過上面的講解,我們知道了要想使用PWM需要配置
配置參數對應框圖位置如下:
1,TIMx_CCMR1寄存器的OC1M[2:0]位,設置輸出模式控制器
2,TIMx_CCER寄存器的CC1P位,設置輸入/捕獲通道1輸出極性
3,TIMx_CCER:CC1E位控制輸出使能電路,信號由此輸出到對應引腳
初始化定時器輸出比較通道
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
- 1
TIM_OCInitTypeDef結構體
typedef struct { uint16_t TIM_OCMode; // PWM模式1或者模式2 uint16_t TIM_OutputState; // 輸出使能 OR失能 uint16_t TIM_OutputNState; // PWM輸出不需要 uint16_t TIM_Pulse; // 比較值,寫CCRx,可以有次函數
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
寫入,這里暫時不設置。
uint16_t TIM_OCPolarity; // 比較輸出極性
uint16_t TIM_OCNPolarity; // PWM輸出不需要
uint16_t TIM_OCIdleState; // PWM輸出不需要
uint16_t TIM_OCNIdleState; // PWM輸出不需要
} TIM_OCInitTypeDef;
2,設置比較值函數-TIM_SetCompare1
作用:外部改變TIM_Pulse值,即改變CCR的值
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
- 1
3,使能輸出比較預裝載-TIM_OC1PreloadConfig
作用:TIM_CCMRx寄存器OCxPE位使能相應的預裝在寄存器
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
- 1
4,使能自動重裝載的預裝載寄存器允許位-TIM_ARRPreloadConfig
作用:操作TIMx_CR1寄存器ARPE位,使能自動重裝載的預裝載寄存器
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
- 1
5,修改通道極性
作用:操作TIMx_CCER的CC1P位,修改通道極性
void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
- 1
PWM輸出實驗
使用定時器3初始PWM信號,輸出占空比可變的PWM波驅動LED(PB5引腳),實現LED亮度變換
LED:低電平點亮,高電平熄滅,占空比越大,一個周期中高電平持續時間越長,亮度越大,反之越暗.
查找手冊PB5引腳為定時器3的通道2,需要部分重映射
PWM輸出實驗步驟
1,使能定時器3和相關IO時鍾(LED-PB5)
使能定時器3時鍾:RCC_APB1PeriphClockCmd();
使能GPIOB時鍾:RCC_APB2PeriphClockCmd();
2,初始化IO口為復用功能輸出 GPIO_Init();
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
3,PB5輸出PWM(定時器3通道2),需要部分沖突映射
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//開啟AFIO時鍾設置
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE);//部分重映射
4,初始化定時器 (重裝載值ARR,與分頻系數PSC等)
TIM_TimeBaseInit();//決定PWM周期
5,初始化輸出比較參數:
TIM_OC2Init();//通道2輸出比較初始化
6,使能預裝載寄存器
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);//定時器3 通道2
7,使能定時器
TIM_Cmd();
8,不斷改變比較值CCRx,達到不同的占空比效果
TIM_SetCompare2(); //通道2,改變比較值CCRx
代碼實現
基於 定時器中斷實驗 代碼進行編寫
timer.h添加PWM初始化函數定義 void TIM3_PWM_Init(u16 arr,u16 psc);
#ifndef __TIMER_H #define __TIMER_H #include "sys.h" void TIM3_PWM_Init(u16 arr,u16 psc); #endif
timer.c 實現定時器PWM初始化函數
#include "timer.h" //TIM3 PWM初始化 //arr 重裝載值 //psc 預分頻系數 void TIM3_PWM_Init(u16 arr,u16 psc) { GPIO_InitTypeDef GPIO_InitStrue; TIM_OCInitTypeDef TIM_OCInitStrue; TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrue; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //使能TIM3和相關GPIO時鍾 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);// 使能GPIOB時鍾(LED在BP5引腳),使能AFIO時鍾(定時器3通道2需要重映射到BP5引腳) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitStrue.GPIO_Pin=GPIO_Pin_5; // TIM_CH2 GPIO_InitStrue.GPIO_Mode=GPIO_Mode_AF_PP; // 復用推挽 GPIO_InitStrue.GPIO_Speed=GPIO_Speed_50MHz; //設置最大輸出速度 GPIO_Init(GPIOB,&GPIO_InitStrue); //GPIO端口初始化設置 GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE); TIM_TimeBaseInitStrue.TIM_Period=arr; //設置自動重裝載值 TIM_TimeBaseInitStrue.TIM_Prescaler=psc; //預分頻系數 TIM_TimeBaseInitStrue.TIM_CounterMode=TIM_CounterMode_Up; //計數器向上溢出 TIM_TimeBaseInitStrue.TIM_ClockDivision=TIM_CKD_DIV1; //時鍾的分頻因子,起到了一點點的延時作用,一般設為TIM_CKD_DIV1 TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStrue); //TIM3初始化設置(設置PWM的周期) TIM_OCInitStrue.TIM_OCMode=TIM_OCMode_PWM2; // PWM模式2:CNT>CCR時輸出有效 TIM_OCInitStrue.TIM_OCPolarity=TIM_OCPolarity_High;// 設置極性-有效為高電平 TIM_OCInitStrue.TIM_OutputState=TIM_OutputState_Enable;// 輸出使能 TIM_OC2Init(TIM3,&TIM_OCInitStrue); //TIM3的通道2PWM 模式設置 TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable); //使能預裝載寄存器 TIM_Cmd(TIM3,ENABLE); //使能TIM3 }
main.c 改變CCR值實現PWM占空比變化,小燈亮暗變化
int main(void) { u8 i=1; // 設置方向 0:變暗 1:變亮 u16 led0pwmval; // 設置CCR值 delay_init(); // 延時函數初始化 LED_Init(); // LED初始化 TIM3_PWM_Init(899,0); //設置頻率為80KHz,公式為:溢出時間Tout=(arr+1)(psc+1)/Tclk //Tclk為通用定時器的時鍾,如果APB1沒有分頻,則就為系統時鍾,72MHZ //PWM時鍾頻率=72000000/(899+1) = 80KHZ,設置自動裝載值899,預分頻系數0(不分頻) while(1) { delay_ms(10); if(i)led0pwmval++; // 由暗變亮 else led0pwmval--; // 由亮變暗 if(led0pwmval==0)i=1; // 已達到最亮,開始變暗 if(led0pwmval>100)i=0; // 已達到最暗,開始變亮 TIM_SetCompare2(TIM3,led0pwmval); //改變比較值TIM3->CCR2達到調節占空比的效果 } }
//此處led0pwmval值最大可以設置到899,輸出pwm波基本全為1,大小隨意設定。