有人對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()函數只是發送一個系統事件,而和用戶自定義的事件無關(自己猜想的,以后驗證。)。 現在大家應該豁然開朗了吧,我們終于走到了新的一步了。
|