实验要求
两个单片机各驱动8个LED灯,构成两个跑马灯,要求甲单片机LED的点亮方式是从上至下,首先是最上面第一个点亮、其次是前两个点亮、其次是前三个点亮……直至8个灯全部点亮,8个灯全部灭,重复这个过程,乙单片机就是从下至上重复这个过程。按下甲单片机的独立按键S1,则乙单片机LED按照甲单片机的跑马灯方向运行三次,在继续原定程序;按乙单片机的独立按键S2,则甲单片机LED按照乙单片机的跑马灯方向先运行三次,在继续原定程序。
参考链接
串行口的工作原理及应用-CSDN博客
外中断的应用-CSDN博客
LED数码管的静态显示与动态显示(Keil+Proteus)_proteus数码管显示-CSDN博客
独立键盘接口设计(Keil+Proteus)-CSDN博客
51单片机入门之点亮LED灯_如何用单片机点亮led灯_yongy_u的博客-CSDN博客
51单片机中断笔记 - 知乎 (zhihu.com)
【51单片机实验笔记】开关篇(一) 独立按键_单片机 按键-CSDN博客
单片机 | 51单片机原理_c51单片机原理-CSDN博客
元器件 | Proteus关键字 |
51单片机 | AT89C51 |
按钮 | BUTTON |
LED灯 | LED |
电阻 | RES |
电源 | POWER |
地 | GROUND |
Proteus软件操作
LED数码管的静态显示与动态显示(Keil+Proteus)_proteus数码管显示-CSDN博客https://blog.csdn.net/weixin_64066303/article/details/134101256?spm=1001.2014.3001.5501
查询控制
当面对一个题目的时候可以进行分解,我这里是将题目分解成了以下三个部分:LED跑马灯、独立按键、双机通信。
LED跑马灯
之前经常实现的是流水灯,这个跑马灯是类似,如果对移位掌握的不好的话,就建议还是采用数组来存储将对应点亮的LED灯,数组储存起来之后遍历数组就行了,因为他情况有有限,用数组储存最好理解了,我这里采用的是共阳极的接法,给0表示亮,1表示不亮,然后8根线对应01,用十六进制数来表示将LED点亮的状态。
独立按键
独立键盘接口设计(Keil+Proteus)-CSDN博客https://blog.csdn.net/weixin_64066303/article/details/134253686?spm=1001.2014.3001.5501这个的思路就是我给一个引脚接一个按钮,然后先给他赋值为1,如果读这个引脚他的值变成了0,就表示他被按下去了。
双机通信
串行口的工作原理及应用-CSDN博客https://blog.csdn.net/weixin_64066303/article/details/134761169?spm=1001.2014.3001.5501双机通信就是两个单片机的RX和TX交换连接,其中有两个变量,如果接收到/发送完数据就会产生中断变成1,我们要做的就是控制这两个变量,一旦接收到就执行其他的操作,传输数据的话需要双方规定好波特率。
这个我最开始想复杂了,还想读取一边LED输出口的状态传递给另一个单片机来显示,其实就只需要传递让另一个单片机知道就行了,直接定义两个输出的数组就行了,应该从上到下,一个从下到上。
甲机和乙机的代码几乎一样,就是输出的数组不一样,大家理解其中一个就可以了。
甲机
//甲机
#include<reg51.h>
#define uchar unsigned char
#define uint unsigned int
//定义跑马灯数组
uchar code paomadeng1[]={0xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x80,0x00,0xFF};
uchar code paomadeng2[]={0x7F,0x3F,0x1F,0x0F,0x07,0x03,0x01,0x00,0xFF};
#define S1 P1^7
//延时程序
void delay(uint t){
uchar i;
while(t--)
for(i=0;i<200;i++);
}
void main(){
uchar i,j,k;
TMOD=0x20;//设置定时器T1为方式2
TH1=0xFD;//波特率9600
TL1=0xFD;
SCON=0x50;//设置串口为方式1接收
PCON=0x00;//SMOD=0
TR1=1;//启动T1
while(1){
while(RI==0){//如果RI=0,表示没有接收到数据
//正常走马灯
for(i=0;i<=8;i++){
P2=paomadeng1[i];
delay(123);
//判断按钮是否被按下
P1=0xFF;
if((P1&0x80)!=0x80){
delay(5);
if(S1==0){
//按钮被按下
SBUF=0xFF;//数据送串口发送
while(TI==0);//如果TI=0,表示没有发送完,循环等待
TI=0;
}
}
}
}
for(j=0;j<3;j++){
//重复三次
for(k=0;k<=8;k++){
P2=paomadeng2[k];
delay(123);
}
}
RI=0;//软件清零
}
}
乙机
//乙机
#include<reg51.h>
#define uchar unsigned char
#define uint unsigned int
//定义跑马灯数组
uchar code paomadeng1[]={0xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x80,0x00,0xFF};
uchar code paomadeng2[]={0x7F,0x3F,0x1F,0x0F,0x07,0x03,0x01,0x00,0xFF};
#define S1 P1^7
//延时程序
void delay(uint t){
uchar i;
while(t--)
for(i=0;i<200;i++);
}
void main(){
uchar i,j,k;
TMOD=0x20;//设置定时器T1为方式2
TH1=0xFD;//波特率9600
TL1=0xFD;
SCON=0x50;//设置串口为方式1接收
PCON=0x00;//SMOD=0
TR1=1;//启动T1
while(1){
while(RI==0){//如果RI=0,表示没有接收到数据
//正常走马灯
for(i=0;i<=8;i++){
P2=paomadeng2[i];
delay(123);
//判断按钮是否被按下
P1=0xFF;
if((P1&0x80)!=0x80){
delay(5);
if(S1==0){
//按钮被按下
SBUF=0xFF;//数据送串口发送
while(TI==0);//如果TI=0,表示没有发送完,循环等待
TI=0;
}
}
}
}
//跳出循环表示接收到数据
for(j=0;j<3;j++){
//重复三次
for(k=0;k<=8;k++){
P2=paomadeng1[k];
delay(123);
}
}
RI=0;//软件清零
}
}
原理图
正常运行
S1按钮被按下
S2按钮被按下
中断控制
外中断的应用-CSDN博客https://blog.csdn.net/weixin_64066303/article/details/134360733?spm=1001.2014.3001.5501就是把查询要执行的函数主体写到中断服务函数,为了让他能够直接执行我在循环中还加了一个break,就是为了让按钮更加灵敏一点。
甲机
//甲机
#include<reg51.h>
#define uchar unsigned char
#define uint unsigned int
//定义跑马灯数组
uchar code paomadeng1[]={0xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x80,0x00,0xFF};
uchar code paomadeng2[]={0x7F,0x3F,0x1F,0x0F,0x07,0x03,0x01,0x00,0xFF};
//延时程序
void delay(uint t){
uchar i;
while(t--)
for(i=0;i<200;i++);
}
void main(){
uchar i,j,k;
TMOD=0x20;//设置定时器T1为方式2
TH1=0xFD;//波特率9600
TL1=0xFD;
SCON=0x50;//设置串口为方式1接收
PCON=0x00;//SMOD=0
TR1=1;//启动T1
EA=1;//总中断允许
EX0=1;//INT0开中断
IT0=0;//选择外部中断为低电平触发方式
while(1){
if(RI==0){//如果RI=0,表示没有接收到数据
//正常走马灯
for(i=0;i<=8;i++){
P2=paomadeng1[i];
delay(123);
//有数据传过来直接中断
if(RI!=0){
//break;
}
}
}else{
for(j=0;j<3;j++){
//重复三次
for(k=0;k<=8;k++){
P2=paomadeng2[k];
delay(123);
}
}
RI=0;//软件清零
}
}
}
//用于串口传递数据
void int0() interrupt 0 using 0{
EX0=0;//禁止外部中断0
SBUF=0xFF;
//数据送串口发送
while(TI==0);//如果TI=0,表示没有发送完,循环等待
TI=0;
EX0=1;//中断返回前,打开外部中断0
}
乙机
//乙机
#include<reg51.h>
#define uchar unsigned char
#define uint unsigned int
//定义跑马灯数组
uchar code paomadeng1[]={0xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x80,0x00,0xFF};
uchar code paomadeng2[]={0x7F,0x3F,0x1F,0x0F,0x07,0x03,0x01,0x00,0xFF};
//延时程序
void delay(uint t){
uchar i;
while(t--)
for(i=0;i<200;i++);
}
void main(){
uchar i,j,k;
TMOD=0x20;//设置定时器T1为方式2
TH1=0xFD;//波特率9600
TL1=0xFD;
SCON=0x50;//设置串口为方式1接收
PCON=0x00;//SMOD=0
TR1=1;//启动T1
EA=1;//总中断允许
EX0=1;//INT0开中断
IT0=0;//选择外部中断为低电平触发方式
while(1){
if(RI==0){//如果RI=0,表示没有接收到数据
//正常走马灯
for(i=0;i<=8;i++){
P2=paomadeng2[i];
delay(123);
//如果有信息传过来,直接中断
if(RI!=0){
break;
}
}
}else{
for(j=0;j<3;j++){
//重复三次
for(k=0;k<=8;k++){
P2=paomadeng1[k];
delay(123);
}
}
RI=0;//软件清零
}
}
}
//用于串口传递数据
void int0() interrupt 0 using 0{
EX0=0;//禁止外部中断0
SBUF=0xFF;
//数据送串口发送
while(TI==0);//如果TI=0,表示没有发送完,循环等待
TI=0;
EX0=1;//中断返回前,打开外部中断0
}
采用中断之后灵敏多了,大家快去试试吧!!!
总结
继续加油。