一、为什么叫"流水灯"
本质是一组LED按顺序轮流点亮 ,视觉上像水流一样移动。实现手段就是让单片机的一个端口(通常用P1或P2)输出一组循环移位的值,每个值点亮不同的LED组合,配合延时,人眼就看到"流动"效果。
二、硬件电路:LED怎么接
51单片机驱动LED有两种接法,理解这一点是看懂代码的前提:
共阳极接法(灌电流驱动,最常用)
-
LED阳极接VCC,阴极通过限流电阻接单片机IO口。
-
IO口输出低电平(0) 时LED亮,输出**高电平(1)**时灭。
-
51的IO口灌电流能力比拉电流强,所以这种接法更可靠。
共阴极接法
-
LED阴极接地,阳极通过电阻接IO口。
-
IO口输出**高电平(1)**时亮,低电平灭。
以8个LED接P1口、共阳极接法为例:要让最低位LED亮,P1应输出 1111 1110(即0xFE);次低位亮则 1111 1101(0xFD)......依次类推。

图中的P10等是网络标签,相同的网络标签表示两条线连在一起
RST接的高电平复位电路
51单片机的RST引脚内部有一个施密特触发结构 :当RST引脚上的电压高于某个阈值(通常约1.5V~2.5V,具体随芯片型号和电源电压变化)并持续2个机器周期以上(12MHz晶振下约2μs),芯片就会进入复位状态。正常工作时RST应保持低电平(接近0V),复位时则需把它拉高。这就是"高电平复位"的含义。
复位后单片机会:PC归零(从0000H重新执行)、各SFR恢复默认值、端口输出高电平(P0~P3默认全1)。所以复位期间LED通常全灭(共阳极接法下输出1即灭),复位结束后程序从头跑起。
三、代码实现
cs
#include<reg51.h>
#define out P1
void delay_ms(unsigned int ms)//延时函数
{
unsigned int k, j;
for (k = 0; k < ms; k++)
for (j = 0; j < 110; j++);
}
void main()
{
unsigned int i = 0;
unsigned int head = 0;
unsigned int tail = 0;
unsigned int mid = 0;
unsigned int LED[] = { 0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f };
//p1口低电平亮灯,LED[0]表示P10上的灯亮
unsigned int sz = sizeof(LED) / (sizeof(LED[0]));
out = 0xff;
while (tail < sz)//找到中间和尾部
{
tail = tail + 2;
head++;
mid = head;
}
while (1)
{
for (i; i < sz; i++)//从0-7依次亮灯
{
out = LED[i];
delay_ms(200);
}
while(i)//从7-0依次亮灯
{
i--;
out = LED[i];
delay_ms(200);
}
head = tail = mid;
while (head && (tail < sz))//从中间左右依次亮灯
{
out = LED[head];
delay_ms(200);
out = LED[tail];
head--;
tail++;
delay_ms(200);
}
}
}
四、仿真
Keil 端 :新建工程 → 编写代码 → 编译并生成 .hex 文件
Proteus 端 :绘制电路图 → 双击单片机元件 → 在 Program File 栏选中刚才生成的 .hex 文件
屏幕录制