FreeRTOS快速入门
FreeRTOS快速入门
一、FreeRTOS概述与体验
1. FreeRTOS目录架构

source根目录是核心文件,这些文件是通用的。portable目录下是移植时需要实现的文件。
FreeRTOS的最核心只有两个文件:FreeRTOS/Source/tasks.c与FreeRTOS/Source/list.c
在FreeRTOS中,任务就是一个函数,该函数不能返回。
任务优先级的取值范围是:0-{configMAX_PRIORITIES-1},数值越大表示优先级越高。
四个任务状态

就绪态的任务,可以被调度器挑选出来切换为运行态,调度器永远都是挑选最高优先级的就绪态任务并使其进入运行状态。
PS:用口诀理解C语言:
变量变量,能变,就是能读能写,必定在内存里;
指针指针,保存的是地址,32位处理器中地址都是32位的,无论是什么类型的指针变量,都是4字节。
二、任务间通信
消息队列
消息队列(Message Queue) 是 FreeRTOS 中实现任务间通信的核心机制之一,它允许任务以 FIFO(先进先出)的方式发送和接收数据。
1. 消息队列的基本概念
- 队列项(Queue Item):队列中存储的基本单位,可以是任意类型的数据
- 队列长度(Queue Length):队列能够存储的最大项数
- 队列项大小(Item Size):每个队列项占用的字节数
- 阻塞时间(Block Time):任务在队列操作时的最大等待时间
2. 消息队列的创建
// 创建消息队列
QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,
UBaseType_t uxItemSize );
// 示例:创建一个能存储10个整数的队列
QueueHandle_t xIntegerQueue;
xIntegerQueue = xQueueCreate(10, sizeof(int32_t));
// 示例:创建一个能存储5个结构体的队列
typedef struct {
uint8_t command;
uint32_t parameter;
} Message_t;
QueueHandle_t xMessageQueue;
xMessageQueue = xQueueCreate(5, sizeof(Message_t));
3. 消息队列的操作函数
- 发送消息到队列尾:
xQueueSend()或xQueueSendToBack() - 发送消息到队列头:
xQueueSendToFront() - 从队列接收消息:
xQueueReceive() - 查看队列消息(不取出):
xQueuePeek() - 重置队列:
xQueueReset() - 删除队列:
vQueueDelete()
4. 使用示例
// 发送消息示例
void vSenderTask(void *pvParameters) {
int32_t lValueToSend = 100;
BaseType_t xStatus;
while(1) {
// 发送消息到队列,等待10个tick
xStatus = xQueueSend(xIntegerQueue, &lValueToSend, 10);
if(xStatus == pdPASS) {
// 发送成功
lValueToSend++;
} else {
// 发送失败(超时)
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
// 接收消息示例
void vReceiverTask(void *pvParameters) {
int32_t lReceivedValue;
BaseType_t xStatus;
while(1) {
// 从队列接收消息,无限等待
xStatus = xQueueReceive(xIntegerQueue, &lReceivedValue, portMAX_DELAY);
if(xStatus == pdPASS) {
// 处理接收到的消息
printf("Received: %d\n", lReceivedValue);
}
}
}
5. 消息队列的应用场景
- 任务间数据传递:生产者任务产生数据,消费者任务处理数据
- 事件通知:一个任务通知另一个任务某个事件已发生
- 缓冲数据:在数据处理速度不匹配的任务间提供缓冲
- 命令传递:发送命令和控制信息给其他任务
6. 重要注意事项
- 队列操作是线程安全的,多个任务可以同时访问同一个队列
- 队列满时发送操作会阻塞,队列空时接收操作会阻塞
- 可以设置阻塞时间为0实现非阻塞操作
- 使用
portMAX_DELAY可以实现无限等待 - 队列创建后需要检查返回值是否为NULL
7. 队列与任务通知的比较
| 特性 | 消息队列 | 任务通知 |
|---|---|---|
| 存储容量 | 可配置多个项 | 单个值 |
| 数据类型 | 任意类型 | 32位值 |
| 阻塞机制 | 支持 | 支持 |
| 内存占用 | 较多 | 极少 |
| 性能 | 较慢 | 极快 |
| 适用场景 | 复杂数据传递 | 简单事件通知 |
消息队列是 FreeRTOS 中最灵活的任务间通信机制,适用于需要传递复杂数据或需要缓冲的场景。
信号量
简介:信号量是一种实现任务间通信的机制,可以实现任务间同步与互斥访问,常用于协助一组相互竞争的任务访问临界资源。在多任务系统中,各任务之间需要同步或互斥实现临界资源的保护,信号量功能可以为用户提供这方面的支持。
1. 信号量的基本概念与类型
信号量是一种用于任务间同步和互斥的机制,本质上是一个计数器,用于控制对共享资源的访问。FreeRTOS 提供了三种类型的信号量:
-
二值信号量(Binary Semaphore)
- 只有 0 和 1 两种状态
- 常用于任务同步和互斥
- 创建后初始值为 0
-
计数信号量(Counting Semaphore)
- 值可以在 0 到创建时指定的最大值之间变化
- 用于管理多个相同的资源
- 例如:停车场有 10 个车位,信号量初始值为 10,每进一辆车减 1,每出一辆车加 1
-
互斥信号量(Mutex Semaphore)
- 特殊的二值信号量,具有优先级继承机制
- 用于解决优先级反转问题
- 确保高优先级任务能够及时获取资源
2. 创建与使用信号量的核心 API 函数
创建信号量:
// 创建二值信号量
SemaphoreHandle_t xSemaphoreCreateBinary(void);
// 创建计数信号量
SemaphoreHandle_t xSemaphoreCreateCounting(
UBaseType_t uxMaxCount, // 最大计数值
UBaseType_t uxInitialCount // 初始计数值
);
// 创建互斥信号量
SemaphoreHandle_t xSemaphoreCreateMutex(void);
获取信号量(Take):
BaseType_t xSemaphoreTake(
SemaphoreHandle_t xSemaphore, // 信号量句柄
TickType_t xTicksToWait // 等待时间(portMAX_DELAY 表示无限等待)
);
释放信号量(Give):
BaseType_t xSemaphoreGive(
SemaphoreHandle_t xSemaphore // 信号量句柄
);
代码示例:使用二值信号量进行任务同步
// 全局信号量句柄
SemaphoreHandle_t xBinarySemaphore;
// 任务1:发送信号
void vTaskSender(void *pvParameters) {
while(1) {
// 执行一些操作
vTaskDelay(pdMS_TO_TICKS(1000));
// 释放信号量,通知任务2
xSemaphoreGive(xBinarySemaphore);
printf("信号已发送\n");
}
}
// 任务2:等待信号
void vTaskReceiver(void *pvParameters) {
while(1) {
// 等待信号量,无限等待
if(xSemaphoreTake(xBinarySemaphore, portMAX_DELAY) == pdTRUE) {
printf("收到信号,开始处理\n");
// 执行处理操作
}
}
}
// 主函数中创建信号量和任务
int main(void) {
// 创建二值信号量
xBinarySemaphore = xSemaphoreCreateBinary();
// 创建任务
xTaskCreate(vTaskSender, "Sender", 128, NULL, 2, NULL);
xTaskCreate(vTaskReceiver, "Receiver", 128, NULL, 1, NULL);
// 启动调度器
vTaskStartScheduler();
return 0;
}
3. 信号量的典型应用场景
-
任务同步
- 一个任务等待另一个任务完成某项操作
- 例如:任务A完成数据采集后,通知任务B开始数据处理
- 使用二值信号量实现
-
资源互斥访问
- 保护共享资源,防止多个任务同时访问
- 例如:多个任务需要访问同一个串口或共享内存
- 使用互斥信号量实现,避免优先级反转
-
资源计数管理
- 管理有限数量的相同资源
- 例如:缓冲池管理、连接池管理
- 使用计数信号量实现
-
事件通知
- 通知其他任务某个事件已发生
- 例如:按键按下、定时器超时、数据到达
- 使用二值信号量实现
使用注意事项:
- 获取和释放信号量必须成对出现
- 互斥信号量只能由获取它的任务释放
- 在中断服务程序中使用
xSemaphoreGiveFromISR()和xSemaphoreTakeFromISR() - 避免信号量嵌套,防止死锁
- 合理设置等待超时时间,避免任务永久阻塞
信号量是 FreeRTOS 中非常重要的同步机制,合理使用信号量可以大大提高多任务系统的可靠性和效率。
PS:进程与线程的区别:
附录
进程与线程的区别
在操作系统中,进程(Process)和线程(Thread)是两个核心的并发执行概念,理解它们的区别对于掌握操作系统原理至关重要。
1. 基本定义
- 进程:是操作系统进行资源分配和调度的基本单位。每个进程都有独立的地址空间、数据栈和其他系统资源。
- 线程:是进程中的一个执行单元,是CPU调度和分派的基本单位。同一进程内的多个线程共享进程的资源。
2. 主要区别
| 对比维度 | 进程 | 线程 |
|---|---|---|
| 资源拥有 | 拥有独立的地址空间和系统资源 | 共享所属进程的资源 |
| 创建开销 | 较大(需要分配独立资源) | 较小(共享进程资源) |
| 通信方式 | 进程间通信(IPC)较复杂 | 线程间通信较简单(共享内存) |
| 切换开销 | 上下文切换开销大 | 上下文切换开销小 |
| 独立性 | 相互独立,一个进程崩溃不影响其他进程 | 一个线程崩溃可能导致整个进程崩溃 |
| 并发性 | 进程间并发执行 | 线程间并发执行(更轻量) |
3. 在FreeRTOS中的体现
FreeRTOS作为一个实时操作系统,主要使用**任务(Task)**的概念,这更接近于线程而非进程:
- FreeRTOS任务共享同一地址空间(类似于线程)
- 任务间通过队列、信号量等机制通信(类似于线程间通信)
- 没有传统意义上的进程隔离机制
4. 实际应用场景
- 使用进程的场景:需要高隔离性、安全性的应用(如浏览器标签页、虚拟机)
- 使用线程的场景:需要高效并发、共享数据的应用(如Web服务器、GUI应用)
5. 总结
进程提供了更好的隔离性和安全性,但开销较大;线程提供了更高的并发效率和资源共享能力,但需要更谨慎的同步管理。在嵌入式实时系统中,由于资源限制,通常采用类似线程的任务模型。
- https://blog.csdn.net/qq_43212092/article/details/104845158
FreeRTOS说明文档精心整理【适合新手+入门】
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)