刚刚接触单片机的朋友一定对于单片机最小系统完成的流水灯有所了解,但是要着手开发一个流水灯,少不了对89C52RC的相对系统化的认识。我基于我对单片机的认识,不讲述单片机基本上的工作原理,只是从一个新手能够接触到的方面来教会你用最小系统来做一个自己的单片机流水灯。
## 1.准备材料
- Keil μVision 5(自行激活)
- Proteus 8 Professional
- 22pf 独石电容 x 3(用于上电中断和时钟控制)
- STC89C52RC x 1
- 12MHz晶振 x 1
- 40P插槽(非必须,但是芯片将被直接焊接在洞洞板上)
- LED(颜色自选) x 9
- 自锁开关 x 1 + 微动开关 x 1
- 洞洞板(适当大小) x 1
- 焊台焊枪焊锡
## 2.电路准备
首先,我们要搭建流水灯系统的电路。先在Proteus 8 中放置一个AT89C51芯片作为整个工程的基础
- 这里要注意,STC89C52RC软硬件层面完全兼容AT89C51,因此可以选择AT89C51作为简单替代。
接着放入8个LED灯管(不会添加电子元器件的请移步Bilibili学习Proteus基础课),从上自下依次连接到单片机的P2.0 -- P2.7口,注意正负极不要连反(取决于你的LED灯管接法,如果是阳极相接就正进负出,阴极相接就负进正出)。
连好应该是下面的效果

上侧的PW线标接通的是电源指示灯电路,本电路进路Vcc需安装自锁开关(电路板Vcc接口第一个通过的就应当是自锁开关,不然存在烧板子的风险)。 ,我这里为了方便选择阳极相接法。
紧接着将微动开关连接至单片机,控制流水灯的开关,我这里采用边缘触发外部中断的方式,将微动开关的一段连接到单片机的P3.2口,另一端接地(GND)即可。
单片机的时钟电路和上电/按键复位如下图所示,不过多赘述。

完成电路部分的设计,下一步就是完成流水灯代码的设计,打开Keil,创建一个新的Project取个名字,进入下一步的工作。
## 3.代码开发
对于51系列的单片机我们先引入头文件
#include "reg51.h"
接着为第一阶段的开发做准备。
我们都知道,流水灯的逻辑为“点亮LED0-等待时间-点亮LED1”,那我们首先考虑的是设计一个函数,使其能够实现延迟的效果。
同时我们也知道,代码的运行是需要消耗时间的,那么有没有一种方法能够做到既简单又可以高效的让单片机低效率运行呢?
这个时候就要拿出 没经过优化的屎山代码 强大的for()循环体来完成这个操作了。
但是我们还有一个小问题,能不能在这一套函数中设置一个参量来实现可以改变的延时控制呢?
当然可以,还方便了变速流水灯的实现控制:
void delay(unsigned int wait_time)
{
unsigned int loop1,loop2;
loop1 = loop2 = 0;
for(loop1 = 0; loop1 < wait_time; loop1++)
{
for(loop2 = 0; loop2 < 120; loop2++);
}
}
到这里,卡住我们的第一个坎就被我们轻松的越过去了。
接下来我们需要考虑的问题:如何实现流水灯的实现?
流水灯的实现方法很多,但是目前使用最多的方法只有两种:数组定义和二进制移位。
单片机的针脚有两种状态:高电平和低电平,对应二进制代码中的1和0。因此,只需要正确的设置高低电平,我们就可以让对应位置的LED点亮。但是新的问题来了,我们只能让一个灯亮,那么就说明应该是类似于0xFE(11111110)这样只有一个位为0的二进制码,数组可以自定,但是怎样才能做有效的移位?
C语言中,我们可以通过“~”来取一个二进制数的反码,那么我们就可以按照这来做一个流水函数,比如:
int now_led = 0;
int delay_time[3] = {100,300,500};
void led(int getin)
{
int loop = 0;
for(loop = getin; loop < 8; loop++)
{
P2 = ~(0x01<<loop);
now_led = loop;
}
if(loop = 7){
loop = 0;
now_led = 0;
}
}
0x01<<loop的意思是将0000 0001中的1向左移位loop个,来控制当前亮灯的针脚,再通过取反码,完成一次亮灯的操作,十分的简单。
如果使用数组,可以参考以下代码:
unsigned char code nowled[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
void led(getin){
int loop = 0;
for(loop = getin; loop < 8; loop++){
P2 = nowled[loop];
}
if(loop = 7){
loop = 0;
now_led = 0;
}
}
不出意外的话,你现在已经可以实现对应的流水灯了。
## 4.外部中断控制流水灯开关(可选)
到这一步,我们可以试着接触下外部中断。
外部中断,顾名思义,借助外部的力量来中断正在完成的事情,转而去完成信号想让你完成的事情。单片机的P3.2和P3.3口就是用来做外部中断的,分别为INT0和INT1口。
首先我们需要在初始化中打开外部中断:
void initpin(void)
{
IT0 = 0; //设置中断触发方式为边缘触发
EX0 = 1; //开启外部中断0
EA = 1; //开启总中断,就是允许系统进行中断
PX0 = 0; //设置外部中断0的优先级为0
}
然后则是设置启动按键的消抖判断。
### 按键消抖的重要性和原理
当按键按下时,不可避免的会产生相应的震动,使得单片机接收的信号产生抖动,从而干扰信号的正常判断,而消抖的目的便是提高信号的辨识精准度。
消抖的原理便是通过延时来判断两个相隔一定时间的信号是否能保持对应状态(比如延时20ms,如果对应的相隔20ms的两次扫描都是按下,则可确认按键已经被按下,抬起同理)。为了使接收结果更准确,我们可以再添加一个标志位,强化信号的准确性。
### 代码实现
unsigned char key_cnt_seton = 0; //循环次数判断
unsigned char key_press_flag_seton = 0; //按下状态判断
unsigned char key_release_flag_seton = 0; //抬起状态判断
unsigned char i = 0; //循环变量
sbit SET_ON = P3^2; //绑定针脚
bit set_on = 0; //设置开关判定变量,bit类型为布尔值
void ex0_isr(void) interrupt 0
{
if (SET_ON == 0)
{
if (key_cnt_seton < 4) //进行延时
{
key_cnt_seton++;
}
else if (set_on != 0) //通过按键之前的状态进行重置
{
set_on = 0;
P2 = 0xFF;
now_led = 0;
key_press_flag_seton = 1;
key_release_flag_seton = 0; //重置标志位
}
}
else
{
if (key_cnt_seton < 4) //进行延时
{
key_cnt_seton++;
}
else if (set_on != 1) //进行开启初始化
{
set_on = !set_on;
P2 = 0xFF;
now_led = 0;
key_release_flag_seton = 1;
key_press_flag_seton = 0; //重置标志位
}
}
}
紧接着我们把主函数稍微改动:
int main(){
while(1);
if(set_on){
led();
}
}
完工!
希望我的经验能够帮到你,点个关注呗!
6万+

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



