一、PWM介绍
PWM是脉宽调制(Pulse Width Modulation)的缩写,你可以把它想象成一个迷你的开关,这个开关可以非常非常快地打开和关闭。正如你在开关灯时,灯是全亮或全暗,同样,这个小开关也只有两种状态:全开(满电)或全关(没电)。但是,如果我们足够快地打开和关闭这个开关,我们就可以获得介于全开和全关之间的任何效果。这就是PWM的原理。
举个例子,假设你的小车上有一个可以用PWM控制的电动风扇。如果你想让风扇全速运转,你就让开关一直保持打开状态(这就像把灯开到最大一样)。如果你想让风扇停止运转,你就让开关保持关闭状态(就像把灯关掉一样)。但是,如果你想让风扇以半速运转,那么你可以让该开关半时间打开,半时间关闭。这样,风扇就会以你想要的速度运转了。
总的来说,PWM就像是一个快速的开关,可以控制各种设备的功率,从而改变它们的工作状态。不论是控制小车的速度,亮度的LED灯,还是家里的空调,使用PWM的设备都无处不在。
二、PWM的两个重要参数
2.1 频率
频率是指PWM信号每秒钟重复的次数。例如,如果一个PWM信号的频率是100Hz,那就意味着这个信号在每秒钟内会重复100次。
2.2 占空比
占空比是指PWM信号在一个周期内处于"高"电平(通常是电压的最大值)的时间占整个周期时间的百分比。例如,如果一个PWM信号的频率是100Hz,每个周期的时间就是1/100秒,如果在这个周期内,它有1/2的时间处于高电平,那么我们就说它的占空比是50%。
三、51生成PWM
3.1 如何实现
有接触过51就可以知道,51的定时器不像STM32可以通过配置相关寄存器直接输出PWM。那么51怎么实现PWM呢?
具体做法:
在定时器中断中不断翻转IO状态来模拟PWM,比如每隔100us进入一次定时器中断,进入100次中断算一个周期,(T=100us*100=10ms)那么PWM的频率就是
1
s
/
100
u
s
∗
100
=
100
H
z
1s/100us*100=100Hz
1s/100us∗100=100Hz
在第50次进入中断时,翻转一次IO口状态,那么我们就生成了占空比为50%,频率为100Hz的PWM波。
3.2 定时器初始化
PWM周期和占空比控制精度决定了定时器的初始化,例如PWM频率为100Hz,控制精度为1%,那么定时器进入中断的频率就为: 1 / 100 H z ∗ 100 = 100 u s 1/100Hz*100=100us 1/100Hz∗100=100us
如果我们想把控制精度提高到0.1%,那么定时器进入中断的频率为:
1 / 100 H z ∗ 1000 = 10 u s 1/100Hz*1000=10us 1/100Hz∗1000=10us
三、代码示例
这里演示的PWM频率为100Hz,控制精度为1%。
这里我在keyscan里面写了一行duty+=25;
也就是S7按键每按下一次,LED灯变亮一点,一共有四个档位,大家可以把代码直接复制到工程里面直接使用。
#include <stc15f2k60s2.h>
/************* bit define ***************/
sbit R1=P3^0;
sbit R2=P3^1;
sbit R3=P3^2;
sbit R4=P3^3;
sbit C1=P4^4;
sbit C2=P4^2;
sbit C3=P3^5;
sbit C4=P3^4;
/************* end define ***************/
volatile unsigned char count=0;
unsigned char duty=0;
void Delay20ms(void) //@12.000MHz
{
unsigned char data i, j;
i = 234;
j = 115;
do
{
while (--j);
} while (--i);
}
void selectHC573(unsigned char num)
{
switch(num)
{
case 4:
P2=(P2 & 0x1f) | 0x80;
break;
case 5:
P2=(P2 & 0x1f) | 0xa0;
break;
case 6:
P2=(P2 & 0x1f) | 0xc0;
break;
case 7:
P2=(P2 & 0x1f) | 0xe0;
break;
case 0:
P2=(P2 & 0x1f) | 0x00;
break;
}
}
void system_Init()
{
selectHC573(5);
P0=0x00;
selectHC573(0);
selectHC573(4);
P0=0xff;
selectHC573(0);
}
void keyscan()
{
char keyID=-1;
R1=0;R2=1;R3=1;R4=1; //扫描第一行
C1=1;C2=1;C3=1;C4=1;
if(C1==0)
{
Delay20ms();
if(C1==0)
{
while(C1==0);
duty+=25;
if(duty>100)
{
duty=0;
}
}
}
}
void Timer0_Isr(void) interrupt 1
{
count++;
if(count==100) // 一个周期结束
{
count=0;
}
if(count <= duty)
{
selectHC573(4);
P0=~0x01; // 点亮LED灯
selectHC573(0);
}
else
{
selectHC573(4);
P0=~0x00; // 熄灭LED灯
selectHC573(0);
}
}
void Timer0_Init(void) //100us@12.000MHz
{
AUXR &= 0x7F; //Timer clock is 12T mode
TMOD &= 0xF0; //Set timer work mode
TL0 = 0x9C; //Initial timer value
TH0 = 0xFF; //Initial timer value
TF0 = 0; //Clear TF0 flag
TR0 = 1; //Timer0 start run
ET0 = 1; //Enable timer0 interrupt
}
int main()
{
system_Init();
Timer0_Init();
EA=1;
while(1)
{
keyscan();
}
}