第一章:PX4驱动架构概览
各位同学好,我是老张。做嵌入式飞控这些年,我见过太多人在PX4驱动面前栽跟头。其实说白了,驱动层没那么神秘——它就是连接硬件和操作系统的桥梁。今天咱们先把架构理清楚,后面读代码才不会迷路。
1.1 PX4系统架构:三层结构
PX4的整体架构,我习惯把它分成三层来看:
- 应用层:飞行模式、导航算法、任务规划。这部分跑在用户空间,用uORB通信。
- 中间层:uORB消息总线、参数系统、日志系统。这是整个系统的“血管”。
- 驱动层:传感器、执行器、外设的底层控制。直接跟寄存器打交道。
你想想看,应用层想读一个陀螺仪数据,它不需要知道I2C时序怎么调、SPI时钟怎么配。它只需要通过uORB订阅一个话题,数据就来了。这就是分层的好处——各司其职。
核心要点:驱动层是唯一直接操作硬件的代码。其他层只能通过uORB或系统调用间接访问。
我在项目中遇到过一个问题:有个同事把传感器校准逻辑写进了驱动里,结果每次固件升级都要重新调参。这就是典型的层次混乱。记住,驱动只负责“读数据”和“写控制”,别掺和业务逻辑。
1.2 驱动层在系统中的位置
驱动层到底在哪儿?我画个图你就明白了。
这张图我每次培训都会放。注意看,驱动层下面是硬件,上面是uORB。驱动把硬件数据“翻译”成uORB消息,应用层只管订阅。嗯,这里要注意:驱动层不能直接调用应用层的函数,反过来也不行。这是架构红线。
1.3 uORB消息机制简介
uORB是PX4的“内部快递员”。我刚开始接触时觉得它很绕,后来发现其实就是发布-订阅模式。
1.3.1 核心概念
- 话题(Topic):比如
sensor_accel、vehicle_attitude。每个话题有固定的数据结构。 - 发布者(Publisher):驱动层通常是发布者。比如IMU驱动发布加速度数据。
- 订阅者(Subscriber):应用层订阅。比如EKF订阅加速度数据做融合。
小技巧:调试时可以用uorb top命令查看当前所有话题的发布频率。我曾经靠这个抓到一个驱动没启动的bug——话题频率为0,说明驱动根本没在发数据。
1.3.2 驱动中的典型流程
一个传感器驱动的工作流,我总结为四步:
- 初始化:配置GPIO、SPI/I2C、中断。注册设备。
- 数据采集:在中断或轮询中读取寄存器。
- 数据发布:调用
orb_publish()把数据塞进uORB。 - 错误处理:通信失败时重试或报错。
举个实际例子,这是MPU6000驱动发布加速度数据的核心代码:
// 伪代码,实际代码在 src/drivers/mpu6000/
struct sensor_accel_s accel_data;
accel_data.timestamp = hrt_absolute_time();
accel_data.x = raw_x * scale;
accel_data.y = raw_y * scale;
accel_data.z = raw_z * scale;
// 发布到uORB
orb_publish(ORB_ID(sensor_accel), _accel_pub, &accel_data);
你看,驱动只负责填数据结构和调用orb_publish。至于谁在用这个数据、怎么用,驱动完全不关心。这就是解耦。
1.3.3 避坑指南
我曾经踩过的坑:uORB消息的timestamp字段必须用硬件时间戳,不能用软件获取时间。有一次我偷懒用了hrt_absolute_time(),结果EKF的延迟补偿全乱了。记住,传感器数据的时间戳应该在中断服务函数里就打上,越靠近硬件越好。
1.4 本章小结
总结一下今天的内容:
| 层级 | 职责 | 通信方式 |
|---|---|---|
| 应用层 | 飞行控制、导航决策 | uORB订阅 |
| 中间层 | 消息路由、参数管理 | uORB总线 |
| 驱动层 | 硬件数据采集与控制 | uORB发布 + 寄存器操作 |
| 硬件层 | 物理传感器、执行器 | SPI/I2C/UART/CAN |
我个人觉得,理解PX4驱动架构的关键就一句话:驱动是硬件的翻译官,uORB是消息的快递员。后面章节我们会深入每个驱动的具体实现,到时候你会发现,所有驱动都逃不出今天讲的这个框架。
课后思考:如果你要写一个全新的气压计驱动,第一步应该做什么?是找数据手册,还是先搭uORB话题结构?想清楚这个问题,你就入门了。