首页 > 代码库 > 多机串口通讯

多机串口通讯

★使用器件

使用了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则表明校验出错,重新发送 
     }


}