4、串口通信实现:Linux串口配置、波特率与数据位、DMA与中断模式、串口调试技巧
串口通信,说白了就是飞控和地面站之间最传统、也最可靠的那条「电话线」。PX4 通过串口把 MAVLink 数据包发出去,QGC 在另一端收回来。别看现在 USB、Wi-Fi、4G 满天飞,真正到了飞场调试、固件刷写、日志导出这些场景,串口依然是最后的保底手段。
我刚开始做飞控集成的时候,总觉得串口太简单——不就是 TX、RX、GND 三根线嘛。结果第一次联调,QGC 死活连不上飞控,折腾了整整一个下午。后来发现,是波特率配错了。嗯,从那以后我再也不敢小看串口配置了。
4.1 Linux 串口配置:从设备节点到 termios
在 Linux 下,串口设备通常映射为 /dev/ttyS0、/dev/ttyUSB0 或 /dev/ttyACM0。PX4 的串口驱动跑在 NuttX 上,但 QGC 跑在 Linux/Windows/macOS 上。我们重点讲 Linux 端的配置,因为这是开发者最常碰到的环境。
配置串口的核心,是操作 termios 结构体。我习惯把它拆成三步:
- 打开设备:用
open()系统调用,注意加上O_RDWR | O_NOCTTY | O_NDELAY。 - 读取当前参数:
tcgetattr()拿到当前配置。 - 修改并写入:设置波特率、数据位、停止位、校验位,最后
tcsetattr()生效。
给你看一段我常用的配置代码骨架:
#include <termios.h>
#include <fcntl.h>
#include <unistd.h>
int setup_serial(const char *device, int baud) {
int fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd < 0) return -1;
struct termios tty;
tcgetattr(fd, &tty);
// 设置波特率
cfsetispeed(&tty, baud);
cfsetospeed(&tty, baud);
// 8N1:8数据位,无校验,1停止位
tty.c_cflag &= ~PARENB; // 无校验
tty.c_cflag &= ~CSTOPB; // 1停止位
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; // 8数据位
// 启用接收,忽略调制解调器控制线
tty.c_cflag |= CREAD | CLOCAL;
// 原始模式输入输出
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
tty.c_oflag &= ~OPOST;
// 设置读取超时
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 10; // 1秒超时
tcsetattr(fd, TCSANOW, &tty);
return fd;
}
B57600、B115200。PX4 默认串口波特率是 57600,但 QGC 经常用 115200。你最好在飞控参数里统一设置 SER_GPS1_BAUD 或 MAV_0_CONFIG 对应的波特率,两边保持一致。
4.2 波特率与数据位:为什么是 57600 和 8N1?
波特率决定了串口通信的速度。PX4 和 QGC 之间最常用的波特率是 57600 和 115200。为什么不是 9600?因为 MAVLink 数据包通常几十到几百字节,9600 太慢,传心跳都卡。为什么不是 921600?因为飞控的串口硬件和线缆质量不一定撑得住,尤其是有线转接板的时候。
数据位方面,MAVLink 协议规定使用 8 位数据、无校验、1 位停止位,也就是常说的 8N1。你想想看,MAVLink 的帧结构里已经有 CRC 校验了,串口层的校验位其实是多余的。所以 8N1 是最干净的选择。
| 参数 | PX4 默认值 | QGC 常用值 | 说明 |
|---|---|---|---|
| 波特率 | 57600 | 115200 | 建议统一,否则连不上 |
| 数据位 | 8 | 8 | MAVLink 标准 |
| 校验位 | 无 | 无 | 帧内 CRC 已足够 |
| 停止位 | 1 | 1 | 标准配置 |
| 流控制 | 无 | 无 | 硬件流控容易出问题 |
4.3 DMA 与中断模式:谁更适合飞控通信?
串口数据收发有两种底层模式:中断模式和 DMA 模式。PX4 的 NuttX 内核两种都支持,但默认用的是中断模式。
中断模式:每收到一个字节,CPU 就跳进中断服务程序,把数据搬到缓冲区。优点是响应快、延迟低;缺点是频繁中断会占用 CPU 时间。飞控的 CPU 本来就要跑姿态解算、控制律、传感器读取,串口中断太多会影响实时性。
DMA 模式:DMA 控制器直接搬运数据,CPU 只在缓冲区满或传输完成时才被通知。优点是 CPU 负载低,适合大数据量传输;缺点是 DMA 通道资源有限,配置复杂,而且 DMA 传输有固定长度限制。
我个人建议:MAVLink 数据量不大(通常 50~200 字节/包),用中断模式就够了。如果你在做高带宽日志下载或 OTA 固件升级,可以考虑 DMA 模式。我在一个项目中试过 DMA 传日志,速度确实快,但调试 DMA 中断优先级和缓存对齐花了不少时间。
核心对比:
- 中断模式:简单可靠,适合小包、高频交互。PX4 默认选择。
- DMA 模式:CPU 负载低,适合大块数据。配置门槛高,容易踩坑。
4.4 串口调试技巧:三板斧解决 90% 的问题
串口通信出问题,别慌。我总结了三个调试步骤,基本能覆盖大部分场景。
第一板斧:硬件回路测试
把 TX 和 RX 短接,用串口工具发数据,看能不能收到自己发的。收不到?检查线序、电压、驱动。收到了?说明硬件没问题,问题在软件配置。
第二板斧:波特率扫描
QGC 有个隐藏功能:自动波特率检测。但有时候它猜不准。我习惯用 stty 命令手动验证:
# 查看当前串口配置
stty -F /dev/ttyUSB0 -a
# 设置波特率并测试
stty -F /dev/ttyUSB0 57600 cs8 -cstopb -parenb
cat /dev/ttyUSB0 | xxd | head -20
如果看到一堆 0xFE 0x09 之类的 MAVLink 帧头,说明波特率对了。
第三板斧:逻辑分析仪抓波形
这是终极大法。用逻辑分析仪夹在 TX 和 GND 上,抓一段波形。看起始位、数据位、停止位是否对齐。我遇到过一种情况:飞控输出正常,但 USB 转串口模块的晶振不准,导致波特率偏差 3%,数据全错。逻辑分析仪一看就明白了。
screen 和 minicom 是 Linux 下最轻量的串口调试工具。我常用 screen /dev/ttyUSB0 57600 直接看原始数据流。如果看到乱码,先别怀疑飞控,检查一下你的终端模拟器是不是设成了 8N1。
4.5 串口通信流程总览
下面这张图总结了从 PX4 串口驱动到 QGC 接收的完整数据流。你可以看到,数据从飞控的 MAVLink 发送线程出发,经过 NuttX 串口驱动、硬件 UART、USB 转串口模块,最后到达 QGC 的串口读取线程。每一步都可能出问题,但只要你掌握了上面的三板斧,大部分问题都能快速定位。
串口通信看起来简单,但实际项目中出问题的概率不低。我见过有人因为 USB 转串口模块的驱动没装,折腾了一整天。也见过有人把 TX 和 RX 接反了,数据发出去收不回来。记住:先查硬件,再查软件,最后查配置。这三步走完,90% 的问题都能解决。