RTOS邮箱
概述
邮箱是一种任务间通信的工具,主要用于不同数据之间的数据传递。
功能
- 数据传输
- 邮箱允许一个任务向另一个任务发送固定大小的数据块(通常是一个指针或者直接包含一定字节数的数据)
- 发送方将数据放入邮箱中,接收方从邮箱取出数据,从而实现任务间通信
- 同步机制
- 当接收任务等待特定数据时,它可以阻塞并等待邮箱中有新的数据到来。一旦有数据放入邮箱,接收任务就会被唤醒并获得数据,这样实现了任务间的同步。
- 事件触发与通知
- 当邮箱状态发生变化时,可以通知等待的相关任务。
实现
邮箱机制类似信号量,也是基于事件控制块实现的,它需要包含一个事件控制块,一个消息缓存区留作存储邮件,一个读索引,一个写索引,还有一个保存能存储的最大消息数量。
核心的函数有两个:邮箱的获取与投放
- 邮箱的获取:对一个指定的邮箱的邮件进行读取,如果此时没有邮件,那么将这个任务阻塞在此,否则直接读出一个邮件给此任务。
- 邮箱的投递:对一个指定的邮箱投放一个邮件,若发现此时有任务等待在此,这直接唤醒一个任务并将邮件直接给与这个任务,不进行存储,否则查看邮箱是否已满,如果满了就退出,否则将此邮件进行存储。 可以发现,由于在获取邮箱时如果有邮件就会直接给与任务,在投递邮箱时如果有任务阻塞再次会将邮件直接给到其中一个任务,因此,邮箱不可能出现又有邮件又有任务在此等待的情况。
代码定义:
1
2
3
4
5
6
7
8
9
typedef struct _rt_mailbox {
eventCtrlBlock_t event;
uint32_t counter; // 记录邮件数量 也可以没有这个字段,利用readPos, writePos进行运算也可以得到这个值,但是这样更快,代价也很小。
uint32_t maxcount; // 能够保存的最大消息数量
uint32_t readPos; // 读索引
uint32_t writePos; // 写索引
void** messageBuffer; // 消息缓存区
}mailBox_t;
初始化
1
2
3
4
5
6
7
8
9
10
// 邮箱初始化
void mboxInit(mbox_t* mailbox, void** messageBuffer, uint32_t maxcount) {
eventInit(&mailbox->event, EVENT_TYPE_MAILBOX);
mailbox->messageBuffer = messageBuffer;
mailbox->readPos = 0;
mailbox->writePos = 0;
mailbox->counter = 0;
mailbox->maxcount = maxcount;
}
读取邮件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/ 读取一个邮件,如果没有邮件就将任务阻塞在此
uint32_t mboxWait(mbox_t* mbox, void** msg, uint32_t waitTime) {
uint32_t st = enterCritical();
if (mbox->counter > 0) {
--mbox->counter;
*msg = mbox->messageBuffer[mbox->readPos];
if (++mbox->readPos == mbox->maxcount) {
mbox->readPos = 0;
}
leaveCritical(st);
return NO_ERROR;
}
eventWait(&mbox->event, currentTask, NULL, TASK_STATUS_WAIT_MAILBOX, waitTime);
taskSched();
leaveCritical(st);
*msg = currentTask->eventMsg;
return currentTask->eventWaitResult;
}
// 读取一个邮件,没有邮件就拉到
uint32_t mboxGetWithNoWait(mbox_t* mbox, void** msg) {
uint32_t st = enterCritical();
if (mbox->counter > 0) {
--mbox->counter;
*msg = mbox->messageBuffer[mbox->readPos];
if (++mbox->readPos == mbox->maxcount) {
mbox->readPos = 0;
}
leaveCritical(st);
return NO_ERROR;
}
leaveCritical(st);
return ERROR_RESOURCE_UNAVAILABLE;
}
发送邮件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 发送一个邮件,如果有任务在等待邮件,就直接给其中一个任务
// 如果邮箱已经满了,此邮件将被丢弃
// 否则存储在邮箱内
uint32_t mboxPost(mbox_t* mbox, void* msg, uint32_t isHighPriority) {
uint32_t st = enterCritical();
if (eventGetWaitNum(&mbox->event) > 0) {
task_t* task = eventWakeUp(&mbox->event, (void*)msg, NO_ERROR);
if (task->priority < currentTask->priority) {
taskSched();
}
leaveCritical(st);
return NO_ERROR;
}
if (mbox->counter >= mbox->maxcount) {
leaveCritical(st);
return ERROR_MAILBOX_FULL;
}
if (isHighPriority) {
if (mbox->readPos == 0) {
mbox->readPos = mbox->maxcount - 1;
mbox->messageBuffer[mbox->readPos] = msg;
}else {
mbox->messageBuffer[--mbox->readPos] = msg;
}
}else {
mbox->messageBuffer[mbox->writePos] = msg;
if (++mbox->writePos == mbox->maxcount) {
mbox->writePos = 0;
}
}
++mbox->counter;
leaveCritical(st);
return NO_ERROR;
}
清空邮箱
1
2
3
4
5
6
7
8
9
10
11
12
void mboxFlush(mbox_t* mbox) {
uint32_t st = enterCritical();
// 如果没有任务在此等待,将邮箱清空,这里不能用counter>0来判断,因为counter==0的时候无法判断是否有任务在此等待
if (eventGetWaitNum(&mbox->event) == 0) {
mbox->counter = 0;
mbox->readPos = 0;
mbox->writePos = 0;
}
leaveCritical(st);
}
销毁邮箱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
uint32_t mboxDestory(mbox_t* mbox) {
uint32_t st = enterCritical();
uint32_t count = eventRemoveAllTask(&mbox->event, NULL, ERROR_DELETED);
mbox->counter = 0;
mbox->readPos = 0;
mbox->writePos = 0;
if (count) {
taskSched();
}
leaveCritical(st);
return count;
}
END
本文由作者按照 CC BY 4.0 进行授权