1.概述
OSAL为操作系统抽象层,并非是正真意义上的操作系统,Ti的Z-Stack协议栈和蓝牙协议栈都运行在此基础之上,且OSAL有且仅有一个main()函数,其位置在ZMain->ZMain.c下,具体代码如下:
int main( void )
{
osal_int_disable( INTS_ALL ); // 关中断
HAL_BOARD_INIT();//初始化板载外设,如LED
zmain_vdd_check(); // 检查芯片是否上电正常
InitBoard( OB_COLD );//初始化I/O,LED,定时器等
HalDriverInit();//初始化硬件驱动
osal_nv_init( NULL );//初始化Flash存储器
ZMacInit();//初始化MAC层
zmain_ext_addr();//确定IEEE64位地址
zgInit()
#ifndef NONWK
// Since the AF isn't a task, call it's initialization routine
afInit();
#endif
osal_init_system();//初始化操作系统
osal_int_enable( INTS_ALL );//使能中断
InitBoard( OB_READY );//设置标志表示系统初始化完毕
zmain_dev_info();//Display information about this device
#ifdef LCD_SUPPORTED
zmain_lcd_init();
#endif
#ifdef WDT_IN_PM1
/* If WDT is used, this is a good place to enable it. */
WatchDogEnable( WDTIMX );
#endif
LS164_Cfg();//数码管初始化
KeysIntCfg();//按键初始化
InitUart();//串口初始化
osal_start_system(); // No Return from here,至此协议栈开始运行
return 0; // Shouldn't get here.
}
在上述代码中我们只需要关注3个部分
osal_init_system()
osal_start_system();
LS164_Cfg();//数码管初始化(这里是自己添加的相关初始化函数,建议添加在最后,用以覆盖掉协议栈里面的初始化
2.任务与任务处理函数
系统分配了8个任务,每一个任务对应一个任务处理函数,在代码中主要体现在一个函数和一个数组之中,函数为任务分配函数osalInitTasks()
,数组保存任务处理函数的函数名
-
osal_init_system()
函数位于OSAL->OSAL.c下,具体如下:
uint8 osal_init_system( void )
{
osal_mem_init(); // 初始化内存分配
osal_qHead = NULL; // 初始化消息队列,用于不同任务之间的通信
osalTimerInit();//初始化定时器
osal_pwrmgr_init();//初始化电源管理系统
osalInitTasks();//初始化系统任务
osal_mem_kick();//Setup efficient search for the first free block of heap.
return ( SUCCESS );
}
其中需要关注osalInitTasks();
函数用于初始化系统任务位于APP->OSAL_GenericApp.c下,具体实现如下:
void osalInitTasks( void )
{
uint8 taskID = 0;
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
macTaskInit( taskID++ );//0
nwk_init( taskID++ );//1
Hal_Init( taskID++ );/2
#if defined( MT_TASK )
MT_TaskInit( taskID++ );//3
#endif
APS_Init( taskID++ );//4
#if defined ( ZIGBEE_FRAGMENTATION )
APSF_Init( taskID++ );//5
#endif
ZDApp_Init( taskID++ );//6
#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )
ZDNwkMgr_Init( taskID++ );//7
#endif
GenericApp_Init( taskID );//8
}
在任务分配上,协议栈为每一层分配了不同的任务,在代码中通过taskID体现,应用层任务为8,在此调用应用层初始化函数GenericApp_Init( taskID );
,传入任务ID号8
-
tasksArr[]
,同样位于APP->OSAL_GenericApp.c下
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
};
在该数组中保存了每一个层的任务处理函数,应用层为GenericApp_ProcessEvent
3.任务触发和任务处理过程
3.1任务,事件和消息
- 任务:任务为最高层,协议栈给每个层定义了一个任务,如:用户层任务号为8
- 事件:一个任务有16个事件,每个事件用16位独热码表示(为什么用独热码?),其中SYS_EVENT_MSG是系统的事件,是协议栈中定义好的,也是一个强制事件
- 消息:消息位于事件之下,每一个事件包含256个消息(0x0000~0xFFFF)
3.2任务处理
- 开始任务的代码如下:
void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
for(;;) // Forever Loop,死循环
#endif
{
uint8 idx = 0;
osalTimeUpdate();
Hal_ProcessPoll(); // This replaces MT_SerialPoll() and osal_check_timer().
do {
if (tasksEvents[idx]) // 如果任务变量非0
{
break;//任务事件不为0,跳出循环,idx为当前任务号
}
} while (++idx < tasksCnt);
if (idx < tasksCnt)
{
uint16 events;//定义事件号
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState);
events = tasksEvents[idx];//将tasksEvevts数组内序号为idx的任务号给evevts
tasksEvents[idx] = 0; // 将此任务的任务号清零
HAL_EXIT_CRITICAL_SECTION(intState);
events = (tasksArr[idx])( idx, events );//找到任务事件,由此调用任务事件处理函数,通过指针调用(此处为调用任务的关键地方)
HAL_ENTER_CRITICAL_SECTION(intState);
tasksEvents[idx] |= events; // 如果任务未处理,则返回未处理的任务号
HAL_EXIT_CRITICAL_SECTION(intState);
}
#if defined( POWER_SAVING )
else // Complete pass through all task events with no activity?
{
osal_pwrmgr_powerconserve(); // Put the processor/system into sleep
}
#endif
}
}
- 任务处理函数(以应用层任务为例)
UINT16 GenericApp_ProcessEvent( byte task_id, UINT16 events )
{
afIncomingMSGPacket_t *MSGpkt;
afDataConfirm_t *afDataConfirm;
byte sentEP;
ZStatus_t sentStatus;
byte sentTransID;
(void)task_id;
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID );//从消息队列中取出引发系统事件的消息
while ( MSGpkt )
{
switch ( MSGpkt->hdr.event )
{
case ZDO_CB_MSG:
GenericApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt );
break;
case KEY_CHANGE:
GenericApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
break;
case AF_DATA_CONFIRM_CMD:
afDataConfirm = (afDataConfirm_t *)MSGpkt;
sentEP = afDataConfirm->endpoint;
sentStatus = afDataConfirm->hdr.status;
sentTransID = afDataConfirm->transID;
(void)sentEP;
(void)sentTransID;
if ( sentStatus != ZSuccess )
{
// The data wasn't delivered -- Do something
}
break;
case AF_INCOMING_MSG_CMD:
GenericApp_MessageMSGCB( MSGpkt );
break;
case ZDO_STATE_CHANGE:
GenericApp_NwkState = (devStates_t)(MSGpkt->hdr.status);
if ( (GenericApp_NwkState == DEV_ZB_COORD)
|| (GenericApp_NwkState == DEV_ROUTER)
|| (GenericApp_NwkState == DEV_END_DEVICE) )
{
}
if(GenericApp_NwkState == DEV_ZB_COORD)
{
keyChange_t *msgPtr;
msgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) );
if ( msgPtr )
{
msgPtr->hdr.event = KEY_CHANGE;//填写相关消息值,这里为按键状态改变
msgPtr->keys=3;//消息内容为3
osal_msg_send( GenericApp_TaskID, (uint8 *)msgPtr );//把该消息发生给消息队列,在消息队列里面设置任务事件
}
LS164_BYTE(12);//如果为协调器,数码管显示C
}else if(GenericApp_NwkState == DEV_ROUTER)
{
LS164_BYTE(13);//如果为路由器,数码管显示R
}else if(GenericApp_NwkState == DEV_END_DEVICE)
{
LS164_BYTE(14);//如果为终端,数码管显示E
}
break;
default:
break;
}
// Release the memory
osal_msg_deallocate( (uint8 *)MSGpkt );
// Next
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID );
}
// return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
// Send a message out - This event is generated by a timer
// (setup in GenericApp_Init()).
if ( events & GENERICAPP_SEND_MSG_EVT )
{
// Send "the" message
GenericApp_SendTheMessage();
// Setup to send message again
osal_start_timerEx( GenericApp_TaskID,
GENERICAPP_SEND_MSG_EVT,
GENERICAPP_SEND_MSG_TIMEOUT );
// return unprocessed events
return (events ^ GENERICAPP_SEND_MSG_EVT);
}
// Discard unknown events
return 0;
}
在这段代码中,任务号为events作为参数传入任务处理函数中,任务处理函数一共判断了2个事件,分别为SYS_EVENT_MSG和GENERICAPP_SEND_MSG_EVT其宏定义如下:
#define SYS_EVENT_MSG 0x8000
#define GENERICAPP_SEND_MSG_EVT 0x0001
在此对为什么使用独热码做解释:
独热码的使用方便系统提取事件,其中events & SYS_EVENT_MSG用于提取事件,events ^ SYS_EVENT_MSG用于清除事件.例如系统同时产生了上述2种事件,那么events为0x8001,如果SYS_EVENT_MSG事件未处理则返回0x8000
故如果有一个用户自定义事件,那么可以仿照此例程写出对应事件处理函数
- 例如,我向自定义一个GENERICAPP_MY_EVT事件,首先应该去GenericApp.h去定义一个宏
#define GENERICAPP_MY_EVT 0x0002
,之后在事件处理函数中添加如下代码
if(events & GENERICAPP_MY_EVT)
{
if(0 == P1_1)//如果按钮3按下
{
LS164_BYTE(3);
GenericApp_DstAddr.addrMode = (afAddrMode_t)Addr16Bit;
GenericApp_DstAddr.addr.shortAddr = 0x0000;//指定接收方IP
GenericApp_DstAddr.endPoint = GENERICAPP_ENDPOINT;//指定接收方端口,默认为10
char theMessageData[] = "8";
AF_DataRequest( &GenericApp_DstAddr, &GenericApp_epDesc,
GENERICAPP_CLUSTERID,
1,//(byte)osal_strlen( theMessageData ) + 1
(byte *)&theMessageData,
&GenericApp_TransID,
AF_DISCV_ROUTE, AF_DEFAULT_RADIUS );
P1SEL &= 0xfe;//设置Port1_0端口为普通IO
P1DIR |= 0x01;//设置Port1_0端口为输出
P1_0 ^= 1;//灯翻转
}else if(0 == P2_0)//如果按钮4按下
{
LS164_BYTE(4);
}else if(0 == P0_5)//如果按钮5按下
{
LS164_BYTE(5);
}
return(events ^ GENERICAPP_MY_EVT);
}
3.3任务触发
首先得了解两个结构体:
typedef struct
{
void *next;//指向下一个消息
uint16 len;len//为消息的长度
byte dest_id;//指向当前的任务ID,分配消息时,初始化为TASK_NO_TASK
} osal_msg_hdr_t;//消息头结构体
typedef struct
{
uint8 event;
uint8 status;
} osal_event_hdr_t;//事件结构体
其次几个函数:
uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr )//发送消息到指定任务(destination_task为0~8号任务),将消息放入消息队列,并把任务的相应事件标志置位。
uint8 osal_msg_send( uint8 destination_task, uint8 *msg_ptr )
{
//参数有效性检查
if ( msg_ptr == NULL )
return ( INVALID_MSG_POINTER );
if ( destination_task >= tasksCnt )
{
osal_msg_deallocate( msg_ptr );
return ( INVALID_TASK );
}
// Check the message header
if ( OSAL_MSG_NEXT( msg_ptr ) != NULL ||
OSAL_MSG_ID( msg_ptr ) != TASK_NO_TASK )
{
osal_msg_deallocate( msg_ptr );
return ( INVALID_MSG_POINTER );
}
OSAL_MSG_ID( msg_ptr ) = destination_task;//将当前消息的任务指定为入口参数
// queue message
osal_msg_enqueue( &osal_qHead, msg_ptr );//将此消息插入到全局消息队列的尾部
// Signal the task that a message is waiting
osal_set_event( destination_task, SYS_EVENT_MSG );//给此任务发起一个系统消息事件
return ( SUCCESS );
}
其中有2个宏定义
#define OSAL_MSG_ID(msg_ptr) ((osal_msg_hdr_t *) (msg_ptr) - 1)->dest_id //获得msg_ptr所指向的任务(消息分配时,初始化为TASK_NO_TASK)
#define OSAL_MSG_NEXT(msg_ptr) ((osal_msg_hdr_t *) (msg_ptr) - 1)->next //获得msg_ptr所指向的下一个消息
注意看此函数最后一句,该函数触发的事件为SYS_EVENT_MSG类型,如果要触发自定义事件,得使用必须要用osal_start_timerEx()函数来触发
uint8 osal_set_event( uint8 task_id, uint16 event_flag )
uint8 osal_set_event( uint8 task_id, uint16 event_flag )
{
if ( task_id < tasksCnt )
{
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState); // Hold off interrupts
tasksEvents[task_id] |= event_flag; // 给当前的task赋予事件值,以便于在OSAL大循环中可以处理
HAL_EXIT_CRITICAL_SECTION(intState); // Release interrupts
return ( SUCCESS );
}
else
{
return ( INVALID_TASK );
}
}
uint8 osal_start_timerEx( uint8 taskID, uint16 event_id, uint16 timeout_value )
uint8 osal_start_timerEx( uint8 taskID, uint16 event_id, uint16 timeout_value )
{
halIntState_t intState;
osalTimerRec_t *newTimer;
HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts.
// Add timer
newTimer = osalAddTimer( taskID, event_id, timeout_value );
HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts.
return ( (newTimer != NULL) ? SUCCESS : NO_TIMER_AVAIL );
}
- 添加消息
keyChange_t *msgPtr;
msgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) );//为消息结构体分配内存
if ( msgPtr )
{
msgPtr->hdr.event = KEY_CHANGE;//填写相关消息值,这里为按键状态改变
msgPtr->keys=3;//消息内容为3
osal_msg_send( GenericApp_TaskID, (uint8 *)msgPtr );//把该消息发生给 消息队列,在消息队列里面设置任务事件
}
osal_start_timerEx(GenericApp_TaskID,GENERICAPP_MY_EVT,3000);
- 解析消息
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( GenericApp_TaskID );//从消息队列中取出引发系统事件的消息
while ( MSGpkt )
{
switch ( MSGpkt->hdr.event )
{
case ZDO_CB_MSG:
GenericApp_ProcessZDOMsgs( (zdoIncomingMsg_t *)MSGpkt );
break;
case KEY_CHANGE:
GenericApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );//消息处理函数,需要自己编写
break;