3. 事件发送与接收API详解:eventSend()、eventReceive()、eventClear()
好,咱们直接进入正题。事件机制的核心,说白了就是三个API:发、收、清。我当年刚接触VxWorks时,觉得事件不就是个标志位嘛,有啥好学的?结果第一次做多任务同步就踩了坑——事件没清干净,任务一直等,系统直接卡死。嗯,从那以后我再也不敢小看这三个函数了。
3.1 eventSend() —— 事件的“发射器”
这个函数负责向指定任务发送事件。原型长这样:
STATUS eventSend
(
int taskId, /* 目标任务的ID */
UINT32 events /* 要发送的事件掩码,按位设置 */
)
参数其实就两个,但每个都有讲究。
taskId:目标任务的ID。你可能会问,能不能给自己发事件?当然可以。我有个习惯,在调试阶段经常用 taskIdSelf() 给自己发事件,用来模拟异步信号。
events:32位的事件掩码。每一位代表一个独立事件。比如 0x01 表示事件0,0x02 表示事件1,以此类推。你可以一次发多个事件,像这样:
eventSend(taskId, 0x01 | 0x04); /* 同时发送事件0和事件2 */
#define EVT_DATA_READY 0x01,这样代码可读性高很多。别直接写魔法数字,否则三个月后你自己都看不懂。
3.2 eventReceive() —— 事件的“接收器”
这个函数是事件机制的核心。任务调用它来等待特定事件。原型如下:
STATUS eventReceive
(
UINT32 eventMask, /* 要等待的事件掩码 */
UINT8 options, /* 等待选项 */
int timeout, /* 超时时间(tick数) */
UINT32 *eventsReceived /* 输出参数:实际收到的事件 */
)
参数有点多,咱们一个一个拆。
eventMask:你想等哪些事件?比如你只关心事件0和事件1,那就传 0x03。
options:这个参数决定了等待逻辑。常用的有:
| 选项 | 含义 | 说明 |
|---|---|---|
| EVENTS_WAIT_ANY | 等待任意一个事件 | 只要mask中任何一个事件发生,就返回 |
| EVENTS_WAIT_ALL | 等待所有事件 | mask中所有事件都发生,才返回 |
| EVENTS_RETURN_ALL | 返回所有已挂起事件 | 常与上面两个组合使用 |
举个例子:
UINT32 received;
/* 等待事件0或事件1,超时100个tick */
eventReceive(0x03, EVENTS_WAIT_ANY, 100, &received);
/* 等待事件0和事件1都发生,永不超时 */
eventReceive(0x03, EVENTS_WAIT_ALL, WAIT_FOREVER, &received);
timeout:超时时间,单位是系统tick。传 WAIT_FOREVER(即-1)表示一直等,直到事件到来。传0表示不等待,立即返回。我建议你在正式产品中尽量设置超时,别用 WAIT_FOREVER,否则一旦事件没来,任务就永远卡死了。
eventsReceived:输出参数,返回实际收到的事件掩码。这个很有用,你可以根据它判断是哪个事件触发了返回。
3.3 eventClear() —— 事件的“清理工”
这个函数用来清除任务中已挂起的事件。原型:
STATUS eventClear
(
int taskId, /* 目标任务ID */
UINT32 eventMask /* 要清除的事件掩码 */
)
你可能会问:什么时候需要手动清事件?
嗯,这里有个关键点:eventReceive() 默认不会自动清除事件。如果你用 EVENTS_WAIT_ANY 收到了事件0,但事件1还在挂起状态。下次再调用 eventReceive() 时,事件1可能还在。这时候就需要手动清一下。
我举个例子:
/* 清除任务中所有事件 */
eventClear(taskIdSelf(), 0xFFFFFFFF);
/* 只清除事件0和事件1 */
eventClear(taskIdSelf(), 0x03);
3.4 三个API的配合使用
实际项目中,这三个函数通常是配合使用的。我给你画个典型流程:
- 任务A调用
eventReceive()等待事件 - 任务B或ISR调用
eventSend()发送事件 - 任务A被唤醒,处理事件
- 任务A调用
eventClear()清理已处理的事件
代码示例:
/* 任务A:等待数据就绪事件 */
void taskA(void)
{
UINT32 events;
while(1)
{
/* 等待事件0或事件1 */
eventReceive(0x03, EVENTS_WAIT_ANY, WAIT_FOREVER, &events);
if(events & 0x01)
{
/* 处理事件0 */
printf("Event 0 received\n");
}
if(events & 0x02)
{
/* 处理事件1 */
printf("Event 1 received\n");
}
/* 清理已处理的事件 */
eventClear(taskIdSelf(), events);
}
}
/* 任务B:发送事件 */
void taskB(void)
{
/* 模拟数据就绪 */
eventSend(taskIdA, 0x01);
taskDelay(100);
eventSend(taskIdA, 0x02);
}
eventsReceived 的值,再决定怎么处理。别假设一定是你要的那个事件。尤其是在多任务环境下,可能有多个任务同时发事件,你收到的可能是个“组合包”。
好了,这三个API的用法就讲到这里。说白了,eventSend() 是发信号,eventReceive() 是等信号,eventClear() 是清信号。记住这个逻辑,你就能用好VxWorks的事件机制了。