ZIGBEE通讯 您所在的位置:网站首页 p0ien寄存器 ZIGBEE通讯

ZIGBEE通讯

#ZIGBEE通讯| 来源: 网络整理| 查看: 265

在ZIGBEE协议栈中已经自带了按键与LED的驱动与使用函数,所以只需要将按键与LED修改为使用的开发板所连接IO就可以使用了。接下来将主要分析在协议栈中按键的初始化、按键的检测以及按键事件的传递与处理。按键流程分析过后,着手于无线数据传输,而协议栈已经写好了无线广播,只需要直接调用就可以使用了。

1、修改LED灯IO

由于协议栈中按键与LED所配置IO口与使用的开发板不同,所以需要对按键LED的IO口进行修改。

在协议栈ZMain.c文件中的main函数中找到函数HAL_BOARD_INIT(),右键go to进入该函数,可以看到关于LED的配置,由于本实验使用的开发板只有两个LED,所以我们只需要修改LED1、LED2的IO口就可以了。同样的,利用右键找到LED底层定义的地方(文件)hal_board_cfg.h中,将其修改如下:

/* 1 - Green */ #define LED1_BV BV(3) // BV(0)改为BV(3) #define LED1_SBIT P1_3 // P1_0改为P1_3 #define LED1_DDR P1DIR #define LED1_POLARITY ACTIVE_HIGH/* 2 - Red */ #define LED2_BV BV(2) // BV(1)改为BV(2) #define LED2_SBIT P1_2 // P1_1改为P1_2 #define LED2_DDR P1DIR #define LED2_POLARITY ACTIVE_HIGH

LED的开与关也需要进行修改,原理同不带协议栈的无线点灯实验。在函数HAL_BOARD_INIT()中,右键进入HAL_TURN_OFF_LED1()中,可以找到两个LED的开关定义,修改如下:

#define HAL_TURN_OFF_LED1() st( LED1_SBIT = LED1_POLARITY (1); ) // 括号中0改为1 #define HAL_TURN_OFF_LED2() st( LED2_SBIT = LED2_POLARITY (1); ) // 括号中0改为1 #define HAL_TURN_OFF_LED3() st( LED3_SBIT = LED3_POLARITY (0); ) #define HAL_TURN_OFF_LED4() HAL_TURN_OFF_LED1()#define HAL_TURN_ON_LED1() st( LED1_SBIT = LED1_POLARITY (0); ) // 括号中1改为0 #define HAL_TURN_ON_LED2() st( LED2_SBIT = LED2_POLARITY (0); ) // 括号中1改为0 #define HAL_TURN_ON_LED3() st( LED3_SBIT = LED3_POLARITY (1); ) #define HAL_TURN_ON_LED4() HAL_TURN_ON_LED1()

2、按键IO修改与流程分析

在main函数中找到函数HalDriverInit(),右键进入其中,可以看到有很多外设的初始化,其他的在这里我们都不关心,我们只关注按键KEY,找到KEY的初始化函数右键进入其中,KEY_SW_6即为接下来我们要用的按键 ,JOY为摇杆按键,我们用不到,不用关心或者注释掉。右键进入到HAL_KEY_SW_6_SEL定义的地方,进行如下修改:

/* SW_6 is at P1.1 */ #define HAL_KEY_SW_6_PORT P1 // P0改为P1 #define HAL_KEY_SW_6_BIT BV(1) #define HAL_KEY_SW_6_SEL P1SEL // P0SEL改为P1SEL #define HAL_KEY_SW_6_DIR P1DIR // P0DIR改为P1DIR /* SW_6 interrupts */ #define HAL_KEY_SW_6_IEN IEN2 // IEN1改为IEN2 #define HAL_KEY_SW_6_IENBIT BV(5) // 5改为4 IEN2第4位P1中断 #define HAL_KEY_SW_6_ICTL P1IEN // P0IEN改为P1IEN #define HAL_KEY_SW_6_ICTLBIT BV(1) /* P1EN – P1.1 enable/disable bit */ #define HAL_KEY_SW_6_PXIFG P1IFG /* Interrupt flag at source */

按键初始化后, InitBoard( OB_READY )用来配置按键触发方式以及按键的回调函数,如下:

/********************************************************************** @fn InitBoard()* @brief Initialize the CC2420DB Board Peripherals* @param level: COLD,WARM,READY* @return None*/ void InitBoard( uint8 level ) {if ( level == OB_COLD ){// IAR does not zero-out this byte below the XSTACK.*(uint8 *)0x0 = 0;// Interrupts offosal_int_disable( INTS_ALL );// Check for Brown-Out resetChkReset();}else // !OB_COLD{/* Initialize Key stuff */// 配置为按键中断不使能,扫描触发 回调函数HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback);} }

进入HalKeyConfig函数中,根据所传参数HAL_KEY_INTERRUPT_DISABLE,中断不使能的话,就会配置为定时检测,这个时候会触发HAL_KEY_EVENT事件:

else /* Interrupts NOT enabled */{HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); /* don't generate interrupt */HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT); /* Clear interrupt enable bit */osal_set_event(Hal_TaskID, HAL_KEY_EVENT); // 触发HAL_KEY_EVENT事件}

这个事件将会在HAL层事件处理函数中被处理,在OSAL_SampleApp.c文件中找到HAL层事件处理函数Hal_ProcessEvent,进入其中,可以看到这样一个判断:

if (events & HAL_KEY_EVENT) // HAL_KEY_EVENT事件{#if (defined HAL_KEY) && (HAL_KEY == TRUE)/* Check for keys */HalKeyPoll(); // 检测按键/* if interrupt disabled, do next polling */if (!Hal_KeyIntEnable){osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);} #endif // HAL_KEYreturn events ^ HAL_KEY_EVENT;}

如果判断成功,就会调用HalKeyPoll()对按键进行扫描,进入这个扫描函数,函数前一部分都是跟摇杆按键有关的,可以直接删除或者注释,或者将下部分对KEY_SW_6按键的判断放在前面,不然会影响按键的检测,如下:

/*************************************************************************************************** @fn HalKeyPoll** @brief Called by hal_driver to poll the keys** @param None** @return None**************************************************************************************************/ void HalKeyPoll (void) {uint8 keys = 0;if (HAL_PUSH_BUTTON1()) // 按键检测判断{keys |= HAL_KEY_SW_6;}/* Invoke Callback if new keys were depressed */if (keys && (pHalKeyProcessFunction)){(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL); // 调用回调函数} }

由于官方板按键按下去是高电平,所以检测if判断里读取按键状态的函数需要进行修改,右键goto之后,将PUSH1_POLARITY的定义以及PUSH1_SBIT的定义进行修改,如下:

/* S1 */ #define PUSH1_BV BV(1) #define PUSH1_SBIT P1_1 // P0_1 改为 P1_1 #define PUSH1_POLARITY ACTIVE_LOW // ACTIVE_HIGH改为ACTIVE_LOW

最后函数调用按键回调函数将按键检测值传进去,又回到了一开始初始化时配置的回调函数。在回调函数OnBoard_KeyCallback()中,我们并不会在这里对按键事件进行处理,而是通过函数OnBoard_SendKeys( keys, shift )将检测值又传递到了其他地方,如下:

/********************************************************************** @fn OnBoard_SendKeys** @brief Send "Key Pressed" message to application.** @param keys - keys that were pressed* state - shifted** @return status*********************************************************************/ uint8 OnBoard_SendKeys( uint8 keys, uint8 state ) {keyChange_t *msgPtr;if ( registeredKeysTaskID != NO_TASK_ID ){// Send the address to the taskmsgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) );if ( msgPtr ){msgPtr->hdr.event = KEY_CHANGE; // 按键事件msgPtr->state = state;msgPtr->keys = keys;osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr ); // 把事件发往绑定的任务}return ( ZSuccess );}elsereturn ( ZFailure ); }

在这个函数里,又将按键检测值标记为KEY_CHANGE事件,通过osal_msg_send()函数将事件打包发给了registeredKeysTaskID任务,registeredKeysTaskID为任务号,通过RegisterForKeys()进行注册,注册在APP层里面,现在我们去APP层的初始化任务中查找一下,可以通过osal_init_system()函数找到osalInitTasks()函数,在任务初始化函数中调用了APP层的初始化函数,在SampleApp_Init()中调用了RegisterForKeys(),把按键事件绑定在了APP层,如下:

// Register for all key events - This app will handle all key eventsRegisterForKeys( SampleApp_TaskID ); // 这句代码在SampleApp_Init()中

所以按键事件最后会到达APP层,被APP层事件处理函数处理,如下图:

 最后如果事件判断成功,就会调用SampleApp_HandleKeys()函数,在这个函数中,我们就可以对按键事件作出相应,比如接下来我们要做的,通过调用无线发送函数,发送一个数据,如下:

void SampleApp_HandleKeys( uint8 shift, uint8 keys ) {(void)shift; // Intentionally unreferenced parameterif(keys & HAL_KEY_SW_6){SampleApp_SendPeriodicMessage(); // 广播发送一个数据} }

在广播发送函数中,将会发送出去一个0,如下:

void SampleApp_SendPeriodicMessage( void ) {if ( AF_DataRequest( &SampleApp_Periodic_DstAddr, &SampleApp_epDesc,SAMPLEAPP_PERIODIC_CLUSTERID, // 数据类型ID 现在为表示广播1, // 发送数据长度(uint8*)&SampleAppPeriodicCounter, // 发送数据的地址 变量值为0&SampleApp_TransID,AF_DISCV_ROUTE,AF_DEFAULT_RADIUS ) == afStatus_SUCCESS ){}else{// Error occurred in request to send.} }

3、LED灯响应

发送部分完成之后,再来看接收部分。小灯响应部分比较简单,首先需要关注数据的来源,来源就是无线广播接收,这个事件的处理也在APP层事件处理函数中,如图:

 在事件处理函数中,会调用SampleApp_MessageMSGCB( MSGpkt )函数对接到的数据包进行处理,判断如果接到的数据为0,即为接收正确,就可以让小灯响应了,如下:

void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt ) {uint16 flashTime;switch ( pkt->clusterId ){case SAMPLEAPP_PERIODIC_CLUSTERID: // 判断为广播数据if( *pkt->cmd.Data == 0) // 从数据包中找到用户数据{HalLedSet(1,HAL_LED_MODE_TOGGLE); // 切换LED的状态}break;case SAMPLEAPP_FLASH_CLUSTERID:flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] );HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) );break;} }

从数据包中找到用户数据有必要说明一下,首先函数传进来的参数类型为afIncomingMSGPacket_t,这个结构体内存放了所有的数据,如下:

typedef struct {osal_event_hdr_t hdr; /* OSAL Message header */uint16 groupId; /* Message's group ID - 0 if not set */uint16 clusterId; /* Message's cluster ID */afAddrType_t srcAddr; /* Source Address, if endpoint is STUBAPS_INTER_PAN_EP,it's an InterPAN message */uint16 macDestAddr; /* MAC header destination short address */uint8 endPoint; /* destination endpoint */uint8 wasBroadcast; /* TRUE if network destination was a broadcast address */uint8 LinkQuality; /* The link quality of the received data frame */uint8 correlation; /* The raw correlation value of the received data frame */int8 rssi; /* The received RF power in units dBm */uint8 SecurityUse; /* deprecated */uint32 timestamp; /* receipt timestamp from MAC */uint8 nwkSeqNum; /* network header frame sequence number */afMSGCommandFormat_t cmd; /* Application Data 用户数据*/ } afIncomingMSGPacket_t;

这个结构体里面又包含了一个结构体afMSGCommandFormat_t,这个结构体用于存放用户数据,如下:

typedef struct {uint8 TransSeqNumber;uint16 DataLength; // Number of bytes in TransDatauint8 *Data; // 数据 } afMSGCommandFormat_t;

而我们通过无线发送的数据就在以Data为首地址的内存中。

实验完成,将程序分别下载到协调器和终端节点。点击协调器的按键,终端节点的LED状态会随之切换。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有