概念先知道
- RTOS(实时操作系统):让多个“任务”轮流跑的操作系统,每个任务是一个死循环函数,由调度器按优先级决定谁先运行。
- 任务(Task):用
xTaskCreate创建,有自己的栈;例程里消费者、生产者、测试进程都是任务。 - 队列(Queue):任务间传递数据的“信箱”,
xQueueSend放入、xQueueReceive取出,先进先出。 - 信号量(Semaphore):用来互斥或同步的“令牌”。二值信号量
xSemaphoreCreateBinary拿(Take)/放(Give)各一次,例程用它保护队列读写。 - 互斥锁(Mutex):同一时刻只允许一个任务进入临界区。例程里两个任务用它保护共享计数器,并演示高优先级任务让出后优先级的恢复。
- 优先级抢占:高优先级任务就绪会打断低优先级任务;例程专门建了一个高优先级任务演示这一行为。
例程功能简介
本页对应博流官方 SDK 的 freertos 例程(examples/freertos),是学习 FreeRTOS 的“多合一”演示:
- PHASE 1 队列与信号量:创建 5 格队列、二值信号量;1 个生产者每 500ms 发一条消息,2 个消费者竞争接收并打印(
task号 + 时间戳); - PHASE 2 优先级与互斥锁:创建高优先级任务(纯计算 50 万次空循环)和两个不同优先级的共享资源任务,用互斥锁保护共享计数器,观察“高优先级任务先跑、互斥锁持有期间低优先级无法进入”;
- 所有任务测试完毕打印
All Test Complete.,随后挂起调度器进入死循环。 - 同族例程:
examples/posix/freertos_posix(POSIX 兼容层,见本栏目的「POSIX 接口」页)。
提示
Wi-Fi、MQTT 等无线例程都跑在 FreeRTOS 上(vTaskStartScheduler 启动调度器),先理解本页的任务/队列/信号量,再看无线例程会轻松很多。
操作步骤
在终端进入 SDK 的 FreeRTOS 例程目录(前提:已按快速开始(Linux)或Windows搭建好环境):
cd examples/freertos执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可:
make CHIP=bl616 BOARD=bl616dk用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):
make flash CHIP=bl616 COMX=/dev/ttyUSB0打开串口助手(波特率 2000000),观察三个阶段日志:PHASE 1(队列+信号量:2 个消费者 + 1 个生产者收发消息)、PHASE 2(优先级+互斥锁:高优先级任务抢占、两个任务互斥访问共享变量)、最后打印 All Test Complete.。
代码执行流程
例程从启动到完成全部测试的流程如下:
例程调用的 API 介绍
xTaskCreate(task, name, stack, arg, prio, handle)
创建并注册一个任务。例程用它创建测试进程、消费者、生产者、高优先级任务与共享资源任务。
参数:
task:任务函数指针name:任务名(调试用)stack:栈大小(字,例程 512)arg:传给任务的参数(例程传任务编号)prio:优先级(configMAX_PRIORITIES - 1最高)handle:返回任务句柄
返回值:pdPASS(1)成功;errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY 内存不足
xQueueCreate / xQueueSend / xQueueReceive(...)
创建队列、发送/接收消息。例程队列长度 5、每条消息是 struct Message(任务号 + 时间戳)。
参数:
uxQueueLength/uxItemSize:队列格数与每条消息大小xItem:要发送/接收的消息指针xTicksToWait:阻塞等待时间,0表示不等待
返回值:pdPASS(1)成功;errQUEUE_FULL/errQUEUE_EMPTY 失败
xSemaphoreCreateBinary / xSemaphoreTake / xSemaphoreGive(...)
创建二值信号量并执行拿/放。例程先 Give 一次让信号量可用,之后生产者、消费者在读写队列前后 Take/Give 实现互斥。
参数:
xSemaphore:信号量句柄xBlockTime:阻塞时间,portMAX_DELAY表示一直等
返回值:pdTRUE/pdPASS 成功
xSemaphoreCreateMutex / vTaskStartScheduler(...)
xSemaphoreCreateMutex 创建互斥锁,共享资源任务在修改 shared_counter 前后 Take/Give;vTaskStartScheduler 启动调度器,从此不再返回。
参数:
xSemaphoreCreateMutex无参数vTaskStartScheduler无参数
返回值:互斥锁句柄;调度器永不返回
完整代码
以下为 freertos/main.c 完整源码,与官方示例一致,默认折叠,点击展开:
📜 点击展开 freertos/main.c 完整代码
#include <FreeRTOS.h>
#include "semphr.h"
#include "board.h"
#define DBG_TAG "MAIN"
#define QUEUE_LENGTH 5
#define QUEUE_SIZE sizeof(struct Message)
#include "log.h"
typedef enum {
Que_Sem_Test,
Priority_Mutex_Test,
End
} TestProcedure_t;
TestProcedure_t phase;
struct Message {
uint8_t task;
uint32_t timestamp;
};
QueueHandle_t xqueue;
SemaphoreHandle_t xsemaphore;
SemaphoreHandle_t xMutex;
static volatile uint16_t shared_counter = 0;
static TaskHandle_t TestProcess_handle;
static TaskHandle_t consumer_handle_1;
static TaskHandle_t consumer_handle_2;
static TaskHandle_t producer_handle;
static TaskHandle_t ht_task_handle;
static TaskHandle_t sharetask_handle_1;
static TaskHandle_t sharetask_handle_2;
/*******************Test Queue,Semaphore************************/
static void consumer_task(void *pvParameters) {
struct Message msg;
int consumer_id = (int)pvParameters;
LOG_I("[Consumer %d]: task started\r\n", consumer_id);
vTaskDelay(pdMS_TO_TICKS(1000));
while (1) {
if (phase != Que_Sem_Test) {
LOG_I("[Consumer %d]: Exiting due to phase change\r\n", consumer_id);
vTaskDelete(NULL);
}
if (xSemaphoreTake(xsemaphore, portMAX_DELAY)) {
if (xQueueReceive(xqueue, &msg, 0) == pdPASS) {
LOG_I("[Consumer %d]: received msg (task:%d, ts:%lu)\r\n",
consumer_id, msg.task, (unsigned long)msg.timestamp);
} else {
LOG_I("[Consumer %d]: queue empty\r\n", consumer_id);
}
xSemaphoreGive(xsemaphore);
}
vTaskDelay(pdMS_TO_TICKS(500));
}
vTaskDelete(NULL);
}
static void producer_task(void *pvParameters) {
struct Message msg;
int producer_id = (int)pvParameters;
msg.task = producer_id;
while (1) {
if (phase != Que_Sem_Test) {
LOG_I("[Producer %d]: Exiting due to phase change\r\n", producer_id);
vTaskDelete(NULL);
}
msg.timestamp = xTaskGetTickCount();
if (xSemaphoreTake(xsemaphore, portMAX_DELAY)) {
if (xQueueSend(xqueue, &msg, 0) != pdPASS) {
LOG_I("[Producer %d]: queue full\r\n", producer_id);
}else
{
LOG_I("[Producer %d]: message send success\r\n", producer_id);
}
xSemaphoreGive(xsemaphore);
}
vTaskDelay(pdMS_TO_TICKS(500));
}
vTaskDelete(NULL);
}
/*******************Test Mutex************************/
static void SharedResourceTask(void *pvParameters) {
int task_id = (int)pvParameters;
while(1) {
if (phase != Priority_Mutex_Test) {
LOG_I("[Mutex Task %d]: Exiting due to phase change\r\n", task_id);
vTaskDelete(NULL);
}
LOG_I("[Mutex Task %d]: Attempt to acquire mutex\r\n", task_id);
if(xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
uint32_t temp = shared_counter;
shared_counter = temp + 1;
LOG_I("[Mutex Task %d]: Aquired.\r\n",task_id);
LOG_I("[Mutex Task %d]: value = %d\r\n", task_id, shared_counter);
LOG_I("[Mutex Task %d]: Holding mutex for 500ms\r\n", task_id);
vTaskDelay(pdMS_TO_TICKS(500));
LOG_I("[Mutex Task %d]: Release mutex\r\n", task_id);
xSemaphoreGive(xMutex);
}
vTaskDelay(pdMS_TO_TICKS(200));
}
}
/****************Test Preemptive scheduling*******************/
static void HighPriorityTask(void* pvParameters) {
if (phase != Priority_Mutex_Test) {
LOG_I("[High Priority Task]: Exiting due to phase change\r\n");
vTaskDelete(NULL);
}
vTaskDelay(pdMS_TO_TICKS(2000));
LOG_I("[High Priority Task]: High priority task running\r\n"); // Correct spelling
for (size_t i = 0; i < 500000; i++)
{
}
LOG_I("[High Priority Task]: Completed\r\n");
vTaskDelete(NULL);
}
static void TestProcessTask(void * pvParameters) {
LOG_I("[Process Task]: Test Start\r\n");
LOG_I("\n===== PHASE 1: Queue/Semaphore Test =====\r\n");
phase = Que_Sem_Test;
LOG_I("[Process Task]: Starting consumer tasks...\r\n");
xTaskCreate(consumer_task, "cons1", 512, (void *)1, configMAX_PRIORITIES - 2, &consumer_handle_1);
xTaskCreate(consumer_task, "cons2", 512, (void *)2, configMAX_PRIORITIES - 2, &consumer_handle_2);
LOG_I("[Process Task]: Starting producer task...\r\n");
xTaskCreate(producer_task, "prod1", 512, (void *)1, configMAX_PRIORITIES - 3, &producer_handle);
vTaskDelay(pdMS_TO_TICKS(5000)); // Run 5s
LOG_I("\n===== END PHASE 1 =====\r\n");
LOG_I("\n===== PHASE 2: Priority/Mutex Test =====\r\n");
phase = Priority_Mutex_Test;
vTaskDelay(pdMS_TO_TICKS(200)); // ensure task already exit
LOG_I("[Process Task] Starting High Priority task...\r\n");
xTaskCreate(HighPriorityTask, "ht1", 512, NULL, configMAX_PRIORITIES - 1, &ht_task_handle);
LOG_I("[Process Task] Starting Shared Resource tasks...\r\n");
// Create different priority task to test priority convert
xTaskCreate(SharedResourceTask, "mutex_high", 512, (void *)3, configMAX_PRIORITIES - 1, &sharetask_handle_1);
xTaskCreate(SharedResourceTask, "mutex_low", 512, (void *)4, configMAX_PRIORITIES - 3, &sharetask_handle_2);
vTaskDelay(pdMS_TO_TICKS(5000)); // Run 5s
LOG_I("\n===== END PHASE 2 =====\r\n");
// All Complete
phase = End;
vTaskDelay(pdMS_TO_TICKS(1000)); // Wait for exit
LOG_I("[Process Task] All Test Complete.\r\n");
// Stop all task
vTaskSuspendAll();
for(;;);
}
int main(void)
{
board_init();
configASSERT((configMAX_PRIORITIES > 4));
/* Init source */
xqueue = xQueueCreate(QUEUE_LENGTH, QUEUE_SIZE);
xsemaphore = xSemaphoreCreateBinary();
xMutex = xSemaphoreCreateMutex();
if (xqueue == NULL || xsemaphore == NULL || xMutex == NULL) {
LOG_E("Resource creation failed! Halting...\r\n");
while(1);
}
xSemaphoreGive(xsemaphore);
/* Process Task Create */
xTaskCreate(TestProcessTask, "TestProc", 512, NULL, configMAX_PRIORITIES - 1, &TestProcess_handle);
vTaskStartScheduler();
while (1);
}FAQ
为什么串口日志顺序看起来乱?
正常。多个任务并发打印,串口输出会被打断;这正是多任务并发的直观体现。日志行首的 [Consumer 1]/[Producer 1] 等标签可以帮助对应到具体任务。
高优先级任务和共享资源任务谁先跑?
高优先级任务(configMAX_PRIORITIES - 1)就绪后立即抢占,所以日志里先出现 High priority task running;互斥锁测试里两个任务同抢一把锁,持锁期间另一个只能等待(日志表现为 value 连续由同一任务 +1)。
`configMAX_PRIORITIES` 是多少?
由工程配置决定(例程 FreeRTOSConfig.h 中定义),main 开头用 configASSERT((configMAX_PRIORITIES > 4)) 保证任务优先级设置有效。想改优先级上限,改这个宏后重新编译。
这个例程能在其他芯片上跑吗?
SDK README 标注支持 BL602/BL702/BL616/BL618 等多款芯片,编译时把 CHIP/BOARD 换成对应芯片即可;本页按 Ai-M6x(bl616)编写。

