stm32 PWM輸出學習
STM32 的定時器除了 TIM6 和 7,其他的定時器都可以用來產生 PWM 輸出。其中高階定時器 TIM1 和 TIM8 可以同時產生多達 7 路的 PWM 輸出。通用定時器也能同時產生多達 4路的 PWM 輸出。
今天的實驗,我們僅利用 TIM3的 CH2 通道產生一路 PWM 輸出。
1.相關暫存器介紹
1)捕獲/比較模式暫存器 (TIMx_CCMR1/2)
捕獲/比較模式暫存器(TIMx_CCMR1/2),該暫存器總共有 2 個,TIMx _CCMR1和 TIMx _CCMR2。TIMx_CCMR1 控制 CH1 和 CH2,而 TIMx_CCMR2 控制 CH3 和 CH4。
OCxx描述了通道在輸出模式下的功能(上行),ICxx描述了通道在輸出模式下的功能(下行)。因此必須注意,同一個位在輸出模式和輸入模式下的功能是不同的。
這裡我們需要說明的是模式設定位 OCxM,此部分由 3 位組成。總共可以配置成 7 種模式,我們使用的是 PWM 模式,所以這 3 位必須設定為 110/111。這兩種PWM 模式的區別就是輸出電平的極性相反。
這裡的有效電平或者無效電平是可以配置的。
2) 捕獲/比較使能暫存器(TIMx_CCER)
3)捕獲/比較暫存器(TIMx_CCR1~4)
該暫存器總共有 4 個,對應 4 個通道 CH1~CH4。因為這 4 個暫存器都差不多,我們僅以 TIMx_CCR1 為例介紹。
我們通過修改這個暫存器的值,就可以控制 PWM 的輸出脈寬。
2. TIM3_REMAP 重對映
我們要利用 TIM3 的 CH2 輸出 PWM 來控制 LED的亮度,但是 TIM3_CH2 預設是接在 PA7上面的,而我們的 LED接在 PB5 上面,如果是普通 MCU,可能就只能用飛線把 PA7 飛到 PB5上;不過,我們用的是 STM32,可以通過重對映功能,把 TIM3_CH2對映到 PB5 上。
3.程式碼難點分析
1)RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//使能TIM3時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
//使能GPIOB時鐘(PB5連線LED)和AFIO複用功能時鐘
為什麼要使能RCC_APB2Periph_AFIO呢?
參考手冊上說:
對暫存器AFIO_EVCR(事件控制暫存器),AFIO_MAPR( 複用重對映和除錯I/O配置暫存器),AFIO_EXTICRX(外部中斷配置暫存器)進行讀寫操作前,應當首先開啟AFIO的時鐘。
因為我們在這個實驗中用到了AFIO_MAPR,所以要開啟AFIO時鐘。
2)GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重對映,TIM3_CH2->PB5
3)GPIO配置
//設定該引腳為複用輸出功能,輸出TIM3-CH2的PWM波形
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //推輓複用輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
4)TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
可以參考捕獲/比較使能暫存器(TIMx_CCER)位0, 輸出使能。
5)TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
可以參考捕獲/比較使能暫存器(TIMx_CCER)位1, 高/低 電平有效。
6)TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); 預裝載使能
可以參考捕獲/比較模式暫存器 (TIMx_CCMR1/2) 位3.
7)TIM_SetCompare2(TIM3,300);
參考捕獲/比較暫存器(TIMx_CCR1~4),這裡設定比較值為300.
4. 程式碼參考
//PWM輸出初始化函式
//arr:自動重灌載值
//psc: 預分頻係數
void TIM3_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能TIM3時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
//使能GPIOB時鐘(PB5連線LED)和AFIO複用功能時鐘
GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重對映,TIM3_CH2->PB5
//設定該引腳為複用輸出功能,輸出TIM3-CH2的PWM波形
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //複用推輓輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//初始化TIM3
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler = psc;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上計數
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
//TIM3 Channel2 PWM 初始化
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //PWM2模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //捕獲/比較使能暫存器(TIMx_CCER) 輸出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //高電平有效
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //預裝載使能
TIM_Cmd(TIM3, ENABLE); //開啟TIM3
}
main函式如下
int main(void)
{
TIM3_PWM_Init(999,71); //72/(71+1)=1MHz, T=1us*(999+1) = 1ms
TIM_SetCompare2(TIM3,300); //脈寬= 300/1000 *1ms = 0.3ms
while(1)
{
;
}
}
模擬說明:我們的程式碼設定為:
PWM2模式,向上計數,高電平有效。
所以,TIMx_CNT<TIMx_CCRx時通道x為低,否則為高。
5.模擬效果圖如下
6. 疑問
如果調換main函式兩句的順序,則無法達到預期效果。
<span style="font-size:18px;">int main(void) //實驗失敗
{
TIM_SetCompare2(TIM3,300); //脈寬= 300/1000 *1ms = 0.3ms
TIM3_PWM_Init(999,71); //72/(71+1)=1MHz, T=1us*(999+1) = 1ms
while(1)
{
;
}
}</span>
至於原因,這裡不討論,留待以後思考。
7. 用PWM控制LED的亮度
int main(void)
{
u16 led0pwmval = 0; //比較值
u8 dir = 1;
delay_init();
TIM3_PWM_Init(899,0);
while(1)
{
delay_ms(10);
if(dir)
led0pwmval++;
else
led0pwmval--;
if(led0pwmval>300)
dir=0;
if(led0pwmval==0)
dir=1;
TIM_SetCompare2(TIM3,led0pwmval);
}
}
這裡,我們將 led0pwmval 這個值設定為 PWM 比較值,也就是通過 led0pwmval 來控制 PWM 的佔空比,進而控制LED的平均電流,達到控制LED亮度的目的。
led0pwmval 的值從 0 變到 301,然後又從 301 變到 0,如此迴圈,LED的亮度也會跟著從暗變到亮,然後又從亮變到暗。至於這裡的值,我們為什麼取 300,是因為 PWM 的輸出佔空比達到並且超過這個值的時候, LED 亮度變化就不明顯了。