PWM的應用可以說非常廣泛,控制電機速度、燈光亮度、通信調制等眾多領域。
PWM的問題小伙伴問的比較多,最近也在用PWM,這里就分享一下關于PWM的一些內容。
什么是PWM?
PWM: Pulse Width Modulation,脈沖寬度調制。
網上的解釋很多,通過下圖,你就能直觀的理解PWM,其實就是高低電平組成的脈沖信號。
通過改變其中頻率(脈沖周期)、占空比,就能應用在很多場合。
PWM常見輸出方式
通過上面描述,PWM就是一個IO口以不同的時間周期輸出高、低電平。
1.新手 (菜鳥) 級別
while循環中,阻塞延時,控制IO口高低輸出:
while(1)
{
IO口高電平
Delay阻塞延時
IO口低電平
Delay阻塞延時
}
2.入門 (初級) 級別
while循環中,非阻賽延時,控制IO口高低輸出:
while(1)
{
IO口高電平
Delay非阻塞延時
IO口低電平
Delay非阻塞延時
}
3.熟悉 (中級) 級別
定時器中斷控制IO高低電平輸出:
定時器中斷配置 ——> 啟動定時器 ——> 響應中斷,控制IO高低電平···
4.熟練 (中級+) 級別
定時器PWM硬件控制輸出:
配置PWM對應的IO,以及定時器PWM輸出 ——> 啟動PWM自動輸出···
void AppTask(void *p_arg)
{
PWM_TIM_Configuration();
PWM_Output(頻率, 占空比);
while(1)
{
//自己的應用代碼
}
}
比較:
上面幾種PWM輸出方式,前面三種都會CPU干預PWM的輸出,也就是會占用CPU資源,特別是前面兩種方式,不僅占用CPU,誤差還比較大。
使用第三種中斷方式,如果頻率比較高,CPU消耗的也比較嚴重。這種情況適合于沒有硬件PWM輸出的單片機。
第四種就是單片機自帶硬件PWM輸出功能,只需要簡單配置就可以自動輸出PWM波形,無需CPU干預。
硬件輸出PWM例子
這里以大家熟悉的STM32F1為例:為大家簡單分享一下硬件定時器輸出PWM波形。
PWM定時器相關宏定義:
//定時器計數時鐘(1M次/秒)
#define PWM_COUNTER_CLOCK 1000000
//預分頻值(與系統時鐘、計數值有關)
#define PWM_PRESCALER_VALUE (SystemCoreClock/PWM_COUNTER_CLOCK - 1)
**PWM配置:
**
/**
* @brief 定時器PWM輸出配置
* @param 無
* @retval 無
*/
void PWM_TIM_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
/* 時鐘配置 */
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);
/* 時基配置 */
TIM_TimeBaseStructure.TIM_Prescaler = PWM_PRESCALER_VALUE; //預分頻值
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上計數
TIM_TimeBaseStructure.TIM_Period = 0xFFFF; //定時周期(暫定值)
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對應通道1)
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
}
PWM輸出函數接口:
/**
* @brief 輸出PWM
* @param Frequency:頻率
Dutycycle:占空比
* @retval 無
*/
void PWM_Output(uint32_t Frequency, uint32_t Dutycycle)
{
uint32_t tim_period;
uint32_t tim_pulse;
tim_period = PWM_COUNTER_CLOCK/Frequency - 1; //計算出計數周期(決定輸出的頻率)
tim_pulse = (tim_period + 1)*Dutycycle / 100; //計算出脈寬值(決定PWM占空比)
TIM_Cmd(TIM2, DISABLE); //失能TIM
TIM_SetCounter(TIM2, 0); //計數清零
TIM_SetAutoreload(TIM2, tim_period); //更改頻率
TIM_SetCompare1(TIM2, tim_pulse); //更改占空比(TIM_SetCompare1對應通道1)
TIM_Cmd(TIM2, ENABLE); //使能TIM
}
初始化配置,調用函數接口,直接就輸出PWM波形了:
void AppTask(void *p_arg)
{
PWM_TIM_Configuration();
PWM_Output(1000, 20);
while(1)
{
//自己的應用代碼
}
}
輸出PWM波形:
說明:
本例使用的是STM32標準外設庫,如果要深入理解其中原理,還是建議使用標準外設庫。
當然,如果想要快速使用PWM這個功能,不想理解其原理,可以直接使用STM32CubeMX配置生成代碼:
配置注意事項
想要更加精確控制,并更加滿足應用層的需求,就需要自己一步一步深入了解原理。
下面說幾點常見的問題吧。
1.引腳映射
如果你使用的引腳需要映射,就需要配置對應的參數。
比如:STM32F1使用PB11(需要查看數據手冊):
需要增加對應的“映射”代碼:
//復用功能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//定時器(PWM)引腳映射
GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE);
2.頻率和占空比精度
如果使用32位定時器的話,頻率范圍更寬、精度也可以達到更高。比如:頻率:0.01Hz、 占空比0.01%等。
如果是16位的話,其中的參數都不能超過16位(65535):
#define PWM_COUNTER_CLOCK 1000000
#define PWM_PRESCALER_VALUE (SystemCoreClock/PWM_COUNTER_CLOCK - 1)
tim_period = PWM_COUNTER_CLOCK/Frequency - 1; //計算出計數周期(決定輸出的頻率)
tim_pulse = (tim_period + 1)*Dutycycle / 100; //計算出脈寬值(決定PWM占空比)
具體可根據自己情況進行配置,比如PWM(定時器)計數時鐘、分頻值等。
實際應用代碼,建議增加各個參數的判斷,以防越界(這里為了方便理解,就寫的比較簡單)。
3.更多
STM32都有硬件PWM輸出功能,但不同的系列,其配置可能略有一些差異,簡單參考官方例程以及手冊。
現在大部分單片機都自帶有硬件PWM輸出功能,硬件的好處就是不用CPU干預。如果沒有,可以嘗試上面說的定時器中斷的方式。
-
IO
+關注
關注
0文章
448瀏覽量
39148 -
PWM
+關注
關注
114文章
5186瀏覽量
213862 -
通信調制器
+關注
關注
0文章
2瀏覽量
5265 -
電機速度控制
+關注
關注
0文章
10瀏覽量
1410
發布評論請先 登錄
相關推薦
評論