专栏首页嵌入式iot串口驱动框架的设计思考

串口驱动框架的设计思考

串口驱动框架的设计思考

  • 1.本文概述
  • 2.简易串口协议设计
  • 3.基于固定缓冲区的设计
  • 4.基于报文的处理解析
  • 5.循环缓冲区的使用
  • 6.基于帧处理的串口框架
  • 7.总结

1.本文概述

串口驱动不简单,在实际工作中,往往串口驱动框架的设计都是需要考虑的非常清楚的,特别是实际的项目中。比如基于串口模块功能的协议开发,以及基于串口模块的网络数据收发等等,都是一些值得好好设计和思考的问题。本文目的是总结一下目前我见到过的常用的几种模型,并且对这些设计提出自己的一些想法。

2.简易串口协议设计

简单的串口的使用就是收发数据,当串口数据到来后,通过中断通知,拷贝到一个内存固定数组中,下次协议需要处理的时候,直接从该数组中去取数据。

这种设计的思维逻辑是比较理想化的模型,这种模型的思考主要是主从设备的考虑,按照下面的逻辑来进行:

1.主机发送数据到模块

2.等待一定时间后,模块回复数据给主机

3.此时MCU会通过串口中断接收到一帧数据,然后拷贝到固定的数组中

4.MCU去处理固定数组中的数据,然后清空数组

以上的设计从原理上没问题,似乎没有任何漏洞。但是实际上,真实设备的通信往往不会那么完美,会出现以下的情况:

1.模块发过来的数据,可能有断帧的情况,明明模块只需要一帧回复就可以,但是因为一些原因导致程序分为前后两帧数据发过来,这样导致第二帧覆盖了数据,导致数据不准确。

2.设备的外部电平干扰,这种情况发生在主机给从机发送完成后,等待从机回复后,缓冲区中的数据没有及时取走,外部信号导致主机认为一帧数据的到来,从而破坏了缓冲区中的数据。

3.主机发送数据后,等待从机回复数据的时间不确定,有可能导致处理数据时还在接收数据。

这三种风险的存在,使得往往真实的设备上出现了许许多多问题,导致设备的使用体验不佳,或者程序出现频频的bug。为了解决这些问题,不同的嵌入式程序员在上面思考了很多解决办法。

3.基于固定缓冲区的设计

这种设计就是因为看到了模块的数据发送的特点,以及数据没及时取走就被覆盖的问题,于是设计了一个带有接收数量的缓冲区模型。

前面一种是基于串口的帧数据模型,而缓冲区考虑的是串口的字节模型。帧数据模型一般就是串口接收的时候,发生串口接收中断,把数据放到缓冲区,当一帧数据接收完成后会发生空闲中断,或者DMA完成中断,或者是采用定时器时定时器中断,这样判定接收到一帧数据。而循环缓冲区则不需要考虑一帧数据完成的中断,这种设计都是在软件中完成。

这种设计都会采用一个USART_RX_STA接收状态寄存器的变量来表明接收数据的状态,通用的设计规则是

1.USART_RX_STA的0~14位用于存储串口接收数据的个数,每次接收完成都会将USART_RX_STA++

2.USART_RX_STA的15,16两位表示接收的1状态,简而言之,比如接收状态0x4000表示接收到了0x0d,而0x8000则表示接收完成。

数据处理在任务中,当判断数据接收完成时,处理缓冲区中的数据即可。这种设计很多开发板例程有,所以很多人直接拿开发板例程做项目时经常可以看到。

问题也是很明确,当做协议处理,十分不友好,需要及时去取数据,否则会掉帧。当数据接收的很快时,也会出现问题。

4.基于报文的处理解析

这种我只在轨道交通某些程序上看到这种设计,设计是基于报文格式,这种报文处理方式的好处很明显,可以一个字节一个字节的处理,比如可以判断报头是0x10,第二个报文是0x02等等,然后依次往数据包中处理写数据,判断结尾帧后,一次性交给其他程序处理数据包。

这个数据包的处理放在串口接收中断里面,然后去解析报文,获取相关的信息。

基于报文的解析规则,需要的串口波特率比较低,比如9600以下等等,并且采用485等进行数据传输。这样可靠性有一定的保障,但是数据的发送一定需要在比较低的波特率下进行。而且两帧数据之间需要有一定时间的间隔。

5.循环缓冲区的使用

这一种设计需要一个read_index指针和write_index指针。

设计的关键点在于读写分离,每次去读数据的时候,不用关注是否一帧数据,直接从循环buffer中去取数据就可以了。当read_index=write_index的时候,表示buffer中的数据已经处理干净了。

这种读写分离的设计考虑可以参见rt-thread物联网操作系统的serial框架设计。

这种设计从一定程度上,避免了数据丢包的情况,但是在读数据的时候,如果出现了连包的情况,处理起来也比较的复杂。特别是在不定长数据协议栈的处理上,也会显的无力。

对于收发有序的逻辑处理比较好,但是不定期发送数据,处理起来也需要一定工作量。

6.基于帧处理的串口框架

目前,正在做一个基于物联网的通用系统模型,所以思考了一下基于物联网上面的串口模块使用的架构,最后设计了一个可以使用的框架。当前不一定很完善,但是还是把自己思考的部分分享出来。

由于物联网模块的AT指令都是不定长的,而且时一帧一帧的数据,而且实际透传模式时,收发没有固定的逻辑,都是随机的,所以针对这种特性设计以下的模型。

其设计思想基于串口中间件的考虑,上层应用不直接访问串口驱动硬件。每次都调用同样的接口去包管理器中去取一帧数据,所以这种设计的出发点是基于一帧数据的模型。关于确保一帧数据的机制,可以使用定时器去判断,如果在一定的时间里面没有收到数据,发生了定时器中断,那么就认为这一帧数据的结束。如果这个假设出现了太多问题,那么这个设计也是不行的。

而通用接口又不用关注是否有数据到来的问题,直接调用接口,有数据则返回数据信息,没有数据则告诉没有数据。

所有的数据包管理在一个对象结构体中进行,读数据时,将data_r_index向后偏移,比如最大定义8个pkgs,那么达到8时再回到0,然后去读对应index的数据。并且读数据,会有rest_size,表示这个里面还剩下多少数据没有读完,下次调用读函数时,直接去处理没读完的数据就好了,而不用去找到第二帧数据。

int uart_buffer_read(char *uart_buf, uint16_t len)
{   
    int ret = 0;
    //两者相等,无数据
    if(uart_manage.data_w_index == uart_manage.data_r_index)
    {
        ret = 0;
    }
    else
    {
        if(len >= uart_pkg[uart_manage.data_r_index].rest_size)
        {
            len = uart_pkg[uart_manage.data_r_index].rest_size;
            rt_memcpy(uart_buf, uart_pkg[uart_manage.data_r_index].user_data + (uart_pkg[uart_manage.data_r_index].recv_size - uart_pkg[uart_manage.data_r_index].rest_size), len);
            uart_pkg[uart_manage.data_r_index].rest_size = 0;
            ret = len;
            
            uart_pkg[uart_manage.data_r_index].recv_size = 0;
            
            if(uart_manage.data_r_index < 7)
            {
                uart_manage.data_r_index = uart_manage.data_r_index + 1;
            }
            else
            {
                uart_manage.data_r_index = 0;
            }
        }
        else
        {
            rt_memcpy(uart_buf, uart_pkg[uart_manage.data_r_index].user_data + (uart_pkg[uart_manage.data_r_index].recv_size - uart_pkg[uart_manage.data_r_index].rest_size), len);
            uart_pkg[uart_manage.data_r_index].rest_size = uart_pkg[uart_manage.data_r_index].rest_size - len;
            ret = len;
        }
    }
    return ret;
}

对于串口写数据的设计,则是由接收到一帧数据后触发,由中断触发,此时一帧数据接收完成,会有一定的休息时间,所以一般足够将收到的数据写到对应的包中。

int uart_buffer_write(char *uart_buf, uint16_t len)
{
    if(len > 512)
    {
        rt_kprintf("write buffer too big!\n");
        return 0;
    }
    rt_memset(uart_pkg[uart_manage.data_w_index].user_data, 0, 512);
    uart_pkg[uart_manage.data_w_index].recv_size = len;
    uart_pkg[uart_manage.data_w_index].rest_size = len;
    rt_memcpy(uart_pkg[uart_manage.data_w_index].user_data, uart_buf, len);
    uart_manage.buff = &uart_pkg[uart_manage.data_w_index];

    if(uart_manage.data_w_index < 7)
    {
        uart_manage.data_w_index = uart_manage.data_w_index + 1;
    }
    else
    {
        uart_manage.data_w_index = 0;
    }
    return len;
}

通过上述的设计逻辑,很好的处理了一帧数据包的问题,同时每帧数据都带有长度信息和相关的读写状态信息,也能够不丢帧。同时采用循环index的机制,能够不干扰数据正常的存取的情况下,保障的数据的可靠性。

当然,这种设计的前提是保证一帧数据的完整性与可靠性的比例很高,如果断帧的情况比较严重,那还是采用循环buffer,通过字节管理的方式进行设计。

7.总结

对于串口框架的设计,是需要好好思考的,设计串口驱动程序时,不要认为串口驱动简单,在做协议时,也不能太过于数据传输的理想化,应该综合考虑连包、断帧、超时、干扰等等因素,这样设计的驱动才会更加的稳定,上文只是简单的描述常见的一些处理串口数据的逻辑,应该还有更多的,更好的设计框架值得去学习理解,也希望有更多的人提出一些更好的设计模型。

本文分享自微信公众号 - 嵌入式IoT(Embeded_IoT),作者:bigmagic

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2021-03-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 设计公司组织架构的思考框架

    曾经有一个公司超过400人的销售运营负责人找到我,说她希望替换掉现有CRM,尽快上一套新的。问了她很多关于公司组织及业务上的问题,我最后给她画了下面这张图。

    ToB行业头条
  • SaaS渠道设计和建设的思考框架

    ? 来源 :SaaS白夜行 作者:王东 ---- ? 引言 经常会接到一些朋友关于渠道业务的咨询,提到一些问题,如: 该不该搞独家代理商? 是直营和渠道并存...

    腾讯SaaS加速器
  • 接口自动化测试--框架设计思路

    之前文章跟大家分享了一下自己在接口自动化测试中进行测试准备的一些相关知识点,接下来本篇文章详细分享一下接口自动化框架设计的思路总结,希望能对初次探索接口自动化...

    用户5521279
  • 自己动手撸个简单的LCD驱动框架吧!

    废话不多说,理论讲太多没啥感觉,这些条条框框本质就是基于面对对象的设计模式相关的一些理论,设计模式就是前人实践多了发现一些规律然后总结出来的那么一套好用的框架,...

    morixinguan
  • DOV原创驱动-短视频社交设计思考

    ? 腾讯ISUX isux.tencent.com 社交用户体验设计 ? ? 项目背景 为什么要做短视频? 随着微视、YOO视频等短视频APP的异军突起,短...

    腾讯ISUX
  • 小钢的架构思考:架构设计

    最近一个多月因为忙于工作上的项目重构,所以文章一直没能更新。现在,重构终于暂时告一段落,于是,赶紧抽时间把文章写完更新发布。下面进入正文。

    Keegan小钢
  • 【设计思维框架】框架 :为现代企业重新设想的设计思维

    我们认为世界体系应该为人服务。 我们以人为本的使命的核心是企业设计思维:一个以现代企业的速度和规模解决用户问题的框架。

    首席架构师智库
  • 【我们一起写框架】领域驱动设计的CodeFirst框架(一)—序篇

    领域驱动设计就是我们俗称的DDD,英文全拼是Domain-Driven Design。

    Kiba518
  • 架构设计-商品模块的领域驱动设计思路及实现

    因脱敏关系,这里面三个角色就用A,B,C来代替,可以抽象理解为, A 是需求发起方,B是平台运营方, C是资源提供方. 代入今天的商品模块,就是A 要商品, C...

    微笑的小小刀

扫码关注云+社区

领取腾讯云代金券