单片机双机通信控制跑马灯

实验要求

两个单片机各驱动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博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_64066303/article/details/134101256?spm=1001.2014.3001.5501

查询控制

当面对一个题目的时候可以进行分解,我这里是将题目分解成了以下三个部分:LED跑马灯、独立按键、双机通信。

LED跑马灯

之前经常实现的是流水灯,这个跑马灯是类似,如果对移位掌握的不好的话,就建议还是采用数组来存储将对应点亮的LED灯,数组储存起来之后遍历数组就行了,因为他情况有有限,用数组储存最好理解了,我这里采用的是共阳极的接法,给0表示亮,1表示不亮,然后8根线对应01,用十六进制数来表示将LED点亮的状态。

独立按键

独立键盘接口设计(Keil+Proteus)-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_64066303/article/details/134253686?spm=1001.2014.3001.5501这个的思路就是我给一个引脚接一个按钮,然后先给他赋值为1,如果读这个引脚他的值变成了0,就表示他被按下去了。

双机通信

串行口的工作原理及应用-CSDN博客icon-default.png?t=N7T8https://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博客icon-default.png?t=N7T8https://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
}

 采用中断之后灵敏多了,大家快去试试吧!!!

总结

继续加油。

猜你喜欢

转载自blog.csdn.net/weixin_64066303/article/details/134796058