单片机或嵌入式中使用环形缓冲区对串口数据的接收

缓冲区的基本认识

    在进行嵌入式的开发的时候,经常会使用串口,会用到对串口数据的接收或发送,比如log日志的打印,命令的接收,或者解析一些串口设备的数据。等等。

    经常会有人碰到数据接收不全,或当串口设备发送的数据前后帧时间间隔比较短时,出现所谓的粘包,而没有办法完成完整的数据的解析。

    环形缓冲区可以帮你完成这些操作。

    要介绍的是环形缓冲区。先复制一个网上的图片,大家一个环形缓冲区的基本认识。

图片

  环形缓冲区,顾名思义就是一个环形。但其实呢,他还是一个线性的数组,是首尾相连的数组而已,所谓首尾相连,就是写或读到最后一个位置的时候,下一次开始操作第一个位置。

两个重要的操作

  这其中有个重要的操作就是需要知道什么时候装满了。什么时候取空了。

  空:当读的位置追上写的位置,两者相等,即代表空。

  满:当写的位置追上读的位置,写的下一个位置和读相等,即代表满。

  如果理解了上上面两句,就对这个缓冲的操作基本掌握了。

定义如下接口:

/*初始化全局指针变量*/extern void buf_ring_init();/*向缓冲区中写入一个Byte*/extern int buf_ring_write_ch(const unsigned char ch);/*从缓冲区中读出一个Byte*/extern int buf_ring_read_ch(unsigned char *outch);/*从缓冲区是否空*/extern int buf_ring_is_empty();/*从缓冲区是否满*/extern int buf_ring_is_full();

在中断函数中调用 buf_ring_write_ch 将数据存入缓冲区,

在主函数中调用 ring_read_ch 将数据读出。

以上就是基本思路。

代码实现

==============直接上代码=================

定义一个枚举类型表示buf的状态

typedef enum{eBUF_NONE,eBUF_EMPTY,eBUF_FULL,}ebuf_ring_status_t;

定义结构体类型对buf进行管理。

#define BUF_RING_LEN_MAX (1024*4)typedef struct{unsigned char buf[BUF_RING_LEN_MAX];unsigned short write_index;unsigned short read_index;ebuf_ring_status_t status;}buf_ring_t;

定义缓冲区实体

buf_ring_t buf_ring;

函数实现

void buf_ring_init(){buf_ring_t *pbuf = &buf_ring;pbuf->write_index = 0;pbuf->read_index = 0;pbuf->status = eBUF_NONE;}

是否为空

/*** @brief** @return int*/int buf_ring_is_empty(){int ret = 0;buf_ring_t *pbuf = &buf_ring;/*when read equal write,buf is empty*/if(pbuf->read_index == pbuf->write_index){ret = 1;}else{ret = 0;}return ret;}

是否满

/*** @brief** @return int*/int buf_ring_is_full(){int ret = 0;buf_ring_t *pbuf = &buf_ring;unsigned short tmp_index = 0;/*when next write (w+1), is equal read,buf is full*/tmp_index = (pbuf->write_index+1)%BUF_RING_LEN_MAX;if(tmp_index == pbuf->read_index){ret = 1;}else{ret = 0;}return ret;}

还有就是读出和写入一个Byte的操作,这里就不展开了。

可以文末关注我,查看相关原文。有相应的代码。

===============分割线====================

扩展应用

         思考一下,其实单片机或嵌入式的中断服务函数和主函数,类似于RTOS中的TASK,或者操作系统中的多线程。此环形缓冲的思路也可以用在上位机软件的多线程中。

        一个线程对设备数据进行读取并放入缓冲区。

        另一个线程对缓冲区中的数据进行读取然后进行解析。

        相信作为开发者的你,读了之后也多少会有收获。同时也可以给出意见。

        欢迎关注进行交流。

图片

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,根据您的要求,以下是使用环形缓冲区,基于8位单片机串口单字节接收断,接收串口数据的逻辑代码,代码使用了C语言: ``` #define BUFFER_SIZE 16 // 缓冲区大小 unsigned char buffer[BUFFER_SIZE]; // 环形缓冲区 unsigned char in = 0, out = 0; // 缓冲区的输入输出指针 // 串口接收断服务函数 void uart_rx_interrupt() interrupt 4 { if (RI) { // 接收数据 RI = 0; // 清除接收断标志位 buffer[in] = SBUF; // 将数据存入缓冲区 in = (in + 1) % BUFFER_SIZE; // 修改缓冲区的输入指针 } } // 主函数 void main() { // 初始化串口 SCON = 0x50; // 串口工作在模式1:8位数据,可变波特率,无校验位,1个停止位 TMOD = 0x20; // 定时器1工作在模式2:8位自动重载定时器 TH1 = 0xFD; // 波特率9600,定时器1初值 TL1 = TH1; // 定时器1初值 TR1 = 1; // 启动定时器1 ES = 1; // 使能串口断 while(1) { if (in != out) { // 判断缓冲区是否有数据 unsigned char data = buffer[out]; // 从缓冲区读取数据 out = (out + 1) % BUFFER_SIZE; // 修改缓冲区的输出指针 // 处理接收到的数据 // ... } } } ``` 在这段代码,我们定义了一个长度为16的缓冲区,并声明了输入输出指针。当串口接收数据时,断服务函数会将数据存入缓冲区,此时输入指针指向缓冲区的下一个位置。当主函数需要处理数据时,会从缓冲区读取数据,并将输出指针指向缓冲区的下一个位置。这样,就实现了基于环形缓冲区串口数据接收

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值