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驱动。


 

你可能感兴趣的:(z-stack)