【事件驱动框架OSAL】一.事件和任务管理

【事件驱动框架OSAL】一.事件和任务管理

OSAL的事件驱动框架

OSAL的事件驱动框架一、任务的管理和调度1.1 任务结构体和任务链表1.2 事件和任务事件编码的注意事项事件编码的典型流程系统消息事件

1.3 源码链接地址

二、消息的管理机制三、定时器的实现四、内存管理

OSAL的事件驱动框架

OSAL(Operating System Abstraction Layer)是一种基于事件驱动的框架,常见于嵌入式系统(如Zigbee协议栈Z-Stack)。OSAL即操作系统抽象层,其核心目标是通过事件管理实现多任务协作,避免复杂的实时操作系统(RTOS)依赖。在OSAL中实现了多任务的管理,任务之间的通知,消息队列和内存管理。

事件(Event):表示任务需要处理的特定动作或状态变化(如传感器数据到达、定时器到期、消息接收等)。嵌入式系统中,外设(如传感器、通信模块)或内部逻辑(如定时器)产生的动作通常是异步的。事件机制通过这些异步动作触发任务执行,而无需任务主动轮询状态。

任务(Task):是一个独立的功能模块,负责处理特定的事件集合,任务通过事件触发的方式运行。例如一个开关灯的任务,可以通过按键事件来触发开关灯。

一、任务的管理和调度

OSAL的事件驱动框架,采用的是协作式调度机制,即多个任务在同一个系统中轮询运行,但任务之间不会主动抢占 CPU,而是需要任务自身主动让出 CPU,以便其他任务执行。任务的执行由事件触发激活,没有事件时,任务处于休眠状态。

任务的处理化流程如下图所示:

其中osal_start_system的处理流程图下图所示:

相对应的代码如下:

void osal_start_system(void)

{

uint16 events;

uint16 retEvents;

while(1)

{

TaskActive = osalNextActiveTask();

if(TaskActive)

{

HAL_ENTER_CRITICAL_SECTION();

events = TaskActive->events;

// Clear the Events for this task

TaskActive->events = 0;

HAL_EXIT_CRITICAL_SECTION();

if(events != 0)

{

// Call the task to process the event(s)

if(TaskActive->pfnEventProcessor)

{

retEvents = (TaskActive->pfnEventProcessor)(TaskActive->taskID, events);

// Add back unprocessed events to the current task

HAL_ENTER_CRITICAL_SECTION();

TaskActive->events |= retEvents;

HAL_EXIT_CRITICAL_SECTION();

}

}

}

}

}

1.1 任务结构体和任务链表

任务的声明如下:

/**

* @brief 任务链表

*/

typedef struct OSALTaskREC

{

struct OSALTaskREC *next;

pTaskInitFn pfnInit; //任务初始化函数指针

pTaskEventHandlerFn pfnEventProcessor; //任务事件处理函数指针

uint8 taskID; //任务ID

uint8 taskPriority; //任务优先级

uint16 events; //任务事件

} OsalTadkREC_t;

其中,taskID用于任务身份唯一标识,events为该激活该任务所对应的事件。taskPriority为任务的优先级,区别于抢占式调度,在OSAL系统中,优先级高的任务会被插入到链表头结点中,当事件触发时,高优先级的任务会最先被轮询到。

以新建三个优先级不同的任务为例,首先调用osal_add_Task,新增任务1,优先级为2;然后新增任务2,它的优先级为1,最后增加任务3,设置它的优先级为3。则最终形成的任务链表示意图如下所示:

在这个任务链表示意图中,用绿色数字标注出了新增三个任务时,分配到的任务ID。任务ID是根据调用osal_add_Task函数的顺序,自动累计的,同时用于后续区分和查找任务。

查找任务的函数如下:

OsalTadkREC_t *osalFindTask(uint8 taskID)

{

OsalTadkREC_t *TaskSech;

TaskSech = TaskHead;

while(TaskSech)

{

if(TaskSech->taskID == taskID)

{

return (TaskSech);

}

TaskSech = TaskSech->next;

}

return ((OsalTadkREC_t *)NULL);

}

遍历任务链表,根据TaskID返回所对应的任务。

1.2 事件和任务

一个任务对应着多个不同的事件,OSAL中为了方便区分不同的事件,任务的事件编码方式采用 位掩码(Bitmask) 机制,通过将不同的事件类型映射到一个整数变量(通常为uint16类型)的二进制位上,实现事件的轻量化表示、触发和管理。这种设计高效且资源友好,尤其适合嵌入式系统。

位掩码表示:

每个事件对应一个独立的二进制位,通过置位(1)或清零(0)表示事件触发与否。

事件类型定义:使用16位的无符号整数(uint16),最多支持16种事件(从第0位到第15位)。

事件编码的注意事项

1.避免事件位冲突

每个任务独立管理自己的事件位,不同任务的事件定义可以重复(因为事件变量是任务独立的)。同一任务内的事件位必须唯一,避免位冲突。

#define SAMPLE_EVENT_DATA_READY 0x0001 // 数据就绪 (二进制:0000 0000 0000 0001)

#define SAMPLE_EVENT_TIMEOUT 0x0002 // 发送失败 (二进制:0000 0000 0000 0010)

2. 事件优先级

OSAL默认按轮询顺序处理事件,无内置优先级。

uint16 Task_ProcessEvent(...) {

// 按照判断顺序,轮询处理

if (events & SAMPLE_EVENT_DATA_READY) { ... }

else if (events & SAMPLE_EVENT_TIMEOUT) { ... }

}

3.复合事件

支持一次性触发多个事件(通过位或操作|):

// 同时触发"数据就绪"和"发送失败"事件

osal_set_event(task_id, SAMPLE_EVENT_DATA_READY | SAMPLE_EVENT_SEND_FAILED);

事件编码的典型流程

(1) 定义事件类型

在头文件中明确定义任务的专属事件:

#define SAMPLE_EVENT_DATA_READY 0x0001 // 数据就绪 (二进制:0000 0000 0000 0001)

#define SAMPLE_EVENT_TIMEOUT 0x0002 // 超时 (二进制:0000 0000 0000 0010)

(2) 触发事件

在外部逻辑(如中断、其他任务)中触发事件:

// 触发任务的"数据就绪"和"超时"事件

osal_set_event(sampleTaskID, SAMPLE_EVENT_DATA_READY | SAMPLE_EVENT_TIMEOUT);

(3) 处理事件

在任务的事件处理函数中依次处理事件:

uint16 SampleTask_ProcessEvent(uint8 task_id, uint16 events) {

if (events & SAMPLE_EVENT_DATA_READY) {

process_data(); // 处理数据就绪事件

events ^= SAMPLE_EVENT_DATA_READY; // 清除事件

}

if (events & SAMPLE_EVENT_TIMEOUT) {

handle_timeout(); // 处理超时事件

events ^= SAMPLE_EVENT_TIMEOUT;

}

return events; // 返回剩余未处理的事件(若有)

}

系统消息事件

为了不同任务之间能够发送和接收消息,进行数据通信,特别定义SYS_EVENT_MSG通常占用最高位(如0x8000),表示系统消息事件,用户自定义事件应避免使用该位。通常情况下,把系统消息事件的处理放在其他事件处理之前。

uint16 SampleTask_ProcessEvent(uint8 task_id, uint16 events) {

if (events & SYS_EVENT_MSG) { // 处理系统消息事件

afIncomingMSGPacket_t *msg = (afIncomingMSGPacket_t *)osal_msg_receive(task_id); // 接收消息

if (msg != NULL) {

if (msg->hdr.event == AF_INCOMING_MSG_CMD) {

handle_zigbee_message(msg->data); // 处理消息

}

osal_msg_deallocate(msg); // 销毁消息

}

events ^= SYS_EVENT_MSG;

}

if (events & SAMPLE_EVENT_DATA_READY) {

process_data(); // 处理数据就绪事件

events ^= SAMPLE_EVENT_DATA_READY; // 清除事件

}

if (events & SAMPLE_EVENT_TIMEOUT) {

handle_timeout(); // 处理超时事件

events ^= SAMPLE_EVENT_TIMEOUT;

}

return events; // 返回剩余未处理的事件(若有)

}

同样的,消息的发送函数中也是要调用 osal_set_event(task_id, SYS_EVENT_MSG);

事件驱动任务的运行机制,当有事件到来时,才去激活任务,执行任务处理流程。

在前面提到,osalNextActiveTask函数会依次检查当前的任务链表中的每一个任务,是否有事件发生。如果有就根据任务链表排列顺序,依次执行任务,任务处理完事件后,需显式清除对应事件位,避免重复触发,直到所有的事件都处理完毕。

OsalTadkREC_t *osalNextActiveTask(void)

{

OsalTadkREC_t *TaskSech;

// Start at the beginning

TaskSech = TaskHead;

// When found or not

while(TaskSech)

{

if(TaskSech->events)

{

// task is highest priority that is ready

return TaskSech;

}

TaskSech = TaskSech->next;

}

return NULL;

}

在没有事件发生的时候,任务不会被激活。当有事件发生时,调用 osal_set_event(byte task_id, uint16 event_flag) 来激活任务,event_flag是指当前所发生的事件标志位。osal_set_event给task_id所对应的任务的events值赋值。

uint8 osal_set_event(byte task_id, uint16 event_flag)

{

OsalTadkREC_t *srchTask;

srchTask = osalFindTask(task_id);

if(srchTask)

{

// Hold off interrupts

HAL_ENTER_CRITICAL_SECTION();

// Stuff the event bit(s)

srchTask->events |= event_flag;

// Release interrupts

HAL_EXIT_CRITICAL_SECTION();

}

else

return (INVALID_TASK);

return (ZSUCCESS);

}

而 osal_set_event(byte task_id, uint16 event_flag) 则是清除事件标志位:

uint8 osal_clear_event(uint8 task_id, uint16 event_flag)

{

OsalTadkREC_t *srchTask;

srchTask = osalFindTask(task_id);

if(srchTask)

{

// Hold off interrupts

HAL_ENTER_CRITICAL_SECTION();

// Stuff the event bit(s)

srchTask->events &= ~event_flag; // 等价于srchTask->events ^= event_flag;

// Release interrupts

HAL_EXIT_CRITICAL_SECTION();

}

else

return (INVALID_TASK);

return (ZSUCCESS);

}

1.3 源码链接地址

OSAL github 链接地址 stm32 osal keil工程链接地址如下: stm32 osal 测试工程链接

二、消息的管理机制

三、定时器的实现

四、内存管理

相关推荐

机器人大战AP 完美通关攻略(一)
beat365官方登录入口

机器人大战AP 完美通关攻略(一)

📅 07-08 👁️ 7241
SQL 创建视图(CREATE VIEW 语句)
365骑士版app下载

SQL 创建视图(CREATE VIEW 语句)

📅 08-10 👁️ 4897
如何打造一个高端大气高档次的活动
28365365体育在线投注

如何打造一个高端大气高档次的活动

📅 08-28 👁️ 4701