51单片机——串口1收发

- 日理万妓 2022-11-10 03:49 297阅读 0赞

0. 序

使用串口1产生波特率,115200,使用STC-ISP波特率计算器生成

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM1Njk3OTc4_size_16_color_FFFFFF_t_70

1. 修改

参考网上代码,发现无法运行,于是修改

修改串口初始化,不加这几个没法接收

  1. TI=0;
  2. RI=0;
  3. REN=1; //不开启这个无法接受数据
  4. //PS=1; //提高串口中断优先级
  5. ES=1; //开启串口中断使能
  6. EA=1;

2. 问题

修改后发现有不合理的地方,待后期修改,如下图,应该接收超时然后返回0,但是返回0之后就错了,如果不从这里跳出函数,发现是1个字1个字接收,无法正常收发

我对51的串口收发机制还不够熟悉,后期再来解决这个问题

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM1Njk3OTc4_size_16_color_FFFFFF_t_70 1

3. 代码

本文参考的代码非常好,模块化和帧头帧尾判断都写的很好,参考文章链接在文末。

使用如下代码,只需要把uartInit()函数放在main里面初始化即可。

  1. #include <UART.H>
  2. #include "intrins.h"
  3. # include <string.h>
  4. //------------------串口通信的数据包协议-----------------//
  5. /*
  6. 此程序的串口字符串通信使用到下面的一个自定义协议,每次通信都是发送或接收一个数据包,数据包格式解释如下(长度恒为15):
  7. 例如:A01_fmq_01Off___#
  8. A--------数据包的开始标记(可以为A到Z,意味着数据包可以有26种)
  9. 01-----设备代号
  10. fmq_01Off___--------指令(长度恒为10),指令的前4个人字符是指令头部,指令的后6个字符是指令尾部
  11. #---------数据包的结束标记
  12. 例如:A02_SenT010250#
  13. A--------数据包的开始标记(可以为A到Z,意味着数据包可以有26种)
  14. 02-----设备代号
  15. SenT010250--------指令(长度恒为10),指令的前4个人字符是指令头部,指令的后6个字符是指令尾部
  16. #---------数据包的结束标记
  17. */
  18. char RecvString_buf[16]; //定义数据包长度为15个字符 //写15会超出字符,数组最后一位默认\0终止符号
  19. #define deviceID_1Bit '0' //用于串口通信时,定义本地设备ID的第1位
  20. #define deviceID_2Bit '2' //用于串口通信时,定义本地设备ID的第2位
  21. #define datapackage_headflag 'A' //用于串口通信时,定义数据包头部的验证标记
  22. char DataPackage_DS18B20[16]= {datapackage_headflag,deviceID_1Bit,deviceID_2Bit,
  23. '_','S','e','n','T','X','X','X','X','X','X','#'
  24. }; //这个是曾经用来控制温度传感模块(DS18B20)的数据包
  25. char HeartBeat[16]= {datapackage_headflag,deviceID_1Bit,deviceID_2Bit,
  26. '_','B','e','a','t','X','X','X','X','X','X','#'
  27. }; //我随便定义了一个数据包用来做"心跳包",比如单片机系统向电脑每2秒发送一次该数据包,如果电脑没有按时接收到,就认为单片机死掉了
  28. //串口初始化
  29. void uartInit(void)
  30. {
  31. PCON &= 0x7F; //波特率不倍速
  32. SCON = 0x50; //8位数据,可变波特率
  33. AUXR |= 0x40; //定时器1时钟为Fosc,即1T
  34. AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
  35. TMOD &= 0x0F; //清除定时器1模式位
  36. TMOD |= 0x20; //设定定时器1为8位自动重装方式
  37. TL1 = 0xFD; //设定定时初值
  38. TH1 = 0xFD; //设定定时器重装值
  39. ET1 = 0; //禁止定时器1中断
  40. TR1 = 1; //启动定时器1
  41. REN=1; //不开启这个无法接受数据
  42. TI=0;
  43. RI=0;
  44. REN=1; //不开启这个无法接受数据
  45. //PS=1; //提高串口中断优先级
  46. ES=1; //开启串口中断使能
  47. EA=1;
  48. PutString("打开串口1\r\n");
  49. }
  50. //串口发送函数
  51. void PutString(unsigned char *TXStr)
  52. {
  53. ES=0;
  54. while(*TXStr!=0)
  55. {
  56. SBUF=*TXStr;
  57. while(TI==0);
  58. TI=0;
  59. TXStr++;
  60. }
  61. ES=1;
  62. }
  63. //串口接收函数
  64. bit ReceiveString()
  65. {
  66. u8 * RecStr=RecvString_buf;
  67. u8 num=0;
  68. u8 count=0;
  69. loop:
  70. *RecStr=SBUF;
  71. count=0;
  72. RI=0;
  73. if(num<14) //数据包长度为15个字符,尝试连续接收15个字符
  74. {
  75. num++;
  76. RecStr++;
  77. while(!RI)
  78. {
  79. count++;
  80. if(count>60) //只能收到1个 40接收单个 50接收2-3个 60可接收全部15个数字
  81. return 1; //接收数据等待延迟,等待时间太久会导致CPU运算闲置,太短会出现"数据包被分割",默认count=130
  82. }
  83. goto loop;
  84. }
  85. return 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. memset(RecvString_buf,0,16);
  117. PutString("\r\n");
  118. if(0)
  119. //if(RecvString_buf[0]==datapackage_headflag&&buf_string[14]=='#') //进行数据包头尾标记验证
  120. {
  121. switch(RecvString_buf[1]) //识别发送者设备ID的第1位数字
  122. {
  123. case '0':
  124. switch(RecvString_buf[2]) //识别发送者设备ID的第2位数字
  125. {
  126. case '3':
  127. if(CompareCMD_head("Ligt")) //判断指令头部是否为"Ligt"
  128. {
  129. //下面是指令尾部分析
  130. switch(RecvString_buf[8])
  131. {
  132. case '0':
  133. switch(RecvString_buf[9])
  134. {
  135. case '0':
  136. return 0;
  137. case '1':
  138. if(CompareCMD_tail(10,3,"Off")) //判断整个数据包是否为:A03_Ligt01Off_#
  139. {
  140. //如果是则执行以下代码
  141. return 1;
  142. }
  143. if(CompareCMD_tail(10,3,"On_")) //判断整个数据包是否为:A03_Ligt01On__#
  144. {
  145. //如果是则执行以下代码
  146. return 1;
  147. }
  148. return 0;
  149. default:
  150. return 0;
  151. }
  152. default:
  153. return 0;
  154. }
  155. }
  156. return 0;
  157. default:
  158. return 0;
  159. }
  160. default:
  161. return 0;
  162. }
  163. }
  164. return 0;
  165. }
  166. //串口中断服务函数-----------
  167. void USART() interrupt 4 //标志位TI和RI需要手动复位,TI和RI置位共用一个中断入口
  168. {
  169. if(ReceiveString())
  170. {
  171. //数据包长度正确则执行以下代码
  172. Deal_UART_RecData();
  173. }
  174. else
  175. {
  176. //数据包长度错误则执行以下代码
  177. //LED1=~LED1;
  178. PutString("接收失败\r\n");
  179. }
  180. RI=0; //接收并处理一次数据后把接收中断标志清除一下,拒绝响应在中断接收忙的时候发来的请求
  181. }

本文参考:https://www.cnblogs.com/weifeng727/p/5617924.html

发表评论

表情:
评论列表 (有 0 条评论,297人围观)

还没有评论,来说两句吧...

相关阅读

    相关 51单片机系列--串口通讯

    串行通信和并行通信 串行通信和并行通信都是一种通信传输方式,都适用于计算机与计算机、计算机与单片机之间的数据通信,在传输中存在相互转换的关系。但是两者在使用上还是有着不同

    相关 51 单片机串口实验

    51单片机有一个全双工的串行口。 在 PROTEUS 软件中,可以使用虚拟终端,和单片机的串口进行通信实验。 此时,并不需要在电路中加上 TTL-RS2

    相关 51单片机串口通信(UART)

    项目描述: 1.串口工作方式为1(8位UART,波特率可变),无校验位; 2.通信数据格式为:1位起始位 + 8位数据位 + 1位停止位; 3.上电后MCU给上位机