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之cc2530LED驱动详解相关推荐
- 使用VS2010编译MongoDB C++驱动详解
最近为了解决IM消息记录的高速度写入.多文档类型支持的需求,决定使用MongoDB来解决. 考虑到MongoDB对VS版本要求较高,与我现有的VS版本不兼容,在leveldb.ssdb.redis.h ...
- torch.stack(), torch.cat()用法详解
torch.stack(), torch.cat()用法详解 if __name__ == '__main__':import torchx_dat = torch.tensor([[1, 2], [ ...
- Pixhawk(PX4)之驱动详解篇(0)_前期准备(招贤令)
Pixhawk(PX4)之驱动详解篇(0)_前期准备(招贤令) 原创 2017年03月01日 22:58:39 标签: 开发人员 / UAV / 软件 / 硬件 一.开篇 开源精神常在!!! 谁说软件 ...
- MTK 驱动(64)---Mtk touch panel驱动/TP驱动详解
Mtk touch panel驱动/TP驱动详解 TP还算是比LCM好理解的多. 在启动过程中,先注册/mediatek/custom/command/kernel/touchpanel目录下的具体驱 ...
- imx6ul 驱动详解
链表的知识: struct list_head {struct list_head *next, *prev; }; API函数 函数 功能 LIST_HEAD 声明并初始化双向链表. INIT_LI ...
- linux usb gadget驱动详解(一)
由于PC的推广,USB(通用串行总线)是我们最熟知的通信总线规范之一,其他的还有诸如以太网.PCIE总线和RS232串口等.这里我们主要讨论USB. USB是一个主从通信架构,但只能一主多从.其中us ...
- 博通wifi驱动详解
1 WLAN技术 WLAN是英文WirelessLAN的缩写,就是无线局域网的意思.无线以太网技术是一种基于无线传输的局域网技术,与有线网络技术相比,具有灵活.建网迅速.个人化等特点.将 ...
- LCD液晶屏驱动详解
开发环境: 开发板:JZ2440V3 CPU:samsunS3C2440 内核:Linux3.4.2 编译工具:arm-linux-gcc 4.3.2 LCD:4.3存液晶屏AT043TN24 参考文 ...
- nvme 驱动详解[转]
nvme 驱动详解 之1 http://blog.csdn.net/qqqqqq999999/article/details/47732319 首先打开driver/block下的kconfig文件, ...
最新文章
- CVPR 2021 | 利用时序差分进行动作识别的最新Backbone—TDN
- 关于数据库的增删改查
- java设计模式---三种工厂模式之间的区别
- SQL Server 2005中更改sa的用户名和密码
- Vue.js 服务端渲染
- Spark算子总结版
- java远程debug
- 如何把Python脚本导出为exe程序
- JEECG 树列表操作总刷新列表,需要重新展开问题 【官方补丁,适用所有版本】
- 大数据之-Hadoop之HDFS的API操作_定位读取文件_只读取某个block的内容_案例---大数据之hadoop工作笔记0065
- 作为参数给后端为空_后端 API 接口文档 Swagger 使用指南
- 关于TCP/IP,必知必会的十个问题
- 关于Asp.Net中的返回的操作
- 超全面!Autodesk Maya重安装时显示已安装的解决办法
- Namesilo域名注册流程
- 学习 Tornado
- 54、记录yolov7 训练、部署ncnn、部署mnn、部署rk3399 npu、部署openvino、部署oak vpu、部署TensorRT
- 一个牛人在美国的跳槽经历(转)
- 如何用Python量化“相似K线”实现形态选股?
- 8bit音乐的一些相关知识
热门文章
- 盗版不需要考虑,直接企业版(Enterprise)走起?
- 操作系统——可变分区空闲空间管理
- 病毒下载器利用搜索引擎广告推广,推装超过30款软件
- 钻石闪耀天体,但我还是忘不了-孔卡
- 再来聊聊Redis到底是什么?
- UOS(Deepin V20)体验
- 2022.11.27 第10次周报
- mSystems | 中农汪杰组揭示影响土壤“塑料际”微生物群落的机制
- 贝壳云php源码,贝壳云P1刷Armbian系统后的折腾笔记
- Android adb 环境变量配置的坑 adb 不是内部或外部命令