★使用器件
使用了3块80c51的单片机,其中U1为主机控制其他两个从机U2,U3。每个单片机上都有一个数码管用来显示数据。主机上有两个按键KEY_1,KEY_2,分别用来控制不同的从机。
★实现目标
主要实现的目标就是通过写多机通讯来了解他们其中的协议,以及简单协议的写法!本程序主要达到了一下效果,主机可以通过发送命令来控制从机:发送数据给从机、接收从机的数据。然后将从机或者主机显示的数据显示在数码管上。
★协议要求
1、地址:主机的地址设置为0x01,从机1(U3)的地址为0x03,从机2(U2)的地址为0x02
2、命令:
0x01表示:主机向从机发送数据
0x02表示:从机向主机发送数剧
3、基本步骤:
●主机发送地址帧,从机接收到地址帧后就会与本机地址进行比对,如果正确则发送自己的地址应答;如果不正确,则丢弃数据,继续监测等待数据;
●从机接收地址正确后,返回自己的地址作为应答信号;如果主机接收到的数据与自己发送的地址数据相同,就会发送命令(是让从机接收数据还是发送数据);如果与自己发送的地址数据不同,则发送0xff,当从机接收到数据得到RB8 ==1,则进入继续监测等待数据。
●主机发送命令,从机接收到命令后,就会返回接收到的命令作为应答(没有处理异常情况);主机监测到应答数据正确就会发送数据,并且等待从机的应答信号(0x11),接收到应答信号后,再发送第二个数据(这个地方主机没有处理异常情况),指导发送数据结束
●发送数据接收后,主机会发送校验和,与从机接收的数据的校验和比较,如果检验和相同说明发送数据正确,然后就退出程序;如果检验和发送不正确则重新发送数据。
4、具体实现
串行口有一个很重要的寄存器:
SM2为多机通讯控制位。当从机的SM2=1时,可以利用收到的RB8来控制RI。当RB8 = 1接收数据并且使RI = 1,当RB8 = 0时不接受数据;当SM2= 0时,无论RB8是否为1都会接收数据,并且使RI = 1。因此可以根据这个特点来区别地址和数据。从机首先将SM2置1,然后将要发送数据的TB8置1,从机接收到数据后就会比较地址是否与本机地址相同,如果相同就是将SM2置0,然后发送数据(TB = 0),而其他的从机仍然处于SM=1的状态,接收不到数据。
★电路图
★程序实现
主机:
#include <reg52.h> #define uint unsigned int #define uchar unsigned char sbit key_1 = P1^0; sbit key_2 = P1^1; sbit Led = P1^7; #define master_U1 0x01 //主机地址 #define slave_U2 0x02 //从机地址 #define slave_U3 0x03 //从机地址 uchar tbuf[16] = {0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c, 0x39,0x5e,0x79,0x71}; uchar rbuf[16];//待接收数据的数组 void DelayUs2x(unsigned char t) { while(--t); } void DelayMs(unsigned char t) { while(t--) { //大致延时1mS DelayUs2x(245); DelayUs2x(245); } } void Uart_init(void) { SCON |= 0xf8;//工作方式1,T1,R1置零,允许串行口接收数据 TMOD |= 0x20;//定时器1,模式2 TH1 = 0xFD; TL1 = 0xFD; TR1 = 1; EA = 1;//打开总中断 ES = 1;//打开串口中断 } void master_send(uchar addr,uchar command) { uchar status,check,i = 0; SBUF = addr;//发送要操作的地址 while(!TI); DelayMs(10); TI = 0;//发送从机地址 while(!RI); RI = 0;//等待从机地址答复 if(addr!=SBUF)//如果返回的地址与要操作的地址不同 { SBUF=0xff; while(!TI); TI=0; } else { TB8=0; SBUF=command; while(!TI); TI = 0; //发送命令 while(!RI); RI = 0;//等待从机的回复 status = SBUF; //从机发送确认命令 if(status == 0x80) { TB8 = 1; } else { if(status == 0x01)//主机知道从机已经准备好接收数据,就进入发送状态 { while(1) { check = 0; for(i = 0;i < 16;i++) { SBUF = tbuf[i]; while(!TI); TI = 0; while(!RI); //等待从机返回确认信号0x11 RI = 0; check += tbuf[i];//校验和 DelayMs(2000); } SBUF = check;//向从机发送校验和 while(!TI);//发送校验和给从地址 TI = 0; while(!RI); RI = 0;//接收从地址返回的数据(0x00或者0xff) if(SBUF == 0x00) { break; } //接收到0x00表明校验正确,则跳出发送函数,如果接收到0xff则表明校验出错,重新发送 } } if(status == 0x02)//主机知道要接收来自从机的数据了 { while(1) { check = 0; for(i = 0;i < 16; i++) { while(!RI); RI = 0; rbuf[i] = SBUF; P2 = rbuf[i]; SBUF = 0x11;//每收到一个数据,发送0x11 while(!TI); //表示接受到数据后的确认 TI = 0; check += rbuf[i]; } while(!RI); RI = 0; if(check == SBUF)//如果主机数据的校验和与从机的校验和相等,则返回主机0x00 { //否则,需要重新等待接收数据。 SBUF = 0x00; while(!TI); TI = 0; P2 = 0; break; } } } } } } void main() { Uart_init(); P2 = 0; Led = 1; if(!key_1) { DelayMs(20); if(!key_1) { master_send(slave_U2,0x02); } } if(!key_2) { DelayMs(20); if(!key_2) { master_send(slave_U3,0x01); } Led = 0; } }
从机1:
/**从机1 U3**/ #include <reg52.h> #define uint unsigned int #define uchar unsigned char //#define slave_U2 0x02 #define slave_U3 0x03 sbit key = P1^0; static uchar tbuf[] = {0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c, 0x39,0x5e,0x79,0x71}; static uchar rbuf[16]; void Uart_receive(); void Send_receive(); void DelayUs2x(unsigned char t) { while(--t); } void DelayMs(unsigned char t) { while(t--) { //大致延时1mS DelayUs2x(245); DelayUs2x(245); } } void Uart_init()//串口初始化 { PCON = 0; SCON |= 0xf8; TMOD |= 0x20; TH1 = 0xfd; TL1 = 0xfd; TR1 = 1; EA = 1; ES = 1; } void main() { P2 = 0; Uart_init(); while(1); } void Ser_uart() interrupt 4 { uchar save; RI = 0; ES = 0; if(SBUF != slave_U3) { ES = 1; goto end; //如果发送的从机地址与本从机地址不相符则,执行此语句。 } SM2 = 0;//设置为单击模式 SBUF = slave_U3;//发送从机地址给主机 while(!TI); TI = 0; while(!RI);//接收主机发来的命令 RI = 0; if(RB8 == 1)//如果返回的地址出错误,则发送0xff,RB8 = 1; { SM2 = 1; ES = 1; goto end;//进入等待地址帧模式 } save = SBUF;//将接收到的命令存在save中 if(save == 0x01)//如果接收的命令是0x01 { SBUF = 0x01;//向主机发送03表示确认,已经做好准备 while(!TI); TI = 0; Uart_receive();//进入接收状态等待 } else { if(save == 0x02) { SBUF = 0x02;//告诉主机,要准备发送数据了 while(!TI); TI = 0; Send_receive();//进入发送数据函数 } else { SBUF = 0x80; while(!TI); TI = 0; SM2 = 1; ES = 1; goto end; } } end:; } void Uart_receive() { uchar check,i; while(1) { check = 0; for(i = 0;i < 16;i++) { while(!RI); RI = 0; rbuf[i] = SBUF; P2 = rbuf[i];//显示接受到的数据 SBUF = 0x11;//每收到一个数据,发送0x11 while(!TI); //表示接受到数据后的确认 TI = 0; check += rbuf[i]; } while(!RI); RI = 0; if(SBUF == check)//如果主机数据的校验和与从机的校验和相等,则返回主机0x00 { SBUF = 0x00; while(!TI); TI = 0; P2 = 0; break; } else//如果主机数据的校验和与从机的校验和不相等,则重新发送数据 { SBUF = 0xff; while(!TI); TI = 0; } } } void Send_receive() { uchar check,i; while(1) { check = 0; for(i = 0;i < 11;i++) { SBUF = tbuf[i]; while(!TI); TI = 0; check += tbuf[i]; } SBUF = check; while(!TI); //发送校验数据 TI = 0; while(!RI); RI = 0;//等待校验结果,接收主地址返回的数据(0x00或者0xff) if(SBUF == 0x00) break; //接收到0x00表明校验正确,则跳出发送函数,如果接收到0xff则表明校验出错,重新发送 } while(1) { for(i = 0;i < 16;i ++) P2 = rbuf[i]; DelayMs(1000); } }
从机2::
/**从机2,电路图U2**/ #include <reg52.h> #define uint unsigned int #define uchar unsigned char #define slave_U2 0x02 //#define slave_U3 0x03 sbit Key = P1^0; static uchar tbuf[] = {0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c, 0x39,0x5e,0x79,0x71}; static uchar rbuf[16]; void Uart_receive(); void Send_receive(); static uchar rbuf[16]; void DelayUs2x(unsigned char t) { while(--t); } void DelayMs(unsigned char t) { while(t--) { //大致延时1mS DelayUs2x(245); DelayUs2x(245); } } void Uart_init()//串口初始化 { PCON = 0; SCON |= 0xf8; TMOD |= 0x20; TH1 = 0xfd; TL1 = 0xfd; TR1 = 1; EA = 1; ES = 1; } void main() { P2 = 0; Uart_init(); while(1); } void Ser_uart() interrupt 4 { uchar save; RI = 0; ES = 0; if(SBUF != slave_U2) { ES = 1; goto end; //如果发送的从机地址与本从机地址不相符则,执行此语句。 } SM2 = 0;//设置为单击模式 SBUF = slave_U2;//发送从机地址给主机 while(!TI); TI = 0; while(!RI);//接收主机发来的命令 RI = 0; if(RB8 == 1)//如果返回的地址出错误,则发送0xff,RB8 = 1; { SM2 = 1; ES = 1; goto end;//进入等待地址帧模式 } save = SBUF;//将接收到的命令存在save中 if(save == 0x01)//如果接收的命令是0x01 { SBUF = 0x01;//向主机发送01表示确认,已经做好准备 while(!TI); TI = 0; Uart_receive();//进入接收状态等待 } else { if(save == 0x02) { SBUF = 0x02;//告诉主机,要准备发送数据了 while(!TI); TI = 0; Send_receive();//进入发送数据函数 //DelayMs(1000); } else { SBUF = 0x80; while(!TI); TI = 0; SM2 = 1; ES = 1; goto end; } } end:; } void Uart_receive() { uchar check,i; while(1) { check = 0; for(i = 0;i < 11;i++) { while(!RI); RI = 0; tbuf[i] = SBUF; SBUF = 0x11;//每收到一个数据,发送0x11 while(!TI); //表示接受到数据后的确认 TI = 0; check += tbuf[i]; } while(!RI); RI = 0; if(SBUF == check)//如果主机数据的校验和与从机的校验和相等,则返回主机0x00 { SBUF = 0x00; while(!TI); TI = 0; break; } else//如果主机数据的校验和与从机的校验和不相等,则重新发送数据 { SBUF = 0xff; while(!TI); TI = 0; } } } void Send_receive() { uchar check,i; while(1) { check = 0; for(i = 0;i < 16;i++) { SBUF = tbuf[i]; while(!TI); TI = 0; while(!RI); RI = 0; check += tbuf[i]; DelayMs(2000); } SBUF = check; while(!TI); //发送校验数据 TI = 0; while(!RI); RI = 0;//等待校验结果,接收主地址返回的数据(0x00或者0xff) if(SBUF == 0x00) break; //接收到0x00表明校验正确,则跳出发送函数,如果接收到0xff则表明校验出错,重新发送 } }