开篇先说一句废话····
本旺名字叫萨摩耶,,Please 叫我旺财,,,哈哈,招财进宝嘛!

开篇

计算机按照下行数据通信协议,串口发送数据,地址为自己的学号(十六进制),单片机收到后(收到的是数据,不是地址),如果和自己的学号比较,如果相等,则延时100ms后根据自己拟定的通信协议应答,其中数据域为0xAA,如果不是自己的学号,则应答的数据域为0x55。

 单片机   ------>   计算机* 前导符 : 2* 地址   : 1* 功能号 : 1* 长度   : 1* 数据域 : 自定* 校验 :   1
计算机   ------>   单片机* 前导符 : 2* 地址   : 1* 功能号 : 1* 长度   : 1* 数据域 : 自定* 校验 :   1

项目分析

项目中自定义部分,再次说明(16进制数)
计算机 -------> 单片机部分
前导符1: 57
前导符2: 4a
地址 : 0a
功能号 : 01
长度 : 0c(12)
数据 :32 30 31 39 31 38 30 36 30 32 31 30(学号 ,ASCii码)
校验 :01
单片机 -------> 计算机部分
前导符1: 57
前导符2: 4a
地址 : 0a
功能号 : 01
长度 : 01(1)
数据 :AA 或者 55(取决于判断结果)
校验 :A0 或者 5F

串口通信协议

因为单片机每次收发只能是一个字节,而正常使用不会只发一个字节,而是很多字节一起,为了确保数据包的正确性,双方约定好一个数据包的每一个字节的意思(串口通讯协议);为了统一,所以每个数据包里会大致规定。在此项目中,一个数据包中,前导字节有两位;地址一位;功能号一位;长度一位;校验位一位。题目要求接收数据要与学号比较,所以先规定学号是12个字节,即计算机发送到单片机的数据域为12个字节数据;在比较后按数据域响应 0xAA或者0x55.(判断结果

接收部分

根据题目要求,先是计算机给单片机按照通信协议发送数据,对于单片机而言,我们先是接收。
接收时,接收的数据有不同的意思,有前导(2个字节)、地址、功能号、长度、数据、校验。每一个意思需要各自的操作,所以需要定义一个变量用表示每一个意思。

u8 RxStus = 0;

前导和地址和校验只需要判断,不需要保存起来使用;而长度功能号需要保存起来,(长度接受数据时使用,功能号在判断功能时使用);数据是变化的,所以定义一个数组保存。接收数据时需要判断接收了多少了,毕竟不能一直接收,所以也需要一个变量。所有的数据接收完还要给一个标志

u8 RxLth = 0;       //长度
u8 RxFunc = 0;     //接收功能号
u8 RxBuf[16];       //接收数据数组
u8 Rxcounter = 0;  //接收数据当前个数
bit bRxFlag = 0;   //接收完成标志

因为我们是接收,所以数据不确定什么会从计算机发来,所以所有接收部分都应该在中断中处理。
步骤:

  1. 如果前导符1正确进入前导2
  2. 如果前导2正确进入地址
  3. 如果地址正确进入功能号
  4. 接收完功能号进入长度
  5. 接收完长度进入数据;如果长度为0(没有数据)进入校验
  6. 数据接收完进入校验;如果没有接收完继续接收
  7. 如果校验正确反馈接收完成标志,清空接收长度和接收数据计数
    (需要校验的数据有地址、功能号、长度、所有数据域数据)
void UART_ISR(void) interrupt 4
{u8 tmp;        //用来保存每次进入接收部分的数据(前导、地址等数据)static u8 sum = 0;        //用来保存校验结果,以便于与最后校验部分比较if(RI){RI = 0;tmp = SBUF;       //保存数据switch(RxStus){       //判断此时是哪种状态case 0 : //前导if(tmp == 0x57)       //自定义前导符1,此时规定是 0x57RxStus = 1;     //进入前导符2break;              //退出判断case 1 :  //前导if(tmp == 0x4a)       //自定义RxStus = 2;break;case 2 : //地址if(tmp == 0x0a){  //自定义(题目规定学号) 10RxStus = 3;sum = tmp;       //开始校验}break;case 3 :   //功能号RxFunc = tmp;RxStus = 4;sum ^= tmp;     //校验功能号break;case 4 :   //长度RxLth = tmp;RxStus = 5;sum ^= tmp;       //校验长度if(tmp == 0)    //没有数据RxStus = 6;break;case 5 :    //数据sum ^= tmp;        //校验数据RxBuf[Rxcounter++] = tmp;if(Rxcounter >= RxLth)        //接收数据域完成RxStus = 6;break;case 6 : //校验if(tmp == sum)    //接收回来的校验数据和根据需要校验的数据进行校验后的结果比较bRxFlag = 1;    //接收完成标志置位RxStus = 0;Rxcounter = 0;break;default :RxStus = 0;Rxcounter = 0;break;}}
}

其实这个接收部分采用的状态机,而里面提到的每一个意思就是状态机里的每个状态。(长知识,嘿嘿)

比较数据是否正确及发送数据

接收部分完成,在主函数中判断接收完成标志是否置位,如果置位说明一个数据包接收完成,开始比较数据是否与学号相等,如果相等则返回数据域0xAA,否则返回0x55.

因为要发送数据,所以把所有的数据(包括前导符、地址等)保存到一个数组中,来发送。因为要比较,所以需要一个数组存放学号,发送需要发送长度,发送计数(保存当前发送个数)

u8 TxBuf[16];                    //保存所有发送数据
u8 StuNum[] = "201918060210";    //保存学号,用来比较
u8 TxLth = 0;                  //发送数据长度
u8 Txcounter = 0;              //发送数据计数

主函数步骤:

  1. 检测接收完成标志
  2. 比较数据(接收数据和学号)
  3. 计算校验结果
  4. 设置发送长度
  5. 延时100ms
  6. 开始发送数据

比较数据有两种:
1.使用C库函数 strcmp() 字符比较函数,当它相等时返回0,具体可以自行搜索查看
2. 用for循环判断,设置一标志位,如果相等标志位置位,否则标志位清除并退出循环(只要有错就跳出,不在检查剩余数据)(若使用此方法,在中断接收后,接收长度不能清零(在判断完后清空就好),或者赋给专门的变量)

TxBuf数组
------------------------------------------------
|   [0]  |   [1] | [2]|  [3] | [4] | [5]  |[6] |
------------------------------------------------
| 前导符1 |前导符2 |地址 |功能号 |长度 |数据域 |校验 |
------------------------------------------------
if(bRxFlag){                     //判断数据是否正确bRxFlag = 0;if(strcmp(RxBuf,StuNum) == 0)  //采用第一种方式比较TxBuf[5] = 0xAA;            //数据域elseTxBuf[5] = 0x55;TxBuf[6] = TxBuf[2];for(i = 3;i <= 5;i++)         //从功能号 ---> 数据域  TxBuf[6] ^= TxBuf[i];      //进行异或校验TxLth = 7;                     //0~6 数组有效长度7Delay_ms(100);SBUF = TxBuf[Txcounter++];        //开始发送
}

因为主函数开始发送,接下来要在中断中接着发

if(TI){TI = 0;if(Txcounter < TxLth){         //如果没有发完度,接着发SBUF = TxBuf[Txcounter++];}else{Txcounter = 0;              //发送完毕后,计数和长度清零TxLth = 0;}
}

因为反馈时前导、地址、功能号不变,所以可以在初始化时初始化好,所以在初始化函数中,除了串口初始化还需要添加前导、地址、功能号。

void MCU_Init()
{//串口初始化   默认方式1SCON = 0x50;TMOD &= 0x0f;TMOD |= 0x20;TH1 = TL1 = 0xfd;    //9600TR1 = 1;ES = 1;//发送数据TxBuf[0] = 0x4a;      //前导    0x4a  TxBuf[1] = 0x43;     //前导    0x43TxBuf[2] = 0x0a;       //地址    0x0a    学号:10TxBuf[3] = 0x01;       //功能号  1TxBuf[4] = 0x01;       //长度    1
}

到此,串口协议已经完成。

串口调试助手发送数据

添加测试数据时,别手残哦。手底快点昂······





接着发送即可。

理论效果是:

测试错误数据,即学号不同(学号修改后需要重新校验,步骤如上(不校验前导))

效果:

完整代码

#include "main.h"u8 RxBuf[16];
u8 TxBuf[16];
u8 StuNum[] = "201918060210";
bit bRxFlag = 0;
u8 RxLth = 0;
u8 TxLth = 0;
u8 Rxcounter = 0;
u8 Txcounter = 0;
u8 RxStus = 0;
u8 RxFunc = 0;void main(void)
{u8 i;MCU_Init();EA = 1;while(1){if(bRxFlag){      //判断数据是否正确bRxFlag = 0;if(strcmp(RxBuf,StuNum) == 0)TxBuf[5] = 0xAA;     //数据域elseTxBuf[5] = 0x55;TxBuf[6] = TxBuf[2];for(i = 3;i <= 5;i++)TxBuf[6] ^= TxBuf[i];TxLth = 7;Delay_ms(100);SBUF = TxBuf[Txcounter++];}}
}void UART_ISR(void) interrupt 4
{u8 tmp;static u8 sum = 0;if(RI){RI = 0;tmp = SBUF;switch(RxStus){case 0 :   //前导if(tmp == 0x57)RxStus = 1;break;case 1 : //前导if(tmp == 0x4a)RxStus = 2;break;case 2 : //地址if(tmp == 0x0a){  //10RxStus = 3;sum = tmp;}break;case 3 :  //功能号RxFunc = tmp;RxStus = 4;sum ^= tmp;break;case 4 :   //长度RxLth = tmp;RxStus = 5;sum ^= tmp;if(tmp == 0) //没有数据RxStus = 6;break;case 5 :    //数据RxBuf[Rxcounter++] = tmp;sum ^= tmp;if(Rxcounter >= RxLth)RxStus = 6;break;case 6 :    //校验if(tmp == sum)bRxFlag = 1;RxStus = 0;Rxcounter = 0;break;default :RxStus = 0;Rxcounter = 0;break;}}if(TI){TI = 0;if(Txcounter < TxLth){SBUF = TxBuf[Txcounter++];}else{Txcounter = 0;TxLth = 0;}}
}void MCU_Init()
{//串口初始化   默认方式1SCON = 0x50;TMOD &= 0x0f;TMOD |= 0x20;TH1 = TL1 = 0xfd;    //9600TR1 = 1;ES = 1;TxBuf[0] = 0x4a;        //前导    0x4a  TxBuf[1] = 0x43;     //前导    0x43TxBuf[2] = 0x0a;       //地址    0x0a    学号:10TxBuf[3] = 0x01;       //功能号  1TxBuf[4] = 0x01;       //长度    1
}void Delay_ms(u16 ms)
{unsigned char i, j;while(ms--){_nop_();i = 2;j = 199;do{while (--j);} while (--i);}
}

51单片机学习篇-- --基于51单片机的串口通信协议相关推荐

  1. 51单片机学习杂记——基于STC89C52RC

    51单片机学习杂记--基于STC89C52RC 我是看的b站郭天祥老师的课,说实话,我觉得我能力不是很够,所以记得很杂.废物了属于是. 接下来就是正文了 基本的元器件以及字母符号含义: 电容:帮助晶振 ...

  2. 51单片机五层电梯控制器 基于51单片机的五层电梯控制系统

    51单片机五层电梯控制器 基于51单片机的五层电梯控制系统 包括源代码和proteus仿真 系统硬件由51单片机最小系统.蜂鸣器电路.指示灯电路.内部按键电路.外部按键电路.直流电机.内部显示电路.外 ...

  3. 【51毕设案例】基于51单片机-定时喂食供氧智能鱼缸

    目录 功能介绍: 整体功能简述: 硬件框图: 软件流程图: 原理图: 仿真图: 元器件清单: 主要部分代码展示:void main() 工程管理展示: 下载内容展示: 更详细代码视频讲解及代码下载请看 ...

  4. 单片机stm32LED流水灯C语言,STM32单片机入门 篇二:单片机编程:从点亮LED灯开始(二)...

    STM32单片机入门 篇二:单片机编程:从点亮LED灯开始(二) 2019-04-26 13:21:48 19点赞 73收藏 19评论 写在前面的话:本篇是承接上一篇文章,属于小白向. 没看过的朋友点 ...

  5. 51单片机计算器_基于51单片机的倒计时温度检测报警器

    基于51单片机的倒计时温度检测报警器 学习单片机断断续续半年了,在学长学姐的教学帮助下,完成了51单片机的入门学习,开始实践做项目,在一周时间内设计和制作出个人项目.起初,我设计和选择的是12864显 ...

  6. c语言51单片机计算器,新基于51单片机的简易计算器

    <新基于51单片机的简易计算器>由会员分享,可在线阅读,更多相关<新基于51单片机的简易计算器(24页珍藏版)>请在人人文库网上搜索. 1.基于51单片机的简易计算器1.前言: ...

  7. 51单片机开发实例 基于51单片机的光控灯

    一.系统设计 通过光敏电阻模块检测环境中的光强,系统会根据程序设定的阈值完成小灯的控制,环境较暗且低于控制阈值小灯就会亮起.光敏电阻模块可以根据不同的光照强度输出相应的模拟电压信号,模拟信号会通过AD ...

  8. 万年历设计单片机c语言,基于51单片机的电子万年历的设计

    基于51单片机的电子万年历的设计(论文9400字) 功能要求 1. 万年历能用数码管显示阳历年.月.日.星期.[小]时.分.秒并设置指定时间的闹铃. 2. 数字式温度计要求测温范围-50~100°C, ...

  9. 项目篇 | 基于STM32单片机NBIOT定位实战项目

    前言 绘制基于 STM32 单片机的 NBIOT 实战项目. 文章目录 前言 一.原理图 1.绘制 1)电源供电 a.USB 转 TTL 电路 b.锂电池充电管理电路 c.3.3V电压转换电路 d.一 ...

最新文章

  1. 关于IOS的屏幕适配(iPhone)——资源适配
  2. raid5数据恢复成功案例
  3. 使用RBTool自动提交code review请求
  4. 第三届山东大数据-威海赛区-民宿空置预测-排行榜
  5. boost::phoenix::delete和using boost::phoenix::new_相关的测试程序
  6. FPGA学习之路—应用程序—原码二位乘法器及Verilog代码分析
  7. 【HDU - 1326】Box of Bricks(模拟水题)
  8. 超级vga显示卡_VGA视频采集卡常见故障分析
  9. matlab2c使用c++实现matlab函数系列教程-exppdf函数
  10. hdu acm2549
  11. 【零基础】PostgreSQL从入门到精通
  12. 上传本地新项目到SVN服务器
  13. 技术点:使用vue和element实现地图选点功能
  14. cdlinux中minidwep的使用
  15. markdown段落首行缩进2个字符
  16. mysql replace into 异常1365 - Division by 0
  17. 计算机体系结构基础2(究竟什么是冯诺依曼瓶颈(von Neumann Bottleneck)?)
  18. 股票学习-量柱和k线-第五天
  19. 项目经理的岗位职责和要求
  20. 编写简单的中文分词程序

热门文章

  1. 勿以恶小而为之,勿以善小而不为
  2. 计算机课程调整论文,计算机课程改革论文
  3. 安卓Socket与pc端c#服务器的通信 附完整代码
  4. 计算机cpu的功能和作用是什么意思,电脑的CPU和内存都起什么作用?
  5. JAVA表示姓名和对应的出生日期
  6. 关于DVDScr, Screener,TS, TC等常见术语
  7. 自动化行业软件工程师工作第一年总结
  8. Arch Linux KDE Plasma + Windows 11 双系统安装指南
  9. 电商平台数据查询工具(京东数据分析软件)
  10. pandas批量处理体育成绩