一.PWM簡介
PWM是 Pulse Width Modulation 的縮寫,中文意思就是脈沖寬度調 制,簡稱脈寬調制。它是利用微處理器的數字輸出來對模擬電路進行控 制的一種非常有效的技術,其控制簡單、靈活和動態響應好等優點而成 為電力電子技術最廣泛應用的控制方式,其應用領域包括測量,通信, 功率控制與變換,電動機控制、伺服控制、調光、開關電源,甚至某些 音頻放大器,因此學習PWM具有十分重要的現實意義。 其實我們也可以這樣理解,PWM是一種對模擬信號電平進行數字編碼 的方法。通過高分辨率計數器的使用,方波的占空比被調制用來對一個 具體模擬信號的電平進行編碼。PWM 信號仍然是數字的,因為在給定的 任何時刻,滿幅值的直流供電要么完全有(ON),要么完全無(OFF)。電壓 或電流源是以一種通(ON)或斷(OFF)的重復脈沖序列被加到模擬負載上去 的。通的時候即是直流供電被加到負載上的時候,斷的時候即是供電被 斷開的時候。只要帶寬足夠,任何模擬值都可以使用 PWM 進行編碼。
二.STM32F1 PWM介紹
STM32F1除了基本定時器TIM6和TIM7,其他定時器都可以產生PWM輸出 。其中高級定時器 TIM1 和 TIM8 可以同時產生多達 7 路的 PWM 輸出 。而通用定時器也能同時產生多達 4路的 PWM 輸出,這些在定時器中斷 章節中已經介紹過。 PWM的輸出其實就是對外輸出脈寬可調(即占空比調節)的方波信號 ,信號頻率是由自動重裝寄存器 ARR 的值決定,占空比由比較寄存器 CCR 的值決定。
PWM輸出比較模式總共有8種,具體由寄存器 CCMRx 的位 OCxM[2:0] 配置。我們這里只講解最常用的兩種PWM輸出模式:PWM1和PWM2,其他幾 種模式可以參考《STM32F10x中文參考手冊》13、14、15定時器章節。
PWM1和PWM2這兩種模式用法差不多,區別之處就是輸出電平的極性不 同。
PWM模式根據計數器CNT計數方式,可分為邊沿對齊模式和中心對齊模 式。
(1)PWM邊沿對齊模式
當 TIMx_CR1 寄存器中的 DIR 位為低時執行遞增計數,計數器CNT從 0 計數到自動重載值(TIMx_ARR 寄存器的內容),然后重新從 0 開始 計數並生成計數器上溢事件。 以 PWM 模式 1 為例。只要TIMx_CNT < TIMx_CCRx, PWM 參考信號 OCxREF 便為有效的高電平,否則為無效的低電平。如果 TIMx_CCRx 中 的比較值小於自動重載值(TIMx_ARR 中),則 OCxREF 保持為“ 1”。 如果比較值為 0, 則 OCxREF 保持為“ 0”。
當 TIMx_CR1 寄存器中的 DIR 位為高時執行遞減計數,計數器CNT從 自動重載值(TIMx_ARR 寄存器的內容)遞減計數到0,然后重新從 TIMx_ARR值開始計數並生成計數器下溢事件。 以 PWM 模式 1 為例。只要TIMx_CNT >TIMx_CCRx, PWM 參考信號 OCxREF 便為無效的低電平,否則為有效的高電平。如果 TIMx_CCRx 中 的比較值大於自動重載值(TIMx_ARR 中),則 OCxREF 保持為“ 1”。 此模式下不能產生0%的PWM波形。
(2)PWM中心對齊模式
在中心對齊模式下,計數器 CNT 是工作做遞增/遞減模式下。開始的 時候, 計數器CNT 從 0 開始計數到自動重載值減 1(ARR-1),生成計數 器上溢事件;然后從自動重載值開始向下計數到 1 並生成計數器下溢事 件。之后從 0 開始重新計數。
我們以ARR=8,CCRx=4為例進行介紹。第一階段計數器CNT工作在遞增 計數方式,從0開始計數,當TIMx_CNT < TIMx_CCRx時,PWM 參考信號 OCxREF為高電平,當TIMx_CNT >= TIMx_CCRx時,PWM 參考信號 OCxREF 為低電平。第二階段計數器CNT工作在遞減計數方式,從ARR開始遞減計 數,當TIMx_CNT > TIMx_CCRx時,PWM 參考信號 OCxREF為低電平,當 TIMx_CNT <= TIMx_CCRx時,PWM 參考信號 OCxREF為高電平。
三.PWM輸出配置步驟
(1)使能定時器及端口時鍾,並設置引腳復用器映射 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE); 可選的參數在 stm32f10x_gpio.h 都已經列出來非常詳細
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//復用推挽輸出
(2)初始化定時器參數,包含自動重裝值,分頻系數,計數方式等
void TIM_TimeBaseInit(TIM_TypeDef*TIMx,TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
(3)初始化PWM輸出參數,包含PWM模式、輸出極性,使能等
void TIM_OCxInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
(4)開啟定時器
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState); TIM_Cmd(TIM3,ENABLE); //開啟定時器
(5)修改TIMx_CCRx的值控制占空比
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint32_t Compare1);
(6)使能TIMx在CCRx上的預裝載寄存器 使能輸出比較預裝載庫函數是:
void TIM_OCxPreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
第一個參數用於選擇定時器,第二個參數用於選擇使能還是失能輸出比較預裝載寄存器,可選擇為TIM_OCPreload_Enable、TIM_OCPreload_Disable。
(7)使能 TIMx 在 ARR 上的預裝載寄存器允許位 使能 TIMx 在 ARR 上的預裝載寄存器允許位庫函數是:
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState); //第一個參數用於選擇定時器,第二個參數用於選擇使能還是失能。
高級定時器要想輸出PWM波形,必須要設置一個 MOE 位(TIMx_BDTR 的第 15 位),以使能主輸出,否則不會輸出 PWM。庫函數設置的函數為:
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);
pwm.h
1 #ifndef _pwm_H 2 #define _pwm_H
3
4 #include "system.h"
5
6 void TIM3_CH1_PWM_Init(u16 per,u16 psc); 7
8 #endif
問題一:19行問什么要AFIO使能 27行改變管腳映射怎么回事
27行改變管腳映射是對應第三步而言的,就是將定時器輸出的管腳TIM_CH1即PA6管腳映射到PC6管腳輸出(這里以TIM_CH1為例)
而管腳映射需要用到AFIO這個外設里面寄存器的配置才能實現
問題二:24行為什么GPIOC使用復用推挽輸出,為什么使用復用功能
復用功能是接受其他外設所傳遞的數據(這里是復用的定時器結構圖右下角所輸出的數據)然后通過管腳輸出
總結:所比較的數據從TIM3_CH1口出來,經過AFIO外設內寄存器的配置將TIM3_CH1管腳即PA6管腳所映射到的PC6管腳上,經過PC6管腳的復用功能輸出的模式將TIM3_CH1的數據輸出
四、PWM代碼
pwm.c
1 #include "pwm.h"
2
3 /******************************************************************************* 4 * 函 數 名 : TIM3_CH1_PWM_Init 5 * 函數功能 : TIM3通道1 PWM初始化函數 6 * 輸 入 : per:重裝載值 7 psc:分頻系數 8 * 輸 出 : 無 9 *******************************************************************************/
10 void TIM3_CH1_PWM_Init(u16 per,u16 psc) 11 { 12 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; 13 TIM_OCInitTypeDef TIM_OCInitStructure; 14 GPIO_InitTypeDef GPIO_InitStructure; 15
16 /* 開啟時鍾 */
17 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE); 18 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); 19 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //AFIO使能 20
21 /* 配置GPIO的模式和IO口 */
22 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6; 23 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; 24 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//復用推挽輸出
25 GPIO_Init(GPIOC,&GPIO_InitStructure); 26
27 GPIO_PinRemapConfig(GPIO_FullRemap_TIM3,ENABLE);//改變指定管腳的映射
28
29 TIM_TimeBaseInitStructure.TIM_Period=per; //自動裝載值
30 TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分頻系數
31 TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; 32 TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //設置向上計數模式
33 TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure); 34
35 TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1; 36 TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_Low; 37 TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable; 38 TIM_OC1Init(TIM3,&TIM_OCInitStructure); //輸出比較通道1初始化
39
40 TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable); //使能TIMx在 CCR1 上的預裝載寄存器
41 TIM_ARRPreloadConfig(TIM3,ENABLE);//使能預裝載寄存器
42
43 TIM_Cmd(TIM3,ENABLE); //使能定時器
44
45 }
main.c
1 #include "system.h"
2 #include "SysTick.h"
3 #include "led.h"
4 #include "pwm.h"
5
6 int main() 7 { 8 u16 i=0; 9 u8 fx=0; 10 SysTick_Init(72); 11 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中斷優先級分組 分2組
12 LED_Init(); 13 TIM3_CH1_PWM_Init(500,72-1); //頻率是2Kh
14
15 while(1) 16 { 17
18 if(fx==0) 19 { 20 i++; 21 if(i==300) 22 { 23 fx=1; 24 } 25 } 26 else
27 { 28 i--; 29 if(i==0) 30 { 31 fx=0; 32 } 33 } 34 TIM_SetCompare1(TIM3,i); //i值最大可以取499,因為ARR最大值是499.
35 delay_ms(10); 36 } 37 }