关于单片机串口接收不固定字节长度的解决方案

之前老是有个问题困惑着小编,串口这种一字节一字节的接收和发送的机制,而在实际应用中如果收发双方不制定一个协议,没有帧头,帧尾做一帧数据接收开始和结束的标准。或者收发数据长度不固定,怎么去确定每一帧长度不同的帧呢?

面对这种每次发送长度不固定的情况,我最开始采用的是定时器中断加串口中断的方式。

    如现在来了一帧长度为86字节的数据,我在串口接收中断中将这86个字节一一入循环队列,假设循环队列的长度是256个字节,现在86个字节已经接收完毕,那么,我在哪里出队来确保这86个字节能一次性出完呢,很多人可能考虑的是主函数轮询出队,但是你可想过,如果主函数里面处理的任务不多,导致轮询过快,如果此时出队列就会导致86个字节出队时不能出完,从而会被多次拆分形成多帧数据,处理数据无效。

    所以可以采用定时器中断,每80ms中断一次,每次中断,数据出队列,80ms已经可以实现一次性接收256个字节都会完整的出队了。而且80ms肉眼基本看不出,只能看到这边发送,那边就已经把完整的数据打印出来了,而且256个字节以内任意发多少,都没有数据被拆分,丢失的问题。这样显然是初步解决了任意长度发送,接收的问题,但是在实际应用使用中,如果要求串口高速度,发不定长的数据,就会出现一系列的问题,如现在客户要求任意长度的一帧数据,我连续发送多帧,而且每帧的时间间隔要小于10ms,并且我们之间收发没有通信协议,我想给你发什么就发什么,你要解析数据。面对这种客户要求这种定时器中断加串口中断肯定是不行了的。那么采用什么会有效解决这个问题呢。

    仔细看单片机的数据手册关于串口中断的描述,会发现,其实串口中断中,存在一个空闲中断,这个空闲中断帮了我大忙,空闲中断并不是说串口空闲就触发,这样的话岂不是串口如果没有数据过来,空闲中断一直触发,那么整个程序不就一直处于中断之中嘛。空闲中断是指,当最后一个字节接收完毕后,硬件接收中断接收完数据后使得IDLE寄存器中的标志位置高,于是,我们便可以采用此空闲中断标志置高来提醒主程序可以出队,因为一帧数据已经发送完毕了。

1、下图为初始化时使能串口接收中断和空闲中断,及重写串口中断函数方法:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Lic5Lmd77yB,size_20,color_FFFFFF,t_70,g_se,x_16

 注意:USART5_IRQHandler函数里面,用关于先读SR,再读DR是为了清除空闲中断标志,以至于下一帧数据还能触发此中断,而下面那个变量置1,提醒主程序一帧数据接收完毕,你该去处理了。

2、下图为在主程序里轮询检测数据结束标志,如果标志被置1则进行相应处理。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Lic5Lmd77yB,size_20,color_FFFFFF,t_70,g_se,x_16

 注意:uart5_handle函数里面据实际情况而实现,如果你只想单纯的转发数据,那么直接判断队列里数据长度,根据长度出队所有数据,然后进行转发就行,如果需要解析处理数据的话,也可以先出队再解析处理。

以上就是关于单片机接收不定长数据的解决方案的全部处理过程,其中如果收发双方如果制定了协议,则可以根据协议收发不定长数据。如果没有串口通信协议则可以使用,串口接收中断加串口空闲中断的方式处理!!小编也是刚刚入坑不久,欢迎各位大佬对不对的地方评论。谢谢!!

 

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是51单片机串口接收固定长度字符串的代码示例: ```c #include <reg52.h> #include <stdio.h> #include <string.h> #define FOSC 11059200L #define BAUD 9600 #define TH1 (256 - FOSC / 12 / BAUD) sbit LED = P1^0; char buffer[20]; unsigned char index = 0; void UART_init() { TMOD &= 0x0F; TMOD |= 0x20; TH1 = TL1 = TH1; TR1 = 1; SM0 = 0; SM1 = 1; REN = 1; EA = 1; ES = 1; } void UART_interrupt() interrupt 4 { if (RI) { RI = 0; // 接收到数据 char data = SBUF; if (data == '\r') { // 字符串接收完毕 buffer[index] = '\0'; if (strcmp(buffer, "on") == 0) { LED = 1; } else if (strcmp(buffer, "off") == 0) { LED = 0; } index = 0; } else { // 继续接收 buffer[index] = data; index++; } } } void main() { UART_init(); while (1); } ``` 在上面的代码中,我们定义了一个字符数组 `buffer` 和一个整型变量 `index`。`buffer` 数组用于存储接收到的字符串,`index` 变量表示当前接收到的字符在 `buffer` 数组中的位置。 在 `UART_interrupt()` 函数中,我们首先判断 RI 寄存器的值,如果为 1,表示接收到了数据。我们读取 SBUF 寄存器的值,然后判断接收到的数据是否为回车符(`\r`)。如果是回车符,表示字符串接收完毕,我们将 `buffer` 数组末尾加上字符串结束符 `\0`,然后使用 `strcmp()` 函数来判断接收到的字符串是不是等于 "on" 或 "off"。根据判断结果来控制 LED 灯的亮灭,并将 `index` 变量清零。如果不是回车符,表示字符串还未接收完毕,我们将接收到的字符存储到 `buffer` 数组中,并将 `index` 变量加 1。 最后在 `main()` 函数中,我们调用 `UART_init()` 函数初始化串口,并进入一个无限循环中等待串口中断。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值