什么是PWM?
PWM:Pulse Width Modulation,脈沖寬度調(diào)制。 ?網(wǎng)上的解釋很多,通過(guò)下圖,你就能直觀的理解PWM,其實(shí)就是高低電平組成的脈沖信號(hào)。 ?通過(guò)改變其中頻率(脈沖周期)、占空比,就能應(yīng)用在很多場(chǎng)合。 ?PWM常見(jiàn)輸出方式
通過(guò)上面描述,PWM就是一個(gè)IO口以不同的時(shí)間周期輸出高、低電平。 ?1.新手(菜鳥(niǎo))級(jí)別while循環(huán)中,阻塞延時(shí),控制IO口高低輸出:
while(1)
{
IO口高電平
Delay阻塞延時(shí)
IO口低電平
Delay阻塞延時(shí)
}
阻塞延時(shí)可以是:軟件模擬延時(shí),定時(shí)器阻塞延時(shí)等。
?2.入門(初級(jí))級(jí)別while循環(huán)中,非阻賽延時(shí),控制IO口高低輸出:
while(1)
{
IO口高電平
Delay非阻塞延時(shí)
IO口低電平
Delay非阻塞延時(shí)
}
?非阻賽延時(shí)可以是:定時(shí)器標(biāo)識(shí)檢測(cè)、RTOS(系統(tǒng))延時(shí)等。
?3.熟悉(中級(jí))級(jí)別定時(shí)器中斷控制IO高低電平輸出:定時(shí)器中斷配置 ——>?啟動(dòng)定時(shí)器?——>?響應(yīng)中斷,控制IO高低電平···?4.熟練(中級(jí)+)級(jí)別定時(shí)器PWM硬件控制輸出:
配置PWM對(duì)應(yīng)的IO,以及定時(shí)器PWM輸出?——>?啟動(dòng)PWM自動(dòng)輸出···?
void AppTask(void *p_arg)
{
PWM_TIM_Configuration();
PWM_Output(頻率, 占空比);
while(1)
{
//自己的應(yīng)用代碼
}
}
?比較:上面幾種PWM輸出方式,前面三種都會(huì)CPU干預(yù)PWM的輸出,也就是會(huì)占用CPU資源,特別是前面兩種方式,不僅占用CPU,誤差還比較大。
?使用第三種中斷方式,如果頻率比較高,CPU消耗的也比較嚴(yán)重。這種情況適合于沒(méi)有硬件PWM輸出的單片機(jī)。
?第四種就是單片機(jī)自帶硬件PWM輸出功能,只需要簡(jiǎn)單配置就可以自動(dòng)輸出PWM波形,無(wú)需CPU干預(yù)。
?硬件輸出PWM例子
這里以大家熟悉的STM32F1為例:為大家簡(jiǎn)單分享一下硬件定時(shí)器輸出PWM波形。 ?PWM定時(shí)器相關(guān)宏定義:
//定時(shí)器計(jì)數(shù)時(shí)鐘(1M次/秒)
#define PWM_COUNTER_CLOCK 1000000
//預(yù)分頻值(與系統(tǒng)時(shí)鐘、計(jì)數(shù)值有關(guān))
#define PWM_PRESCALER_VALUE (SystemCoreClock/PWM_COUNTER_CLOCK - 1)
?
?PWM配置:
/**
* @brief 定時(shí)器PWM輸出配置
* @param 無(wú)
* @retval 無(wú)
*/
void PWM_TIM_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* 時(shí)鐘配置 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* 引腳配置 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 時(shí)基配置 */
TIM_TimeBaseStructure.TIM_Prescaler = PWM_PRESCALER_VALUE; //預(yù)分頻值
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上計(jì)數(shù)
TIM_TimeBaseStructure.TIM_Period = 0xFFFF; //定時(shí)周期(暫定值)
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //分頻因子
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
/* PWM模式配置 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //輸出PWM1模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能輸出
TIM_OCInitStructure.TIM_Pulse = 0; //脈寬值(暫定值)
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //輸出極性(TIM_OC1對(duì)應(yīng)通道1)
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
}
?PWM輸出函數(shù)接口:
/**
* @brief 輸出PWM
* @param Frequency:頻率
Dutycycle:占空比
* @retval 無(wú)
*/
void PWM_Output(uint32_t Frequency, uint32_t Dutycycle)
{
uint32_t tim_period;
uint32_t tim_pulse;
tim_period = PWM_COUNTER_CLOCK/Frequency - 1; //計(jì)算出計(jì)數(shù)周期(決定輸出的頻率)
tim_pulse = (tim_period + 1)*Dutycycle / 100; //計(jì)算出脈寬值(決定PWM占空比)
TIM_Cmd(TIM2, DISABLE); //失能TIM
TIM_SetCounter(TIM2, 0); //計(jì)數(shù)清零
TIM_SetAutoreload(TIM2, tim_period); //更改頻率
TIM_SetCompare1(TIM2, tim_pulse); //更改占空比(TIM_SetCompare1對(duì)應(yīng)通道1)
TIM_Cmd(TIM2, ENABLE); //使能TIM
}
初始化配置,調(diào)用函數(shù)接口,直接就輸出PWM波形了:
void AppTask(void *p_arg)
{
PWM_TIM_Configuration();
PWM_Output(1000, 20);
while(1)
{
//自己的應(yīng)用代碼
}
}
?輸出PWM波形:
?說(shuō)明:本例使用的是STM32標(biāo)準(zhǔn)外設(shè)庫(kù),如果要深入理解其中原理,還是建議使用標(biāo)準(zhǔn)外設(shè)庫(kù)。
?當(dāng)然,如果想要快速使用PWM這個(gè)功能,不想理解其原理,可以直接使用STM32CubeMX配置生成代碼:
?配置注意事項(xiàng)
想要更加精確控制,并更加滿足應(yīng)用層的需求,就需要自己一步一步深入了解原理。 ?下面說(shuō)幾點(diǎn)常見(jiàn)的問(wèn)題吧。 ?1.引腳映射如果你使用的引腳需要映射,就需要配置對(duì)應(yīng)的參數(shù)。 ?比如:STM32F1使用PB11(需要查看數(shù)據(jù)手冊(cè)): ?需要增加對(duì)應(yīng)的“映射”代碼:
//復(fù)用功能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//定時(shí)器(PWM)引腳映射
GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE);
?2.頻率和占空比精度如果使用32位定時(shí)器的話,頻率范圍更寬、精度也可以達(dá)到更高。比如:頻率:0.01Hz、?占空比0.01%等。
?如果是16位的話,其中的參數(shù)都不能超過(guò)16位(65535):
#define PWM_COUNTER_CLOCK 1000000
#define PWM_PRESCALER_VALUE (SystemCoreClock/PWM_COUNTER_CLOCK - 1)
tim_period = PWM_COUNTER_CLOCK/Frequency - 1; //計(jì)算出計(jì)數(shù)周期(決定輸出的頻率)
tim_pulse = (tim_period + 1)*Dutycycle / 100; //計(jì)算出脈寬值(決定PWM占空比)
?具體可根據(jù)自己情況進(jìn)行配置,比如PWM(定時(shí)器)計(jì)數(shù)時(shí)鐘、分頻值等。
?實(shí)際應(yīng)用代碼,建議增加各個(gè)參數(shù)的判斷,以防越界(這里為了方便理解,就寫的比較簡(jiǎn)單)。
?3.更多STM32都有硬件PWM輸出功能,但不同的系列,其配置可能略有一些差異,簡(jiǎn)單參考官方例程以及手冊(cè)。
?現(xiàn)在大部分單片機(jī)都自帶有硬件PWM輸出功能,硬件的好處就是不用CPU干預(yù)。如果沒(méi)有,可以嘗試上面說(shuō)的定時(shí)器中斷的方式。
?審核編輯:湯梓紅
評(píng)論
查看更多