原创:51单片机串口通信(字符串接收和发送)
下面的示例代码基于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 }
版权声明:本文为weifeng727原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。