ZIGBEE: 任务处理分析

首先看的当然是main()函数,不过这个函数不是今天的重点,里面有我添加的注释,先就一笔带过吧。

int main( void )

{

  // Turn off interrupts

  osal_int_disable( INTS_ALL );//关闭全局中断EA=0,初始化过程不响应任何中断

 

  // Initialization for board related stuff such as LEDs

  HAL_BOARD_INIT();//配置了时钟、LED、串口

 

  // Make sure supply voltage is high enough to run

  zmain_vdd_check();//检查电源电压

 

  // Initialize stack memory

  zmain_ram_init();//初始化堆内存

 

  // Initialize board I/O /初始化板子用到的IO

  InitBoard( OB_COLD );

 

  // Initialze HAL drivers

  HalDriverInit();//初始化外设

 

  // Initialize NV System //系统初始化

  osal_nv_init( NULL );

 

  // Initialize basic NV items//任务初始化

  zgInit();

 

  // Initialize the MAC

  ZMacInit();

 

  // Determine the extended address //确定长地址

  zmain_ext_addr();

#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 ); //sd rest

  // Display information about this device //显示设备信息

  zmain_dev_info();

  /* Display the device info on the LCD */

#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

 

  osal_start_system(); // No Return from here//正常情况下不返回

 

  // Shouldn't get here

  return ( 0 );

} // main()

其中含有osal的都是与操作系统相关的。这里主要提一下这些函数:

  // Initialze HAL drivers

  HalDriverInit();//初始化外设

片内外设与片外外设基本上在这个函数中初始化,像TimerDMALCD等。该函数调用后设备即可使用。

  // Initialize basic NV items

  zgInit();

这个函数通过调用

  // Initialize the items table

  zgInitItems( setDefault );

初始化了zgItemTable[]//ZGlobal Item Table

我反正没搞懂这个数组干嘛用的,至少跟我们今天讨论的任务没有关系。我们讨论的任务在  

// Initialize the operating system

  osal_init_system();

函数中调用osalInitTasks()进行初始化,在该函数中为每一个任务分配了一个ID号,这个ID号在任务切换的时候将用到。该函数中的初始化函数的顺序与函数指针数组

const pTaskEventHandlerFn tasksArr[]

中对应的任务的顺序是一致的,这一点不难理解,就是为了保证任务与ID号的对应。该函数中还有这么两天语句值得注意:

  tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);//申请空间,用于存放任务

  osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt)); //0初始化申请到的空间

tasksEvents是一个指针,C语言好的不用看它的定义都看得出来。任务切换的时候就是通过tasksEvents来查找需要处理的任务。tasksEvents指向的对象保存的是对应任务的掩码。

最后通过调用函数osal_start_system(); /* No Return from here*/启动操作系统,该函数正常情况下是不返回的。

#if !defined ( ZBIT ) && !defined ( UBIT )

  for(;;)  // Forever Loop

#endif

然后所有的任务都在这个for循环中被处理:

  {

    uint8 idx = 0;

    

    osalTimeUpdate();//定时器任务更新

 

//轮询处理

    Hal_ProcessPoll();  // This replaces MT_SerialPoll() and osal_check_timer().

 

    do {

      if (tasksEvents[idx])  // Task is highest priority that is ready.//查找优先级最高的任务

      {

        break;

      }

    } while (++idx < tasksCnt);//tasksCnt为总的任务数

 

    if (idx < tasksCnt)//任务数检查

    {

      uint16 events;

      halIntState_t intState;

 

      HAL_ENTER_CRITICAL_SECTION(intState);//保存中断状态

      events = tasksEvents[idx];//取出任务后立即清除

      tasksEvents[idx] = 0;  // Clear the Events for this task.

      HAL_EXIT_CRITICAL_SECTION(intState);//恢复中断状态

 

      events = (tasksArr[idx])( idx, events );//执行任务,返回值是未处理的事件的掩码

 

      HAL_ENTER_CRITICAL_SECTION(intState);

      tasksEvents[idx] |= events;  // Add back unprocessed events to the current task.//添加未被处理的任务

      HAL_EXIT_CRITICAL_SECTION(intState);

    }

#if defined( POWER_SAVING )

    else  // Complete pass through all task events with no activity?

    {

     //当任务ID号出错时进入睡眠

      osal_pwrmgr_powerconserve();  // Put the processor/system into sleep

    }

#endif

  }

当有任务需要处理时便调用函数osal_set_event()添加任务:

/*********************************************************************

 * @fn      osal_set_event

 *

 * @brief

 *

 *    This function is called to set the event flags for a task.  The

 *    event passed in is OR'd into the task's event variable.

 **设置事件标志,这些事件保存到变量task

 * @param   uint8 task_id - receiving tasks ID

 * @param   uint8 event_flag - what event to set

 *

 * @return  SUCCESS, INVALID_TASK

 */

uint8 osal_set_event( uint8 task_id, uint16 event_flag )

{

  if ( task_id < tasksCnt )//正确的ID

  {

    halIntState_t   intState;

    HAL_ENTER_CRITICAL_SECTION(intState);    // Hold off interrupts

    tasksEvents[task_id] |= event_flag;  // Stuff the event bit(s)/添加需要处理的事件的掩码

    HAL_EXIT_CRITICAL_SECTION(intState);     // Release interrupts

  }

   else

    return ( INVALID_TASK );

 

  return ( SUCCESS );

}

再看函数osal_start_system()对于tasksEvents就比较清楚了。

在所有调用osal_set_event()的函数中比较值得关注的是void osalTimerUpdate( uint16 updateTime )

先来看看函数体:

/*********************************************************************

 * @fn      osalTimerUpdate

 *

 * @brief   Update the timer structures for a timer tick.

 *

 * @param   none

 *

 * @return  none

 **更新定时器任务

 *********************************************************************/

void osalTimerUpdate( uint16 updateTime )

{

  halIntState_t intState;

  osalTimerRec_t *srchTimer;

  osalTimerRec_t *prevTimer;

 

  HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts./保存中断状态

  // Update the system time/更新系统时间

  osal_systemClock += updateTime;

  HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts./恢复中断状态

 

  // 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 )

    {

      osalTimerRec_t *freeTimer = NULL;

     

      HAL_ENTER_CRITICAL_SECTION( intState );  // Hold off interrupts.

      

      if (srchTimer->timeout <= updateTime)//超时检查

      {

        srchTimer->timeout = 0;

      }

      else

      {

        srchTimer->timeout = srchTimer->timeout - updateTime;

      }

      

      // When timeout or delete (event_flag == 0)/需要处理的事件

      if ( srchTimer->timeout == 0 || srchTimer->event_flag == 0 )

      {

        // Take out of list

        if ( prevTimer == NULL )

          timerHead = srchTimer->next;

        else

          prevTimer->next = srchTimer->next;

 

        // Setup to free memory/设置要被释放的资源

        freeTimer = srchTimer;

 

        // Next

        srchTimer = srchTimer->next;

      }

      else

      {

        // Get next/下一个任务

        prevTimer = srchTimer;

        srchTimer = srchTimer->next;

      }

      

      HAL_EXIT_CRITICAL_SECTION( intState );   // Re-enable interrupts.

      

      if ( freeTimer )//释放任务

      {

        if ( freeTimer->timeout == 0 )

        {

          osal_set_event( freeTimer->task_id, freeTimer->event_flag );//时间到了,设置事件标志以等待处理

        }

        osal_mem_free( freeTimer );//释放该定时器任务的资源

      }

    }

  }

}

在我所使用的版本中该函数只被两个函数调用,分别是:

/*********************************************************************

 * @fn      osal_adjust_timers

 *

 * @brief   Update the timer structures for elapsed ticks.

 *

 * @param   none

 *

 * @return  none

 *********************************************************************/

void osal_adjust_timers( void )

{

  uint16 eTime;

 

  if ( timerHead != NULL )

  {

    // Compute elapsed time (msec)

    eTime = TimerElapsed() /  TICK_COUNT;

 

    if ( eTime )

      osalTimerUpdate( eTime );

  }

}

 

/*********************************************************************

 * FUNCTIONS

 *********************************************************************/

 

/*********************************************************************

 * @fn      osalTimeUpdate

 *

 * @brief   Uses the free running rollover count of the MAC backoff timer;

 *          this timer runs freely with a constant 320 usec interval.  The

 *          count of 320-usec ticks is converted to msecs and used to update

 *          the OSAL clock and Timers by invoking osalClockUpdate() and

 *          osalTimerUpdate().  This function is intended to be invoked 

 *          from the background, not interrupt level.

 *

 * @param   None.

 *

 * @return  None.

 */

void osalTimeUpdate( void )//定时器任务更新

{

  uint16 tmp;

  uint16 ticks320us;

  uint16 elapsedMSec = 0;

 

  // Get the free-running count of 320us timer ticks//设置时间片

  tmp = macMcuPrecisionCount();//获取溢出值,该溢出值是一个累计的溢出值

  

  if ( tmp != previousMacTimerTick )//相等则代表没有溢出

  {

    // Calculate the elapsed ticks of the free-running timer.//计算已经消耗的时间

    ticks320us = tmp - previousMacTimerTick;

  

    // Store the MAC Timer tick count for the next time through this function.

    previousMacTimerTick = tmp;//保存当前时间

  

    /* It is necessary to loop to convert the usecs to msecs in increments so as 

     * not to overflow the 16-bit variables.

     **这是必要的循环转换usecs毫秒的增量,以免溢出16位变量。

     */

    while ( ticks320us > MAXCALCTICKS )

    {

      ticks320us -= MAXCALCTICKS;

      elapsedMSec += MAXCALCTICKS * 8 / 25;

      remUsTicks += MAXCALCTICKS * 8 % 25;

    }

  

    // update converted number with remaining ticks from loop and the 

    // accumulated remainder from loop/更新转换从循环和循环积累的其余部分与其余蜱

    tmp = (ticks320us * 8) + remUsTicks;

      

    // Convert the 320 us ticks into milliseconds and a remainder

    elapsedMSec += tmp / 25;

    remUsTicks = tmp % 25;

        

    // Update OSAL Clock and Timers//更新系统定时器

    if ( elapsedMSec )

    {

      osalClockUpdate( elapsedMSec );//更新系统时间

      osalTimerUpdate( elapsedMSec );//更新定时器任务,并设置需要处理的任务的掩码标志

    }

  }

}

都是跟定时器相关的。在函数osalTimerUpdate开头定义了两个指针:

  osalTimerRec_t *srchTimer;

  osalTimerRec_t *prevTimer;

其结构osalTimerRec_t定义为

typedef struct

{

  void *next;

  uint16 timeout;

  uint16 event_flag;

  uint8 task_id;

} osalTimerRec_t;

操作一个单向链表,链表头为timerHead,该链表中保存了所有需要定时执行的任务。这些任务在函数osalTimerRec_t * osalAddTimer( uint8 task_id, uint16 event_flag, uint16 timeout )中被创建。而该函数(只)被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 );

}

其中event_id跟函数uint8 osal_set_event( uint8 task_id, uint16 event_flag )中event_flag的作用是一样的,都是最后被写入到tasksEvents中的掩码。通过函数osal_set_event()更改tasksEvents:

    tasksEvents[task_id] |= event_flag;  // Stuff the event bit(s)/添加需要处理的事件的掩码

函数osal_start_timerEx使用结构osalTimerRec_t临时保存newTimer->event_flag = event_flag;

最后通过在主循环中调用osalTimeUpdate()又调用osalTimerUpdate( elapsedMSec )

最后通过代码

      if ( freeTimer )//释放任务

      {

        if ( freeTimer->timeout == 0 )

        {

          osal_set_event( freeTimer->task_id, freeTimer->event_flag );//时间到了,设置事件标志以等待处理

        }

        osal_mem_free( freeTimer );//释放该定时器任务的资源

      }

看到没,最后还是调用函数osal_set_event()对tasksEvents进行更改。当一个任务处理完毕时在主循环中清除相应的掩码。

当然当一开始进入函数osal_start_system()中时,应该是没有任务的,这个时候任务是通过中断层层调用,最后还是调用函数osal_set_event()添加任务。在ZigBee协议栈中,中断函数是通过宏来实现的。这些宏在Hal_mcu.h文件中定义:

#define _PRAGMA(x) _Pragma(#x)

#define HAL_ISR_FUNC_DECLARATION(f,v)   _PRAGMA(vector=v) __near_func __interrupt void f(void)

#define HAL_ISR_FUNC_PROTOTYPE(f,v)     _PRAGMA(vector=v) __near_func __interrupt void f(void)

#define HAL_ISR_FUNCTION(f,v)   HAL_ISR_FUNC_PROTOTYPE(f,v); HAL_ISR_FUNC_DECLARATION(f,v)

当需要编写一个中断函数实体时使用宏HAL_ISR_FUNCTION(f,v)如:

HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )//P0口中断服务函数

{

  if ((HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)||(HAL_KEY_SW_7_PXIFG & HAL_KEY_SW_7_BIT))

  {

    halProcessKeyInterrupt();

  }

  /*

    Clear the CPU interrupt flag for Port_0

    PxIFG has to be cleared before PxIF

  */

  //清除中断标志

  HAL_KEY_SW_6_PXIFG = 0;

  HAL_KEY_CPU_PORT_0_IF = 0;

}

HAL_ISR_FUNCTION( halTimer1Isr, T1_VECTOR )//定时器1中断服务函数

{

  halProcessTimer1 ();

}

HAL_ISR_FUNCTION( macMcuRfIsr, RF_VECTOR )//RF中断服务函数

只要对这些宏进行展开就得到了CC2530中断服务函数的形式。

下面就通过来看一下中断服务函数是怎么添加任务的。在P0口中断服务函数中调用了这么一个函数halProcessKeyInterrupt(),该函数的实现如下:

/**************************************************************************************************

 * @fn      halProcessKeyInterrupt

 *

 * @brief   Checks to see if it's a valid key interrupt, saves interrupt driven key states for

 *          processing by HalKeyRead(), and debounces keys by scheduling HalKeyRead() 25ms later.

 *

 * @param

 *

 * @return

 **************************************************************************************************/

void halProcessKeyInterrupt (void)

{

  bool valid=FALSE;

 

//检查中断源

  if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)  /* Interrupt Flag has been set */

  {

    HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT); /* Clear Interrupt Flag */

    valid = TRUE;

  }

  if (HAL_KEY_SW_7_PXIFG & HAL_KEY_SW_7_BIT)  /* Interrupt Flag has been set */

  {

    HAL_KEY_SW_7_PXIFG = ~(HAL_KEY_SW_7_BIT); /* Clear Interrupt Flag */

    valid = TRUE;

  }

 

  if (HAL_KEY_JOY_MOVE_PXIFG & HAL_KEY_JOY_MOVE_BIT)  /* Interrupt Flag has been set */

  {

    HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT); /* Clear Interrupt Flag */

    valid = TRUE;

  }

 

  if (valid)//添加定时器任务

  {

   osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);//使用定时器定时触发任务

  }

}

不知道你是否还记得这个函数:osal_start_timerEx(),在这个函数中添加了一个定时任务,其中ID号就不用我多说了,HAL_KEY_EVENT是将要被添加到tasksEvents的任务的掩码,HAL_KEY_DEBOUNCE_VALUE则是用来进行消抖的一个时间值。用过单片机的都知道当按键按下时会产生抖动,而通常消抖的方法是延时。所以我们也可以知道在这里产生IO口中断的是按键。如果使用IO口中断来读其它设备则可以根据实际情况读或调用函数osal_set_event()对添加任务。当然并不是每个中断都必须添加对应的任务,如定时器中断。在中断服务函数中是否添加对应的任务应视具体情况而定。

你可能感兴趣的:(zigbee)