文章目录
- 前言
- 硬件准备
- 软件准备
- 代码分析
- 总结
前言
,总之就是,当时为什么我选择了猪油蒙心? EFR32 来学习 ZigBee 使用啊? 
EFR32 性能真的很好,但是信息太少了,EmberZnet SDK 也很迷茫。能找到的教程和例子基本都是控制的。LED ,配置入网,具体涉及常用ADC,I2C什么的资料太难找了,SDK 里面没有类似的demo总之,很痛苦。
和大家分享一些好东西!EFR32和EFM32 非常全面的驱动示例 demo 这东西救了我狗的命!我不知道为什么中国没有人分享这么好的东西。当我找到它下载时,我需要钱!让我在这里与你分享。
https://github.com/SiliconLabs/peripheral_examples/tree/master/series2 超级实用的 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/efm32-base/blob/master/emlib/src/em_iadc.c
下载 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-desktop/DEV-lib-arduino.git
头文件和初始配置
#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;
}
我尝试通过 命令触发 回调函数从而获取 输出,但是失败了,不知道为什么我报文发过去,板子也收到了,但是就是没办法触发 的回调函数,但是 命令的回调却是正常的,于是我在这取了个巧,通过 EFR32 的事件机制规避了这个问题。
通过发送 命令触发 函数的回调函数,然后在 回调函数中激活事件,调用事件函数获取 传感器数据然后通过 通道发送给精灵一号。
这是 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 板子和精灵一号的相性极差,设备非常容易掉线,而且重连很慢,板子断电后想要重新连上也是非常困难的事情。不知道是什么情况。但是好歹是成功了