F´ PosixUartDriver 串口驱动组件深度解析:从 termios 底层实现到 FPP 拓扑集成

F´ PosixUartDriver 串口驱动组件深度解析:从 termios 底层实现到 FPP 拓扑集成

【免费下载链接】fprime F´ - A flight software and embedded systems framework 【免费下载链接】fprime 项目地址: https://gitcode.com/GitHub_Trending/fpr/fprime

本技术指南以 F´(F Prime)飞行软件框架中 Drv/PosixUartDriver/docs/sdd.md 为核心,完整讲解 POSIX UART 串口驱动组件的设计、配置与集成方法。文章将带你理解该组件如何实现 ByteStreamDriver 字节流驱动模型、如何通过 termios 完成波特率/流控/校验位配置,以及如何在 FPP 拓扑中实例化并接入速率组,最终获得一套可直接运行的串口通信实战方案。

1. 组件定位与总体介绍

Drv::PosixUartDriver 是 F´ 框架中面向 POSIX 系统的 UART(通用异步收发器)串行通信驱动组件,位于 Drv/PosixUartDriver。它实现字节流驱动模型接口(参见 Drv/Interfaces/ByteStreamDriver.fpp),使 F´ 组件能够通过 POSIX 系统上的 UART 端口与外部设备进行串行通信。

该组件封装了 POSIX termios API,提供可配置的串行通信能力,支持多种波特率、流控选项与校验位设置,并借助专用接收线程与同步发送操作实现双向通信。其字节流模型规范可进一步参考 Drv/ByteStreamDriverModel/docs/sdd.md——该模型同样被 Drv::TcpClient、Drv::TcpServer、Drv::Udp 等组件采用,因此掌握本组件也就掌握了 F´ 字节流驱动家族的统一用法。

说明:本组件仅面向 POSIX 平台。从 Drv/PosixUartDriver/CMakeLists.txt 中的 restrict_platforms(Posix) 可见,该模块在 CMake 层面已被限定为仅在 POSIX 平台构建。

2. 需求规约

组件设计遵循以下需求(来源: Drv/PosixUartDriver/docs/sdd.md):

名称描述验证方式
POSIX-UART-COMP-001组件应实现 Drv.ByteStreamDriver 接口inspection
POSIX-UART-COMP-002应提供 9600 至 4MHz 的可配置波特率inspection
POSIX-UART-COMP-003应提供可配置流控(无/硬件流控)inspection
POSIX-UART-COMP-004应提供可配置校验位(无/奇/偶)inspection
POSIX-UART-COMP-005应提供专用读线程接收数据inspection
POSIX-UART-COMP-006应上报发送与接收字节数遥测inspection
POSIX-UART-COMP-007应处理 UART 错误并通过事件上报inspection
POSIX-UART-COMP-008应支持接收操作的缓冲区分配inspection

上述需求在源码中均有对应实现:接口继承见 PosixUartDriver.hpp 中 class PosixUartDriver final : public PosixUartDriverComponentBase;波特率、流控、校验枚举定义见同文件的 enum UartBaudRate / UartFlowControl / UartParity;读线程见 serialReadTaskEntry;遥测见 Telemetry.fppi;事件见 Events.fppi。

3. 组件架构设计

组件实现由 Drv.ByteStreamDriver 接口规定的设计,其 FPP 定义见 PosixUartDriver.fpp,包含以下关键要素:

  • UART 配置:使用 POSIX termios API 完成设备打开、波特率、流控与校验位设置;
  • 发送处理:通过 send 端口(受保护输入端口)同步发送数据;
  • 接收线程:专用线程异步接收数据并调用 recv 输出端口;
  • 缓冲区管理:与 F´ 缓冲区分配系统集成,通过 allocate(Fw.BufferGet)与 deallocate(Fw.BufferSend)输出端口管理内存;
  • 遥测上报:统计并上报发送/接收字节数;
  • 错误处理:全面的错误检测与事件上报。

从 FPP 定义可见,组件还提供 run 同步输入端口(Svc.Sched,用于速率组周期驱动遥测)、ready 输出端口(设备配置成功后通知上层),以及事件、遥测、文本事件、时间获取等特殊端口。

3.1 发送操作(send)

当通过 send 输入端口发送数据时(对应 PosixUartDriver.cpp 中 send_handler):

  1. 组件校验文件描述符与缓冲区:若 m_fd == -1、数据指针为空或大小为 0,则返回 ByteStreamStatus::OTHER_ERROR;
  2. 数据通过 POSIX write() 系统调用写入 UART 设备;
  3. 若写入字节数与期望大小不一致或返回 -1,记录 WriteError 事件并返回 OTHER_ERROR;否则更新 m_bytesSent 计数器供遥测使用;
  4. 返回 ByteStreamStatus::OP_OK 表示成功。

发送为同步(受保护)操作,调用方在 send 返回后即可获得操作状态。状态枚举语义遵循 Drv/ByteStreamDriverModel/docs/sdd.md:OP_OK 表示正常、SEND_RETRY 表示应重试、OTHER_ERROR 表示出错且后续发送可能持续失败。

3.2 接收操作(recv)

接收操作运行在独立线程 serialReadTaskEntry 中(源码见 PosixUartDriver.cpp):

  1. 从缓冲区管理器通过 allocate_out(0, m_allocationSize) 分配缓冲区;
  2. 若分配失败(数据指针为空),记录 NoBuffers 事件并通过 recv_out 返回错误状态,随后延时 50ms 避免空转(Os::Task::delay(Fw::TimeInterval(0, 50000)))后继续循环;
  3. 线程阻塞在 read() 上等待数据;读线程在 stat == 0(超时)时持续重试,直至读到数据或收到退出请求;
  4. 收到数据后封装进 Fw::Buffer,经 recv 输出端口送出,并更新 m_bytesReceived 计数器;
  5. read() 返回 -1 时记录 ReadError 事件;返回 0 且线程收到退出请求时以错误状态归还缓冲区;
  6. 错误均通过事件上报。

3.3 线程模型

组件使用单一专用线程 serialReadTaskEntry 处理接收(PosixUartDriver.hpp 中 static void serialReadTaskEntry(void* ptr)):

  • 持续运行直到调用 quitReadThread() 置位 m_quitReadThread 原子标志;
  • 每次接收操作都分配缓冲区;
  • 优雅处理超时与错误;
  • 可通过 start(priority, stackSize, cpuAffinity) 配置优先级与栈大小。

线程内部通过 std::atomic<bool> m_quitReadThread、std::atomic<FwSizeType> m_bytesSent/m_bytesReceived 实现跨线程安全的状态同步。

4. 使用方法与典型流程

使用前必须为组件配置设备参数,典型流程为:

  1. 打开设备:open() 以所需参数配置 UART 设备;
  2. 启动接收线程:start() 启动接收线程;
  3. 收发数据:通过 ByteStreamDriverModel 端口通信;
  4. 关闭:quitReadThread() 停止线程,join() 等待线程结束,析构时关闭设备。

4.1 配置示例(C++)

按 F´ 惯例,组件在 FPP 拓扑中实例化,并通过独立函数完成配置:

// 配置函数 —— 拓扑装配期间调用
void configureTopology() {
    // 打开 UART 设备并进行配置
    bool success = uart.open("/dev/ttyUSB0",                    // 设备路径
                             Drv::PosixUartDriver::BAUD_115K,   // 115200 波特率
                             Drv::PosixUartDriver::NO_FLOW,     // 无流控
                             Drv::PosixUartDriver::PARITY_NONE, // 无校验位
                             1024);                             // 接收缓冲区大小(字节)
    if (!success) {
        // 处理配置错误(此时组件已上报 OpenError 事件)
    }
    ...
}

// 启动函数 —— 启动任务时调用
void setupTopology() {
    // 启动接收线程
    uart.start(UART_PRIORITY,           // 线程优先级
               32 * 1024,               // 线程栈大小(字节)
               Os::Task::TASK_DEFAULT); // CPU 亲和性掩码
}

// 关闭函数 —— 拆除时调用
void teardownTopology() {
    uart.quitReadThread();
    uart.join();
}

4.2 与速率组集成

组件的 run 输入端口用于上报遥测,应在 FPP 拓扑中连接到速率组:

# 在 topology.fpp 的 connections 段中
connections RateGroups {
  # 将 UART 驱动连接到速率组以周期性上报遥测
  rateGroup1Comp.RateGroupMemberOut[N] -> uart.run
}

run_handler 的实现(见 PosixUartDriver.cpp)每次被调用时执行 tlmWrite_BytesSent(m_bytesSent) 与 tlmWrite_BytesRecv(m_bytesReceived),因此速率组周期即遥测上报周期。

4.3 缓冲区生命周期

接收缓冲区在接收线程中通过 allocate 端口获取、经 recv 端口交给消费组件、再由 recvReturnIn_handler 通过 deallocate 端口归还缓冲区管理器(PosixUartDriver.cpp 中 recvReturnIn_handler 仅一行:this->deallocate_out(0, fwBuffer))。该闭环与 ByteStreamDriverModel 的缓冲区所有权约定一致。

5. 配置参数详解

5.1 设备参数

参数类型说明合法取值
deviceconst char*UART 设备路径POSIX 设备路径(如 "/dev/ttyUSB0")
baudDrv::PosixUartDriver::UartBaudRate通信波特率见波特率枚举表
fcDrv::PosixUartDriver::UartFlowControl流控设置NO_FLOW、HW_FLOW
parityDrv::PosixUartDriver::UartParity校验位设置PARITY_NONE、PARITY_ODD、PARITY_EVEN
allocationSizeFwSizeType接收缓冲区大小正整数(字节)

5.2 波特率选项

枚举定义位于 PosixUartDriver.hpp。从源码可见,BAUD_460K 及以上的高速率枚举均以 #ifdef B460800 / B921600 / B1000000 等宏保护,只有宿主编译环境(termios.h)定义了对应波特率常量时才可用,这就是 sdd 中标注“Platform-dependent”的由来:

枚举数值可用性
BAUD_96009600所有平台
BAUD_1920019200所有平台
BAUD_3840038400所有平台
BAUD_5760057600所有平台
BAUD_115K115200所有平台
BAUD_230K230400所有平台
BAUD_460K460800依赖平台(需 B460800)
BAUD_921K921600依赖平台(需 B921600)
BAUD_1000K1000000依赖平台(需 B1000000)
BAUD_1152K1152000依赖平台(需 B1152000)
BAUD_1500K1500000依赖平台(需 B1500000)
BAUD_2000K2000000依赖平台(需 B2000000)
BAUD_2500K2500000依赖平台(需 B2500000)
BAUD_3000K3000000依赖平台(需 B3000000)
BAUD_3500K3500000依赖平台(需 B3500000)
BAUD_4000K4000000依赖平台(需 B4000000)

在 open() 实现中,枚举值经 switch 映射为 termios 波特率常量(如 BAUD_9600 -> B9600),并通过 cfsetispeed / cfsetospeed 分别设置输入/输出速率;若传入未定义枚举,将触发 FW_ASSERT(false, baud)。

5.3 线程配置

接收线程可通过 start() 的三个可选参数配置(默认值定义于 Os::Task):

参数类型默认值说明
priorityFwTaskPriorityTypeOs::Task::TASK_PRIORITY_DEFAULT线程优先级
stackSizeOs::Task::ParamTypeOs::Task::TASK_DEFAULT线程栈大小
cpuAffinityOs::Task::ParamTypeOs::Task::TASK_DEFAULTCPU 亲和性掩码

start() 内部创建名为 "SerReader" 的任务(见 PosixUartDriver.cpp),若启动失败将触发断言 FW_ASSERT(stat == Os::Task::OP_OK, stat)。

5.4 事件与遥测

事件定义见 Events.fppi,均携带设备路径与错误码参数:

事件严重级别说明
OpenErrorwarning highUART 设备打开失败(含 strerror(errno) 错误字符串)
ConfigErrorwarning highUART 配置失败
WriteErrorwarning high,节流 5数据发送错误
ReadErrorwarning high,节流 5数据接收错误
PortOpenedactivity high设备配置成功
NoBufferswarning high,节流 20缓冲区分配失败
BufferTooSmallwarning high缓冲区过小(含实际大小与所需大小)

遥测定义见 Telemetry.fppi:

遥测类型说明
BytesSentFwSizeType累计发送字节数
BytesRecvFwSizeType累计接收字节数

注意 WriteError / ReadError / NoBuffers 事件带有 throttle(节流)限制,避免错误风暴淹没日志系统。

6. 底层实现要点(termios 配置剖析)

结合 PosixUartDriver.cpp 的 open() 实现,可以还原完整的设备配置链路:

  1. 打开设备:::open(device, O_RDWR | O_NOCTTY)。O_NOCTTY 防止程序成为该端口的“控制终端”,避免键盘中断等输入影响进程;
  2. 超时语义:设置 cfg.c_cc[VMIN] = 0、cfg.c_cc[VTIME] = 10(1 秒无数据超时),代码注释详细说明了 VMIN/VTIME 的四种组合行为——本组件采用“MIN=0、TIME>0”模式,即读到单个字符即返回,或超过 1 秒超时返回 0;
  3. 硬件流控:仅当 fc == HW_FLOW 时设置 t.c_cflag |= CRTSCTS(RTS/CTS 硬件握手);
  4. 数据位与本地模式:newtio.c_cflag |= CS8 | CLOCAL | CREAD,即 8 数据位、本地线路、使能接收;
  5. 校验位:PARITY_ODD 置位 PARENB | PARODD(7O1),PARITY_EVEN 置位 PARENB(7E1),PARITY_NONE 清除 PARENB;
  6. 波特率:cfsetispeed / cfsetospeed 同时设置收发速率;
  7. 原始模式:c_oflag = 0(原始输出)、c_lflag = 0(非规范模式、无回显)、c_iflag = INPCK(输入奇偶校验使能);
  8. 收尾:tcflush(fd, TCIFLUSH) 冲刷旧数据,tcsetattr(fd, TCSANOW, &newtio) 立即生效,随后保存 m_fd、上报 PortOpened 事件,并通过 ready_out(0) 通知上层驱动已就绪。

上述每一步失败都会关闭 fd、上报 OpenError 事件并返回 false,保证异常路径可观测、可调试。

7. 总结

Drv::PosixUartDriver 是 F´ 在 POSIX 平台上的标准串口驱动实现:它通过受保护的 send 端口提供同步发送、通过专用读线程提供异步接收、通过 termios 完成波特率/流控/校验位配置、通过事件与遥测提供完整的可观测性,并通过 allocate/deallocate 端口接入 F´ 缓冲区管理系统。开发者只需在 FPP 拓扑中实例化组件、调用 open() 与 start() 完成配置、将 run 端口接入速率组,即可获得稳定可用的串行通信通道。更进一步,由于字节流驱动模型的一致性,围绕 send/recv 端口编写的上层组件可无缝切换至 TcpClient、TcpServer、Udp 等同类驱动(参见 Drv/ByteStreamDriverModel/docs/sdd.md),这正是本组件设计价值的核心所在。

【免费下载链接】fprime F´ - A flight software and embedded systems framework 【免费下载链接】fprime 项目地址: https://gitcode.com/GitHub_Trending/fpr/fprime

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值