原创:51单片机串口通信(字符串接收和发送)

时间:2024-02-17 11:39:13

下面的示例代码基于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 }