为什么要使用Mail Queue
以蜂鸣器举例:为什么要用邮箱,而不是直接 BeepOn()?
-
避免阻塞调用者
蜂鸣器响需要时间,如果每个调用方自己去响蜂鸣器,它们都会被这个延时卡住。现在改成邮箱后,调用方发完请求就能继续干别的。
-
统一串行处理多个请求
如果多个任务同时想蜂鸣器发声,邮箱会把这些请求排队,蜂鸣器任务按顺序处理,不会互相打架。
-
线程安全、职责清晰
硬件操作只放在一个任务里做,其他任务只负责发消息,结构更稳,后续好维护。
让蜂鸣器请求变成一个异步队列,避免阻塞其他任务,并把硬件控制集中到一个地方。
什么是Mail Queue
FreeRTOS当中传统的Mailbox
对队列(Queue)的一种特殊用法
Mailbox = Queue(Queue length = 1)
特点:
只能存 1 条消息
新消息会覆盖旧消息
CMSIS_v1当中的Mail Queue
多消息缓存队列,带内存管理的消息传递机制
CMSIS Mail Queue = Queue + Memory Pool
内部结构:
内存池 → 存放多个 BeepMsg_t
↓
队列 → 存放指针(指向这些消息)
接口函数(CMSIS_v1)
以下我均以蜂鸣器使用举例子
初始化接口
osMailQDef --- 定义邮箱
c
// 定义蜂鸣器消息队列,队列长度为10,消息类型为BeepMsg_t
osMailQDef(BeepMail, BEEP_MSG_QUENE_SIZE, BeepMsg_t);
osMailCreate --- 创建邮箱
c
// 创建邮箱队列,供外部调用接口使用
BeepMail = osMailCreate(osMailQ(BeepMail), NULL);
外部调用接口
osMailAlloc --- 申请内存
c
// 从邮箱申请一个消息块,失败则直接丢弃本次请求
p = osMailAlloc(BeepMail, 0);
osMailPut --- 消息投递
c
// 将消息投递给蜂鸣器任务,由任务统一控制 IO 输出
osMailPut(BeepMail, p);
Task内部处理接口
osMailGet --- 阻塞等待
c
// 阻塞等待蜂鸣器控制消息
evt = osMailGet(BeepMail, osWaitForever);
osMailFree --- 释放内存
c
// 释放消息块
osMailFree(BeepMail, p);
整体工程---beep.c
c
osThreadId BeepTaskHandle;
osMailQId BeepMail;
// 定义蜂鸣器消息队列,队列长度为10,消息类型为BeepMsg_t
osMailQDef(BeepMail, BEEP_MSG_QUENE_SIZE, BeepMsg_t);
// 外部调用蜂鸣器
void Beep(uint8_t sound, uint8_t time)
{
BeepMsg_t *p;
// 从邮箱申请一个消息块,失败则直接丢弃本次请求
p = osMailAlloc(BeepMail, 0);
if (p != NULL)
{
p->sound = sound;
p->time = time;
// 将消息投递给蜂鸣器任务,由任务统一控制 IO 输出
osMailPut(BeepMail, p);
}
}
// 蜂鸣器任务入口
static void BeepTaskEntry(void const *argument)
{
osEvent evt;
BeepMsg_t *p; // 包含sound和time
/* Infinite loop */
// 任务启动时先关闭蜂鸣器,避免上电瞬间误响
BeepOff();
for (;;)
{
// 阻塞等待蜂鸣器控制消息
evt = osMailGet(BeepMail, osWaitForever);
if (evt.status == osEventMail)
{
p = evt.value.p;
// 打开对应音色,保持指定时间后关闭
BeepOn(p->sound);
osDelay(p->time);
osMailFree(BeepMail, p); // 释放消息块
BeepOff();
}
}
}
// 定义蜂鸣器任务
osThreadDef(BeepTask, BeepTaskEntry, osPriorityAboveNormal, 0, 128);
// 创建蜂鸣器任务及其邮箱队列
void BeepTaskInit(void)
{
// 创建邮箱队列,供外部调用接口使用
BeepMail = osMailCreate(osMailQ(BeepMail), NULL);
// 创建蜂鸣器任务
BeepTaskHandle = osThreadCreate(osThread(BeepTask), NULL);
}