多機串口通訊
★使用器件
使用了3塊80c51的單片機,其中U1為主機控制其他兩個從機U2,U3。每個單片機上都有一個數位管用來顯示資料。主機上有兩個按鍵KEY_1,KEY_2,分別用來控制不同的從機。
★實現目標
主要實現的目標就是通過寫多機通訊來瞭解他們其中的協議,以及簡單協議的寫法!本程式主要達到了一下效果,主機可以通過發送命令來控制從機:發送資料給從機、接收從機的資料。然後將從機或者主機顯示的資料顯示在數位管上。
★協議要求1、地址:主機的地址設定為0x01,從機1(U3)的地址為0x03,從機2(U2)的地址為0x022、命令:
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 charsbit 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;//每收到一個資料,發送0x11while(!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 0x03sbit 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;//每收到一個資料,發送0x11while(!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 0x03sbit 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;//每收到一個資料,發送0x11while(!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則表明校正出錯,重新發送 }}