硬件准备
在编写代码之前,你需要准备以下硬件:

(图片来源网络,侵删)
- 单片机最小系统板:包含STC89C52芯片、晶振(通常11.0592MHz或12MHz)、复位电路。
- LED灯:8个(或其他数量)。
- 电阻:8个,阻值通常为220Ω - 1kΩ,用于限流,防止LED烧坏。
- 杜邦线:若干,用于连接。
- 下载器:如CH340/CP2102模块,用于将程序烧录到单片机。
硬件连接图:
将8个LED的正极(长脚)通过限流电阻连接到单片机的 P0口 (P0.0, P0.1, ..., P0.7),LED的负极(短脚)连接到 GND(地)。
+5V
|
[电阻 R]
|
P0.0 --|>|-- GND (LED1)
P0.1 --|>|-- GND (LED2)
...
P0.7 --|>|-- GND (LED8)
注意:STC89C52的P0口是开漏输出,作为输出时通常需要外接上拉电阻才能正常驱动高电平,如果你的开发板已经集成了上拉电阻,则无需额外处理,如果没有,你可以在P0口和VCC之间接一个排阻(如10kΩ)。
程序实现
我们将从最基础的代码开始,逐步优化。

(图片来源网络,侵删)
基础版 - 逐个点亮,不带延时函数
这个版本最直观,通过循环依次让每个LED亮起,然后熄灭。
#include <reg52.h> // 包含STC89C52的头文件
// 定义LED连接的端口,使用P0口
#define LED_PORT P0
void main(void)
{
// 初始化P0口为输出模式(STC89C52上电后默认为弱上拉输入模式,最好手动设置)
// P0 = 0xFF; // 将P0口所有位设置为1,LED熄灭(共阴极接法)
while(1) // 主循环,让程序跑起来
{
// 第一步:点亮第一个LED (P0.0输出低电平)
LED_PORT = 0xFE; // 二进制 1111 1110,P0.0为0,其余为1
// 这里没有延时,所以你会看到所有LED几乎同时闪烁,效果不佳
// 第二步:点亮第二个LED (P0.1输出低电平)
LED_PORT = 0xFD; // 二进制 1111 1101
// 第三步:点亮第三个LED (P0.2输出低电平)
LED_PORT = 0xFB; // 二进制 1111 1011
// 第四步:点亮第四个LED (P0.3输出低电平)
LED_PORT = 0xF7; // 二进制 1111 0111
// 第五步:点亮第五个LED (P0.4输出低电平)
LED_PORT = 0xEF; // 二进制 1110 1111
// 第六步:点亮第六个LED (P0.5输出低电平)
LED_PORT = 0xDF; // 二进制 1101 1111
// 第七步:点亮第七个LED (P0.6输出低电平)
LED_PORT = 0xBF; // 二进制 1011 1111
// 第八步:点亮第八个LED (P0.7输出低电平)
LED_PORT = 0x7F; // 二进制 0111 1111
// 然后循环回去
}
}
问题分析: 这个程序虽然能实现“跑”的效果,但速度太快,肉眼只能看到所有LED在闪烁,我们必须加入 延时函数。
标准版 - 带有延时函数
这是最常用、最经典的跑马灯实现方式,我们加入一个简单的延时函数来控制速度。
#include <reg52.h>
#define LED_PORT P0
// 简单的延时函数
// 注意:这是一个粗略的延时,其精确时间与编译器优化、晶振频率有关
void Delay(unsigned int t) // t 是一个无符号整型,用于控制延时长短
{
unsigned int i, j;
for (i = 0; i < t; i++)
for (j = 0; j < 120; j++); // 内层循环,通过调整j的值来改变延时
}
void main(void)
{
while(1)
{
// 从右向左逐个点亮
LED_PORT = 0xFE; // 1111 1110
Delay(500); // 延时约500ms
LED_PORT = 0xFD; // 1111 1101
Delay(500);
LED_PORT = 0xFB; // 1111 1011
Delay(500);
LED_PORT = 0xF7; // 1111 0111
Delay(500);
LED_PORT = 0xEF; // 1110 1111
Delay(500);
LED_PORT = 0xDF; // 1101 1111
Delay(500);
LED_PORT = 0xBF; // 1011 1111
Delay(500);
LED_PORT = 0x7F; // 0111 1111
Delay(500);
// 从左向右逐个熄灭 (或者反向跑)
LED_PORT = 0xBF; // 1011 1111
Delay(500);
LED_PORT = 0xDF; // 1101 1111
Delay(500);
LED_PORT = 0xEF; // 1110 1111
Delay(500);
LED_PORT = 0xF7; // 1111 0111
Delay(500);
LED_PORT = 0xFB; // 1111 1011
Delay(500);
LED_PORT = 0xFD; // 1111 1101
Delay(500);
LED_PORT = 0xFE; // 1111 1110
Delay(500);
// 全部熄灭一下
LED_PORT = 0xFF;
Delay(500);
}
}
代码解释:

(图片来源网络,侵删)
#include <reg52.h>:引入了STC89C52寄存器定义的头文件,这样我们才能使用P0等端口。#define LED_PORT P0:使用宏定义,方便以后修改端口,只需改这一行即可。void Delay(unsigned int t):延时函数。- 它通过一个
for循环来消耗时间。 t是一个参数,我们可以传入不同的值来控制延时的长短。- 注意:这种延时并不精确,但对于跑马灯这种应用完全足够,如果需要精确延时,需要使用定时器/计数器。
- 它通过一个
main()函数:程序的入口。while(1):创建一个无限循环,保证程序持续运行。LED_PORT = ...:给P0端口赋值,我们赋的是十六进制数,它对应8个二进制位。0代表低电平,LED点亮;1代表高电平,LED熄灭。Delay(500):调用延时函数,让LED的亮灭状态持续一段时间,形成视觉暂留效果。
进阶版 - 使用移位和循环优化代码
方案二的代码虽然能工作,但太冗长,我们可以用 位操作 来优化,让代码更简洁、更专业。
#include <reg52.h>
#include <intrins.h> // 包含了_nop_()函数和循环左移/右移函数的头文件
#define LED_PORT P0
void Delay(unsigned int t)
{
unsigned int i, j;
for (i = 0; i < t; i++)
for (j = 0; j < 120; j++);
}
void main(void)
{
unsigned char led_pattern = 0xFE; // 初始模式,1111 1110,最右边LED亮
while(1)
{
// 从右向左循环移位
for(int i = 0; i < 8; i++)
{
LED_PORT = led_pattern; // 输出当前模式
Delay(300); // 延时
// 将led_pattern的值循环左移一位
// 1111 1110 -> 1111 1101 -> 1111 1011 ...
led_pattern = _crol_(led_pattern, 1);
}
// 从左向右循环移位
for(int i = 0; i < 8; i++)
{
LED_PORT = led_pattern; // 输出当前模式
Delay(300); // 延时
// 将led_pattern的值循环右移一位
// 0111 1111 -> 1011 1111 -> 1101 1111 ...
led_pattern = _cror_(led_pattern, 1);
}
}
}
代码解释:
#include <intrins.h>:这个头文件是Keil C51编译器特有的,提供了一些编译器内置函数。unsigned char led_pattern = 0xFE;:我们用一个变量led_pattern来存储当前LED的亮灭状态。_crol_(led_pattern, 1):这是intrins.h中的 循环左移 函数。- 它将
led_pattern的所有位向左移动1位,最左边移出的位会补到最右边。 _crol_(0xFE, 1)的结果是0xFD。_crol_(0x7F, 1)的结果是0xFE。
- 它将
_cror_(led_pattern, 1):这是 循环右移 函数,原理与左移相反。
这个版本的代码量大大减少,逻辑也更清晰,是实际开发中更推荐的做法。
如何编译和下载
- 编译:使用Keil μVision等集成开发环境(IDE),将上面的代码保存为
.c文件(led.c),然后新建一个工程,将此文件添加进去,编译生成.hex文件。 - 下载:使用STC-ISP等下载软件,将生成的
.hex文件烧录到你的STC89C52单片机中。 - 运行:烧录成功后,给单片机上电,你就能看到跑马灯效果了。
希望这个详细的教程能帮助你理解单片机跑马灯的原理和实现!
