文章目录
OSAL的主要功能如下:
任务的注册,初始化和分配
任务的同步和异步
存储器的分配和管理
中断的处理
OSAL工作原理
1 通过tasksEvents指针访问事件表的每一项,
2 如果有事件发生,则查找函数表找到事件处理函数进行处理,处理完后,继续访问事件表,查看是否有事件发生,无限循环。
根据工程中代码看原理
1 指针 tasksEvent 的定义和引用
- 在OSAL_GenericApp.c定义了初始化了 tasksEvent
- 在 OSAL.c中 包含的头文件中OSAL_Tasks.h中 ,用extends 定义了 全局变量 tasksEvent
2 通过指针访问事件表的每一项,查找是否有事件发生
定义了个 索引 idx ,用来在事件表中索引。
故此 事件会 根据 索引值, 拥有有不同的 优先级。
第4、5两行,更新系统时钟,同时查看硬件方面是否有事件发生,如串口是否收到数据、是否有按键按下等信息,这部分内容在此可以暂时不予考虑。.??????????
3 有事件发生,跳出循环;查找函数表找到 事件处理函数 进行处理
先判断是否是 因为有事件发生 而跳出循环。
事件表粗略介绍
事件表
ZigBee协议栈使用-一个unsigned short 型的变量,
因为unsigned short 类型占2个字节,即16个二进制位,
因此,可以使用每个二进制位表示一-个事件.
事件 | 十六进制 | 二进制 |
---|---|---|
串口接收新数据 | 0x01 | 0b00000001 |
接收到无线数据 | 0x02 | 0b00000010 |
读取温度数据 | 0x04 | 0b00000100 |
系统初始化时,所有的任务初始化为0
事件表代码
SYS_EVENT_MSG 是一个事件集合
是一个事件集合,主要包括以下几个事件(其中前两个较为常用):
①AF_INCOMING_MSG_CMD
表示收到了一个新的无线数据。
②ZDO_STATE_CHANGE
当网络状态发生变化时,会产生该事件,如终端节点加入网络时,就可以通过判断该事件来决定何时向协调器发送数据包
③ZDO_CB_MSG
指示每一-个注册的ZDO响应消息。
④AF_DATA_CONFIRM_CMD
调用AF_ DataRequest()发送数据时,有时需要确认信息,该事件与此有关。
在 comdef.h中
#define SYS_EVENT_MSG 0x8000 // A message is waiting event
事件处理函数
调用处理函数方式:
通过指针的方式调用 事件处理函数
events = (tasksArr[idx])( idx, events );//调用函数去处理
tasksArr[]定义代码如下:
typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );//typedef 函数指针
//unsigned short 表示函数返回值,
//pTaskEventHandlerFn 表示函数名,
//( unsigned char task_id, unsigned short event )表示参数
const pTaskEventHandlerFn tasksArr[] = {
macEventLoop,//函数名
nwk_event_loop,//函数名
Hal_ProcessEvent,//函数名。。。。
#if defined( MT_TASK )
MT_ProcessEvent,
#endif
APS_event_loop,
#if defined ( ZIGBEE_FRAGMENTATION )
APSF_ProcessEvent,
#endif
ZDApp_event_loop,
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
ZDNwkMgr_event_loop,
#endif
GenericApp_ProcessEvent
};
处理函数 处理事件 的大致过程
- 通过osal_msg_receive()函数从消息队列上获取一个消息
该消息包含了 事件 和 接受到的数据 。 - 判断 具体事件类型,执行相应的操作。
- 返回未处理的函数(通过异或)。
例子:
UINT16 GenericApp_ProcessEvent( byte task_id, UINT16 events )
{
afIncomingMSGPacket_t *MSGpkt;//定义一个指向接受消息结构体的指针
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID );//osal_msg_receive()函数从消息队列上接收消息,返回指向接收的无线数据包的指针
while ( MSGpkt )
{
//判断事件类型
switch ( MSGpkt->hdr.event )
{
case AF_INCOMING_MSG_CMD://接收到 无线数据
GenericApp_MessageMSGCB( MSGpkt );//执行相应的操作
break;
default:
break;
}
osal_msg_deallocate( (uint8 *)MSGpkt );//释放消息所占据的存储空间
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID );//处理完一个消息后,再从队列里接收消息。
}
return (events ^ SYS_EVENT_MSG);
}
return 0;
}
void GenericApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
//写入相应的操作.....
}
return (events ^ SYS_EVENT_MSG);
根据 事件粗略介绍中 的 事件表
假设同时发生了串口接收事件和读取温度事件,
则此时events= :0b00000101,即0x05,
假设现在处理完串口接收事件,则应该将第0位清零,如何实现呢?
只需要使用异或运算即可,即enents^0x01 =0x5^0x01=0x04,即0b00000100,
可见使用异或运算恰好可以将处理完的事件清除,仅留下未处理的事件。
总结
可以将OSAL的运行机理总结为:
●通过不断地查询事件表来判断是否有事件发生,如果有事件发生,则查找函数表找到对应的事件处理函数对事件进行处理。
●事件表使用数组来实现,数组的每一项对应一个任务的事件,每-位表示一个事件:函数表使用函数指针数组来实现,数组的每一项是一个函数指针指向了事件处理函数。