本文主要是介绍ZSTACK协议栈--OSAL主循环流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
OSAL系统主循环函数:
void osal_start_system( void )
{
#if !defined ( ZBIT ) //不知道是什么东西
for(;;) // Forever Loop
#endif
{
uint8 idx = 0;
Hal_ProcessPoll(); // This replaces MT_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 is ready.
{
break;
}
} while (++idx < tasksCnt); //tasksCnt=7(针对本例子,随着用户应用任务增多而增大)
//-------------------------
if (idx < tasksCnt)
{
uint16 events;
halIntState_t intState; //中断位状态
HAL_ENTER_CRITICAL_SECTION(intState); //中断临界状态:保存先前中断状态,然后关中断
events = tasksEvents[idx]; //uint16 events;对应有事件发生的任务的数组
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 current task.
//把刚才返回未处理的任务事件添加加当前任务中再进行处理
//(跳出此if(idx < tasksCnt)循环再进行if (tasksEvents[idx])判断并处理)
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
}
}
说明:
(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中,如下:
/* ---------------------------------------------------
* Interrupt Macros
* ----------------------------------------------------
*/
#define HAL_ENABLE_INTERRUPTS() st( EA = 1; ) //开中断
#define HAL_DISABLE_INTERRUPTS() st( EA = 0; ) //关中断
#define HAL_INTERRUPTS_ARE_ENABLED() (EA)
typedef unsigned char halIntState_t; //中断位状态
//中断临界状态:把原先中断状态EA赋给X,然后关中断;以便后面可以恢复原先的中断状态 #define HAL_ENTER_CRITICAL_SECTION(x) st( x = EA; HAL_DISABLE_INTERRUPTS(); )
//跳出上面的中断临界状态,恢复先前的中断状态 #define HAL_EXIT_CRITICAL_SECTION(x) st( EA = x; )
//中断临界状态以及跳出临界状态的全过程:
#define HAL_CRITICAL_STATEMENT(x) st( halIntState_t s; HAL_ENTER_CRITICAL_SECTION(s); x; HAL_EXIT_CRITICAL_SECTION(s); )
st()函数,定义在hal_defs.h中:
* This macro is for use by other macros to form a fully valid C statement.
* Without this, the if/else conditionals could show unexpected behavior.
#define st(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的声明也是 uint16 events;。这里把有事件发生的任务idx的存储空间的地址赋给events。然后清除任务idx的事件 tasksEvents[idx] = 0,(指针变量值为NULL).
//--------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------
3、 events = (tasksArr[idx])( idx, events ); ——任务idx的事件处理函数
----------------------------
首先看下对tasksArr[ ]的定义:
const pTaskEventHandlerFn tasksArr[ ] = //数组tasksArr[ ]有7个元素(MT defined)
{
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中)
typedef unsigned 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()就是通过上面的顺序对用户添加的应用任务进行事件的轮询:
uint16 SampleApp_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 is pressed
/*小事件:按键事件*/
// 如果一个OSAL任务已经被登记注册,那么任何键盘事件都将接受一个KEY_CHANGE事件信息。
case KEY_CHANGE: //#define KEY_CHANGE 0xC0 --Key Events
SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
break; //执行具体的按键处理函数,定义在sampleAPP.c中
// Received when a messages is received (OTA:over the air) for this endpoint
/*小事件:接收数据事件*/
// 接收数据事件,调用函数AF_DataRequest()接收数据
case AF_INCOMING_MSG_CMD: // #define AF_INCOMING_MSG_CMD 0x 1A --Incoming MSG type message
SampleApp_MessageMSGCB( MSGpkt ); //调用回调函数对收到的数据进行处理
break;
// Received whenever the device changes state in the network
/*小事件:设备网络状态变化事件*/
// 只要网络状态发生改变,就通过ZDO_STATE_CHANGE事件通知所有的任务,注意,是所有任务都会收到这消息。
case ZDO_STATE_CHANGE: //#define ZDO_STATE_CHANGE 0xD1 --ZDO has changed the device's network state
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 regular interval.
/*按一定间隔启动定时器*/
//这个定时器是为发送周期信息设置的,我觉得因为在这个例子中,用户自己添加的任务,只有两个事件是用于向外发送消息的,一个是发送flash闪烁消息,属于组寻址,而另一个是发送periodic周期消息,属于广播;这里是一个设备的网络状态发生了变化,必须要告诉同一网络中的其它设备,因此要进行广播通知其它设备…发送的消息中应该会包括本设备的类型。……不知道这样理解对不对~管它的,以后会明白的~
//更新:这个定时器只是为发送周期信息开启的,设备启动初始化后从这里开始触发第一个周期信息的发送,然后周而复始下去.
osal_start_timerEx( SampleApp_TaskID,
SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT );
}
else
{
// Device is no longer in the network
}
break;
default:
break;
}
// Release the memory
//以上把收到系统消息这个大事件处理完了,释放消息占用的内存
osal_msg_deallocate( (uint8 *)MSGpkt );
// Next - if one is available
/*指针指向下一个"已接收到的”[程序在while ( MSGpkt )内]放在缓冲区的待处理的事件,与
// SampleApp_ProcessEvent处理多个事件相对应,返回while ( MSGpkt )重新处理事件,
//直到缓冲区没有等待处理事件为止。*/
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );
}
// return unprocessed 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 a timer
/* 如果大事件是向外发送信息,该事件由定时器产生*/
// (setup in SampleApp_Init()).
if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT ) //发送周期消息事件
{
// Send the periodic message
SampleApp_SendPeriodicMessage();
// Setup to send message again in normal period (+ a little jitter)
//这里为任务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 unknown events 丢弃未知事件(为什么没有发送flash消息的事件!!!???今天发现发送flash消息事件是包含在按键事件里面的~soga)
return 0;
}
//--------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------
4、tasksEvents[idx] |= events;
意思是好理解的,返回未处理的事件到任务idx中。但从语法上面来分析,tasksEvents[idx] 已被清0,我实在不知道是存储空间地址上面的或,还是内容上面的或……不钻了,以后再看看~
这篇关于ZSTACK协议栈--OSAL主循环流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!