• 串口中断(10)自定义通讯协议-协议带数据长度及接收应答处理


    本文为博主 日月同辉,与我共生,csdn原创首发。希望看完后能对你有所帮助,不足之处请指正!一起交流学习,共同进步!

    > 发布人:@日月同辉,与我共生_单片机-CSDN博客

    > 欢迎你为独创博主日月同辉,与我共生点赞❤🧡💛+关注👍+收藏🌹+评论☺。

    系列专栏: CSDN-单片机串口通信学习系列🎁

    > 我的格言是:“尽最大努力,做最好的自己!💪

    要转载,请提前告知!!!

    版权声明:本文为CSDN博主「日月同辉,与我共生」的原创文章,CSDN独一份。

    69d2c602c4ec47cab5112eeed53f9d69.png

    目录

    一、自定义通讯协议

    二、系统设计要求

    三、硬件设计

    3.1UART通信模块

    3.2数码管模块

    3.3仿真图

    四、软件设计

    4.1串口初始化

    4.2接收中断

    4.3数码管模块

    4.4定时器模块

    4.5主程序

    4.6发送数据模块

    4.7清除缓存

    4.8uart.h

    五、系统测试

    5.1正确应答-类型01,长度01

    5.2正确应答-类型02,长度02

    5.3正确应答-类型03,长度03

    5.4错误应答-和效验错误

    5.5错误应答-异或效验错误

     

    一、自定义通讯协议

    自定义通讯协议是指一种特定的数据交互方式,该方式不同于通用的通讯协议,例如HTTP、TCP等。自定义通讯协议可以根据具体的业务需求和通讯要求进行设计,满足数据传输的可靠性、效率、安全性等方面的需求。

    ⛄定义通信内容及传输方式:首先需要明确通信双方需要交换哪些数据。例如,数据头、数据体、校验位、结束符等。同时需要确定数据传输的方式,如串口通信、TCP/IP通信、WiFi通信等。

     

    ⛄定义字节序列:根据通信内容,将需要传输的数据按照一定的规则组成字节序列,以便在传输过程中进行处理。

     

    ⛄添加校验和:在传输数据时,为了保证数据的完整性和正确性,需要给数据添加校验和。常用的校验方式有CRC校验、累加和校验等。

     

    ⛄定义解析规则:通信双方需要约定如何解析接收到的数据。例如,如何区分数据头和数据体,如何解析数据体中的不同字段等。

     

    ⛄计好通信协议后,需要进行通信测试。测试时需要模拟发送和接收端,并验证数据传输和解析是否正确。如果出现问题,需要及时修改协议设计或者程序代码。

    🌟在实际应用中,自定义通讯协议可以应用于各种领域,例如智能家居、工业控制、物联网等。自定义协议的设计能够在满足特殊需求的同时,提高通信效率、降低通信成本,为应用领域提供更加可靠、安全、高效的通信解决方案。

    二、系统设计要求

    2.1总设计要求

    虚拟终端com3发送数据给单片机com1,发送的数据帧由帧头、类型、数据长度、代码块、和校验、异或校验组成。接收完数据后,单片机根据用户双方约定好的校验位是否正确,发送不同的应答(数据帧),如果数据正确,根据不同的数据类型,让4位数码管显示不同的数据。

    2.2设计细节

    数据帧:55 AA 类型(01/02/03) 数据长度(1/2/3) 代码块 和校验 异或校验

    数据类型为01时,数据长度为1,显示1个字节;数据类型为02时,数据长度为2,显示2个字节;数据类型为03时,数据长度为3,显示2个字节(4位数码管只能显示2个字节,这里显示代码块第2、3个数据);

    正确应答:0x55,0xAA,0x80,0x0,0x80,0x80

    错误应答:分和效验错误应答和异或效验错误应答。

    和效验错误应答:0x55,0xAA,0x81,0x0,0x80,0x80

    异或效验错误应答:0x55,0xAA,0x82,0x0,0x80,0x80

    三、硬件设计

    3.1UART通信模块

    d46307903ec043dbb46178025efca0e0.png

    3.2数码管模块

    线码:dp g f e d c b a

    段码(控制数码管第几位显示):1 2 3 4

    数码管模块:采用定时延时法。软件编码步骤:1.消隐 2.段码 3.线码 4.延时1ms(定时器定时1ms)。

    74HC245芯片用于提高P0的电流驱动能力,让数码管能够显示数据。

    a7f04e63db0542daa559ab6dcf9d9bf1.png

    3.3仿真图

    元件清单:四位数码管采用共阴极。在本文基础上,大家可以尝试使用6位数码管用于显示数据。

    e0fb29ddaf3646fe91fbfada31cf5810.png

    9cee1a76ec6e424e8a35b249aa92cc62.png

    四、软件设计

    本文4.1、4.2、4.6、4.7节均为uart.c(串口通信源文件)

    代码文件整理:

    f9ceb0fcb7244a0fb1ef225e355ef69f.png

    4.1串口初始化

    1. void UartInit(void) //9600bps@11.0592MHz
    2. {
    3. PCON &= 0x7F; //波特率不倍速
    4. SCON = 0x50; //8位数据,可变波特率
    5. TMOD &= 0x0F; //设置定时器模式
    6. TMOD |= 0x20; //设置定时器模式
    7. TL1 = 0xFD; //设置定时初始值
    8. TH1 = 0xFD; //设置定时重载值
    9. ET1 = 0; //禁止定时器中断
    10. ES=1; //串口中断打开
    11. TR1 = 1; //定时器1开始计时
    12. }

    4.2接收中断

    数组recv_buf存储类型、长度、代码块数据,用于主程序处理数码管显示。

    定义sum_check、 xor_check分别求和效验值、异或效验值。

    分支0:判断帧头数据55是否正确==>分支1:判断帧头数据AA是否正确==>分支2:接收类型,从分支2开始计算和效验、异或效验==>分支3:接收长度,计算和效验、异或效验==>分支4:接收代码块,计算和效验、异或效验==>分支5:判断和效验是否正确,若错误发送和效验错误应答==>分支6:判断异或效验是否正确,若错误单片机发送异或效验错误应答,正确发送单片机发送正确应答。

    1. void ES_timers() interrupt 4 //接收中断
    2. {
    3. static unsigned char machine_step=0;
    4. static unsigned char sum_check;
    5. static unsigned char xor_check;
    6. unsigned char i;
    7. if(RI)
    8. {
    9. RI=0;
    10. recv_data=SBUF;//recv_data为中间变量,用于接收发送的数据
    11. switch(machine_step) //55 AA 01/02/03 长度 代码块 和效验 异或效验
    12. {
    13. case 0:
    14. if(recv_data==0x55) //帧头:55H AAH
    15. {
    16. machine_step=1;
    17. }
    18. else
    19. {
    20. machine_step=0;
    21. }
    22. break;
    23. case 1:
    24. if(recv_data==0xAA)
    25. {
    26. machine_step=2;
    27. recv_cnt=0;
    28. }
    29. else
    30. {
    31. machine_step=0;
    32. }
    33. break;
    34. case 2:
    35. recv_buf[recv_cnt]=recv_data; //接收数据类型
    36. sum_check=recv_data;
    37. xor_check=recv_data;
    38. machine_step=3;
    39. recv_cnt++;
    40. break;
    41. case 3:
    42. recv_length=recv_data; //数据字节长度
    43. sum_check+=recv_data;
    44. xor_check^=recv_data;
    45. machine_step=4;
    46. break;
    47. case 4:
    48. recv_buf[recv_cnt]=recv_data; //接收数据
    49. sum_check+=recv_data; //计算和效验
    50. xor_check^=recv_data; //计算异或效验
    51. if(recv_cnt==recv_length)
    52. {
    53. machine_step=5;//接收数据完成后跳到下一个分支
    54. }
    55. else
    56. {
    57. machine_step=4;
    58. }
    59. recv_cnt++;
    60. break;
    61. case 5:
    62. if(sum_check==recv_data) //和效验正确
    63. {
    64. machine_step=6;
    65. }
    66. else //和效验错误
    67. {
    68. machine_step=0;
    69. for(i=0;i<6;i++)
    70. {
    71. sendByte(sum_check_error[i]);//和效验错误应答
    72. }
    73. }
    74. break;
    75. case 6:
    76. if(xor_check==recv_data) //异或效验正确
    77. {
    78. recv_flag=1; //接收正确,标志位置1
    79. for(i=0;i<6;i++)
    80. {
    81. sendByte(recv_correct[i]);//发送正确应答
    82. }
    83. }
    84. else
    85. {
    86. for(i=0;i<6;i++)
    87. {
    88. sendByte(xor_check_error[i]);//异或效验错误应答
    89. }
    90. }
    91. machine_step=0;
    92. sum_check=0;
    93. xor_check=0;
    94. recv_cnt=0;
    95. break;
    96. default:break;
    97. }
    98. }
    99. }

    4.3数码管模块

    display.c

    1. #include "display.h"
    2. unsigned char code leddata[]={
    3. 0x3F, //0
    4. 0x06, //1
    5. 0x5B, //2
    6. 0x4F, //3
    7. 0x66, //4
    8. 0x6D, //5
    9. 0x7D, //6
    10. 0x07, //7
    11. 0x7F, //8
    12. 0x6F, //9
    13. 0x77, //A
    14. 0x7C, //B
    15. 0x39, //C
    16. 0x5E, //D
    17. 0x79, //E
    18. 0x71, //F
    19. 0x76, //H
    20. 0x38, //L
    21. 0x37, //n
    22. 0x3E, //u
    23. 0x73, //P
    24. 0x5C, //o
    25. 0x40, //-
    26. 0x00 //熄灭
    27. };//数码管段码表
    28. unsigned char LEDBuf[]={8,8,8,8};//缓冲区
    29. unsigned char code PLACE_COOE[]={0xfe,0xfd,0xfb,0xf7};//位码
    30. /******************************************************************************************
    31. 函数名:display
    32. 功能:数码管显示函数
    33. 参数:无
    34. 返回值:无
    35. ******************************************************************************************/
    36. void display()
    37. {
    38. unsigned char i;
    39. IO_DIG=0x00;//消隐
    40. IO_DIG=leddata[LEDBuf[i]];//段码
    41. IO_PLACE=PLACE_COOE[i];//位码
    42. i++;
    43. if(N==i)
    44. i=0;
    45. }

    display.h

    1. #ifndef __DISPLAY_H__
    2. #define __DISPLAY_H__
    3. #include
    4. #define IO_DIG P0 //段码IO
    5. #define IO_PLACE P2 //位码IO
    6. #define N 4 //数码管个数
    7. unsigned char code leddata[];//变量声明
    8. extern unsigned char LEDBuf[];
    9. void display();//数码管显示函数声明
    10. #endif

    4.4定时器模块

    定时1ms,用于数码管能够显示出数字。

    1. void Timer0_Init(void) //1毫秒@11.0592MHz
    2. {
    3. TMOD &= 0xF0; //设置定时器模式
    4. TMOD |= 0x01; //设置定时器模式
    5. TL0 = 0x66; //设置定时初始值
    6. TH0 = 0xFC; //设置定时初始值
    7. TF0 = 0; //清除TF0标志
    8. ET0=1; //定时器0中断打开
    9. TR0 = 1; //定时器0开始计时
    10. }
    11. void T0_timer() interrupt 1 //利用1ms计数,判断是否接收完成
    12. {
    13. TR0=0;
    14. display();
    15. TL0 = 0x66; //设置定时初始值
    16. TH0 = 0xFC; //设置定时初始值
    17. TR0=1;
    18. }

    4.5主程序

    LEDBuf[ ]:数码管显示缓冲区,用于显示display.c表中对应的数字/字符。

    recv_buf[0]:数据类型

    长度为1,显示recv_buf[0]

    长度为2,显示recv_buf[1]、recv_buf[2]

    长度为3,显示recv_buf[2]、recv_buf[3]

    要想显示数据,就要将数据(2位数)拆成每一位分别显示到对应的位置。十位数=数据右移4位,个位数=数据&0x0f。

    1. #include
    2. #include "uart.h"
    3. #include "display.h"
    4. void Timer0_Init(); //定时器0函数声明
    5. sbit LED=P1^0;//位定义
    6. sbit Beep=P1^7;
    7. void main()
    8. {
    9. UartInit(); //调用串口初始化函数
    10. Timer0_Init();
    11. EA=1; //总中断允许
    12. printf("Wait for Serial Communication Tset Start.\r\n");
    13. printf("Please Send a string of data:\r\n");
    14. while(1)
    15. {
    16. if(recv_flag==1) //接收完毕,开始解析
    17. {
    18. recv_flag=0;
    19. switch(recv_buf[0])//数据类型---命令
    20. {
    21. case 0x01://数码管显示1字节
    22. LEDBuf[0]=23;
    23. LEDBuf[1]=23;
    24. LEDBuf[2]=recv_buf[1]>>4;
    25. LEDBuf[3]=recv_buf[1]&0x0f;
    26. break;
    27. case 0x02://数码管显示2字节(第1、2个数据)
    28. LEDBuf[0]=recv_buf[1]>>4;
    29. LEDBuf[1]=recv_buf[1]&0x0f;
    30. LEDBuf[2]=recv_buf[2]>>4;
    31. LEDBuf[3]=recv_buf[2]&0x0f;
    32. break;
    33. case 0x03://数码管显示2字节(第2、3个数据)
    34. LEDBuf[0]=recv_buf[2]>>4;
    35. LEDBuf[1]=recv_buf[2]&0x0f;
    36. LEDBuf[2]=recv_buf[3]>>4;
    37. LEDBuf[3]=recv_buf[3]&0x0f;
    38. break;
    39. default:clr_recvbuffer(recv_buf); //清除缓存
    40. break;
    41. }
    42. }
    43. }
    44. }

    4.6发送数据模块

    发送数据dat==>等待发送完成(发送完成TI自动置1)==>TI清0,以便下次能够发送数据帧。

    1. void sendByte(unsigned char dat) //发送一帧数据功能函数
    2. {
    3. SBUF=dat;
    4. while(!TI);
    5. TI=0;
    6. }
    7. void sendString(unsigned char *dat)//发送字符串函数
    8. {
    9. while(*dat != '\0')
    10. {
    11. sendByte(*dat++);
    12. }
    13. }

    4.7清除缓存

    1. void clr_recvbuffer(unsigned char *buf)//清除缓冲
    2. {
    3. unsigned char i;
    4. for(i=0;i
    5. {
    6. buf[i]=0;
    7. }
    8. }

    4.8uart.h

    1. #ifndef __UART_H__
    2. #define __UART_H__
    3. #include
    4. #include
    5. #define MAX_REX_NUM 20
    6. #define MAX_timer_cnt 5
    7. extern unsigned char recv_buf[MAX_REX_NUM];
    8. extern unsigned char recv_cnt;
    9. extern unsigned char start_timer;
    10. extern unsigned char recv_timer_cnt;
    11. extern unsigned char recv_flag;
    12. extern unsigned int led_data;
    13. extern unsigned int beep_data;
    14. extern unsigned int led_cnt;
    15. extern unsigned int beep_cnt;
    16. void UartInit(void);
    17. void sendByte(unsigned char dat);
    18. void sendString(unsigned char *dat);
    19. char putchar(char c);
    20. void clr_recvbuffer(unsigned char *buf);
    21. #endif

    五、系统测试

    串口助手模式:十六进制

    5.1正确应答-类型01,长度01

    类型:01,长度:01,代码快:10

    和效验正确,异或效验正确,正确应答55 AA 80 00 80 80

    数码管显示  10

    c7d16f56d8724f18a4682039e34c411a.png

    5.2正确应答-类型02,长度02

    类型:02,长度:02,代码快:10 11

    和效验正确,异或效验正确,正确应答55 AA 80 00 80 80

    数码管显示1011

    461b247642da4dc78415425d318cc246.png

    5.3正确应答-类型03,长度03

    类型:03,长度:03,代码快:10 11 13

    和效验正确,异或效验正确,正确应答55 AA 80 00 80 80

    数码管显示1113

    8b92579ced0145f6927f22de8ac75f2d.png

    5.4错误应答-和效验错误

    和效验错误,异或效验正确,和效验错误应答55 AA 81 00 80 80

    ee0f1c75fcb0415f84200566054b1902.png

    5.5错误应答-异或效验错误

    和效验错误,异或效验错误,异或效验错误应答55 AA 82 00 80 80

    6a3f01d5372648b880685ff3d3524832.png

    想要快速计算出效验值吗?关注我并在评论区留言“666”,博主@日月同辉,与我共生将免费提供快速计算出效验值的网站。

    目前,串口通信系列已经写了9篇,大家可以到我的主页​​​​​​​免费查询。

    下一文将着重讲述CRC16效验,亲爱的读者敬请期待,下一文更精彩!!!

    一日不读书,胸臆无佳想。我叫不白吃,喜欢我的,可以支持我,博主名叫@日月同辉,与我共生

    @日月同辉,与我共生_单片机基础,单片机串口通信-CSDN博客@日月同辉,与我共生擅长单片机基础,单片机串口通信,等方面的知识,@日月同辉,与我共生关注stm32,c语言,51单片机,proteus,单片机领域.https://blog.csdn.net/LIN___IT?spm=1000.2115.3001.5343

     

     

     

     

  • 相关阅读:
    设计模式之策略模式
    spring常见问题汇总
    YOLOv8原创改进:最新原创WIoU_NMS改进点,改进有效可以直接当做自己的原创改进点来写,提升网络模型性能精度
    chatgpt赋能python:如何升级你的Python到最新版本
    【Linux命令】vim 多模式编辑
    C++ DAY3
    Leetcode 39.组合总和
    蓝桥杯嵌入式基础模块——定时器的基本作用(新板)STM32G431(HAL库开发)
    Consul(注册中心)部署
    centos6 升级glibc
  • 原文地址:https://blog.csdn.net/LIN___IT/article/details/134286617