一、PWM是什么?有什么用?
PWM指的是脈沖寬度調制技術,通過對脈沖寬度的調節可以達到通信(如控制舵機)、模擬“模擬輸出”(如調節燈的亮度),前者在以后再結合舵機來講,本文側重講后者。
首先,我們來了解幾個概念:
1、PWM頻率、PWM周期
這是一個約為50Hz的PWM輸出波形
這個PWM的周期約為20ms
PWM頻率指1秒的時間里PWM運行的次數;
PWM周期指一次完整的PWM輸出所使用的時間。
2、占空比
從上往下,占空比分別為25%、50%、75%
占空比指在一個周期內接通的時間占這一周期的比例。
明白這些后,恭喜你已經基本掌握PWM的原理了!
我們知道單片機的IO口只有0和1兩種輸出狀態,只能控制LED的亮與滅,如果我們想要得到下面這樣的輸出效果,思考一下,結合PWM我們可以怎么做?
你可能已經想到了,IO口保持高電平(1)時LED最亮,此時電壓為5V(以5V電壓工作的單片機為例),如果在里面插入低電平,輸出10101010...不就相當于輸出2.5V了嗎?
不嚴謹地說,這樣使用PWM確實能達到“模擬輸出”的效果,但如果真的需要模擬輸出,單單這樣是不夠的(所以前面標了引號),在此不進行細說。
二、怎樣設計PWM程序?
我們先來構造這么一個框架:
1、確定一個單位時間t,每個t內固定地輸出0或1;
2、過了n個t完成一個PWM周期;
3、使用程序控制一個周期內輸出1的數量為m,輸出0的數量為(n-m)。
有了上面的框架,設計程序就不難了:
我們可以使用定時器,每隔一定的時間進入一次中斷,并記錄進入中斷的次數x,直到完成一次PWM周期,將x歸零;
設我們所需要的PWM輸出占空比為y,當x<=y時輸出高電平,x>y時輸出低電平。
這樣,我們的程序基本就設計出來了,是不是很簡單?(〃'▽'〃)
在正式編寫程序前,我們還需要考慮一些小問題:
因為51單片機的運行頻率不高,PWM的頻率也不能設計得太高,過于頻繁地進入中斷也會影響程序的正常運行。
在下面的例程中,我所設置的定時器中斷的間隔為0.1ms,每20ms完成一次PWM周期。
在這一小節的最后,我們整理一下思路,可以得到下面的流程圖:
三、寫個程序試試看!
按上面的流程圖,我們就可以寫一個控制LED亮度的程序了:
#include < reg52.h >
#define PWM_T 200 //產生中斷的時間,因為是24MHz,200即100微秒(0.1毫秒)
#define LED P1
int PWM_count0 = 0; //進入中斷的次數
int PWM0 = 100; //控制PWM的占空比,下同
int PWM1 = 170;
int PWM2 = 188;
int PWM3 = 198;
sbit LED0 = P1^0; //LED引腳定義,下同
sbit LED1 = P1^1;
sbit LED2 = P1^2;
sbit LED3 = P1^3;
void PWM_Start() //PWM初始化函數,打開了定時器0
{
EA = 1;
ET0 = 1;
TMOD = 0x09;
TR0 = 1;
TH0 = (65536-PWM_T)/256;
TL0 = (65536-PWM_T)%256;
}
void main()
{
PWM_Start(); //PWM開始運行
while(1)
{
if(PWM_count0 <= PWM0) //調節LED0的亮度
{
LED0 = 1;
}
else
{
LED0 = 0;
}
if(PWM_count0 <= PWM1) //調節LED1的亮度
{
LED1 = 1;
}
else
{
LED1 = 0;
}
if(PWM_count0 <= PWM2) //調節LED2的亮度
{
LED2 = 1;
}
else
{
LED2 = 0;
}
if(PWM_count0 <= PWM3) //調節LED3的亮度
{
LED3 = 1;
}
else
{
LED3 = 0;
}
}
}
void Timer0() interrupt 1
{
TH0 = (65536-PWM_T)/256;
TL0 = (65536-PWM_T)%256;
PWM_count0++;
if(PWM_count0 == 200) //完成了一個PWM周期,計數變量清零
{
PWM_count0 = 0;
}
}
把上面的程序編譯后下載到開發板上:
小提示:人眼對亮度的感覺不是線性變化的,因此LED0與LED1雖然占空比相差較大,但肉眼感覺亮度不相上下,感興趣的可以去研究一下。
用邏輯分析儀收集一下IO口的輸出信息:
黃框里的為一個PWM周期
上面的程序還有一些需要注意的地方:
1、記得加while循環,因為PWM輸出是持續的,沒有循環就只會進行一個周期;
2、晶振頻率建議設置為24MHz,12MHz也可以,相應地定時器中斷時間也要更改。
我們可以將上面的程序進一步優化,如果我們把if語句寫成子函數,通過參數控制占空比,返回值控制0和1的輸出,程序會簡化很多:
#include < reg52.h >
#define PWM_T 200 //產生中斷的時間,因為是24MHz,200即100微秒(0.1毫秒)
#define LED P1
int PWM_count0 = 0; //進入中斷的次數
int PWM0 = 100; //去掉7~10行 //控制PWM的占空比,下同
int PWM1 = 170;
int PWM2 = 188;
int PWM3 = 198;
sbit LED0 = P1^0; //LED引腳定義,下同
sbit LED1 = P1^1;
sbit LED2 = P1^2;
sbit LED3 = P1^3;
void PWM_Start() //PWM初始化函數,打開了定時器0
{
EA = 1;
ET0 = 1;
TMOD = 0x09;
TR0 = 1;
TH0 = (65536-PWM_T)/256;
TL0 = (65536-PWM_T)%256;
}
int PWM(int PWM_value) //控制PWM輸出的子函數
{
if(PWM_count0 <= PWM_value)
{
return 1;
}
else
{
return 0;
}
}
void main()
{
PWM_Start(); //PWM開始運行
while(1)
{
LED0 = PWM(100); //調節LED0的亮度
LED1 = PWM(170); //調節LED1的亮度
LED2 = PWM(188); //調節LED2的亮度
LED3 = PWM(198); //調節LED3的亮度
}
}
void Timer0() interrupt 1
{
TH0 = (65536-PWM_T)/256;
TL0 = (65536-PWM_T)%256;
PWM_count0++;
if(PWM_count0 == 200) //完成了一個PWM周期,計數變量清零
{
PWM_count0 = 0;
}
}
可以看到,寫成子函數后調用PWM輸出方便了不少。
-
51單片機
+關注
關注
274文章
5705瀏覽量
124081 -
定時器
+關注
關注
23文章
3255瀏覽量
115181 -
脈沖寬度調制
+關注
關注
7文章
81瀏覽量
13766 -
邏輯分析儀
+關注
關注
3文章
214瀏覽量
23234 -
PWM輸出
+關注
關注
1文章
66瀏覽量
5209
發布評論請先 登錄
相關推薦
評論