我使用的协议栈版本及例子信息:
ZigBee2006\TexasInstruments\ZStack-1.4.3-1.2.1\Projects\zstack\Samples\SampleApp
接上篇OSAL初始化流程.记录下个人对OSAL主循环流程的学习:
//--------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------
OSAL系统主循环函数:
voidosal_start_system( void )
{
#if !defined ( ZBIT) //不知道是什么东西
for(;;) // ForeverLoop
#endif
{
uint8 idx =0;
Hal_ProcessPoll(); // This replacesMT_SerialPoll() and osal_check_timer().
//轮询TIMER与UART
//--------------------------
//执行循环语句:tasksEvents[idx]是一个指针变量,指向存放任务idx的存储空间,初始化时由
//osal_memset()设为0,只要不为空类型NULL,
//即有相对应任务事件发生,就break跳出循环体,通过下面的程序进行任务事件处理。
//如果为空,执行判断语句,即idx自增,再返回轮询有无各层的任务事件发生。如果
//执行完循环语句都没有检测到有事件发生,idx=7,进入睡眠。(对于本例子来说,任务数组里只有七个任务,tasksEvents[0]~tasksEvents[6],tasksEvents[6]就是用户自已添加的任务,idx随着用户添加任务的增多而增大)
do {
if (tasksEvents[idx]) // Task is highest priority that isready.
{
break;
}
} while(++idx < tasksCnt); //tasksCnt=7(针对本例子,随着用户应用任务增多而增大)
//-------------------------
if (idx <tasksCnt)
{
uint16 events;
halIntState_t intState; //中断位状态
HAL_ENTER_CRITICAL_SECTION(intState); //中断临界状态:保存先前中断状态,然后关中断
events = tasksEvents[idx]; //uint16events;对应有事件发生的任务的数组
tasksEvents[idx] = 0; //Clear the Events for this task. NULL
HAL_EXIT_CRITICAL_SECTION(intState); //跳出中断临界状态:恢复先前中断状态
events= (tasksArr[idx])( idx, events); //调用相对应的任务事件处理函数处理,各类事件处理函
//数M(task_id,event)返回的都是这个任务未被处理的事件
HAL_ENTER_CRITICAL_SECTION(intState);
tasksEvents[idx] |=events; // Add back unprocessed events to the currenttask.
//把刚才返回未处理的任务事件添加加当前任务中再进行处理
//(跳出此if(idx < tasksCnt)循环再进行if(tasksEvents[idx])判断并处理)
HAL_EXIT_CRITICAL_SECTION(intState);
}
#if defined( POWER_SAVING )
else // Complete passthrough all task events with noactivity?
{
osal_pwrmgr_powerconserve(); // Put the processor/system intosleep
}
#endif
}
}
说明:
(1)OSAL调用Hal_ProcessPoll(); 来轮询UART与TIMER,涉及HAL层,晚点总结.
(2)HAL_ENTER_CRITICAL_SECTION(intState); 与HAL_EXIT_CRITICAL_SECTION(intState); 见1;
(3)events =tasksEvents[idx]; tasksEvents[idx] =0; 见2;
(4) events =(tasksArr[idx])( idx, events); 见3;
(5)tasksEvents[idx] |= events; 见4;
//--------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------
1、HAL_ENTER_CRITICAL_SECTION(intState)与HAL_EXIT_CRITICAL_SECTION(intState)
定义在hal_mcu.h中,如下:
#defineHAL_ENABLE_INTERRUPTS() st( EA = 1; ) //开中断
#defineHAL_DISABLE_INTERRUPTS() st( EA = 0; ) //关中断
#defineHAL_INTERRUPTS_ARE_ENABLED() (EA)
typedef unsigned charhalIntState_t; //中断位状态
//中断临界状态:把原先中断状态EA赋给X,然后关中断;以便后面可以恢复原先的中断状态 #defineHAL_ENTER_CRITICAL_SECTION(x) st( x = EA; HAL_DISABLE_INTERRUPTS();)
//跳出上面的中断临界状态,恢复先前的中断状态 #defineHAL_EXIT_CRITICAL_SECTION(x) st( EA = x; )
//中断临界状态以及跳出临界状态的全过程:
#defineHAL_CRITICAL_STATEMENT(x) st( halIntState_t s; HAL_ENTER_CRITICAL_SECTION(s); x;HAL_EXIT_CRITICAL_SECTION(s); )
st()函数,定义在hal_defs.h中:
* This macrois for use by other macros to form a fully valid C statement.
* Without this, the if/elseconditionals could show unexpectedbehavior.
#definest(x) do { x } while (__LINE__ == -1)
//--------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------
2、events = tasksEvents[idx]; tasksEvents[idx] =0;
个人认为,tasksEvents[ ]是一个指针数组(但是在协议栈里对这个“数组”的定义我还没有找到,只是在OSAL_SampleApp.c下有这么一个声明:uint16*tasksEvents;以及系统任务初始函数下对tasksEvents的定义),内部各元素都是指向uint16类型变量的指针变量(对于本例子来说内部各元素即tasksEvents[0]~tasksEvents[6];tasksEvents[0]指向任务1的存储空间……),对events的声明也是 uint16events;。这里把有事件发生的任务idx的存储空间的地址赋给events。然后清除任务idx的事件 tasksEvents[idx] = 0,(指针变量值为NULL).
//--------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------
3、 events =(tasksArr[idx])( idx, events ); ——任务idx的事件处理函数
----------------------------
首先看下对tasksArr[]的定义:
const pTaskEventHandlerFn tasksArr[ ]= //数组tasksArr[ ]有7个元素(MTdefined)
{
macEventLoop, //MAC层任务事件处理函数
nwk_event_loop, //NWK层任务事件处理函数
Hal_ProcessEvent, //PHY层事件处理函数
#if defined( MT_TASK )
MT_ProcessEvent, //MT任务事件处理函数
#endif
APS_event_loop, //APS层任务事件处理函数
ZDApp_event_loop, //ZDO任务事件处理函数
SampleApp_ProcessEvent //用户应用任务事件处理函数
};
----------------------------
再来看下对pTaskEventHandlerFn的定义:(OSAL_Tasks.h中)
typedefunsigned short (*pTaskEventHandlerFn)( unsigned char task_id,unsigned short event );
//前面有个typedef,把pTaskEventHandlerFn声明成一种函数指针类型的别名
//这种函数指针指向M(task_id,event)这种类型的函数(即各任务的事件处理函数)
//如果用这个别名来生成一个指向函数的指针X,则当X获得函数的地址后,就可以像调用
//原来函数一样来使用这个函数指针X(task_id,event),如上面的pTaskEventHandlerFn tasksArr[]
//因此tasksArr[ ]是一个指针数组,其内部的各元素都是指针,从OSAL_SampleApp.c中对tasksArr[]
//的定义我们可以知道macEventLoop,nwk_event_loop一直到SampleApp_ProcessEvent这些元素都是
//指针变量,最终都是指向M(task_id,event)这种类型的函数,也就是各层的事件处理函数。
//另外我觉得,就像pTaskEventHandlerFn tasksArr[ ];之后可以通过tasksArr[ ]直接
//调用tasksArr[ ](task_id,event),也可以通过tasksArr[ ]里面的元素直接调用
//macEventLoop(task_id,event)...SampleApp_ProcessEvent(task_id,event),而这些任务事件处理
//函数都是uint16类型的.因此osal_start_system()里可以events = (tasksArr[idx])(idx, events );events也是uint16类型的。……………………语法上来解释我自己都一团雾水了,个人想法,不一定正确~
//typedef unsigned short uint16;
//typedef unsigned char uint8;
----------------------------
这里events =(tasksArr[idx])( idx, events); 是因为每一个任务的事件处理函数返回的都是这个任务没有被处理完成的事件。比如说用户应用任务的事件处理函数:SampleApp_ProcessEvent(uint8 task_id, uint16 events ),如下:
每个任务都有不同的事件,因此在进行事件的处理前要进行判断,对于SampleApp
大事件1_接收系统消息:(a)_按键小事件(包含了组发送flash消息事件和进组退组事件)
(b)_接收数据小事件
(c)_设备网络状态变化小事件
大事件2_向外发送消息:(a)_发送 periodic消息
下面的SampleApp_ProcessEvent()就是通过上面的顺序对用户添加的应用任务进行事件的轮询:
uint16SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
afIncomingMSGPacket_t*MSGpkt; //接收到的消息
//则接收系统消息再进行判断
if ( events & SYS_EVENT_MSG )
{
MSGpkt =(afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID); //接收属于本应用任务SampleApp的消息(SampleApp_TaskID标记。
while ( MSGpkt ) //接收到
{
switch ( MSGpkt->hdr.event ) //系统消息的进一步判断
{
// Received when a key ispressed
// 如果一个OSAL任务已经被登记注册,那么任何键盘事件都将接受一个KEY_CHANGE事件信息。
caseKEY_CHANGE: //#defineKEY_CHANGE 0xC0 --KeyEvents
SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state,((keyChange_t *)MSGpkt)->keys );
break; //执行具体的按键处理函数,定义在sampleAPP.c中
// Received when amessages is received (OTA:over the air) for thisendpoint
// 接收数据事件,调用函数AF_DataRequest()接收数据
case AF_INCOMING_MSG_CMD: // #defineAF_INCOMING_MSG_CMD 0x 1A --Incoming MSG type message
SampleApp_MessageMSGCB( MSGpkt ); //调用回调函数对收到的数据进行处理
break;
// Received whenever the device changes state inthe network
// 只要网络状态发生改变,就通过ZDO_STATE_CHANGE事件通知所有的任务,注意,是所有任务都会收到这消息。
caseZDO_STATE_CHANGE: //#defineZDO_STATE_CHANGE 0xD1 --ZDO haschanged the device's networkstate
SampleApp_NwkState =(devStates_t)(MSGpkt->hdr.status); //获取设备当前状态
if ( (SampleApp_NwkState == DEV_ZB_COORD)
|| (SampleApp_NwkState == DEV_ROUTER)
|| (SampleApp_NwkState == DEV_END_DEVICE) )
{
// Start sending the periodic message in a regularinterval.
//这个定时器是为发送周期信息设置的,我觉得因为在这个例子中,用户自己添加的任务,只有两个事件是用于向外发送消息的,一个是发送flash闪烁消息,属于组寻址,而另一个是发送periodic周期消息,属于广播;这里是一个设备的网络状态发生了变化,必须要告诉同一网络中的其它设备,因此要进行广播通知其它设备…发送的消息中应该会包括本设备的类型。……不知道这样理解对不对~管它的,以后会明白的~
//更新:这个定时器只是为发送周期信息开启的,设备启动初始化后从这里开始触发第一个周期信息的发送,然后周而复始下去.
osal_start_timerEx( SampleApp_TaskID,
SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT );
}
else
{
// Device is no longer in thenetwork
}
break;
default:
break;
}
// Release the memory
//以上把收到系统消息这个大事件处理完了,释放消息占用的内存
osal_msg_deallocate( (uint8 *)MSGpkt );
// Next - if one is available
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive(SampleApp_TaskID );
}
// returnunprocessed events
//两者相异或,返回未处理的事件,return到osal_start_system()下的events =(tasksArr[idx])( idx, events)语句中,重新在osal_start_system()下轮询再进入此函数进行处理。
return (events ^SYS_EVENT_MSG);
}
//--------------------------
// Send a message out - This event is generated by atimer
// (setup inSampleApp_Init()).
if ( events &SAMPLEAPP_SEND_PERIODIC_MSG_EVT ) //发送周期消息事件
{
// Send theperiodic message
SampleApp_SendPeriodicMessage();
// Setup to send message again in normal period (+a littlejitter)
//这里为任务SampleApp_TaskID的事件SAMPLEAPP_SEND_PERIODIC_MSG_EVT设定一个定时器,定时时间为 (SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() &0x00FF)),当时间一到,该运行的任务将被通报有事件发生。
osal_start_timerEx( SampleApp_TaskID,SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
(SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF)));
//return unprocessed events
//两者相异或,返回未处理的事件,同样是返回到osal_start_system()下的events = (tasksArr[idx])(idx, events )语句中。
return(events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT);
}
// Discard unknownevents 丢弃未知事件(为什么没有发送flash消息的事件!!!???今天发现发送flash消息事件是包含在按键事件里面的~soga)
return 0;
}
//--------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------
4、tasksEvents[idx] |= events;
意思是好理解的,返回未处理的事件到任务idx中。但从语法上面来分析,tasksEvents[idx]已被清0,我实在不知道是存储空间地址上面的或,还是内容上面的或……不钻了,以后再看看~