1. 初识PWM:它到底是什么,为什么51单片机也能玩?
如果你刚开始接触单片机,听到“PWM”这个词可能会觉得有点高大上,感觉是那些高级芯片才有的功能。其实不然,它的核心思想非常简单,而且用我们最熟悉的51单片机,通过软件就能轻松实现。我刚开始学的时候,也觉得这玩意儿很神秘,直到自己动手用代码“捏”出一个PWM波,控制了一个LED从暗慢慢变亮,那种感觉,就像第一次让硬件“活”了起来。
PWM,中文叫脉冲宽度调制。这个名字听起来复杂,咱们拆开来看。“脉冲”就是一下一下的电平跳变,像人的脉搏一样;“宽度调制”就是调节这个“一下”的持续时间。你可以把它想象成开关水龙头。如果你把水龙头一直开到最大,水流就是最大的(相当于输出高电平1)。如果你快速地开一下、关一下,但开的时间非常短,关的时间非常长,那么平均下来的水流就会很小。PWM干的就是这个事:通过快速、周期性地开关,来调节在一个周期内,“开”的时间所占的比例。这个比例,就是我们常说的占空比。
举个例子,假设一个周期是10毫秒。如果在这10毫秒里,有5毫秒是“开”(高电平),5毫秒是“关”(低电平),那么占空比就是50%。如果“开”的时间是2毫秒,“关”是8毫秒,占空比就是20%。占空比越大,平均输出的“能量”就越高。我们的眼睛、电机这些“有惯性”的系统,是跟不上这种高速开关的,它们感受到的就是一个平均效果。所以,对LED来说,50%占空比看起来就是半亮;对直流电机来说,50%占空比就是半速转动。
那为什么51单片机没有硬件PWM也能用呢?这就是软件模拟的妙处了。硬件PWM是芯片内部有专门的电路帮你自动生成这种波形,你只需要配置几个寄存器。而软件PWM,就是咱们写程序,手动控制一个IO口的高低电平,通过延时函数来精确控制“开”和“关”的时间长度,从而“拼”出一个PWM波。虽然精度和灵活性可能不如硬件,但对于呼吸灯、电机调速这类对实时性要求不极端高的应用,完全够用,而且是理解PWM原理的绝佳途径。接下来,我们就从最简单的呼吸灯开始,亲手“造”出PWM。
2. 手搓PWM:从零开始实现一个呼吸灯
呼吸灯是学习PWM最直观、最有成就感的项目。看着自己板子上的LED像呼吸一样柔和地明暗变化,你会立刻理解占空比的意义。我当年第一个成功的单片机实验就是这个,虽然代码简单,但那种掌控硬件的快乐至今难忘。
2.1 硬件连接与核心思路
硬件再简单不过了:找一块51开发板(比如经典的STC89C52),将一个LED的正极通过一个220欧姆到1k欧姆的限流电阻,连接到单片机的任意一个IO口(比如P2.0),LED的负极接地(GND)。这样就完成了。整个实验的核心,就是写程序让这个IO口输出我们“定制”的PWM波。
软件模拟PWM的关键在于两个延时。我们设定一个固定的周期,比如这个周期总共是100个单位时间(这个单位可以是微秒级别的循环次数)。在这个周期内,我们需要决定:高电平(灯亮)占多少单位时间,低电平(灯灭)占多少单位时间。如果我们让高电平时间从0慢慢增加到100,再慢慢减少到0,LED就会经历从灭到最亮,再从最亮到灭的过程,这就是呼吸效果。
听起来是不是很简单?但这里有个坑我踩过:如果你直接用一个大循环,让高电平时间递增,然后输出,你会发现灯是亮了,但闪烁得非常厉害,根本看不出呼吸效果。为什么?因为你的周期太长了!人眼有视觉暂留效应,但前提是闪烁频率要足够高,通常要高于50Hz(即周期小于20ms)。所以,我们必须在两个层面上循环:内层循环负责高速生成一个固定占空比的PWM波,维持一段时间让人眼能稳定感知到这个亮度;外层循环才负责缓慢地改变这个占空比,实现亮度的平滑过渡。
2.2 代码逐行解析与优化
我们来看一个经过我优化和详细注释的代码,它比原始文章里的更清晰,也更容易调整效果。
#include <REGX52.H>
sbit LED = P2^0; // 定义LED连接的引脚
// 一个简单的微秒级延时函数,通过循环空跑来消耗时间
void Delay_us(unsigned int t) {
while(t--);
}
void main() {
unsigned char highTime, i; // highTime:高电平时间,i:内层循环计数器
while(1) {
// 呼吸变亮的过程:高电平时间从0增加到100
for(highTime=0; highTime<100; highTime++) {
// 内层循环:重复生成20个周期相同的PWM波,以稳定当前亮度
for(i=0; i<20; i++) {
LED = 0; // 输出低电平,LED灭
Delay_us(highTime); // 低电平持续时间 = hi


1万+

被折叠的 条评论
为什么被折叠?



