• 串口中断(9)即时解析用户自定义通讯协议--接收数据固定情况


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

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

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

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

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

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

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

    6fc58cb779cf4b1abfd70ea313d654a5.png

    目录

    一、系统设计要求

    二、系统设计原理

    三、硬件设计

    3.1串口设计

    3.2LED电路设计

    3.3蜂鸣器设计

    四、软件设计

    4.1串口初始化

    4.2接收中断

    4.3定时器T0初始化

    4.4定时器中断

    4.5发送数据函数

    4.6主程序

    4.7uart.h

    五、结果展示

    5.1控制LED

    5.2控制蜂鸣器

    六、效验软件推荐

    一、系统设计要求

    虚拟终端com1发数据给单片机com1,接收后将数据重新发给com3。

    com3发送到数据:帧头(1、2帧)+数据类型(3帧)+数据块(4、5帧)+效验位(6、7帧)

    帧头:共2帧,55 AA   

    数据类型:共1帧,01时控制LED灯,02时控制蜂鸣器

    数据块:共2帧,第一帧为高8位,第二帧为低8位,两帧合并为16位2进制,用于控制LED亮/蜂鸣器发声时间。

    效验位:共2帧,第一帧为和效验,第二帧为异或效验。

    com1将数据重新的数据:发送到数据不是将接收到的数据全部发送给com3,而是第3-5帧数据发送给com3。

    二、系统设计原理

    本次实验采取"边接收边处理式"的思路。

    由于串口通信是一帧一帧接收的,所以我们可以每接收一帧就判断帧头数据的正确性。

    至于com1要重新发送的数据,可以定义一个数组recv_buf用于存储com1要发送的数据。

    使用switch-case语句,分支0、1,分别判断帧头数据55和AA后,进入下一个分支。

    分支2接收类型+代码快(共3帧),并将3帧存储到数组recv_buf,同时定义两个专门计算效验值的变量(和效验变量sum_check和异或效验变量xor_check),在分支2中计算出第3-5帧的和效验值、异或效验值。

    然后进入分支3、4,用于判断计算出的和效验、异或效验值是否与接收到效验帧数据相同。如果和效验和异或效验位正确,则接收完成标志位recv_flag置1,代表接收数据完成且正确 。

    主程序中,在recv_flag==1的基础上进行,根据类型数据帧,采用switch-case语句分别处理LED/蜂鸣器。

    持续时间:高8位左移8位+低8位

    LED发亮持续时间:led_data=recv_buf[1]<<8+recv_buf[2];

    蜂鸣器发声时间:beep_data=recv_buf[1]<<8+recv_buf[2];

    注:recv_buf[0]是数据类型,recv_buf[1]是高8位,recv_buf[2]是低8位。

    三、硬件设计

    3.1串口设计

    com1发送端TXD接com2发送端TXD,com1接收端RXD接com2接收端RXD。

    虚拟终端RXD接TXD(因为单片机发送数据给com3,而虚拟终端可以理解为虚拟串口com3)。

    d530f4a2d47647dbb61a93cd7d44fab1.png

    3.2LED电路设计

    LED灯采用共阳极接法,左端接电源(高电平1),右端通过电阻接P1^0,P1^0低电平时,LED导通,发亮;P1^0高电平时,LED不导通,不亮(熄灭)。

    5086ca43e8d248269ac4b0f196b9d4ec.png

    3.3蜂鸣器设计

    R2右端接到P1^7,P1^7为低电平时,三极管导通,+5V电源(提供电压电流的作用)与蜂鸣器连接,蜂鸣器正极为高电平;P1^7为高电平,三极管不导通,蜂鸣器正极为低电平。

    4b3aaaa7758a4a07bf8557137f7c498b.png

    四、软件设计

    4.1串口初始化

    波特率9600b/s,晶振频率为11.0592Mhz,串口工作方式1(8位异步通信,波特率可变),定时器T1的工作方式2(8位自动重载)。

    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接收中断

    帧头判断55 AA==>将3帧(1帧类型+2帧数据块),计算和效验值、异或效验值==>判断和效验、异或效验是否正确==>正确则接收完成标志位recv_flag置1。

    不直接用数组recv_buf来存储接收数据,因为recv_buf存储的数据用于发送给com3,而单片机只将一部分数据发送给com3,不能将接收到的数据全存储到recv_buf。需要定义recv_data(中间值),用于接收com3发来的数据。

    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. if(RI)
    7. {
    8. RI=0; //RI清0
    9. recv_data=SBUF;//接收数据
    10. switch(machine_step)
    11. {
    12. case 0:
    13. if(recv_data==0x55)//帧头:55
    14. {
    15. machine_step=1;
    16. }
    17. else
    18. {
    19. machine_step=0;
    20. }
    21. break;
    22. case 1:
    23. if(recv_data==0xAA)//帧头:AA
    24. {
    25. machine_step=2;
    26. recv_cnt=0;
    27. }
    28. else
    29. {
    30. machine_step=0;
    31. }
    32. break;
    33. case 2:
    34. recv_buf[recv_cnt]=recv_data;//将单片机com1要发送到数据存储到数组recv_buf
    35. recv_cnt++;
    36. sum_check+=recv_data; //计算和效验
    37. xor_check^=recv_data; //计算异或效验
    38. if(recv_cnt>2) //存储完3个数据(类型01/02+代码快)后进入下一分支
    39. {
    40. machine_step=3;
    41. }
    42. else
    43. {
    44. machine_step=2;
    45. }
    46. break;
    47. case 3:
    48. if(sum_check==recv_data) //和效验正确
    49. {
    50. machine_step=4;
    51. }
    52. else
    53. {
    54. machine_step=0;
    55. }
    56. break;
    57. case 4:
    58. if(xor_check==recv_data) //异或效验正确
    59. {
    60. recv_flag=1;//接收正确
    61. }
    62. machine_step=0;
    63. sum_check=0;//和效验变量清0
    64. xor_check=0;//异或效验变量清0
    65. recv_cnt=0;
    66. break;
    67. default:break;
    68. }
    69. }
    70. }

    4.3定时器T0初始化

    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. }

    4.4定时器中断

    1. void T0_timer() interrupt 1 //利用1ms计数,判断是否接收完成
    2. {
    3. TR0=0;
    4. if(led_cnt//LED发亮
    5. {
    6. LED=0;
    7. led_cnt++;
    8. }
    9. else //LED灯灭
    10. {
    11. LED=1;
    12. }
    13. if(beep_cnt!=0) //蜂鸣器发声
    14. {
    15. Beep=~Beep;
    16. beep_cnt--;
    17. }
    18. TL0 = 0x66; //设置定时初始值
    19. TH0 = 0xFC; //设置定时初始值
    20. TR0=1;
    21. }

    4.5发送数据函数

    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.6主程序

    1. #include
    2. #include "uart.h"
    3. void Timer0_Init(); //定时器0函数声明
    4. sbit LED=P1^0;//位定义
    5. sbit Beep=P1^7;
    6. void main()
    7. {
    8. UartInit(); //调用串口初始化函数
    9. Timer0_Init();
    10. EA=1; //总中断允许
    11. while(1)
    12. {
    13. if(recv_flag==1)//接收完成
    14. {
    15. recv_flag=0;
    16. sendString(recv_buf);//发送缓冲区数据
    17. switch(recv_buf[0])//根据类型判断对LED处理还是蜂鸣器处理
    18. {
    19. case 0x01:
    20. led_data=recv_buf[1]<<8;//高8位recv_buf[1]左移8位
    21. led_data=led_data+recv_buf[2];//计算LED控制时间
    22. led_cnt=0;
    23. break;
    24. case 0x02:
    25. beep_data=recv_buf[1]<<8;//高8位recv_buf[1]左移8位
    26. beep_data=beep_data+recv_buf[2];//计算蜂鸣器控制时间
    27. beep_cnt=beep_data;
    28. break;
    29. default:clr_recvbuffer(recv_buf);//清除缓存
    30. break;
    31. }
    32. }
    33. }
    34. }

    4.7uart.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控制LED

    com3发送给com1的数据:55 AA 01 20 20 41 01

    com1重新发回给com3的数据:01 20 20

    结果:LED亮2020ms,2020ms后灭。

    c7a62e12be0b478ca077b210ca92f679.png

    e4922b820c124eedb1ef586a15da24ec.png

    5.2控制蜂鸣器

    com3发送给com1的数据:55 AA 02 20 20 42 02

    com1重新发回给com3的数据:02 20 20

    结果:蜂鸣器发声2020ms,2020ms后停止发声。

    855db8ca351b49368636bf5dc079fb3a.png

     

    cfaf9f643bfc44adb59cd93c79ee8c16.png

    六、效验软件推荐

    为方便读者们学习串口通信,为大家推荐效验计算工具。

    链接:

    异或校验/BCC校验计算-ME2在线工具 (metools.info)

    efec006f52274022b2e648d12348a47e.png

    5f9e2d7f48cc4eab92c1302d326b0a9d.png

    亲爱的读者敬请期待,下一文更精彩!!!

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

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

     

     

  • 相关阅读:
    【无标题】
    IOS面试题object-c 21-30
    【无标题】
    记一堂公开课《前端架构的设计与进化》思考总结
    内存管理【C++】
    能介绍一下Git的分支管理功能吗?
    RabbitMQ快速入门--simple简单模式
    在顺序表中使用顺序查找法查找某个关键字
    OSPF的LSA优化
    Zebec&Solana基金会AMA圆满召开,Lily Liu盛赞Zebec
  • 原文地址:https://blog.csdn.net/LIN___IT/article/details/134256765