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之cc2530LED驱动详解相关推荐

  1. 使用VS2010编译MongoDB C++驱动详解

    最近为了解决IM消息记录的高速度写入.多文档类型支持的需求,决定使用MongoDB来解决. 考虑到MongoDB对VS版本要求较高,与我现有的VS版本不兼容,在leveldb.ssdb.redis.h ...

  2. torch.stack(), torch.cat()用法详解

    torch.stack(), torch.cat()用法详解 if __name__ == '__main__':import torchx_dat = torch.tensor([[1, 2], [ ...

  3. Pixhawk(PX4)之驱动详解篇(0)_前期准备(招贤令)

    Pixhawk(PX4)之驱动详解篇(0)_前期准备(招贤令) 原创 2017年03月01日 22:58:39 标签: 开发人员 / UAV / 软件 / 硬件 一.开篇 开源精神常在!!! 谁说软件 ...

  4. MTK 驱动(64)---Mtk touch panel驱动/TP驱动详解

    Mtk touch panel驱动/TP驱动详解 TP还算是比LCM好理解的多. 在启动过程中,先注册/mediatek/custom/command/kernel/touchpanel目录下的具体驱 ...

  5. imx6ul 驱动详解

    链表的知识: struct list_head {struct list_head *next, *prev; }; API函数 函数 功能 LIST_HEAD 声明并初始化双向链表. INIT_LI ...

  6. linux usb gadget驱动详解(一)

    由于PC的推广,USB(通用串行总线)是我们最熟知的通信总线规范之一,其他的还有诸如以太网.PCIE总线和RS232串口等.这里我们主要讨论USB. USB是一个主从通信架构,但只能一主多从.其中us ...

  7. 博通wifi驱动详解

    1        WLAN技术 WLAN是英文WirelessLAN的缩写,就是无线局域网的意思.无线以太网技术是一种基于无线传输的局域网技术,与有线网络技术相比,具有灵活.建网迅速.个人化等特点.将 ...

  8. LCD液晶屏驱动详解

    开发环境: 开发板:JZ2440V3 CPU:samsunS3C2440 内核:Linux3.4.2 编译工具:arm-linux-gcc 4.3.2 LCD:4.3存液晶屏AT043TN24 参考文 ...

  9. nvme 驱动详解[转]

    nvme 驱动详解 之1 http://blog.csdn.net/qqqqqq999999/article/details/47732319 首先打开driver/block下的kconfig文件, ...

最新文章

  1. CVPR 2021 | 利用时序差分进行动作识别的最新Backbone—TDN
  2. 关于数据库的增删改查
  3. java设计模式---三种工厂模式之间的区别
  4. SQL Server 2005中更改sa的用户名和密码
  5. Vue.js 服务端渲染
  6. Spark算子总结版
  7. java远程debug
  8. 如何把Python脚本导出为exe程序
  9. JEECG 树列表操作总刷新列表,需要重新展开问题 【官方补丁,适用所有版本】
  10. 大数据之-Hadoop之HDFS的API操作_定位读取文件_只读取某个block的内容_案例---大数据之hadoop工作笔记0065
  11. 作为参数给后端为空_后端 API 接口文档 Swagger 使用指南
  12. 关于TCP/IP,必知必会的十个问题
  13. 关于Asp.Net中的返回的操作
  14. 超全面!Autodesk Maya重安装时显示已安装的解决办法
  15. Namesilo域名注册流程
  16. 学习 Tornado
  17. 54、记录yolov7 训练、部署ncnn、部署mnn、部署rk3399 npu、部署openvino、部署oak vpu、部署TensorRT
  18. 一个牛人在美国的跳槽经历(转)
  19. 如何用Python量化“相似K线”实现形态选股?
  20. 8bit音乐的一些相关知识

热门文章

  1. 盗版不需要考虑,直接企业版(Enterprise)走起?
  2. 操作系统——可变分区空闲空间管理
  3. 病毒下载器利用搜索引擎广告推广,推装超过30款软件
  4. 钻石闪耀天体,但我还是忘不了-孔卡
  5. 再来聊聊Redis到底是什么?
  6. UOS(Deepin V20)体验
  7. 2022.11.27 第10次周报
  8. mSystems | 中农汪杰组揭示影响土壤“塑料际”微生物群落的机制
  9. 贝壳云php源码,贝壳云P1刷Armbian系统后的折腾笔记
  10. Android adb 环境变量配置的坑 adb 不是内部或外部命令