2 基于RTOS的串口三层软件架构设计

串口软件架构可以分为三层,如下:

疑问:很多开发人员为何觉得只有两层?

答案:开发人员把应用层和数据链路层和一块了,判断一帧后,直接调用应用层的函数对数据进行处理了。

通常要按分层设计的话,解析出一帧后通过消息或者函数调用,通知应用层处理,这样就体现出三层设计了。

2.1 底层(物理层收发)

    RS232,RS485物理介质,配置串行通信波特率、数据位、校验位,产生字节的接收中断和发送中断。

2.2 环形缓冲区接收发送

2.2.1 什么是环形缓冲区

Ring Buffer(环形缓冲区)是一种固定大小、头尾相连的数据结构,用于高效处理连续数据流。其核心特点是通过两个指针(读指针和写指针)实现循环读写,适用于嵌入式系统、网络通信、音频/视频流处理等场景。

核心原理 

- 结构:基于数组实现,内存连续,逻辑上形成环形。 

- 指针机制: 

  - 写指针(write_index):指向下一个可写入位置。 

  - 读指针(read_index):指向下一个可读取位置。 

- 状态判断: 

  - 空状态:read_index == write_index 且无数据。 

  - 满状态:read_index == write_index 且有数据(需结合镜像标志位或异或运算判断)。

典型应用场景 

- 嵌入式系统:串口通信、驱动设备数据缓存。

- 网络通信:TCP/IP协议栈数据缓存、多线程数据交换。

- 音频/视频流:实时编解码、流媒体传输。

- 系统日志:循环记录日志信息,覆盖旧数据。

实现方式 

- 固定大小:缓冲区初始化后不可扩容。

- 动态扩容:部分实现支持动态调整大小(如Boost库)。

- 无锁设计:Linux内核kfifo采用镜像指示位法实现无锁操作。

优势 

- 高效性:数据读写无需移动内存,适合实时处理。

- 内存友好:连续存储减少高速缓存缺失。

- 简化逻辑:通过指针回绕自动管理数据覆盖。

2.2.2 环形缓冲区设计

write_index:

当向缓冲区写入一个元素时,元素被写入写索引当前所指向位置,然后写索引write_index加1,指向下一个可写位置。

write_mirror:

写镜像标志,开始为0,当write_index到达最大位置后,取反为1,再次到达最大位置,取反为0。

read_index:

当从缓冲区中读出一个元素时,读索引read_index当前所在位置的元素被读出,然后读索引read_index加1,指向下一个可读位置。

read_mirror:

读镜像标志,开始为0,当read_index到达最大位置后,取反为1,再次到达最大位置,取反为0。

备注:

write_index 指向下一个可以写入位置,read_index指向当前可以读取位置。

2.3 中间层(数据链路层)---解帧组帧处理

2.3.1 帧的概念

串口通信中的帧是指由一定数量的数据位(通常为8位)组成的数据块,在发送和接收数据时,为了确保数据的正确性和完整性,常常需要将数据帧进行包装和解包处理。

数据链路层将比特流划分为帧(Frame),在帧头和帧尾添加控制信息,如帧起始和结束标志、地址信息、帧校验序列等。成帧使得数据传输更加有序和可靠。

2.3.2 两种类型的帧

第一种:安照两帧之间的时间间隔判断帧结束,类似modbus协议。

modbus RTU 协议在9600 bps下,两帧之间间隔3.5个字符传输时间,每字符传输用时1.04ms,帧间隔为 3.5 x 1.04 ms,即3.64 ms。

第二种:按照帧头、帧尾、数据长度、校验判断帧结束。

此种帧要反复扫描接收缓冲区。

AT 命令帧适合第二种,它以\r\n开始,\r\n结束,是有明确帧头帧尾的数据帧。

2.3.3 解帧(以第二种帧为例)

2.3.4 组帧

   接收应用层发的数据包,添加帧头、帧尾、数据长度、校验等。

2.4 上层(应用层)---应用逻辑实现

RS485总线,modbus协议数据解析等。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐