前言

悔恨,总之就是十分悔恨,我过后到底是为啥才会猪油蒙心,抉择了 EFR32 来学习 ZigBee 应用啊?

EFR32 这玩意看性能的确不错,然而材料太少了,EmberZnet SDK 也是用得一头雾水。能找到的教程和例子根本是都是管制一下LED ,配置入网啥的,具体的波及罕用的ADC,I2C什么的材料太难找了,SDK 外面也没有找到相似demo的货色,总之就是十分苦楚。

这里给大家分享一些好货色!EFR32和EFM32 十分全面的驱动示例 demo 这玩意救我狗命啊!国内不晓得为啥都没有人分享这么好的玩意,找到了下载竟然还要钱!这里就分享给大家吧。

https://github.com/SiliconLab...
超级实用的 EFR32 demo !

硬件筹备

我应用的是画时科技的 ZDB-01 是 silicon EFR32MG21 的开发板。
传感器用了以前的 DFRobot 电容式土壤湿度传感器模块
因为第一次接触 ZigBee 我没有什么 ZigBee 的网关和上位机啥的,一开始我还蛮头疼,而后我发现精灵一号就有 ZigBee 网关性能,这玩意还真是不便啊,万万没想到之前买的精灵一号还能在这时候帮上忙。

然而笑死,官网又没有提供开发调试工具,还得本人写。

软件筹备

EFR32 入网流程能够参考我上一篇文章《手把手带你应用ZigBee——通过爱智管制EFR32,以及 Simplicity Studio 应用过程中注意事项》这里就不赘述了。

土壤湿度传感器 的输入是模拟量所以须要在 Simplicity Studio 的 Defaultmode Peripherals 中增加并配置 IADC

不晓得是我 IDE 问题还是啥,主动生成的 SDK 中生成的 IADC 库文件不全,短少 IADC.c 文件,而且 IADC.h 有问题。须要咱们本人增加一下 IADC.c 和 IADC.h 文件,这两个文件的下载地址:

https://github.com/ryankurte/...

将下载下来的 IADC.c 放入我的项目文件夹的 emlib 文件夹下:

而后在 IDE 中 Refresh 一下:

而 IADC.h 尽管存在,然而有问题,无奈通过编译,须要替换成新的 IADC.h ,网上大部分教程都倡议不要批改 SDK
而抉择 Make a Copy

然而通过我亲测,在这里我倡议大家抉择 Edit in SDK ,因为抉择 Make a Copy 的话会报错(尽管不影响编译),提醒某些符号无奈解析,可能是呈现了反复定义的状况,而且这个 SDK 中的文件就是有问题的,保留也没有意义,不如间接替换成新的文件。

代码剖析

这个代码是基于官网 demo 根底上批改而来。
为了不便解说逻辑,我会打乱代码的程序可能还会进行裁剪,要是想间接拿代码跑的敌人能够间接去 灵感桌面的机密宝库 获取代码,或者间接 clone:

https://gitee.com/inspiration...

头文件与初始化配置

#include "app/framework/include/af.h"#include "em_device.h"#include "em_chip.h"#include "em_cmu.h"#include "em_iadc.h"#include "em_gpio.h"// Set CLK_ADC to 10MHz#define CLK_SRC_ADC_FREQ          20000000 // CLK_SRC_ADC#define CLK_ADC_FREQ              10000000 // CLK_ADC - 10MHz max in normal mode/* * Specify the IADC input using the IADC_PosInput_t typedef.  This * must be paired with a corresponding macro definition that allocates * the corresponding ABUS to the IADC.  These are... * * GPIO->ABUSALLOC |= GPIO_ABUSALLOC_AEVEN0_ADC0 * GPIO->ABUSALLOC |= GPIO_ABUSALLOC_AODD0_ADC0 * GPIO->BBUSALLOC |= GPIO_BBUSALLOC_BEVEN0_ADC0 * GPIO->BBUSALLOC |= GPIO_BBUSALLOC_BODD0_ADC0 * GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDEVEN0_ADC0 * GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDODD0_ADC0 * * ...for port A, port B, and port C/D pins, even and odd, respectively. */#define IADC_INPUT_0_PORT_PIN     iadcPosInputPortBPin0;     //  配置输出引脚#define IADC_INPUT_1_PORT_PIN     iadcNegInputPortBPin1;     #define IADC_INPUT_0_BUS          BBUSALLOC                  //  配置总线#define IADC_INPUT_0_BUSALLOC     GPIO_BBUSALLOC_BEVEN0_ADC0#define IADC_INPUT_1_BUS          BBUSALLOC#define IADC_INPUT_1_BUSALLOC     GPIO_BBUSALLOC_BODD0_ADC0/******************************************************************************* ***************************   GLOBAL VARIABLES   ******************************* ******************************************************************************/static volatile uint32_t sample;const float AirValue = 465;                       // 初始化最大干燥 (传感器在地面的状况)这个数据每个传感器不一样,须要本人测试const float WaterValue = 1177;                    // 初始化最大湿度 (传感器放入水中的状况)EmberEventControl AcoinfoAioReportEventControl;   // 申明事件

设置上电打印与上电初始化 IADC

void emberAfMainInitCallback(void){    emberAfCorePrintln("---------------灵感桌面---------------");    // 初始化 IADC    initIADC();}

设置按按钮入网

void emberAfHalButtonIsrCallback(uint8_t button, uint8_t state){  if (state == BUTTON_RELEASED) {      emberAfPluginNetworkSteeringStart();  }}

初始化 IADC ,我比拟奇怪的一点,在下面 Defaultmode Peripherals 的时候就曾经配置过 IADC 了,为什么在这里还须要配置?之前尝试 LED 的时候就不须要。(我试过了,不从新初始化 IADC 是不能用的)

void initIADC (void){      // 初始化构造体申明      IADC_Init_t init = IADC_INIT_DEFAULT;      IADC_AllConfigs_t initAllConfigs = IADC_ALLCONFIGS_DEFAULT;      IADC_InitSingle_t initSingle = IADC_INITSINGLE_DEFAULT;      IADC_SingleInput_t initSingleInput = IADC_SINGLEINPUT_DEFAULT;      // 重置IADC以重置配置,以防它已被其余代码批改      IADC_reset(IADC0);      // 为IADC抉择时钟      CMU_ClockSelectSet(cmuClock_IADCCLK, cmuSelect_FSRCO);  // FSRCO - 20MHz      // 批改init构造体并初始化此处设置HFSCLK预设值      init.srcClkPrescale = IADC_calcSrcClkPrescale(IADC0, CLK_SRC_ADC_FREQ, 0);////      // 默认状况下,扫描和单个转换都应用配置0。应用无缓冲AVDD(供电电压为mV)作为参考      initAllConfigs.configs[0].reference = iadcCfgReferenceVddx;      initAllConfigs.configs[0].vRef = 3300;////      // 除以CLK_SRC_ADC,设置CLK_ADC频率      initAllConfigs.configs[0].adcClkPrescale = IADC_calcAdcClkPrescale(IADC0,                                                 CLK_ADC_FREQ,                                                 0,                                                 iadcCfgModeNormal,                                                 init.srcClkPrescale);////      // 将引脚调配到差分模式下的正输出      initSingleInput.posInput   = IADC_INPUT_0_PORT_PIN;      // 负输出      initSingleInput.negInput   = IADC_INPUT_1_PORT_PIN;////      // 初始化 IADC      IADC_init(IADC0, &init, &initAllConfigs);////      // 初始化Single转换输出      IADC_initSingle(IADC0, &initSingle, &initSingleInput);      // 为ADC0输出调配模仿总线      GPIO->IADC_INPUT_0_BUS |= IADC_INPUT_0_BUSALLOC;      GPIO->IADC_INPUT_1_BUS |= IADC_INPUT_1_BUSALLOC;}

我尝试通过 aio 命令触发 aio 回调函数从而获取 aio 输入,然而失败了,不晓得为什么我报文发过来,板子也收到了,然而就是没方法触发 aio 的回调函数,然而 dio 命令的回调却是失常的,于是我在这取了个巧,通过 EFR32 的事件机制躲避了这个问题。

通过发送 dio 命令触发 dio 函数的回调函数,而后在dio 回调函数中激活事件,调用事件函数获取 传感器数据而后通过 aio通道发送给精灵一号。

这是 dio 函数的回调函数,在这激活事件

void emberAfOnOffClusterServerAttributeChangedCallback(int8u endpoint,                                                       EmberAfAttributeId attributeId){    EmberAfStatus status;    uint8_t data[1];    emberAfCorePrintln("---------------LED---------------");    emberAfCorePrintln("attributeId:%x",attributeId);      status = emAfReadAttribute(endpoint,                                 ZCL_ON_OFF_CLUSTER_ID,                                 attributeId,                                 0x40,                                 0x0000,                                 data,                                 1,                                 NULL);      if (status == EMBER_ZCL_STATUS_SUCCESS) {          if(attributeId == ZCL_ACOINFO_ZB_DIO_ATTR_1_ATTRIBUTE_ID){               //激活事件               emberEventControlSetActive(AcoinfoAioReportEventControl);           }      }}

这是事件处理函数,在这里获取到 IADC 数据,并且发送到精灵一号

void AcoinfoAioReportEventHandler(void){    // 在下次应用之前禁用该事件    emberEventControlSetInactive(AcoinfoAioReportEventControl);////    // 开始转换 IADC    IADC_command(IADC0,iadcCmdStartSingle);    // Wait for conversion to be complete    while((IADC0->STATUS & (_IADC_STATUS_CONVERTING_MASK                | _IADC_STATUS_SINGLEFIFODV_MASK)) != IADC_STATUS_SINGLEFIFODV); //while combined status bits 8 & 6 don't equal 1 and 0 respectively    sample = IADC_pullSingleFifoResult(IADC0).data;    emberAfCorePrintln("sample:%d",sample);    float data = 100 - (((sample - AirValue)/(WaterValue - AirValue))*100);    if(data > 100)    {        data = 100;    } else if(data < 0)    {        data = 0;    }    emberAfCorePrintln("data:%d",data);    uint8_t * p_data = (uint8_t *)&data;    uint8_t buf[7] = {0};    buf[0] = ZCL_ACOINFO_ZB_AIO_ATTR_1_ATTRIBUTE_ID && 0xFF;    buf[1] = ZCL_ACOINFO_ZB_AIO_ATTR_1_ATTRIBUTE_ID >> 8;    buf[2] = ZCL_FLOAT_SINGLE_ATTRIBUTE_TYPE;    for(int i=0;i<4;i++){        buf[3+i] = *p_data++;    }    emberAfFillCommandGlobalServerToClientReportAttributes(ZCL_ACOINFO_ZB_AIO_CLUSTER_ID,                                                           (uint8_t *)buf, 7);    emberAfSetCommandEndpoints(1, 1);    emberAfSendCommandUnicast(EMBER_OUTGOING_DIRECT, 0x0000);    // 提早 5 秒后从新触发事件//    emberEventControlSetDelayMS(AcoinfoAioReportEventControl, 5000);//    // 结尾处重置回未激活状态    emberEventControlSetInactive(AcoinfoAioReportEventControl);}

总结

土壤湿度传感器的 ZigBee 版本就实现了,不过不晓得什么起因,这块 EFR32 板子和精灵一号的相性极差,设施非常容易掉线,而且重连很慢,板子断电后想要从新连上也是十分艰难的事件。不晓得是什么状况。然而好歹是胜利了