有人对osal处理消息的机制及如果有数据包接收,协议栈到底是怎么来处理得到所需要的数据的有点不清楚,以下我简单的按照我的理解写一下。
其实只要触发SYS_EVENT_MSG事件,首先都会有这样一个语句:
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );
让我们一步一步看下函数的意思,首先是osal_msg_receive()这个函数,来看一下原型:
@fn osal_msg_receive
*
* @brief
*
* This function is called by a task to retrieve a received command
* message. The calling task must deallocate the message buffer after
* processing the message using the osal_msg_deallocate() call.
*函数描述:
该函数通过一个任务调用来检索收到的命令信息。调用的任务必须在利用osal_msg_deallocate()函数处理信息之后必须释放消息占用的内存。
* @param byte task_id - receiving tasks ID
*收到的任务ID
* @return *byte - message information or NULL if no message
*消息信息或者空指针
byte *osal_msg_receive( byte task_id )
{
osal_msg_hdr_t *listHdr;
osal_msg_hdr_t *prevHdr=0;
halIntState_t intState;
// Hold off interrupts
HAL_ENTER_CRITICAL_SECTION(intState);
这个主要用于暂缓中断
// Point to the top of the queue
listHdr = osal_qHead;
指向消息队列的头部
// Look through the queue for a message that belongs to the asking task
在队列中查找属于该任务的消息。
while ( listHdr != NULL )
{
if ( (listHdr - 1)->dest_id == task_id )
{
break;
}
prevHdr = listHdr;
listHdr = OSAL_MSG_NEXT( listHdr );
}
// Did we find a message?
是否找到消息?
if ( listHdr == NULL )
{
// Release interrupts
释放中断
HAL_EXIT_CRITICAL_SECTION(intState);
return NULL;
}
// Take out of the link list
从链接链表中拿出。
osal_msg_extract( &osal_qHead, listHdr, prevHdr );
// Release interrupts
HAL_EXIT_CRITICAL_SECTION(intState);
return ( (byte*) listHdr );
}
这个函数看来主要是提取属于该任务的消息,osal在后台维护一个消息队列,对应的任务可以在该消息队列中提取到属于自己的消息,从而判断是哪个时间需要处理,但这又有一个问题,到底osal怎么把消息送到osal的消息队列中去呢,看来还有一些问题没搞清楚,没事,咱在继续研究,让我们在程序中好好找找,这就涉及到osal运行方式了,我们好好看看,一定要弄明白:
我们都知道在开发自己的应用程序时,对于我们开发者来说,一个任务包括最主要的两个部分:首先要做的是做的是写任务初始化函数,然后是相应的事件处理函数。当然,最后一步是将自己编写的任务添加到osal任务队列中去,这个主要由osalAddTasks()函数来完成。
而对于整个osal运行来说,主要的过程如下:
首先主要是进行一系列的初始化,然后就是进入一个死循环函数osal_start_system()
ZSEG int main( void )
{
// Turn off interrupts
osal_int_disable( INTS_ALL );
// Make sure supply voltage is high enough to run
zmain_vdd_check();
// Initialize stack memory
zmain_ram_init();
// Initialize board I/O
InitBoard( OB_COLD );
// Initialze HAL drivers
HalDriverInit();
// Initialize NV System
osal_nv_init( NULL );
// Determine the extended address
zmain_ext_addr();
// Initialize basic NV items
zgInit();
// Initialize the MAC
ZMacInit();
#ifndef NONWK
// Since the AF isn't a task, call it's initialization routine
afInit();
#endif
// Initialize the operating system
osal_init_system();
// Allow interrupts
osal_int_enable( INTS_ALL );
// Final board initialization
InitBoard( OB_READY );
// Display information about this device
zmain_dev_info();
#ifdef LCD_SUPPORTED
zmain_lcd_init();
#endif
osal_start_system(); // No Return from here
} // main()
osal_start_system()这个函数的原型如下:其主要功能是执行任务队列中的任务。主要是通过不断循环来实现。
void osal_start_system( void )
{
uint16 events;
uint16 retEvents;
byte activity;
halIntState_t intState;
// Forever Loop
#if !defined ( ZBIT )
for(;;)
#endif
{
Hal_ProcessPoll();//
activity = false;
activeTask = osalNextActiveTask();
if ( activeTask )
{
如果有任务的话就暂缓中断。
HAL_ENTER_CRITICAL_SECTION(intState);
events = activeTask->events;//进入事件
// Clear the Events for this task
activeTask->events = 0;//对应事件清0
HAL_EXIT_CRITICAL_SECTION(intState);
开放总断。
if ( events != 0 )
{
// Call the task to process the event(s)
if ( activeTask->pfnEventProcessor )// 调用相应的任务事件处理函数
{
retEvents = (activeTask->pfnEventProcessor)( activeTask->taskID, events );
// Add back unprocessed events to the current task
HAL_ENTER_CRITICAL_SECTION(intState);
activeTask->events |= retEvents;
HAL_EXIT_CRITICAL_SECTION(intState);
activity = true;
}
}
}
// Complete pass through all task events with no activity?
if ( activity == false )//没有相应的任务需要处理后就进入休眠模式。
{
#if defined( POWER_SAVING )
// Put the processor/system into sleep
osal_pwrmgr_powerconserve();//进入休眠
#endif
}
}
整个流程下来,你可能并不知道任务里的事件是怎么触发的,消息是怎么进行传递的,所以得继续往下看(有点乱是吧,没办法,哥们,没经过整理的东西往往是这么无力,O(∩_∩)O哈哈~)来,继续:
这个函数里面一个关键函数是Hal_ProcessPoll(),不要小看这个函数,这个函数为系统设置了一个"心跳",称之为心跳的原因主要是因为这个设置了一个定时器作为osal运行的时钟,现在的关键是搞清楚这个时钟在哪设置,怎样引导osal工作(好像扯得远了,没事,把这些先慢慢搞清楚,然后就知道消息的传送过程)这个函数调用的是这个函数 HalTimerTick(),这个函数原型如下:
void HalTimerTick (void)
{
if (!halTimerRecord[HW_TIMER_1].intEnable)
{
halProcessTimer1 ();
}
if (!halTimerRecord[HW_TIMER_3].intEnable)
{
halProcessTimer3 ();
}
if (!halTimerRecord[HW_TIMER_4].intEnable)
{
halProcessTimer4 ();
}
}
妈的,出了点问题,我不知道这些计时器的配置函数在哪,到底是哪个定时器作为osal运行的时钟呢,halTimerRecord[HW_TIMER_1].intEnable到底是在哪配置的我没找到相应的函数,没事,等等,继续找。。。。
按照我的感觉如果在osal任务初始化函数中(初始化我会拿另一节来讲,这节就只讲讲这个)肯定可以找到一些端倪,于是按照思路找下去,有了一个不错的发现:这个是板级初始化函数,我们来找,
void InitBoard( byte level )
{
if ( level == OB_COLD )
{
// Initialize HAL
HAL_BOARD_INIT();
// Interrupts off
osal_int_disable( INTS_ALL );
// Turn all LEDs off
HalLedSet( HAL_LED_ALL, HAL_LED_MODE_OFF );
// Check for Brown-Out reset
ChkReset();
OnboardTimerIntEnable = FALSE;
HalTimerConfig (OSAL_TIMER, // 8bit timer2
HAL_TIMER_MODE_CTC, // Clear Timer on Compare
HAL_TIMER_CHANNEL_SINGLE, // Channel 1 - default
HAL_TIMER_CH_MODE_OUTPUT_COMPARE, // Output Compare mode
OnboardTimerIntEnable, // Use interrupt
Onboard_TimerCallBack); // Channel Mode
}
最主要的是这个函数 HalTimerConfig()设置了时钟。
else // !OB_COLD
{
#ifdef ZTOOL_PORT
MT_IndReset();
#endif
OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
}
来看下HalTimerConfig()的原型:
uint8 HalTimerConfig (uint8 timerId, uint8 opMode, uint8 channel, uint8 channelMode,
bool intEnable, halTimerCBack_t cBack)
{
uint8 hwtimerid;
hwtimerid = halTimerRemap (timerId);
if ((opMode & HAL_TIMER_MODE_MASK) && (timerId < HAL_TIMER_MAX) &&
(channelMode & HAL_TIMER_CHANNEL_MASK) && (channel & HAL_TIMER_CHANNEL_MASK))
{
halTimerRecord[hwtimerid].configured = TRUE;
halTimerRecord[hwtimerid].opMode = opMode;
halTimerRecord[hwtimerid].channel = channel;
halTimerRecord[hwtimerid].channelMode = channelMode;
halTimerRecord[hwtimerid].intEnable = intEnable;
halTimerRecord[hwtimerid].callBackFunc = cBack;//设置回调函数的指针。
}
else
{
return HAL_TIMER_PARAMS_ERROR;
}
return HAL_TIMER_OK;
}
层层调用我最后找到了这个函数(层层调用的我就不写出来了,只写最主要的)
void osal_update_timers( void )
{
osalTimerUpdate( tmr_decr_time );//tmr_decr_time在void osalTimerInit( void )赋值,为: tmr_decr_time = TIMER_DECR_TIME;define TIMER_DECR_TIME 1 // 1ms - has to be matched with TC_OCC 即是一毫秒即是每隔一毫秒更新一次定时器。但是有一个问题,我在例子中看到这个定时器没有被打开。
}
osalTimerUpdate( tmr_decr_time )原型:
*********************************************************************
* @fn osalTimerUpdate
*
* @brief Update the timer structures for a timer tick.
*更新定时器的数据结构
* @param none
*
* @return none
*********************************************************************/
static void osalTimerUpdate( uint16 updateTime )//这个函数真有点让我有点摸不着头脑,不知道为啥多出来一个定时器链表。
{
halIntState_t intState;
osalTimerRec_t *srchTimer;
osalTimerRec_t *prevTimer;
osalTimerRec_t *saveTimer;
HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts.
// Update the system time
osal_systemClock += updateTime;
// Look for open timer slot
if ( timerHead != NULL )
{
// Add it to the end of the timer list
srchTimer = timerHead;
prevTimer = (void *)NULL;
// Look for open timer slot
while ( srchTimer )
{
// Decrease the correct amount of time
if (srchTimer->timeout <= updateTime)
srchTimer->timeout = 0;
else
srchTimer->timeout = srchTimer->timeout - updateTime;
// When timeout, execute the task
if ( srchTimer->timeout == 0 )
{
osal_set_event( srchTimer->task_id, srchTimer->event_flag );//这个函数又是关键。
// Take out of list
if ( prevTimer == NULL )
timerHead = srchTimer->next;
else
prevTimer->next = srchTimer->next;
// Next
saveTimer = srchTimer->next;
// Free memory
osal_mem_free( srchTimer );
srchTimer = saveTimer;
}
else
{
// Get next
prevTimer = srchTimer;
srchTimer = srchTimer->next;
}
}
#ifdef POWER_SAVING
osal_retune_timers();这个函数以后再说。
#endif
}
HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts.
}
还是先上一个图说明定时器数据链表吧,该图来自奥特曼的笔记:
其实就是一个数据结构,没啥复杂的,不要想复杂了,
typedef struct
{
void *next;
UINT16 timeout;
UINT16 event_flag;
byte task_id;
} osalTimerRec_t;。
有点晕,先不管上面的了,我不知道上面这个链表是怎么更新的,即是上述的链表的值是怎么赋值的,为什么要搞个这样的表出来?(妈的,为了搞清楚开头的那玩意跑到现在这么远的地方了,越说越远,没办法了,打破砂锅问到底吧)先把这个高清楚之后在来看上面函数的具体作用,我看了奥特曼写的一些东西,感觉有些不是很对,但是他说的一个函数引起了我的注意,这就是
byte osal_start_timerEx( byte 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 );
添加一个新的任务到定时器链表中。
if ( newTimer )
{
#ifdef POWER_SAVING
// Update timer registers
osal_retune_timers();
(void)timerActive;
#endif
// Does the timer need to be started? //现在如果定时器没有启动,那么启动定时器
if ( timerActive == FALSE )
{
osal_timer_activate( TRUE );//妈的,原来是在这里启动的,难怪在osal初始化中可以开始不启动。搞的我心里惴惴不安,还以为自己看错了,
}
}
HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts.
return ( (newTimer != NULL) ? ZSUCCESS : NO_TIMER_AVAIL );
}
搞清楚了这个函数,看来这个函数主要的任务是将任务的一个对应事件送到上述的定时器链表中,那上面那个函数怎么执行呢,问题又来了,什么样的情况下才去运行osalTimerUpdate()这个函数呢,在初始化的过程中运行了一次,但是,后来又是怎么运行的呢,如果是靠定时器溢出来运行的,那么这个机制又是怎样的呢?
继续看程序,继续思考,别急。。。。。
查了有关资料,如果定时器溢出会调用这个函数halTimerSendCallBack(),看一下这个函数的原型,
void halTimerSendCallBack (uint8 timerId, uint8 channel, uint8 channelMode)
{
uint8 hwtimerid;
hwtimerid = halTimerRemap (timerId);
if (halTimerRecord[hwtimerid].callBackFunc)
(halTimerRecord[hwtimerid].callBackFunc) (timerId, channel, channelMode);
}
即如果系统定时器溢出后将调用这个函数(调用机制等下再来看)并找出对应的定时器回调函数,系统定时器的回调函数是这个void Onboard_TimerCallBack ( uint8 timerId, uint8 channel, uint8 channelMode)
{
if ((timerId == OSAL_TIMER) && (channelMode == HAL_TIMER_CH_MODE_OUTPUT_COMPARE))
{
osal_update_timers();
}
}
这样就知道了如果定时器每溢出一次将会调用这个osal_update_timers();函数一次,现在我们再回来看看到底他妈的这个函数是干嘛用的:。。。。。。。。
osalTimerUpdate( tmr_decr_time )原型:
*********************************************************************
* @fn osalTimerUpdate
*
* @brief Update the timer structures for a timer tick.
*更新定时器的数据结构
* @param none
*
* @return none
*********************************************************************/
static void osalTimerUpdate( uint16 updateTime )
{
halIntState_t intState;
osalTimerRec_t *srchTimer;
osalTimerRec_t *prevTimer;
osalTimerRec_t *saveTimer;//这些是链表的相应指针,不要怕,知道吗,都学过了,呵呵
HAL_ENTER_CRITICAL_SECTION( intState ); // Hold off interrupts.
// Update the system time
osal_systemClock += updateTime;//更新系统时间,这个osal_systemClock 是一个非常大的数,static uint32 osal_systemClock;
// Look for open timer slot
if ( timerHead != NULL )
{
// Add it to the end of the timer list
srchTimer = timerHead;
prevTimer = (void *)NULL;
// Look for open timer slot
while ( srchTimer )
{
// Decrease the correct amount of time
if (srchTimer->timeout <= updateTime)
srchTimer->timeout = 0;
else
srchTimer->timeout = srchTimer->timeout - updateTime;
// When timeout, execute the task
if ( srchTimer->timeout == 0 )
{
osal_set_event( srchTimer->task_id, srchTimer->event_flag );//这个函数又是关键。
// Take out of list
if ( prevTimer == NULL )
timerHead = srchTimer->next;
else
prevTimer->next = srchTimer->next;
// Next
saveTimer = srchTimer->next;
// Free memory
osal_mem_free( srchTimer );
srchTimer = saveTimer;
}
else
{
// Get next
prevTimer = srchTimer;
srchTimer = srchTimer->next;
}
}
#ifdef POWER_SAVING
osal_retune_timers();这个函数以后再说。
#endif
}
HAL_EXIT_CRITICAL_SECTION( intState ); // Re-enable interrupts.
}
这下应该看懂了吧,主要是在timeout的时间后执行
osal_set_event( srchTimer->task_id, srchTimer->event_flag );好的先,把大体流程总结一下在来看,如果假设利用osal_start_timerEx()函数设置了一个事件,并将其加入到定时器链表中,这时候定时器启动,并且如果溢出的时候就会调用osalTimerUpdate()函数,在每次执行后都会检查时间对应timeout是否等于0,如果等于零的时候就会调用这个函数osal_set_event( srchTimer->task_id, srchTimer->event_flag),现在我们来仔细看一下这个函数,
byte osal_set_event( byte task_id, UINT16 event_flag )
{
osalTaskRec_t *srchTask;
halIntState_t intState;
srchTask = osalFindTask( task_id );//找到这个任务
if ( srchTask ) {
// Hold off interrupts
HAL_ENTER_CRITICAL_SECTION(intState);
// Stuff the event bit(s)
srchTask->events |= event_flag;//设定相应事件的标志。
**************************************
注:这里的events标志可以设定为一个非零值,而这个非零值就是对应的事件,在执行的时候就是根据相应的值来执行相应的事件。
**************************************
// Release interrupts
HAL_EXIT_CRITICAL_SECTION(intState);
}
else
return ( INVALID_TASK );
return ( ZSUCCESS );
}
这下知道了这个函数主要是置位相应的事件,然后传给系统的处理,系统处理这个主要是经过轮询
主要靠这个函数实现:
osalTaskRec_t *osalNextActiveTask( void )
{
osalTaskRec_t *srchTask;
// Start at the beginning
srchTask = tasksHead;
// When found or not
while ( srchTask ) {
if (srchTask->events) {
// task is highest priority that is ready
return srchTask;
}
srchTask = srchTask->next;
}
return NULL;
}
它返回一个指向typedef struct osalTaskRec
{
struct osalTaskRec *next;
pTaskInitFn pfnInit;
pTaskEventHandlerFn pfnEventProcessor;
byte taskID;
byte taskPriority;
uint16 events;
} osalTaskRec_t;
的指针。
然后将任务交给retEvents = (activeTask->pfnEventProcessor)( activeTask->taskID, events );这个处理, 这还没有结束,我们在开头提到的这个还没有解决;
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );(终于回来了,累啊)
这里涉及到两个数据结构:
typedef struct
{
void *next;
uint16 len;
byte dest_id;
} osal_msg_hdr_t;
typedef struct
{
uint8 event;
uint8 status;
} osal_event_hdr_t;
我查了一下有这么一个函数和消息有关的,如下:
*********************************************************************
* @fn osal_msg_send
*
* @brief
*
* This function is called by a task to send a command message to
* another task or processing element. The sending_task field must
* refer to a valid task, since the task ID will be used
* for the response message. This function will also set a message
* ready event in the destination tasks event list.
*这个函数主要由任务调用发送一个命令消息到另一个任务或是处理元素,同时这个函数将会设定一个消息事件到目的任务的事件列表中
*
* @param byte destination task - Send msg to? Task ID
* @param byte *msg_ptr - pointer to new message buffer
* @param byte len - length of data in message
*
* @return ZSUCCESS, INVALID_SENDING_TASK, INVALID_DESTINATION_TASK,
* INVALID_MSG_POINTER, INVALID_LEN
*/
byte osal_msg_send( byte destination_task, byte *msg_ptr )
{
if ( msg_ptr == NULL )
return ( INVALID_MSG_POINTER );
if ( osalFindTask( destination_task ) == NULL )
{
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 ( ZSUCCESS );
}
看来,这个osal_msg_send()函数只是发送一个系统事件,而和用户自定义的事件无关(自己猜想的,以后验证。)。
现在大家应该豁然开朗了吧,我们终于走到了新的一步了。