下面的示例代碼基於51單片機,用於快速二次開發實現基於串口字符串通信控制程序(比如要實現電腦控制單片機的開燈和關燈),示例很言簡意賅,並附上了詳盡的注釋,
本示例代碼經過了更新,新版本代碼更加友好了,
尊重作者的勞動,轉載請記得注明來源:http://www.cnblogs.com/weifeng727/p/5617924.html
1 #include<reg52.h> 2 3 //------------------串口通信的數據包協議-----------------// 4 /* 5 此程序的串口字符串通信使用到下面的一個自定義協議,每次通信都是發送或接收一個數據包,數據包格式解釋如下(長度恆為15): 6 例如:A01_fmq_01Off___# 7 A--------數據包的開始標記(可以為A到Z,意味着數據包可以有26種) 8 01-----設備代號 9 fmq_01Off___--------指令(長度恆為10),指令的前4個人字符是指令頭部,指令的后6個字符是指令尾部 10 #---------數據包的結束標記 11 12 例如:A02_SenT010250# 13 A--------數據包的開始標記(可以為A到Z,意味着數據包可以有26種) 14 02-----設備代號 15 SenT010250--------指令(長度恆為10),指令的前4個人字符是指令頭部,指令的后6個字符是指令尾部 16 #---------數據包的結束標記 17 */ 18 char RecvString_buf[16]; //定義數據包長度為15個字符 19 #define deviceID_1Bit '0' //用於串口通信時,定義本地設備ID的第1位 20 #define deviceID_2Bit '2' //用於串口通信時,定義本地設備ID的第2位 21 #define datapackage_headflag 'A' //用於串口通信時,定義數據包頭部的驗證標記 22 23 char DataPackage_DS18B20[16]={datapackage_headflag,deviceID_1Bit,deviceID_2Bit,'_','S','e','n','T','X','X','X','X','X','X','#'}; //這個是曾經用來控制溫度傳感模塊(DS18B20)的數據包 24 char HeartBeat[16]={datapackage_headflag,deviceID_1Bit,deviceID_2Bit,'_','B','e','a','t','X','X','X','X','X','X','#'}; //我隨便定義了一個數據包用來做"心跳包",比如單片機系統向電腦每2秒發送一次該數據包,如果電腦沒有按時接收到,就認為單片機死掉了 25 //----------------------------------------------// 26 /******************************* 27 串口通信 28 MCU:89C52RC 11.0592MHz 29 30 //11.0592MHz 0xd0 1200bps 31 //12MHz 0xcc 1200bps 32 //11.0592MHz 0xfa 9600bps 33 //0xf4 11.0592MHz 0xf3 12MHz 4800bps 34 //均在SMOD=1的情況下(波特率倍增模式) 35 *******************************/ 36 //串口發送函數 37 void PutString(unsigned char *TXStr) 38 { 39 ES=0; 40 while(*TXStr!=0) 41 { 42 SBUF=*TXStr; 43 while(TI==0); 44 TI=0; 45 TXStr++; 46 } 47 ES=1; 48 } 49 //串口接收函數 50 bit ReceiveString() 51 { 52 char * RecStr=RecvString_buf; 53 char num=0; 54 unsigned char count=0; 55 loop: 56 *RecStr=SBUF; 57 count=0; 58 RI=0; 59 if(num<14) //數據包長度為15個字符,嘗試連續接收15個字符 60 { 61 num++; 62 RecStr++; 63 while(!RI) 64 { 65 count++; 66 if(count>130)return 0; //接收數據等待延遲,等待時間太久會導致CPU運算閑置,太短會出現"數據包被分割",默認count=130 67 } 68 goto loop; 69 } 70 return 1; 71 } 72 //定時器1用作波特率發生器 73 void Init_USART() 74 { 75 SCON=0x50; //串口方式1,使能接收 76 TMOD|=0x20; //定時器1工作方式2(8位自動重裝初值) 77 TMOD&=~0x10; 78 TH1=0xfa; //9600bps 79 TL1=0xfa; 80 PCON|=0x80; //SMOD=1 81 TR1=1; 82 TI=0; 83 RI=0; 84 //PS=1; //提高串口中斷優先級 85 ES=1; //開啟串口中斷使能 86 } 87 //比較指令頭部 88 bit CompareCMD_head(char CMD_head[]) 89 { 90 unsigned char CharNum; 91 for(CharNum=0;CharNum<4;CharNum++) //指令長度為10個字符 92 { 93 if(!(RecvString_buf[CharNum+4]==CMD_head[CharNum])) 94 { 95 return 0; //指令頭部匹配失敗 96 } 97 } 98 return 1; //指令頭部匹配成功 99 } 100 //比較指令尾部(start:從哪里開始比較,quality:比較多少個字符,CMD_tail[]:要比較的字符串) 101 bit CompareCMD_tail(unsigned char start,unsigned char quality,char CMD_tail[]) 102 { 103 unsigned char CharNum; 104 for(CharNum=0;CharNum<quality;CharNum++) 105 { 106 if(!(RecvString_buf[start+CharNum]==CMD_tail[CharNum])) 107 { 108 return 0; 109 } 110 } 111 return 1; 112 } 113 bit Deal_UART_RecData() //處理串口接收數據包函數(成功處理數據包則返回1,否則返回0) 114 { 115 //PutString(RecvString_buf); 116 if(RecvString_buf[0]==datapackage_headflag&&buf_string[14]=='#') //進行數據包頭尾標記驗證 117 { 118 switch(RecvString_buf[1]) //識別發送者設備ID的第1位數字 119 { 120 case '0': 121 switch(RecvString_buf[2]) //識別發送者設備ID的第2位數字 122 { 123 case '3': 124 if(CompareCMD_head("Ligt")) //判斷指令頭部是否為"Ligt" 125 { 126 //下面是指令尾部分析 127 switch(RecvString_buf[8]) 128 { 129 case '0': 130 switch(RecvString_buf[9]) 131 { 132 case '0': 133 134 return 0; 135 case '1': 136 if(CompareCMD_tail(10,3,"Off")) //判斷整個數據包是否為:A03_Ligt01Off_# 137 { 138 //如果是則執行以下代碼 139 return 1; 140 } 141 if(CompareCMD_tail(10,3,"On_")) //判斷整個數據包是否為:A03_Ligt01On__# 142 { 143 //如果是則執行以下代碼 144 return 1; 145 } 146 return 0; 147 default: 148 return 0; 149 } 150 default: 151 return 0; 152 } 153 } 154 return 0; 155 156 default: 157 return 0; 158 } 159 default: 160 return 0; 161 } 162 } 163 return 0; 164 } 165 /************************ 166 中斷函數 167 ************************/ 168 //串口中斷服務函數----------- 169 void USART() interrupt 4 //標志位TI和RI需要手動復位,TI和RI置位共用一個中斷入口 170 { 171 if(ReceiveString()) 172 { 173 //數據包長度正確則執行以下代碼 174 Deal_UART_RecData(); 175 } 176 else 177 { 178 //數據包長度錯誤則執行以下代碼 179 //LED1=~LED1; 180 } 181 RI=0; //接收並處理一次數據后把接收中斷標志清除一下,拒絕響應在中斷接收忙的時候發來的請求 182 } 183 /*************************** 184 主函數 185 ***************************/ 186 void main() 187 { 188 EA=1; 189 Init_USART(); //初始化串口中斷通信,當串口接受完數據包后,如果檢測到數據包包含有效指令,則自動執行對應的代碼,執行完自動返回到主函數,為了盡可能不影響主函數的時序,串口中斷函數的執行代碼不要過復雜 190 while(1) 191 { 192 //下面可以放要經常運行的用戶代碼,使用PutString()函數來發送數據包,如PutString(HeartBeat); 注:空格的ASCLL碼是:0x20,回車是:0x0D 193 194 195 } 196 }