一、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文章
5702浏览量
123586 -
定时器
+关注
关注
23文章
3248浏览量
114775 -
脉冲宽度调制
+关注
关注
7文章
81浏览量
13726 -
逻辑分析仪
+关注
关注
3文章
214浏览量
23168 -
PWM输出
+关注
关注
1文章
66浏览量
5138
发布评论请先 登录
相关推荐
评论