脈沖寬度調制(PWM),是英文“Pulse Width Modulation”的縮寫,簡稱脈寬調制,是利用微處理器的數字輸出來對模擬電路進行控制的一種非常有效的技術。簡單一點,就是對脈沖寬度的控制。
STM32 的定時器除了 TIM6 和 7。其他的定時器都可以用來產生 PWM 輸出。其中高級定時器 TIM1 和 TIM8 可以同時產生多達 7 路的 PWM 輸出。而通用定時器也能同時產生多達 4路的 PWM 輸出,這樣,STM32 最多可以同時產生 30 路 PWM 輸出!這里我們僅利用 TIM3的 CH2 產生一路 PWM 輸出。
PWM 相關的函數設置在庫函數文件 stm32f10x_tim.h 和 stm32f10x_tim.c文件中。
(1)使能定時器3和相關IO口時鐘。
void TIM3_PWM_Init(u16 arr,u16 psc);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定時器3時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB , ENABLE); //使能GPIOB
(2)初始化IO口為復用功能輸出
GPIO_InitTypeDef GPIO_InitStructure;
//設置該引腳為復用輸出功能,輸出TIM3 CH2的PWM脈沖波形 GPIOB.5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
(3)我們把PB5作為PWM的輸出引腳,所以要進行重映射。此時,PB5屬于復用功能輸出。
TIM3_CH2 默認是接在 PA7上面的,而我們的 LED0 接在 PB5 上面,如果普通 MCU,可能就只能用飛線把 PA7 飛到 PB5上來實現了,不過,我們用的是 STM32,它比較高級,可以通過重映射功能,把 TIM3_CH2映射到 PB5 上。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //使能AFIO復用功能模塊時鐘
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5
STM32函數庫對TIM3重映射有兩種方法,一種是完全重映射GPIO_FullRemap_TIM3,這個比較好理解就是把TIM3的所有通道端口映射到Remap指定的端口上;還有一種是部分映射GPIO_PartialRemap_TIM3。
(4)初始化定時器
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = arr; //設置在下一個更新事件裝入活動的自動重裝載寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //設置用來作為TIMx時鐘頻率除數的預分頻值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //設置時鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單位
(5)初始化輸出比較參數
在庫函數中,PWM 通道設置是通過函數 TIM_OC1Init()~TIM_OC4Init()來設置的,不同的通道的設置函數不一樣,這里我們使用的是通道 2,所以使用的函數是 TIM_OC2Init()。
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //選擇定時器模式:TIM脈沖寬度調制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根據T指定的參數初始化外設TIM3 OC2
總結:PWM模式1下,TIMx_CCR1大時有效;PWM模式2下,TIMx_CCR1小有效。
有效電平的高低取決于TIM_OCPolarity的定義。本例子情況如下圖:
(6)使能預裝載寄存器
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的預裝載寄存器
(7)使能定時器
TIM_Cmd(TIM3, ENABLE);
至此,PWM初始化結束。
(8)在主函數中,不斷改變比較值CCRx,達到不同的占空比效果
TIM_SetCompare2(TIM3, uint16_t Compare2);
2.生成頻率和占空比可調的PWM波(在Dutycycle = 0時,似乎有bug,還沒用示波器測量)
//定義函數,Freq為pwm波頻率,Dutycycle為pwm高電平的占空比
void TIM3_PWM_OUTPUT(u32 Freq, u16 Dutycycle)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
u32 arr;
u32 tim3_pulse;
arr = TIM3_CLOCK/Freq -1; //計算出計數周期,默認將預分頻系數psc設置為0,TIM3_CLOCK宏定義為72000000
tim3_pulse = (arr+1)*Dutycycle /100; //計算PWM脈寬
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定時器3時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外設和AFIO復用功能模塊時鐘
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射 TIM3_CH2->PB5
//設置該引腳為復用輸出功能,輸出TIM3 CH2的PWM脈沖波形 GPIOB.5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推挽輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr; //設置在下一個更新事件裝入活動的自動重裝載寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler = 0; //設置用來作為TIMx時鐘頻率除數的預分頻值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //設置時鐘分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上計數模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根據TIM_TimeBaseInitStruct中指定的參數初始化TIMx的時間基數單位
//初始化TIM3 Channel2 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //選擇定時器模式:TIM脈沖寬度調制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比較輸出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性:TIM輸出比較極性高
TIM_OCInitStructure.TIM_Pulse = tim3_pulse; //設置PWM波脈寬
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //根據T指定的參數初始化外設TIM3 OC2
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的預裝載寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIM3
}