Z-STACK之cc2530LED驅動詳解

                                               Z-STACK 之LED驅動詳解
     最近一段時間學習ZigBee,用的TI公司的cc2530,協議棧是z-stack,爲了深入瞭解整個Z-stack,我從底層的驅動代碼開始看起,首先是LED驅動。雖然是簡單的LED亮滅,但是z-stack中的LED驅動卻寫的非常好,在這給我們提供了很好的寫驅動的思路。

         首先看一下hal_led.h頭文件中的一些宏定義,其中定義了四個LED,分別是HAL_LED_1,HAL_LED_2,HAL_LED_3,HAL_LED_4,分別對應板子上的Green,Red,Yellow,Blue四個LED,然後定義了五種LED模式,HAL_LED_MODE_OFF,燈滅,HAL_LED_MODE_ON,燈亮,HAL_LED_MODE_BLINK,燈閃爍,HAL_LED_MODE_FLASH,燈閃亮,在這裏,當調用HalLedSet (uint8 leds, uint8 mode)這個函數就會看到HAL_LED_MODE_BLINK和HAL_LED_MODE_FLASH的區別。HAL_LED_MODE_TOGGLE,燈狀態切換。還定義了LED的一些默認參數,HAL_LED_DEFAULT_MAX_LEDS,LED最大個數爲4個,HAL_LED_DEFAULT_DUTY_CYCLE,LED閃爍的默認亮滅的佔空比,HAL_LED_DEFAULT_FLASH_COUNT,默認的閃爍次數,HAL_LED_DEFAULT_FLASH_TIME,默認的閃爍一次的時間爲1000,這個是相對osal_systemClock這個系統tick的,具體單位時間是多少還要看用於tick的定時器,先不管,以後再分析。

          再看hal_led.c這個源文件,其中定義了HalLedControl_t的結構體,這個結構體主要是對led控制的一些參數,

typedef struct {
  uint8 mode;       /* Operation mode */
  uint8 todo;       /* Blink cycles left */
  uint8 onPct;      /* On cycle percentage */
  uint16 time;      /* On/off cycle time (msec) */
  uint32 next;      /* Time for next change */
} HalLedControl_t;

看這個結構體,mode爲led的操作模式,就是頭文件中定義的那5種mode,todo即爲LED剩餘閃爍的次數,onPct爲LED閃爍的亮的時間所佔的比例,time即爲LED每一次閃爍的時間,next爲LED作出下一次轉變的時間,其單位是相對於系統的tick即osal_systemClock這個系統變量。然後定義了HalLedStatus_t這個結構體,其中HalLedControl_t HalLedControlTable[HAL_LED_DEFAULT_MAX_LEDS]是對四個LED的控制情況的數組,sleepActive是指在系統睡眠時候是否啓用LED,如果是則爲true,否則爲false。接下來看HalLedState這個全局變量記錄的是LED上次更新時候的狀態,通過後面的代碼,我的理解是HalLedState的低四位分別表示四個LED,如果某一位爲1,則它上次的狀態爲ON,否則爲OFF。HalSleepLedState這個全局變量主要是記錄了系統進入睡眠模式之前的四個LED的狀態。preBlinkState這個全局變量記錄了LED在進入閃爍模式之前的狀態。然後定義了這個HalLedStatus_t結構體的全局變量HalLedStatusControl記錄四個LED的控制狀態。

       HalLedInit這個函數式LED的初始化,調用HalLedSet (HAL_LED_ALL, HAL_LED_MODE_OFF)將四個LED置OFF,然後HalLedStatusControl.sleepActive = FALSE即在睡眠狀態下不啓用LED;下面看下HalLedSet這個驅動函數:

uint8 HalLedSet (uint8 leds, uint8 mode)
{

#if (defined (BLINK_LEDS)) && (HAL_LED == TRUE)
  uint8 led;
  HalLedControl_t *sts;

  switch (mode)
  {
    case HAL_LED_MODE_BLINK:
      /* Default blink, 1 time, D% duty cycle */
      HalLedBlink (leds, 1, HAL_LED_DEFAULT_DUTY_CYCLE, HAL_LED_DEFAULT_FLASH_TIME);
      break;

    case HAL_LED_MODE_FLASH:
      /* Default flash, N times, D% duty cycle */
      HalLedBlink (leds, HAL_LED_DEFAULT_FLASH_COUNT, HAL_LED_DEFAULT_DUTY_CYCLE, HAL_LED_DEFAULT_FLASH_TIME);
      break;

    case HAL_LED_MODE_ON:
    case HAL_LED_MODE_OFF:
    case HAL_LED_MODE_TOGGLE:

      led = HAL_LED_1;
      leds &= HAL_LED_ALL;
      sts = HalLedStatusControl.HalLedControlTable;

      while (leds)
      {
        if (leds & led)
        {
          if (mode != HAL_LED_MODE_TOGGLE)
          {
            sts->mode = mode;  /* ON or OFF */
          }
          else
          {
            sts->mode ^= HAL_LED_MODE_ON;  /* Toggle */
          }
          HalLedOnOff (led, sts->mode);
          leds ^= led;  //上一個led處理完進入下一個
        }
        led <<= 1;
        sts++;
      }
      break;

    default:
      break;
  }

#elif (HAL_LED == TRUE)
  HalLedOnOff(leds, mode);
#else
  // HAL LED is disabled, suppress unused argument warnings
  (void) leds;
  (void) mode;
#endif /* BLINK_LEDS && HAL_LED   */

  return ( HalLedState );

}

在這個函數中描述瞭如何根據不同的mode實現對LED的控制,如果爲mode爲HAL_LED_MODE_BLINK或HAL_LED_MODE_FLASH,則相應調用HalLedBlink這個函數,不同的模式傳進不同的參數。當然這個參數值是默認的。當mode爲HAL_LED_MODE_ON、HAL_LED_MODE_OFF、HAL_LED_MODE_TOGGLE之一時,程序裏面用一個while循環對四個LED進行相應的處理,其中leds ^= led表示上一個led已經處理完將其清零下次不再處理。在後面的led <<= 1; sts++;兩句代碼根據四個LED對應不同的低四位看,應該很容易明白。最後函數返回HalLedState即led的當前狀態,在上面函數中調用了HalLedOnOff函數,該函數在最後

if (mode)
  {
    HalLedState |= leds;
  }
  else
  {
    HalLedState &= (leds ^ 0xFF);
  }

即如果mode不爲OFF時,則HalLedState將記錄leds爲ON,否則將leds中的某位置0,即爲OFF狀態。

回到HalLedSet這個函數,在mode爲HAL_LED_MODE_BLINK或HAL_LED_MODE_FLASH時調用了HalLedBlink這個函數,下面看看它的實現代碼

void HalLedBlink (uint8 leds, uint8 numBlinks, uint8 percent, uint16 period)
{
#if (defined (BLINK_LEDS)) && (HAL_LED == TRUE)
  uint8 led;
  HalLedControl_t *sts;

  if (leds && percent && period)
  {
    if (percent < 100)
    {
      led = HAL_LED_1;
      leds &= HAL_LED_ALL;
      sts = HalLedStatusControl.HalLedControlTable;

      while (leds)
      {
        if (leds & led)
        {
          /* Store the current state of the led before going to blinking */
          preBlinkState |= (led & HalLedState);

          sts->mode  = HAL_LED_MODE_OFF;                    /* Stop previous blink */
          sts->time  = period;                              /* Time for one on/off cycle */
          sts->onPct = percent;                             /* % of cycle LED is on */
          sts->todo  = numBlinks;                           /* Number of blink cycles */
          if (!numBlinks) sts->mode |= HAL_LED_MODE_FLASH;  /* Continuous */
          sts->next = osal_GetSystemClock();                /* Start now */
          sts->mode |= HAL_LED_MODE_BLINK;                  /* Enable blinking */
          leds ^= led;
        }
        led <<= 1;
        sts++;
      }
      osal_set_event (Hal_TaskID, HAL_LED_BLINK_EVENT);
    }
    else
    {
      HalLedSet (leds, HAL_LED_MODE_ON);                    /* >= 100%, turn on */
    }
  }
  else
  {
    HalLedSet (leds, HAL_LED_MODE_OFF);                     /* No on time, turn off */
  }
#elif (HAL_LED == TRUE)
  percent = (leds & HalLedState) ? HAL_LED_MODE_OFF : HAL_LED_MODE_ON;
  HalLedOnOff (leds, percent);                              /* Toggle */
#else
  // HAL LED is disabled, suppress unused argument warnings
  (void) leds;
  (void) numBlinks;
  (void) percent;
  (void) period;
#endif /* BLINK_LEDS && HAL_LED */
}

這個函數中當percent小於100時先Store the current state of the led before going to blinking,將其保存在preBlinkState中,然後分別對sts指針變量中四個元素賦值,如果numBlinks爲0,則表示LED持續不停的閃爍。將

sts->next賦值爲osal_GetSystemClock(),即系統的tick值,後面會看到這個next的具體含義。最後調用osal_set_event這個函數將HAL_LED_BLINK_EVENT事件傳給Hal_TaskID任務,當系統輪詢任務時,HAL層任務則有事件要處理。在hal_drivers.c文件中Hal_ProcessEvent,即HAL層的事件處理函數,

if ( events & HAL_LED_BLINK_EVENT )
  {
#if (defined (BLINK_LEDS)) && (HAL_LED == TRUE)
    HalLedUpdate();
#endif /* BLINK_LEDS && HAL_LED */
    return events ^ HAL_LED_BLINK_EVENT;
  }

看這裏的代碼知道,當有LEDblink事件時調用了HalLedUpdate()這個函數,這個函數在hal_led.c文件裏面,

void HalLedUpdate (void)
{
  uint8 led;
  uint8 pct;
  uint8 leds;
  HalLedControl_t *sts;
  uint32 time;
  uint16 next;
  uint16 wait;

  next = 0;
  led  = HAL_LED_1;
  leds = HAL_LED_ALL;
  sts = HalLedStatusControl.HalLedControlTable;

  /* Check if sleep is active or not */
  if (!HalLedStatusControl.sleepActive)
  {
    while (leds)
    {
      if (leds & led)
      {
        if (sts->mode & HAL_LED_MODE_BLINK)
        {
          time = osal_GetSystemClock();
          if (time >= sts->next)//到了該閃爍的時間了
          {
            if (sts->mode & HAL_LED_MODE_ON)
            {
              pct = 100 - sts->onPct;               /* Percentage of cycle for off */
              sts->mode &= ~HAL_LED_MODE_ON;        /* Say it's not on */
              HalLedOnOff (led, HAL_LED_MODE_OFF);  /* Turn it off */

              if (!(sts->mode & HAL_LED_MODE_FLASH)) //不是無限閃爍
              {
                sts->todo--;                        /* Not continuous, reduce count */
                if (!sts->todo)                     //閃爍次數到
                {
                  sts->mode ^= HAL_LED_MODE_BLINK;  /* No more blinks */
                }
              }
            }
            else
            {
              pct = sts->onPct;                     /* Percentage of cycle for on */
              sts->mode |= HAL_LED_MODE_ON;         /* Say it's on */
              HalLedOnOff (led, HAL_LED_MODE_ON);   /* Turn it on */
            }

            if (sts->mode & HAL_LED_MODE_BLINK)
            {
              wait = (((uint32)pct * (uint32)sts->time) / 100);
              sts->next = time + wait;
            }
            else
            {
              /* no more blink, no more wait */
              wait = 0;
              /* After blinking, set the LED back to the state before it blinks */
              HalLedSet (led, ((preBlinkState & led)!=0)?HAL_LED_MODE_ON:HAL_LED_MODE_OFF);
              /* Clear the saved bit */
              preBlinkState &= (led ^ 0xFF);
            }
          }
          else
          {
            wait = sts->next - time;  /* Time left */
          }

          if (!next || ( wait && (wait < next) ))
          {
            next = wait;
          }
        }
        leds ^= led;
      }
      led <<= 1;
      sts++;
    }

    if (next)
    {
      osal_start_timerEx(Hal_TaskID, HAL_LED_BLINK_EVENT, next);   /* Schedule event */
    }
  }
}

這個函數只要理解了結構體中幾個元素變量的含義,其實很好理解啦!當LED的mode爲HAL_LED_MODE_BLINK時,首先time = osal_GetSystemClock();獲取系統的tick值,然後比較time和sts->next,如果前者大則表明已經到了LED該閃爍的時間了,此時如果mode爲ON(這裏面有個技巧,即每次LED閃爍之後mode中記錄的都是上次閃爍的LED的狀態),則將mode置爲OFF,然後調用HalLedOnOff將LED置OFF,如果sts->mode不爲HAL_LED_MODE_FLASH,則LED不是無限閃爍模式,即有numBlinks次數的閃爍,在上一個函數中可以看到

if (!numBlinks) sts->mode |= HAL_LED_MODE_FLASH;這句,即numBlinks爲0時,將其mode增加HAL_LED_MODE_FLASH,這就說明要無限閃爍了。看這句

else
            {
              pct = sts->onPct;                     /* Percentage of cycle for on */
              sts->mode |= HAL_LED_MODE_ON;         /* Say it's on */
              HalLedOnOff (led, HAL_LED_MODE_ON);   /* Turn it on */
            }

 即當mode爲HAL_LED_MODE_OFF時,調整pct爲ON的比例,然後mode反轉,亮LED,如果mode中還有HAL_LED_MODE_BLINK,則說明LED還沒有閃爍完,還要接着閃,此時將wait賦值爲(((uint32)pct * (uint32)sts->time);sts->next = time + wait;wait爲下一次要閃爍時候的等待時間,next爲下一次要閃爍的tick值;如果mode中沒有

HAL_LED_MODE_BLINK則說明LED已經閃爍完了,將wait清零,然後調用HalLedSet將LED置爲閃爍之前的狀態。

如果time小於sts->next則閃爍的時間還沒到還要繼續等待,wait = sts->next - time;將等待時間wait調整。

if (!next || ( wait && (wait < next) ))
          {
            next = wait;
          }

如果next爲0則說明是第一次閃爍,則將next置爲wait值,如果( wait && (wait < next) )則是某一次閃爍且閃爍時間沒到則重新將next賦值爲wait。

  如果next不爲0即還需要下次的閃爍,此時調用osal_start_timerEx函數,此函數在經過next時間之後會向Hal_TaskID任務發送HAL_LED_BLINK_EVENT事件,LED需要繼續閃爍。

    以上是整個LED驅動的詳細解析過程。函數HalLedEnterSleep和HalLedExitSleep很少用到,是在系統睡眠的時候用到,這裏不需要解釋。 z-stack的LED主要是用來在組建ZigBee網絡時能通過觀察LED的狀態從而回到網絡的狀態,如LED閃爍時候,協調器還沒有組建網絡,當組建網絡之後,LED不閃爍,而是某一個常亮。在以後的例程中我再仔細分析這個過程。下一次介紹一下key驅動。


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章