PX4 与 FreeRTOS 源码笔记

2026/05/01

读 PX4 飞控代码和 FreeRTOS 时攒下的零碎笔记,合并成一篇。另有《嵌入式实时操作系统 uCOS-II 原理及应用》(任哲)和 FreeRTOS 简介两份 PDF 存于原始目录,未收录进本文。

PX4 ModuleBase 类

ModuleBase 使用了 C++ 模板(见下文 C++ 模板笔记),两个关键点:

  1. 不支持多实例。
  2. 它允许在基类中有一个对每个模块类型都不同的静态对象,并从基类中调用静态方法。

ModuleBase 有两种使用模式:

  1. 模块自己有线程;
  2. 模块加入到工作队列。

两种情况下都使用 task_spawn 方法:

原文此处附有两张类结构截图,未迁移。

工作队列(workqueue)

Zephyr 中的工作队列与 Linux 的工作队列功能类似,用于实现中断的底半部:ISR 中比较耗时的操作,放到工作队列里执行。

Zephyr 中工作队列基于线程实现:有一个线程一直等待工作队列 API 发来的工作项,有工作项(一个待执行的函数)就调用它,多个工作项按顺序处理,没有就休眠。

原始笔记整理自 CSDN 博主「搬砖-工人」的文章(CC BY-SA),原文链接:https://blog.csdn.net/My_CSDN_IT/article/details/117366057

PX4 bootloader 烧录协议笔记

对 PX4 bootloader flash 升级协议的分析,以及自定义协议的一点思考。

PX4 bootloader 协议的特点:

  1. 点对点;
  2. 主从模式;
  3. 通信链路上不能有其他数据干扰,无法跑多协议通讯;
  4. 无路由机制。

一个比较通用的协议应该:

  1. 支持点对点和网络拓扑,支持路由机制;
  2. 无主从机制,任何模块都可以发起通信(硬件层面有主从也没关系);
  3. 简单清晰,编程模型简单。

命令格式:

<SOF><OPCODE><LEN><CRC8>xxxxxxxxxx<CRC16>

回复格式:

<SOF_RLY><OPCODE><LEN><CRC8>xxxxxxxxx<CRC16>

C++ 模板笔记

读 ModuleBase 时补的模板基础。参考:菜鸟教程 C++ 模板、Microsoft Docs 模板 (C++)。

定义和使用模板

template <typename T>
T minimum(const T& lhs, const T& rhs){
	return lhs < rhs ? lhs : rhs;
}

调用时可以显式指定,也可以让编译器推断:

int i = minimum<int>(a, b); // 用户指定 T 为 int
int j = minimum(a, b);      // 编译器推断 T 是 int

类型参数

template <typename... Arguments>
class vtclass;

vtclass<> vtinstance1;
vtclass<int> vtinstance2;
vtclass<float, bool> vtinstance3;

任何内置类型或用户定义类型都可以用作类型参数。

非类型参数

C++ 模板支持非类型参数(值参数),典型应用是用常量整型定义数组长度:

template <typename T, size_t L>
class Array{
	T array[L];
};

L 在编译时传入,必须是 const 或 constexpr 表达式——模板实例化在编译阶段进行,所有模板参数都得能在编译阶段确定。

模板作为模板参数

模板本身也可以是模板参数:

template <typename T, int I>
class Arr{};

// 使用 Arr 模板作为模板参数
template<typename T, template <typename U, int I> class Arr>
class MyClass{};

默认模板参数与模板特化

类和函数模板可以有默认参数。模板特化可以针对专用类型生成特定实现,有点像函数重载:

// 通用模板
template <typename K, typename V>
class MyMap{};

// 专用模板
template <typename V>
class MyMap<string, V>{};

MyMap<int, float> a;    // 使用通用模板
MyMap<string, float> b; // 使用专用模板

explicit 关键字

用 explicit 修饰的构造函数只能被显式调用:

class ClassA
{
public:
	explicit ClassA(int a);
};

ClassA class1 = 10; // 无法使用
ClassA class2(10);  // 可以使用

FreeRTOS Event Groups

任务之间传递事件的方式有信号量(semaphores)和队列(queue),它们的特点是:

  1. 可以让一个任务进入 block 状态,等待一个事件发生;
  2. 事件发生时,可以 unblock 等待该事件的最高优先级任务。

Event Groups 是第三种方式,特点是:

  1. 可以让一个任务等待多个事件的发生;
  2. 可以让等待同一个事件或事件组合的多个任务都 unblock。

适用场景:同步多个任务、向多个任务广播一个事件、一个任务等待多个事件。Event Groups 一般可以替代二值信号量,减少一些内存占用。

Event Group 就是一个有多个 Event Flag 的变量,每个 Flag 是一个 bit,所以也叫 Event Bits——每个位都可以当一个事件的标志位。一个 Event Group 能表示的事件数有上限,根据 config 可设为 8 位或 24 位。

编程模式:暴露 event group 和 event bits 定义给对应任务调用;关心事件的模块创建一个任务,一直等待对应事件。

xEventGroupSync

EventBits_t xEventGroupSync( EventGroupHandle_t xEventGroup,
                             const EventBits_t uxBitsToSet,
                             const EventBits_t uxBitsToWaitFor,
                             TickType_t xTicksToWait );

这个函数等于 xEventGroupSetBits(uxBitsToSet) 和 xEventGroupGetBits(uxBitsToWaitFor) 两个函数连起来用。