读 PX4 飞控代码和 FreeRTOS 时攒下的零碎笔记,合并成一篇。另有《嵌入式实时操作系统 uCOS-II 原理及应用》(任哲)和 FreeRTOS 简介两份 PDF 存于原始目录,未收录进本文。
PX4 ModuleBase 类
ModuleBase 使用了 C++ 模板(见下文 C++ 模板笔记),两个关键点:
- 不支持多实例。
- 它允许在基类中有一个对每个模块类型都不同的静态对象,并从基类中调用静态方法。
ModuleBase 有两种使用模式:
- 模块自己有线程;
- 模块加入到工作队列。
两种情况下都使用 task_spawn 方法:
- 自己建线程时,先生成实例,再调用创建线程的函数。线程里有一个通用函数
run_trampoline,它是任务里实际运行的函数:run_trampoline建立任务的基本框架,并调用子类的 run 函数。 - 工作队列模式下,在工作队列中创建任务。使用工作队列的子类还会继承
ScheduledWorkItem,这个类里需要实现 Run 函数。
原文此处附有两张类结构截图,未迁移。
工作队列(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 协议的特点:
- 点对点;
- 主从模式;
- 通信链路上不能有其他数据干扰,无法跑多协议通讯;
- 无路由机制。
一个比较通用的协议应该:
- 支持点对点和网络拓扑,支持路由机制;
- 无主从机制,任何模块都可以发起通信(硬件层面有主从也没关系);
- 简单清晰,编程模型简单。
命令格式:
<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;
}
- 关键字
typename;T 称为类型参数,命名任意,但单个大写字符最常用。 - 调用时 T 被替换为用户指定或编译器推断的具体类型。编译器从模板生成类或函数的过程称为模板实例化。
- 模板实例化在编译过程中进行;模板可以用于类或函数。
调用时可以显式指定,也可以让编译器推断:
int i = minimum<int>(a, b); // 用户指定 T 为 int
int j = minimum(a, b); // 编译器推断 T 是 int
类型参数
- 类型参数数量没有限制,多个参数用逗号分隔:
template <typename T, typename U, typename V> class Foo{}; class关键字等效于typename。- 可以用省略运算符定义任意数量类型参数的模板:
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),它们的特点是:
- 可以让一个任务进入 block 状态,等待一个事件发生;
- 事件发生时,可以 unblock 等待该事件的最高优先级任务。
Event Groups 是第三种方式,特点是:
- 可以让一个任务等待多个事件的发生;
- 可以让等待同一个事件或事件组合的多个任务都 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) 两个函数连起来用。