FreeRTOS 文件组织
FreeRTOS 的源码结构很清晰,核心文件只有几个:
FreeRTOS/
├── Source/ # 内核源码
│ ├── tasks.c # 任务管理(核心)
│ ├── queue.c # 队列(也包含信号量、互斥锁)
│ ├── list.c # 链表(内核内部使用)
│ ├── timers.c # 软件定时器
│ ├── event_groups.c # 事件组
│ ├── stream_buffer.c # 流缓冲区/消息缓冲区
│ ├── croutine.c # 协程(已过时,很少用)
│ ├── include/ # 内核头文件
│ └── portable/ # 硬件移植层
│ ├── [compiler]/[architecture]/ # 具体移植代码
│ └── MemMang/ # 内存管理方案(heap_1~heap_5)
└── Demo/ # 官方示例
FreeRTOSConfig.h 是应用层的配置文件,通常放在你的项目目录里(不是内核源码目录)。官方示例中它在 examples/coverity/ 路径下,实际项目中你需要自己创建一份放在工程目录。
基本数据类型
FreeRTOS 为了跨平台移植,定义了一套自己的数据类型:
| 类型 | 说明 | 32位架构对应 |
|---|---|---|
TickType_t | 滴答计数/时间类型 | uint32_t(默认32位) |
BaseType_t | 架构最高效的整数类型 | int32_t(32位) |
UBaseType_t | 无符号版 BaseType_t | uint32_t |
StackType_t | 栈存储类型 | 通常是 uint32_t |
TaskHandle_t | 任务句柄(指针) | 指向 TCB 的指针 |
QueueHandle_t | 队列句柄 | 指向队列结构的指针 |
SemaphoreHandle_t | 信号量句柄 | 本质是 QueueHandle_t |
TimerHandle_t | 软件定时器句柄 | 指向定时器的指针 |
EventGroupHandle_t | 事件组句柄 | 指向事件组的指针 |
TickType_t 的宽度配置
TickType_t 的宽度由 configTICK_TYPE_WIDTH_IN_BITS 决定:
| 配置值 | 8位架构 | 16位架构 | 32位架构 | 64位架构 |
|---|---|---|---|---|
TICK_TYPE_WIDTH_16_BITS | uint16_t | uint16_t | uint16_t | N/A |
TICK_TYPE_WIDTH_32_BITS | uint32_t | uint32_t | uint32_t | N/A |
TICK_TYPE_WIDTH_64_BITS | N/A | N/A | uint64_t | uint64_t |
建议
在 ARM Cortex-M 等 32 位 MCU 上,直接使用
TICK_TYPE_WIDTH_32_BITS。旧版的configUSE_16_BIT_TICKS已废弃。
关键宏:pdMS_TO_TICKS
将毫秒转换为系统滴答数,是写延时最常用的宏:
// 无论系统心跳频率 configTICK_RATE_HZ 是多少,都精准延迟 500ms
vTaskDelay(pdMS_TO_TICKS(500));
注意
pdMS_TO_TICKS()在configTICK_RATE_HZ不为 1000 时可能出现精度损失,毫秒以下的延时不要用这个宏。
程序启动流程
一个典型的 FreeRTOS 程序的启动顺序:
int main(void)
{
// 1. 硬件初始化(时钟、串口、GPIO...)
SystemInit();
// 2. 创建任务、队列、信号量等内核对象
xTaskCreate(vLEDTask, "LED", 128, NULL, 2, NULL);
// ...
// 3. 启动调度器(永不返回)
vTaskStartScheduler();
// 如果走到这里,说明调度器启动失败(通常是内存不足)
while(1);
}
调用 vTaskStartScheduler() 后,FreeRTOS 会:
- 创建空闲任务(Idle Task)
- 如果启用了软件定时器,创建 Daemon Task
- 启动 SysTick 中断
- 开始调度最高优先级的就绪任务
从此,main() 函数不会再继续往下执行。
下一步:02-任务管理与调度
相关:10-FreeRTOSConfig配置 | 返回目录