本文介绍了基于51单片机的流水灯实验,主要内容包括:
实验目的:掌握Proteus仿真软件和Keil C51编程的基本使用方法,理解51单片机最小系统构成及流水灯原理。
硬件设计:使用AT89C51单片机构建最小系统(包含晶振电路和复位电路),通过P0口驱动8个LED,采用灌电流方式连接,并添加了上拉电阻。
软件实现:通过C51编程控制LED流水显示,包含延时函数设计(1ms基准)和端口控制逻辑。
重点知识:详细讲解了单片机最小系统的三大部分(主控、晶振、复位电路)原理,以及LED接口电路的计算方法。
开发环境:使用Proteus 9.0进行电路仿真,Keil 4进行程序开发。
该实验可作为51单片机入门学习项目,帮助理解I/O口控制、时序设计等基础概念。
一、 教学目的:
1、 理解,熟悉proteus的使用方法。
2、 学会51单片机的基本原理。
3、 学会流水灯的运行方式。
4、 熟悉C51的编程。
二、 课前准备:
1、 准备一个电脑。
2、 下载和安装一个proteus9.0软件。(安装教程关注本文最下面的公众号,回复 0005)
3、 下载和安装一个keil5 C51软件。(安装教程关注本文最下面的公众号,回复 0007)
PS:本教程使用的Proteus9.0和KEIL5 C51
三、 硬件电路设计:
1、 选择元器件
2、 搭建电路
2.1 单片机最小系统介绍
单片机最小系统包括三大部分。第一部分是单片机主电路,主要器件为AT89C51单片机,共有40个引脚。第二部分是复位电路,主要由电阻、按键和电容组成。第三部分是晶振电路,主要由电容以及晶振组成,用来产生晶振频率。
2.1.1 AT89C51单片机
AT89C51是由Atmel公司生产的一款低电压、高性能CMOS 8位微处理器,属于MCS-51系列兼容单片机。其核心特点是将多功能8位CPU与可闪烁可编程可擦除只读存储器(FPEROM)集成于单芯片中,为嵌入式控制系统提供了灵活性高且成本低廉的解决方案,广泛应用于工业控制、智能家居、电子仪表等领域。AT89C51作为经典的8位单片机,以其高性价比、强兼容性和丰富的外设资源,成为嵌入式开发的入门首选。
晶振电路由电容C2、电容C3以及晶振X1组成。其中电容主要是稳定频率和快速起振,电容值在5~30pf,典型值是30pf。本实验电容大小均为30pf,它的一端用于连接晶振Y1,另一端则连接在电源的地上。晶振Y1主要用于产生晶振频率,其大小要小于12兆赫兹,典型值为:6MHZ、12MHZ、11.0592MHZ,其中,11.0592MHZ应用最多,本实验晶振采用11.0592MHZ。晶振电路的输入端与单片机的XTAL1引脚相连,输出端与单片机的XTAL2引脚相连,当接通晶振电路后,就会形成震荡现象。
2.1.3 复位电路
复位电路是AT89C51单片机系统的重要组成部分,其核心功能是在特定条件下将单片机恢复到初始状态,确保系统从已知的初始配置开始运行。根据触发机制的不同,复位方法可分为以下三类:
1)上电复位:电路通电瞬间自动触发复位,依赖电源电压上升过程中产生的暂态信号实现初始化。
2)手动复位:通过外部按键控制,当按键按下并保持至少2个机器周期(约2μs@12MHz主频)的高电平时,复位信号生效。
3)自动复位:根据程序逻辑或外部电路状态(如 watchdog 定时器溢出)自动触发,适用于系统异常恢复场景。
复位信号要求为:
1)电平标准:复位引脚(RST)需输入高电平,低电平无效。
2)持续时间:最小脉冲宽度为2个机器周期,具体时长与系统时钟频率相关(如12MHz时钟下需≥2μs)。
3)引脚特性:RST引脚内部无上拉电阻,需通过外部电路提供稳定的高电平驱动。
典型电路结构为
RC复位电路:由电阻R9(10kΩ)、电容C1(10μF)串联组成,上电时电容充电过程使RST引脚维持约10ms高电平,满足复位时序要求。
按键复位电路:在RC电路基础上并联复位按键,按下后直接将RST引脚拉至高电平,松开后电容放电恢复低电平。
2.2 LED接口电路介绍
LED与限流电阻:每个LED阳极串联1个限流电阻(实物中推荐470Ω~2kΩ)后连接至P1口引脚。当单片机引脚输出低电平时,电流通过LED与电阻形成回路,LED点亮;输出高电平时,LED熄灭。
1)硬件参数匹配
限流电阻计算:根据LED正向压降(约1.8~ 2.2V)和工作电流(10~20mA),通过公式 R =(Vcc-Vled) /Iled 例如:5V电源下,限流电阻R=(5v-2v)/10ma = 300Ω,实际往往选用1k电阻以留有余量。在proteus仿真中,对电流的限制就小,为了增加仿真时LED的亮度,本仿真采用100欧姆的限流电阻。
2) 端口驱动能力:P0口每引脚最大灌电流为10mA,总电流不超过26mA。
PS:
proteus中51单片机的P0口需要上拉电阻
(1)原因:
51 单片机的 P0 口是一个漏极开路(Open Drain) 结构的双向 I/O 口,其内部没有内置上拉电阻(而 P1、P2、P3 口内部都有内置上拉电阻)。漏极开路结构意味着:当 P0 口作为通用输出口时,若输出高电平(1),内部晶体管截止,此时 P0 口引脚处于 “悬空” 状态,无法稳定输出高电平(电压会随外部电路波动)。只有外接上拉电阻后,P0 口输出高电平时,电流才能通过上拉电阻流向电源(VCC),使引脚稳定在高电平(约等于 VCC 电压)。实际中P0口上拉电阻往往采用10K的排阻。
四、 程序设计:
1、头文件和宏定义
#include<reg52.h>
#include "intrins.h"
#define uchar unsigned char
#define uint unsigned int
1.1 、头文件
#include是引用头文件,本程序引入reg52.h和intrins.h 2个头文件
1)reg52.h头文件介绍
reg52.h是51单片机C语言开发中常用的标准头文件,其核心作用是定义单片机内部特殊功能寄存器(SFR)及位操作符号,使开发者无需直接操作底层硬件地址,即可通过直观的符号(如P1、TCON等)编写控制程序,简化开发流程并提高代码可读性。
reg52.h头文件的关键功能
特殊功能寄存器(SFR)定义
头文件中通过sfr关键字定义了单片机内部所有特殊功能寄存器的地址映射,例如:
sfr P0 = 0x80;(P0端口寄存器地址)
sfr TCON = 0x88;(定时器控制寄存器地址)
sfr SCON = 0x98;(串口控制寄存器地址)
这些定义使开发者可直接使用P0 = 0xFF;等语句操作硬件,而非记忆0x80等十六进制地址。
位操作符号定义
针对寄存器中的可位寻址位,头文件通过sbit关键字定义了位操作符号,例如:
sbit TR0 = 0x8C;(定时器T0运行控制位)
通过这些定义,可直接使用TF1 = 0;等语句操作单个位,无需通过位运算间接控制。
2)intrins.h 头文件介绍
intrins.h是专为8051系列单片机(如AT89C51)设计的C语言头文件,其核心功能是封装汇编级指令为C函数,提供对单片机底层硬件操作的直接支持,简化位操作、循环移位、延时控制等场景的编程实现。该头文件通常在需要使用循环移位、空指令(NOP)或位测试清零等功能时引入,是C51编译器与8051硬件指令集之间的桥梁。intrins.h头文件核心函数分类与功能说明
循环移位函数
此类函数实现数据按位循环移动,支持不同数据类型(char、int、long),移位后高位与低位首尾相接,无符号扩展。
空指令函数_nop_()
功能:生成一个NOP汇编指令,执行时间为1个机器周期(如12MHz晶振下耗时1μs),用于精确延时或时序控制。
特点:C51编译器直接嵌入NOP指令,无函数调用开销,适合作为延时基准或硬件时序匹配(如I²C总线起始信号生成)。位测试与清零函数_testbit_()
原型:bittestbit(bit bitvar)
功能:测试指定位变量bitvar的值,若为1则返回1并自动清零该位,若为0则返回0;等效于8051汇编指令JBC(测试位并跳转清零)。
应用:外部中断标志位检测、按键消抖后状态清除等场景,避免手动清零操作。
1.2 、宏定义
#define是非常基础且常用的宏定义,它们的作用是为数据类型创建简短的别名,主要目的是简化代码编写和提高可读性。本程序采用#define为unsigned char数据类型创建简短的别名为uchar;为unsigned int数据类型创建简短的别名为uint。
2、延时函数
为了能让粉丝看见灯的流水效果,增加了延时函数。
/*****************************************************************
*函数功能:11.0592MHZ 延时约1ms 固定格式记住就行
*****************************************************************/
voiddelay1ms(uint time) //@11.0592MHz
{
uchar i, j;
while(time--)
{
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
3、主程序
/*****************************************************************
*主程序
*****************************************************************/
voidmain(void)
{
uchar i,temp; //引入变量i和temp
while(1)
{
//左流水灯
temp= 0xFE;//化作二进制为11111110
for (i = 0; i < 8; i++)
{
P0 = temp;
delay1ms(500);
temp = _crol_(temp,1); //循环左移函数 该函数在intrins.h头文件中
}
//右流水灯
temp= 0x7F;//化作二进制为0111 1111
for (i = 0; i < 8; i++)
{
P0 = temp;
delay1ms(500);
temp = _cror_(temp,1); //循环右移函数 该函数在intrins.h头文件中 0111 1111 1011 1111
}
}
}
uchar:通过宏定义简化的unsigned char(8 位无符号变量)。
i:用于for循环的计数器(控制流水灯的步数,共 8 步,对应 8 个 LED)。
temp:存储 LED 的状态数据(8 位二进制数,每一位对应一个 LED 的亮灭,0表示亮,1表示灭)。
单片机程序的典型结构,所有需要持续执行的逻辑都放在while(1)中。
初始状态temp = 0xFE;
二进制为11111110,对应 P0 口的 8 个引脚(P0.0P0.7);
最低位(P0.0)为0 → 连接的 LED 点亮;
其余位(P0.1P0.7)为1 → 对应 LED 熄灭。
此时最右边的 LED(接 P0.0)亮。
循环左移_crol_(temp, 1;:
每次将temp的 8 位二进制数向左循环移动 1 位(最高位移到最低位),例如:
第 1 次左移:11111110 → 11111101(P0.1 对应的 LED 亮);
第 2 次左移:11111101 → 11111011(P0.2 对应的 LED 亮);
... 以此类推,直到 8 次循环后,点亮的 LED 从最右移到最左。
delay1ms(500):
延时 500 毫秒(0.5 秒),让每个 LED 的点亮状态保持一段时间,人眼才能看清流水效果。
初始状态temp = 0x7F:
二进制为01111111,最高位(P0.7)为0 → 最左边的 LED(接 P0.7)亮,其余熄灭。
循环右移_cror_(temp, 1):
每次将temp的 8 位二进制数向右循环移动 1 位(最低位移到最高位),例如:
第1次右移:01111111 → 10111111(P0.6 对应的 LED 亮);
第2次右移:10111111 → 11011111(P0.5 对应的 LED 亮);
... 以此类推,8 次循环后,点亮的 LED 从最左移到最右。
五、 获取资料:
关注下面的微信公众,回复 009

