下面是我寫的一個實現多個下位機(單片機)與一個上位機(PC機)的一主多從串口通訊程序,用的STC89C52RC,定時器2做串口通信波特率發生器。
實現功能是這樣的:
用調試助手向單片機發送一個數據包。
通訊協議是這樣的:
數據包的格式如下所示(共10個字節組成):
0x2A,0xEB,0x8D,地址碼,指令碼,數據長度碼,數據碼,數據碼,校驗碼,0xAD
前面三個字節為幀頭,即開始符。
地址碼: 欲傳送的目的地址,即選定哪一個單片機。
指令碼:向單片機發送的指令
數據長度碼: 用于指示后面有效數據的個數
數據碼:傳送的數據,配合指令碼的純數據。
校驗碼: 累加和校驗,對地址碼,指令碼,數據長度碼,數據碼進行累加,用來檢驗數據的完整性和正確性。
0xAD : 幀尾,即結束符。
本程序實現功能是這樣的:
用調試助手向單片機發送一個數據包,單片機收到后對數據解析,再回傳指定的數據。
例如發送:2a eb 8d 01 03 01 01 06 ad
指令碼為01,單片機接收到后解析,回傳0xce 0x7b 0x11 0xed。其中前兩個字節為開始符,最后一個字節為結束符。同理,若收到的指令碼為02,回傳0xce 0x7b 0x12 0xed。以此模擬控制單片機操作。
若接收錯誤,即累加校驗碼不等于單片機實際計算的累加和,回傳0xce 0x7b 0x02 0xed,提示接收錯誤,要求PC重發數據(模擬,需要上位機軟件配合才行)。
單片機開機初始化后即向PC發送一個數據0xce 0x7b 0x00 0xed,用于指示單片機與PC通信已連接。
下面是程序:
#define ID 0x01 //單片機地址
uint8 rec_data; //串口通信接收數據
uint8 state_flag=0; //通信協議解析狀態標志,初始化為0
uint8 retval=0; //通信協議解析函數返回值,初始化為0
uint8 cmd; //指令碼
uint8 Data[2]; //數據碼
uint8 data_count; //數據長度碼
程序大體思想是:
首先定義了幾個全局變量,接收到數據后,串口中斷子程序中用變量rec_data存儲一個字節的數據,隨后對數據進行解析:首先判斷數據包的完整性,正確性,然后提取指令碼,數據碼等數據,存放起來用于主程序處理。
協議解析過程中,使用一個變量state_flag的全局變量作為協議解析狀態標志,用于確定當前字節處于一幀數據中的那個部位,同時在接收過程中自動對接收數據進行校驗和處理,在數據包接收完的同時也進行了校驗的比較。因此當幀尾結束符接收到的時候,則表示一幀數據已經接收完畢,并且也通過了校驗,關鍵數據也保存到了緩沖區(cmd和Data[])中。主程序即可通過查詢retval的標志位來進行協議的解析處理。如果retval=1; //錯誤標志,數據包傳送不正確。如果retval=2; //接收成功標志,數據包傳送成功。
接收過程中,只要哪一步收到的數據不是預期值,則直接將狀態標志復位,用于下一幀數據的判斷,避免狀態自鎖。
以下是程序:
void PortInit(); //各端口初始化
void TimerInit(); //定時器初始化
void UsartInit(); //串口初始化
void usart_cmd_scan(); //串口命令掃描
void Data_analysis(); //通信協議解析函數
void Send(uint8 sendcmd); //數據發送函數
/*-------------------------------- 串口中斷服務子程序 ------------------------------------*/
void ser() interrupt 4
{
RI=0;
rec_data=SBUF; //讀取接收到的數據
Data_analysis();//數據解析
}
/*
* 函數名:Data_analysis
* 描 述:通信協議解析函數
* 輸 入:無
* 輸 出:無
* 備 注:解析串口接收到的數據
/*-------------------------------- 多機通信協議格式 ------------------------------------*/
/* 數據包的格式如下所示(共10個字節組成): */
/* 0x2A,0xEB,0x8D,地址碼,指令碼,數據長度碼,數據碼,數據碼,校驗碼,0xAD */
void Data_analysis()
{
static uchar recdata_sum=0; //存放累加和
static uchar lencnt=0; //數據長度計數器
switch (state_flag)
{
case 0:
{
if(rec_data == 0x2A) // 是否幀頭第一個數據
state_flag = 1;
else
state_flag = 0; // 標志復位
break;
}
case 1:
{
if(rec_data == 0xEB) // 是否幀頭第二個數據
state_flag = 2;
else
state_flag = 0; // 標志復位
break;
}
case 2:
{
if(rec_data == 0x8D) // 是否幀頭第三個數據
state_flag = 3;
else
state_flag = 0; // 標志復位
break;
}
case 3:
{
if(rec_data == ID) // 判斷目的地址是否正確
{
state_flag = 4;
recdata_sum=rec_data; //開始累加
}
else
state_flag = 0; // 標志復位
break;
}
case 4:
{
state_flag = 5;
cmd=rec_data; //指令碼存儲
recdata_sum+=rec_data; //累加
break;
}
case 5:
{
lencnt = 0; //數據長度計數器清零
data_count=rec_data; //數據長度碼存儲
recdata_sum+=rec_data; //累加
if (data_count!=0) //后面有數據碼
state_flag=6;
else
state_flag=8;
break;
}
case 6:
case 7:
{
Data[lencnt++]=rec_data; //數據碼保存
recdata_sum+=rec_data; //累加
if(lencnt==data_count)
{
state_flag=8;
lencnt = 0;
}
else
state_flag=7;
break;
}
case 8:
{
if(recdata_sum==rec_data) //數據校驗,判斷累加和是否相等
state_flag=9;
else
{
retval=1; //置錯誤標志,數據包傳送不正確。
state_flag=0;
}
recdata_sum=0;//累加和清零
break;
}
case 9:
{
if (rec_data==0xAD)
{
retval=2; //置接收成功標志,數據包傳送成功。
state_flag=0;
}
else
state_flag=0;
break;
}
}
}
//主程序 , 不斷掃描串口接收到的命令
void main()
{
PortInit(); //各端口初始化
TimerInit(); //定時器初始化
UsartInit(); //串口初始化
Send(0xce);
Send(0x7b);
Send(0x00);
Send(0xed);
while(1)
{
usart_cmd_scan(); //串口命令掃描
}
}
/*
* 函數名:usart_cmd_scan
* 描 述:串口命令掃描
* 輸 入:無
* 輸 出:無
* 備 注:掃描PC通過串口發送的命令
*/
void usart_cmd_scan()
{
uchar sendcmd; //下位機向PC發送的命令碼
switch (retval)
{
case 1: //數據發送錯誤,請求PC重發
{
sendcmd=2; //向PC發送的重發數據命令,PC識別后向下位機重發數據包。
Send(0xce);
Send(0x7b);
Send(sendcmd);
Send(0xed); //向PC發送命令
retval=0; //標志清零,防止重復掃描,重復執行。 2013/9/24
break;
}
case 2: //數據發送成功,執行命令
{
switch (cmd) //命令解碼
{
case 0x01:
{
Send(0xce);
Send(0x7b);
Send(0x11);
Send(0xed);
cmd=0x00;
break;
}
case 0x02:
{
Send(0xce);
Send(0x7b);
Send(0x12);
Send(0xed);
cmd=0x00;
break;
}
case 0x03:
{
Send(0xce);
Send(0x7b);
Send(0x13);
Send(0xed);
cmd=0x00;
break;
}
}
}
retval=0; //標志清零,防止重復掃描,重復執行。
}
}
/*
* 函數名:Send
* 描 述:串口數據發送函數
* 輸 入:sendcmd - 待發送的數據
* 輸 出:無
* 備 注:
*/
void Send(uint8 sendcmd)
{
ES=0; //關閉串口
SBUF=sendcmd; //發送數據,向PC發送。
while(!TI);
TI=0; //發送完成,TI清零
ES=1; //開串口
}
以上是我寫的這個程序,希望大家指點一下。
程序運行整體可以,但是有個問題,也希望大神們能幫忙看一下什么問題
每次在單片機關機后,再重新上電后,發送都沒反應,只有手動按下開發板的復位鍵后才能正常通信,當再次斷電上電后,又不行了,又得按復位鍵才正常。按說開發板上電就復位了呀
還望大神們幫忙指點啊!