系统内核与服务
本文档介绍杰理 AC630N 蓝牙 SoC SDK 中 include_lib/system 层提供的系统内核与服务能力:实时操作系统(RTOS)抽象层、任务管理、任务队列(消息/事件传递)、信号量、互斥锁与消息队列等同步原语,以及它们在蓝牙协议栈应用中的典型用法。
Purpose and Scope
本页面覆盖 系统内核与服务 这一完整能力面,包括:
- OS 抽象层设计(uC/OS 与 FreeRTOS 双内核可裁剪切换)
- 任务(Task)的创建、删除、延时与自查询
- 任务队列(Task Queue)机制:消息(Q_MSG)、事件(Q_EVENT)、回调(Q_CALLBACK)、用户消息(Q_USER)四种投递类型
- 同步原语:信号量(OS_SEM)、互斥锁(OS_MUTEX)、消息队列(OS_QUEUE)
- 系统节拍(tick)与时间服务(OS_TICKS_PER_SEC = 100)
以下内容属于其他目录页,不在本页展开:蓝牙协议栈各 Profile(SPP/LE/HOGP/Mesh)实现、按键/外设驱动、低功耗电源管理。本 SDK 的内核实现以预编译库形式发布,include_lib/system 下的头文件是应用与内核之间的正式契约(API 表面),本文所有 API 签名均提取自该契约头文件。
Overview
AC630N 系列芯片(AC6311A/AC6313A/AC6318A/AC6319A)是 32bit@120MHz 的蓝牙音频/透传 SoC,SDK 在协议栈之下内置了一个可裁剪的 RTOS 内核。SDK 通过 CONFIG_UCOS_ENABLE 或 CONFIG_FREE_RTOS_ENABLE 两个编译宏选择底层内核实现,而对应用层暴露统一的 API 表面(os_* 前缀函数),使得上层蓝牙协议栈、应用框架无需关心内核差异。
系统服务层位于应用与内核之间,承担三类核心职责:
- 调度与生命周期:
os_task_create创建任务、os_task_del_req/res/del协作式删除、os_time_dly时间片延时; - 任务间通信(IPC):
os_taskq_post/pend系列是 SDK 中最核心的通信原语——所有蓝牙事件(连接、断开、配对、数据到达)都通过任务队列投递到对应任务的处理循环; - 资源互斥:
os_sem_*(计数信号量)与os_mutex_*(互斥锁)保护共享资源,os_q_*提供原始消息队列。
理解这套服务层是阅读 SDK 应用代码(如 apps/common/third_party_profile/jieli/ 下的协议栈示例)的前提:几乎所有上层模块都以 os_taskq_post_msg/os_taskq_post_event 驱动。
Architecture
下图展示了系统内核与服务在 SDK 中的分层位置及与周边组件的关系:
flowchart TD
subgraph sg_App["应用层 apps/"]
App["蓝牙应用/协议栈示例<br/>(SPP/LE/HOGP/Mesh)"]
Dev["外设驱动<br/>(按键/传感器/Codec)"]
end
subgraph sg_Svc["系统服务层 include_lib/system"]
TaskMgr["任务管理<br/>os_task_create / os_task_del"]
TaskQ["任务队列<br/>os_taskq_post / pend / accept"]
Sync["同步原语<br/>os_sem_* / os_mutex_* / os_q_*"]
Tick["节拍服务<br/>os_time_dly / os_init_tick"]
end
subgraph sg_Kernel["RTOS 内核(预编译库)"]
UCOS["uC/OS<br/>CONFIG_UCOS_ENABLE"]
FREERTOS["FreeRTOS<br/>CONFIG_FREE_RTOS_ENABLE"]
end
subgraph sg_HW["硬件层"]
CPU["AC630N SoC<br/>32bit@120MHz"]
BLE["蓝牙射频/基带"]
end
App -->|"os_taskq_post_msg / post_event"| TaskQ
Dev -->|"os_sem_post / os_mutex_pend"| Sync
TaskMgr --> UCOS
TaskMgr --> FREERTOS
TaskQ --> UCOS
TaskQ --> FREERTOS
Sync --> UCOS
Sync --> FREERTOS
Tick --> UCOS
Tick --> FREERTOS
UCOS --> CPU
FREERTOS --> CPU
CPU --> BLE
架构要点:
- 统一 API 表面:
os_api.h中所有os_*函数不区分底层内核,os_type.h通过宏切换类型定义(uC/OS 的自定义结构体 vs FreeRTOS 的StaticSemaphore_t/StaticQueue_t),应用层源码零改动即可换核。 - 任务队列是协议栈的中枢:蓝牙协议栈的事件循环普遍采用"一个任务一个队列、
os_taskq_pend阻塞等待、按消息类型分发"的模式,os_taskq_post_event是跨任务投递蓝牙事件的统一通道。 - 内核为预编译库:实际调度算法、优先级管理、上下文切换在闭源库中实现,头文件即契约;因此本页以 API 语义 + 应用侧调用方式为主线。
内核抽象层:双 RTOS 设计
os_type.h 是内核抽象的根基。它先用 OS_TICKS_PER_SEC 定义系统节拍,再按编译宏选择内核类型:
#define OS_TICKS_PER_SEC 100
#if defined CONFIG_UCOS_ENABLE
#define OS_EVENT_TYPE_UNUSED 0u
#define OS_EVENT_TYPE_Q 1u
#define OS_EVENT_TYPE_SEM 2u
#define OS_EVENT_TYPE_MUTEX 3u
typedef struct {
unsigned char OSEventType;
int aa;
void *bb;
unsigned char value;
unsigned char prio;
unsigned short cc;
} OS_SEM, OS_MUTEX, OS_QUEUE;
#elif defined CONFIG_FREE_RTOS_ENABLE
#include "FreeRTOS/FreeRTOS.h"
#include "FreeRTOS/semphr.h"
typedef StaticSemaphore_t OS_SEM, OS_MUTEX;
typedef StaticQueue_t OS_QUEUE;
#else
#error "no_os_defined"
#endif
来源:os_type.h
设计意图:
- 节拍 100Hz:
OS_TICKS_PER_SEC = 100意味着 1 个 tick = 10ms。所有os_*_pend的超时参数、os_time_dly的延时参数均以 tick 为单位,调用时需按此换算(例如 500ms 超时 = 50 ticks)。 - 类型三合一(uC/OS 模式):uC/OS 分支中
OS_SEM、OS_MUTEX、OS_QUEUE是同一个结构体,仅靠OSEventType字段区分类型,这是 uC/OS-II 经典的事件控制块(ECB)设计;FreeRTOS 分支则映射到各自的静态类型(StaticSemaphore_t/StaticQueue_t),静态分配避免动态堆依赖,适合资源受限的蓝牙 SoC。 - 强制二选一:两个宏都不定义时直接
#error "no_os_defined",把配置错误提前到编译期暴露,防止运行时因内核符号缺失产生难排查的链接错误。
任务管理服务
任务管理 API 全部声明在 os_api.h,是系统服务层最基础的能力:
void os_init(); // 内核初始化(BR25 芯片返回 void*)
void os_start(void); // 启动内核调度
void os_init_tick(int); // 初始化系统节拍
int os_task_create(void (*task)(void *p_arg),
void *p_arg,
u8 prio,
u32 stksize,
int qsize,
const char *name);
const char *os_current_task(); // 获取当前任务名
void os_task_exit(); // 当前任务退出
int os_task_del_req(const char *name); // 请求删除任务
int os_task_del_res(const char *name); // 响应删除请求
int os_task_del(const char *name); // 直接删除任务
void os_time_dly(int time_tick); // 按 tick 延时
来源:os_api.h
关键语义:
os_task_create的第 4 个参数qsize是任务消息队列深度——任务创建时即预分配一条专属消息队列,这是后续os_taskq_post*能定向投递到该任务的前提。任务入口函数原型固定为void (*task)(void *p_arg)。- 任务删除采用协作式(
del_req/del_res成对出现):任务 A 调用os_task_del_req("taskB")发出删除请求,任务 B 在自己的循环里调用os_task_del_res响应后才真正退出。这是嵌入式 RTOS 避免"删除正在使用共享资源的任务"导致死锁/资源泄漏的经典做法。 OS_TASK_SELF/OS_TASK_FATHER两个特殊句柄((char *)0x1/(char *)0x2)允许任务在不知道对方名字的情况下引用自身或父任务,相关删除宏状态定义在 os_api.h。
任务生命周期:
stateDiagram-v2
[*] --> Ready: os_task_create
Ready --> Running: 内核调度
Running --> Ready: 时间片耗尽/被抢占
Running --> Blocked: os_taskq_pend / os_sem_pend / os_mutex_pend
Blocked --> Ready: 消息到达 / 信号量post / 超时
Running --> Ready: os_time_dly 到期
Running --> [*]: os_task_exit / os_task_del_res
任务队列:SDK 的核心 IPC 机制
任务队列(Task Queue)是系统服务层最核心的通信原语,蓝牙协议栈的所有事件分发都建立在它之上。os_api.h 定义了四种队列消息类型与全套投递/接收 API:
#define Q_MSG 0x100000
#define Q_EVENT 0x200000
#define Q_CALLBACK 0x300000
#define Q_USER 0x400000
int __os_taskq_post(const char *name, int type, int argc, int *argv);
int __os_taskq_pend(int *argv, int argc, int tick);
int os_taskq_accept(int, int *); // 非阻塞接收
int os_taskq_pend(const char *fmt, int *argv, int argc); // 阻塞接收
int os_task_pend(const char *fmt, int *argv, int argc); // 任务级接收
int os_taskq_post(const char *name, int argc, ...); // 变参投递
int os_taskq_del(const char *name, int type); // 按类型删除
int os_taskq_post_type(const char *name, int type, int argc, int *argv);
int task_queue_post_event(const char *name, void *data, int len);
int os_taskq_post_msg(const char *name, int argc, ...);
int os_taskq_post_event(const char *name, int argc, ...);
int os_taskq_del_type(const char *name, int type);
int os_taskq_flush();
四种消息类型的语义:
| 类型宏 | 值 | 用途 |
|---|---|---|
Q_MSG | 0x100000 | 普通消息,如协议栈内部状态机迁移、数据指示 |
Q_EVENT | 0x200000 | 异步事件,如蓝牙连接/断开/配对完成通知 |
Q_CALLBACK | 0x300000 | 回调执行请求,跨任务触发某个函数 |
Q_USER | 0x400000 | 用户自定义消息,扩展协议栈私有命令 |
类型值的高位作为"消息类别"标识,低位(通过 os_taskq_post_type 的 type 参数或 os_taskq_post_msg 的变参首参)携带具体消息号。消息号常量(如 OS_MSG_KEY=1、OS_MSG_TOUCH=2、OS_MSG_EVENT=3、OS_MSG_CALLBACK=4、OS_MSG_DEL_REQ=5、OS_MSG_MSG=6)定义于 os_api.h,SDK 用它们区分任务循环内要处理的不同消息来源。
投递与接收的完整流程:
sequenceDiagram
participant Sender as 发送任务(如协议栈)
participant Kernel as 内核任务队列
participant Receiver as 接收任务(如应用主任务)
Sender->>Kernel: os_taskq_post("app_task", argc, args...)
activate Kernel
Kernel->>Kernel: 按任务名查队列,Q_MSG/Q_EVENT 分类入队
Kernel-->>Receiver: 唤醒阻塞中的接收任务
deactivate Kernel
Receiver->>Receiver: os_taskq_pend(fmt, argv, argc) 返回
Receiver->>Receiver: 按消息号分发处理(switch-case)
Receiver-->>Sender: 处理完成(可选回包)
设计意图与关键细节:
- 生产者-消费者解耦:发送方
os_taskq_post_msg只负责入队并唤醒目标任务,不阻塞、不等待处理结果,天然适合中断上下文(如蓝牙 RF 中断 → 协议栈任务)与多任务异步事件上报。 - 阻塞 vs 非阻塞接收:
os_taskq_pend在没有消息时让出 CPU(任务进入 Blocked 状态),os_taskq_accept则立即返回,适合轮询式模块;__os_taskq_pend(argv, argc, tick)支持超时参数,避免任务因消息缺失永久挂起。 - 按任务名寻址:投递函数第一参数是任务名字符串,内核根据
os_task_create时登记的名字定位目标队列——这也是任务名必须全局唯一的原因。 - flush 清理:
os_taskq_flush清空队列,常用于任务退出前或系统休眠前丢弃积压消息,防止陈旧事件在唤醒后被误处理。
同步原语:信号量、互斥锁与消息队列
计数信号量(OS_SEM)
int os_sem_create(OS_SEM *, int); // 创建,初始计数值
int os_sem_accept(OS_SEM *); // 非阻塞获取
int os_sem_pend(OS_SEM *, int timeout); // 阻塞获取(tick 超时)
int os_sem_post(OS_SEM *); // 释放
int os_sem_del(OS_SEM *, int block); // 删除(OS_DEL_NO_PEND=0 / OS_DEL_ALWAYS=1)
int os_sem_set(OS_SEM *, u16 cnt); // 重置计数值
int os_sem_valid(OS_SEM *); // 有效性检查
int os_sem_query(OS_SEM *); // 查询计数值
来源:os_api.h
信号量适合"事件计数"场景:中断/低层服务通过 os_sem_post 通知高层任务,高层 os_sem_pend 等待。os_sem_set 用于异常恢复时重置计数,os_sem_del 的 block 参数选择删除时是否等待持有者释放。
互斥锁(OS_MUTEX)
int os_mutex_create(OS_MUTEX *);
int os_mutex_accept(OS_MUTEX *);
int os_mutex_pend(OS_MUTEX *, int timeout);
int os_mutex_post(OS_MUTEX *);
int os_mutex_del(OS_MUTEX *, int block);
int os_mutex_valid(OS_MUTEX *);
来源:os_api.h
互斥锁保护独占资源(如 Flash 擦写、编解码器、RF 寄存器配置)。与信号量不同,互斥锁具备所有权语义与优先级继承(uC/OS 内核实现),可缓解优先级反转问题——这正是蓝牙协议栈中高优先级 RF 任务与低优先级应用任务共享外设时的关键保障。
消息队列(OS_QUEUE)
int os_q_create(OS_QUEUE *pevent, QS size);
int os_q_del(OS_QUEUE *pevent, u8 opt);
int os_q_flush(OS_QUEUE *pevent);
int os_q_pend(OS_QUEUE *pevent, int timeout, void *msg);
int os_q_post(OS_QUEUE *pevent, void *msg);
int os_q_query(OS_QUEUE *pevent);
int os_q_valid(OS_QUEUE *pevent);
来源:os_api.h
os_q_* 是面向任意数据指针的原始队列:os_q_post 投递指针、os_q_pend 阻塞接收。与任务队列的区别在于:任务队列按任务名定向投递并预置在任务内部,而 os_q_* 由调用方显式创建、独立于任务生命周期,适合在多个任务间共享一条数据管道(如音频数据帧缓冲、AT 命令流)。
同步原语选型速查:
| 场景 | 推荐原语 | 理由 |
|---|---|---|
| 事件通知(计数) | os_sem_post/pend | 不携带数据,仅通知"有事发生" |
| 独占资源保护 | os_mutex_pend/post | 所有权 + 优先级继承,防优先级反转 |
| 跨任务数据传递 | os_taskq_post_msg/event | 定向投递、带消息号、与任务绑定 |
| 多任务共享数据管道 | os_q_post/pend | 显式创建、指针传递、生命周期独立 |
Usage Examples
以下示例均基于 include_lib/system 头文件中的真实 API 契约,展示系统服务层的典型调用方式。
示例 1:创建任务并投递事件
任务创建时指定优先级、栈大小与队列深度;创建后其他任务即可用 os_taskq_post_event 向它投递事件:
#include "os/os_api.h"
/* 任务入口:阻塞等待事件,按消息号分发 */
static void app_main_task(void *p_arg)
{
int msg[4];
while (1) {
/* 阻塞接收,消息写入 msg 数组 */
os_taskq_pend(NULL, msg, 4); /* fmt 为 NULL 时按原始参数接收 */
switch (msg[0]) {
case OS_MSG_EVENT: /* 蓝牙事件 */
/* 处理连接/断开/数据事件 */
break;
case OS_MSG_MSG: /* 普通消息 */
break;
default:
break;
}
}
}
/* 系统初始化:创建任务(prio=5,栈 1KB,队列深度 8) */
void sys_init(void)
{
os_task_create(app_main_task, NULL, 5, 1024, 8, "app_task");
}
/* 任意上下文向 app_task 投递事件(Q_EVENT 类型) */
void notify_bt_connected(void)
{
os_taskq_post_event("app_task", 2, OS_MSG_EVENT, 1 /* connect */);
}
说明:os_taskq_post_event("app_task", 2, OS_MSG_EVENT, 1) 中 2 是参数个数(消息号 + 数据),OS_MSG_EVENT 是消息号常量,1 为事件数据;接收侧 os_taskq_pend 将 argv[0]=OS_MSG_EVENT、argv[1]=1。
示例 2:信号量同步与互斥锁保护
#include "os/os_api.h"
static OS_SEM data_ready; /* 事件信号量 */
static OS_MUTEX flash_lock; /* Flash 独占锁 */
void init_sync(void)
{
os_sem_create(&data_ready, 0); /* 初始计数 0 */
os_mutex_create(&flash_lock);
}
/* 中断/低层服务:数据就绪时通知 */
void isr_data_ready(void)
{
os_sem_post(&data_ready);
}
/* 高层任务:等待数据 */
void consumer_task(void *p_arg)
{
os_sem_pend(&data_ready, 50); /* 超时 500ms(50 ticks) */
os_mutex_pend(&flash_lock, OS_WAIT_FOREVER);
/* 独占写 Flash ... */
os_mutex_post(&flash_lock);
}
说明:os_sem_create(&data_ready, 0) 创建计数为 0 的信号量,os_sem_pend 的第二个参数 50 表示 50 个 tick(10ms/tick,即 500ms),体现 OS_TICKS_PER_SEC=100 的时间换算规则。
示例 3:任务协作式删除
#include "os/os_api.h"
void worker_task(void *p_arg)
{
int msg[4];
while (1) {
os_taskq_pend(NULL, msg, 4);
if (msg[0] == OS_MSG_DEL_REQ) { /* 收到删除请求 */
/* 清理占用的资源 ... */
os_task_del_res("worker_task"); /* 响应删除,任务退出 */
return;
}
/* 正常工作 ... */
}
}
void stop_worker(void)
{
os_task_del_req("worker_task"); /* 请求删除,由对方自行清理 */
}
说明:OS_TASK_DEL_REQ/RES/OK(0x01/0x02/0x03)构成删除协议的三态,os_task_del_res 只在任务自身上下文中调用,确保资源清理发生在拥有者线程内。
Configuration Options
系统内核与服务的配置通过编译宏控制,定义与选择逻辑集中在 os_type.h:
| 编译宏 | 可选值 | 默认/作用 | 说明 |
|---|---|---|---|
CONFIG_UCOS_ENABLE | 定义/未定义 | SDK 默认(uC/OS) | 启用 uC/OS 内核,OS_SEM/MUTEX/QUEUE 使用统一 ECB 结构体 |
CONFIG_FREE_RTOS_ENABLE | 定义/未定义 | 未定义 | 启用 FreeRTOS 内核,类型映射到 StaticSemaphore_t/StaticQueue_t |
OS_TICKS_PER_SEC | 100(常量) | 100 | 系统节拍频率,1 tick = 10ms;所有超时/延时参数以此换算 |
OS_DEL_NO_PEND / OS_DEL_ALWAYS | 0 / 1 | — | os_sem_del/os_mutex_del 删除选项:不等待/无条件删除 |
OS_TASK_DEL_REQ/RES/OK | 0x01/0x02/0x03 | — | 协作式删除协议状态码 |
Q_MSG/Q_EVENT/Q_CALLBACK/Q_USER | 0x100000~0x400000 | — | 任务队列消息类别标识 |
OS_MSG_KEY~OS_MSG_MSG | 1~6 | — | 任务循环内消息号常量(按键/触摸/事件/回调/删除/消息) |
注意:两个内核宏必须且只能定义一个,否则编译期报
#error "no_os_defined"。宏配置一般在工程级头文件或构建脚本中定义,os_type.h仅负责消费这些宏。
芯片平台相关约束(来自 doc/README.md):
| 芯片 | CPU | RAM | 特性 |
|---|---|---|---|
| AC6311A | 32bit@120MHz | 48~56KB | 蓝牙防丢/Beacon/HID/透传,支持 1M/2M/Coded PHY |
| AC6313A | 32bit@120MHz | 48~56KB | 同上 |
| AC6318A | 32bit@120MHz | 48~56KB | 同上(无 DCDC) |
| AC6319A | 32bit@120MHz | 48~56KB | 同上 |
任务栈与队列深度需按芯片 RAM 预算分配(典型任务栈 512B~2KB),任务数量与蓝牙协议栈 Profile 数成正比。
API Reference
以下 API 全部声明于 os_api.h,是系统服务层的公共契约。
内核启动
void os_init()
初始化内核(含中断、节拍源)。在创建任何任务之前调用一次;CONFIG_CPU_BR25 平台返回 void*(兼容旧 SDK 签名)。
void os_start(void)
启动内核调度器。此调用不返回,进入多任务调度后由首个就绪任务接管执行流。因此 os_init 之后的所有静态初始化必须在 os_start 之前完成。
void os_init_tick(int)
配置系统节拍定时器。参数为节拍源频率/分频配置,与 OS_TICKS_PER_SEC=100 配合确定 10ms 调度粒度。
任务管理
int os_task_create(void (*task)(void *p_arg), void *p_arg, u8 prio, u32 stksize, int qsize, const char *name)
- 参数:
task任务入口函数;p_arg入口参数;prio优先级(数值越小优先级越高,uC/OS 约定);stksize栈大小(字节);qsize任务消息队列容量(决定os_taskq_post*可积压消息数);name全局唯一任务名。 - 返回:0 成功,非 0 失败(错误码见
os_error.h,如OS_TASK_NOT_EXIST)。
const char *os_current_task()
返回当前运行任务名,用于日志与调试。
void os_task_exit() / int os_task_del_req(const char *name) / int os_task_del_res(const char *name) / int os_task_del(const char *name)
os_task_exit:当前任务自行结束(不再调度)。os_task_del_req:向目标任务发删除请求(消息OS_MSG_DEL_REQ),返回请求是否受理。os_task_del_res:目标任务确认删除、释放资源后调用,使删除流程完成。os_task_del:强制删除,慎用——目标任务持有的信号量/互斥锁/队列可能遗留脏状态。
void os_time_dly(int time_tick)
当前任务按 tick 延时(1 tick = 10ms),延时期间让出 CPU 供低优先级任务运行。
任务队列
int os_taskq_post(const char *name, int argc, ...)
- 参数:
name目标任务名;argc可变参数个数;后续为消息参数(首参常为消息号,如OS_MSG_MSG)。 - 返回:0 成功;
OS_TASKQ/OS_TASK_NOT_EXIST等错误码。 - 说明:消息按
Q_MSG类别入队,唤醒目标任务的os_taskq_pend。
int os_taskq_post_msg(const char *name, int argc, ...) / int os_taskq_post_event(const char *name, int argc, ...)
语义同 os_taskq_post,但分别以 Q_MSG、Q_EVENT 类别入队,便于接收端按类别区分消息来源。
int os_taskq_post_type(const char *name, int type, int argc, int *argv) / int __os_taskq_post(const char *name, int type, int argc, int *argv)
显式指定消息类别(Q_MSG/Q_EVENT/Q_CALLBACK/Q_USER)的数组式投递;argv 指向消息参数数组。
int os_taskq_pend(const char *fmt, int *argv, int argc) / int __os_taskq_pend(int *argv, int argc, int tick)
- 参数:
argv输出缓冲区;argc期望参数个数;tick超时(__os_taskq_pend)。 - 返回:0 收到消息;超时/错误返回非 0。
- 说明:阻塞式接收,任务无消息时挂起。
fmt为格式串(可传 NULL 按原始 int 数组接收)。
int os_taskq_accept(int, int *)
非阻塞接收:队列有消息立即取出并返回 0,无消息立即返回非 0,适合轮询型任务。
int os_taskq_del(const char *name, int type) / int os_taskq_del_type(const char *name, int type) / int os_taskq_flush()
按消息类别删除目标队列中的消息,或清空整个队列。常用于休眠/复位前的状态清理。
信号量(OS_SEM)
| 函数 | 语义 |
|---|---|
os_sem_create(OS_SEM *sem, int cnt) | 创建信号量,初始计数值 cnt |
os_sem_accept(OS_SEM *sem) | 非阻塞获取,成功返回 0 |
os_sem_pend(OS_SEM *sem, int timeout) | 阻塞获取,timeout tick 超时 |
os_sem_post(OS_SEM *sem) | 释放(计数 +1),唤醒等待者 |
os_sem_del(OS_SEM *sem, int block) | 删除,OS_DEL_NO_PEND/OS_DEL_ALWAYS |
os_sem_set(OS_SEM *sem, u16 cnt) | 重置计数 |
os_sem_valid(OS_SEM *sem) / os_sem_query(OS_SEM *sem) | 有效性检查 / 查询计数 |
互斥锁(OS_MUTEX)
| 函数 | 语义 |
|---|---|
os_mutex_create(OS_MUTEX *mutex) | 创建互斥锁 |
os_mutex_accept(OS_MUTEX *mutex) | 非阻塞尝试加锁 |
os_mutex_pend(OS_MUTEX *mutex, int timeout) | 加锁,超时 tick |
os_mutex_post(OS_MUTEX *mutex) | 解锁(仅持有者可调用) |
os_mutex_del(OS_MUTEX *mutex, int block) / os_mutex_valid(OS_MUTEX *mutex) | 删除 / 有效性检查 |
消息队列(OS_QUEUE)
| 函数 | 语义 |
|---|---|
os_q_create(OS_QUEUE *q, QS size) | 创建容量 size 的队列 |
os_q_pend(OS_QUEUE *q, int timeout, void *msg) | 阻塞接收指针到 msg |
os_q_post(OS_QUEUE *q, void *msg) | 投递数据指针 |
os_q_flush(OS_QUEUE *q) / os_q_query(OS_QUEUE *q) / os_q_valid(OS_QUEUE *q) / os_q_del(OS_QUEUE *q, u8 opt) | 清空 / 查询 / 校验 / 删除 |
Professional Notes
失败模式与边界情况
- 队列溢出:
os_taskq_post*在目标队列已满(qsize耗尽)时返回错误或丢弃消息。高频事件源(如 RF 中断)向慢速消费任务投递时必须评估峰值速率,必要时在接收端用os_taskq_flush丢弃过期事件,避免消息积压拖垮 RAM。 - 协作式删除的悬挂风险:若目标任务阻塞在
os_taskq_pend且队列中已无OS_MSG_DEL_REQ,删除请求可能无法及时被响应;SDK 约定任务循环每次唤醒都检查删除请求消息,确保清理路径可达。 - 超时参数单位:所有
timeout/delay参数以 tick 为单位(10ms),误按毫秒传参会造成 10 倍时间偏差——这是移植 SDK 时最常见的隐性 bug。 os_q_*指针悬垂:os_q_post只传递指针不拷贝数据,发送方栈上变量出作用域后接收方读取即为未定义行为;跨任务传递必须使用静态或堆内存。
并发与一致性
- 中断上下文:
os_sem_post、os_taskq_post_msg/event设计为可在中断中调用(内核关中断临界区保护队列),但禁止在中断中调用os_mutex_pend(可能阻塞)与os_taskq_pend。 - 优先级反转:互斥锁依赖 uC/OS 的优先级继承机制缓解反转;若使用 FreeRTOS 内核,需确认内核配置启用了
INHERIT_PRIORITY等价选项。 - 任务优先级规划:蓝牙 RF 任务优先级最高(数据实时性),协议栈任务次之,应用 UI/外设任务最低;
prio数值越小优先级越高,与常见 RTOS 约定一致。
性能与运维
- 栈预算:每任务栈 512B~2KB,全系统任务栈总和受 48~56KB RAM 约束;
os_task_create的stksize过小会导致栈溢出(通常表现为随机崩溃/看门狗复位),需结合任务调用深度评估。 - 节拍开销:100Hz 节拍(10ms)在功耗与响应之间取平衡;若需更快响应可调整
OS_TICKS_PER_SEC,但会线性增加内核调度开销。 - 调试手段:
os_current_task()可用于崩溃现场确认当前任务上下文;os_taskq_accept非阻塞模式便于在单步调试时观察队列状态。
扩展点
- 新增系统服务任务:遵循"
os_task_create+os_taskq_pend循环 +os_taskq_post_event对外接口"模式即可接入现有架构,无需改动内核。 - 自定义消息类别:
Q_USER(0x400000)为上层预留,可扩展私有协议命令(如厂商 AT 指令),接收端按类别宏 + 消息号双重分发。 - 内核替换:保持
os_api.h契约不变,切换CONFIG_UCOS_ENABLE/CONFIG_FREE_RTOS_ENABLE即可更换内核;移植新内核时需为os_type.h补充类型映射与错误码对齐。
Related Links
- os_api.h — 系统内核 API 契约
- os_type.h — 双内核类型抽象与节拍配置
- os_error.h — 内核错误码定义
- doc/README.md — 芯片平台参数表
- 协议栈上层应用:蓝牙 SPP/LE/HOGP/Mesh 各 Profile 的实现位于
apps/common/third_party_profile/,其事件循环均基于本页所述任务队列机制(见目录页「协议栈」相关文档)。