CC2530 ZIGBEE自组网模块如何使用?

CC2530 ZIGBEE自组网模块如何使用?,第1张

CC2530无线模块是采用TI公司的CC2530芯片制作的模块,一般是工作在2.4G。信阳宝惠电子有限公司生产的CC2530无线模块比较稳定。也有加功率放大的。支持ZIGBEE组网。

主要特点如下:

1. 发射功率2.5mW(4dBm);接收灵敏度 -97dBm (BER=10-2)

2. 开放频段,无需申请频点,载频频率2.4GHz。

3. 空中传输速率高达250kbps, 提供16 个信道,根据环境自动选择可靠信道通信

4. 视距情况下,可靠传输距离可达150 米。加PA的距离更大。

5. 功耗:接收电流≤27mA,发射电流≤40mA。

6. 体积小、重量轻, 尺寸28×21mm。

7. 采用SoC,外围电路少,采用全贴片优质晶振, 可靠性高,故障率低。

8. 和用户PCB连接提供插针和贴片两用连接方式, PCB天线和IPEX外接两用天线连接方式

{ osal_pwrmgr_powerconserve()// Put the processor/system into sleep } #endif } } //临界区资源管理 先看一个临界区代码保护的例子: HAL_ENTER_CRITICAL_SECTION(intState)events = activeTask->eventsactiveTask->events = 0//清楚任务的事件 HAL_EXIT_CRITICAL_SECTION(intState)其中:中断宏定义如下 #define HAL_ENABLE_INTERRUPTS() st( EA = 1) #define HAL_DISABLE_INTERRUPTS() st( EA = 0) #define HAL_INTERRUPTS_ARE_ENABLED() (EA) typedef unsigned char halIntState_t#define HAL_ENTER_CRITICAL_SECTION(x) st( x = EAHAL_DISABLE_INTERRUPTS()) #define HAL_EXIT_CRITICAL_SECTION(x) st( EA = x) #define HAL_CRITICAL_STATEMENT(x) st( halIntState_t sHAL_ENTER_CRITICAL_SECTION(s)xHAL_EXIT_CRITICAL_SECTION(s)) 以及相关的st 宏: #define st(x) do { x } while (__LINE__ == -1) (1)cc2430 芯片中的中断使能的特殊功能寄存器(SFRs):IEN0,IEN1 和IEN2,(见 cc2430 datasheet: P49)。这三个寄存器的不同的位控制了不同的硬件的中断使能,比如IEN2 中的第五位WDTIE 控制着看门狗时钟的中断使能。这其中有一个比较特殊的位是IEN0 的第7 位,名称为EA,控制着所有中断的使能,为0 时 将没有中断相应,为1 时每一个中断源的使能受相应的位的控制。上面的宏即是用芯片的EA=0 来关中断实现临界资源的保护。 (2)set 宏定义如下,表示执行x 指令,注意x 是一个完整的语句,需要加分号。 #define st(x) do { x } while (__LINE__ == -1) 而整个宏的定义结束时没有分号,而是在最后的应用时加的分号,如: HAL_ENTER_CRITICAL_SECTION(intState)(3)HAL_ENABLE_INTERRUPTS()和HAL_DISABLE_INTERRUPTS()这两个宏分别实现了cc2430 的所有中断的开和关。HAL_ENTER_CRITICAL_SECTION(x)宏首先将 EA 的值保存在变量x 中,然后关闭所有中断,进行后面的临街资源处理。 HAL_EXIT_CRITICAL_SECTION(x)宏则是回复刚才保存在x 中的EA 的值。 HAL_CRITICAL_STATEMENT(x)宏的功能是将x 作为临界代码执行,首先声明了用于保存EA 值的变量,然后调用进入临界区宏,执行临界代码x,最后执行退出临界区的宏。 (4)注意HAL_CRITICAL_STATEMENT(x)这个宏,因为st 宏的实现中x 是一些可以执行的完整c 语句,更主要的是写在do{}while()中,它值一个子的程序片段,因此x 可以做很多事,比如声明变量等。否则你会奇怪,这样定义宏在宏展开的时候如果使用多个这个宏,会不会出现重复定义(HAL_CRITICAL_STATEMENT(x) 实现代码中的halIntState_t s),会不会出现在程序的中间来定义变量(c语言要求要使用的变量需在最前面定义)等问题。其实这些问题是不会出现的,真是因为HAL_CRITICAL_STATEMENT(x)的x 的执行在do-while 中的do 子句中。 下面是一个类似的验证例子程序: #include #define st(x) do{x}while(__LINE__==-1) #define enable() st(EA = 1) //使能所有中断 #define disable() st(EA = 0) //关闭所有中断 #define enter(x) st(x = EAdisable()) //进入临界区 #define exit(x) st(EA = x) //退出临界区 //简写临界代码的执行 #define critical(s) st(int tempenter(temp)sexit(temp)) //模拟控制所有中断的变量 int EA = 5int main() { int aenter(a)printf("EA=%d, a=%d\n",EA,a)exit(a)//验证多次执行宏不会出现重复定义变量的问题 critical(printf("hello world-first\n"))critical(printf("hello world-second\n"))//上面的critical(printf("hello world-first\n"))展开后的等价代码 do { int tempdo{ temp = EAdo{ EA = 0}while(__LINE__==-1)}while(__LINE__==-1)printf("hello world\n")do{ EA =temp}while(__LINE__==-1)}while(__LINE__==-1)//验证在子模块中可以再次声明变量 { int a = 12printf("%d\n",a){ int a = 89printf("%d\n",a)} } return 0} 执行结果为: EA=0, a=5 hello world-first hello world-second hello world 12 89 PS: (1)c 程序中的各个宏定义的顺序任意。 (1)c 程序中要求变量需先定义所有要使用的变量,然后才使用,是对用一个层次模块来说,在子层次中可以遵循这个规则再次定义变量。一个花括号中的括起来的内容{...}可以看作一个子模块。 Zstack 设置发送功率(CC2530) 在mac_radio.c 中找到macRadioSetTxPower 函数,其具体内容复制如下: #ifndef HAL_MAC_USE_REGISTER_POWER_VALUES MAC_INTERNAL_API void macRadioSetTxPower(uint8 txPower) { halIntState_t s#if defined MAC_RUNTIME_CC2591 || defined MAC_RUNTIME_CC2590 const uint8 CODE *pTable = macRadioDefsTxPwrTables[macRadioDefsRefTableId >>4]#elif defined HAL_PA_LNA || defined HAL_PA_LNA_CC2590 const uint8 CODE *pTable = macRadioDefsTxPwrTables[0]#else const uint8 CODE *pTable = macRadioDefsTxPwrBare//该table 中含有txPower 的设置值,将该table 的首地址赋给指针pTable, //macRadioDefsTxPwrBare[]定义在mac_radio_defs.c 中 #endif if ((int8)txPower >(int8)pTable[MAC_RADIO_DEFS_TBL_TXPWR_FIRST_ENTRY]) { txPower = pTable[MAC_RADIO_DEFS_TBL_TXPWR_FIRST_ENTRY]//发送功率上限值 } else if ((int8)txPower <(int8)pTable[MAC_RADIO_DEFS_TBL_TXPWR_LAST_ENTRY]) { txPower = pTable[MAC_RADIO_DEFS_TBL_TXPWR_LAST_ENTRY]//发送功率下限值 } HAL_ENTER_CRITICAL_SECTION(s){ uint8 index = pTable[MAC_RADIO_DEFS_TBL_TXPWR_FIRST_ENTRY] - txPower + MAC_RADIO_DEFS_TBL_TXPWR_ENTRIESreqTxPower = pTable[index]} //通过计算转换查表index,得到发送功率值,将其赋给reqTxPower, //函数macRadioUpdateTxPower 中用于更新发送功率 HAL_EXIT_CRITICAL_SECTION(s)macRadioUpdateTxPower()} #else MAC_INTERNAL_API void macRadioSetTxPower(uint8 txPower) {//直接获得reqTxPower halIntState_t sHAL_ENTER_CRITICAL_SECTION(s)reqTxPower = txPowerHAL_EXIT_CRITICAL_SECTION(s)macRadioUpdateTxPower()} 下面了解一下macRadioUpdateTxPower 函数,其函数体如下: MAC_INTERNAL_API void macRadioUpdateTxPower(void) { halIntState_t sHAL_ENTER_CRITICAL_SECTION(s)//进入临界区 if (reqTxPower != macPhyTxPower)//macPhyTxPower 即为当前实际的发送功率 { if (!macRxOutgoingAckFlag &&!MAC_TX_IS_PHYSICALLY_ACTIVE()) //当有发送任务正在进行时,不能改变发送功率。 //当前的发送任务完成后,将重新调用该函数进行发送功率设置。 { macPhyTxPower = reqTxPowerMAC_RADIO_SET_TX_POWER(macPhyTxPower)//设置寄存器TXPOWER 为 macPhyTxPower,即reqTxPower } } HAL_EXIT_CRITICAL_SECTION(s)//离开临界区 } 通过上面的函数,我们根据自己的需要,适当对发送功率进行设置。 关于ZStack-CC2530-2.3.0-1.4.0 中simpleApp 例子的组网(一) 所有的C 语言编写的程序,入口函数一定是main 函数,首先看一下ZMain.c 函数。 int main( void ) { osal_int_disable( INTS_ALL )HAL_BOARD_INIT()zmain_vdd_check()InitBoard( OB_COLD )HalDriverInit()osal_nv_init( NULL )ZMacInit()zmain_ext_addr()zgInit()#ifndef NONWK afInit()#endif osal_init_system()osal_int_enable( INTS_ALL )InitBoard( OB_READY )zmain_dev_info()#ifdef LCD_SUPPORTED zmain_lcd_init()#endif #ifdef WDT_IN_PM1 WatchDogEnable( WDTIMX )#endif osal_start_system()return 0} 主函数要做的事情非常简单,首先进行了一些初始化,包括各层的初始化,硬件初始化,以及任务的初始化等,然后就进入到 *** 作系统当中,即 osal_start_system()就再也出不来了。 *** 作系统的作用就是如果有事件发生,就把这个消息通知给处理该事件的事件处理函数去执行,然后一直的循环查找有没有事件发生。另外说一下,事件是定义在任务当中的,即一个任务可能有多个事件,每个任务对应一个事件处理函数。在这个程序中,一共有6 个任务,有兴趣的同学可以自己查看void osalInitTasks( void )函数的代码。 接下来看一下zigbee 协调器是怎么建立网络的 首先我们必须选择SimpleCollectorEB 版本,在APP 文件下看到sapi.c 源文件。找到SAPI_Init(byte task_id)函数,此函数是进行sapi 层的初始化,代码如下 void SAPI_Init( byte task_id ) { sapi_TaskID = task_id//将 *** 作系统初始化任务时定义的任务id 号传进来 sapi_bindInProgress = 0xffff//设置不允许绑定 sapi_epDesc.task_id = &sapi_TaskID//给端口描述符的任务ID 号赋值,感觉也就是端口收到的数据或者消息就交给ID 号指定的任务来处理。 sapi_epDesc.endPoint = 0//端口描述符端口号初始化为0。 #if ( SAPI_CB_FUNC )//编译通过 sapi_epDesc.endPoint = zb_SimpleDesc.EndPoint//端口号赋值 sapi_epDesc.task_id = &sapi_TaskID//任务ID 赋值,与上面的任务ID 的值是相同的。 sapi_epDesc.simpleDesc = (SimpleDescriptionFormat_t *)&zb_SimpleDesc//简单描述符赋值,是描述一个端口最基本的信息 sapi_epDesc.latencyReq = noLatencyReqs//这是一个枚举类型的,不清楚具体含义,不过大家都设成noLatencyReqs,除此之外还有两个值。 afRegister( &sapi_epDesc )//将定义的端点在AF 层注册,一定要注册后端点才会生效 #endif afSetMatch(sapi_epDesc.simpleDesc->EndPoint, FALSE)//设置描述符不能匹配 // Register callback evetns from the ZDApp ZDO_RegisterForZDOMsg( sapi_TaskID, NWK_addr_rsp )//在sapi 层注册网络地址事件,这个函数可以截取空中发来的消息,有兴趣的可以查查资料,第一个函数是截取的消息发到哪个任务中去,第二个参数,cluserID 是消息的类型。 ZDO_RegisterForZDOMsg( sapi_TaskID, Match_Desc_rsp )//同理,在sapi 层注册匹配描述符事件。 #if ( SAPI_CB_FUNC ) #if (defined HAL_KEY) &&(HAL_KEY == TRUE) // Register for HAL events RegisterForKeys( sapi_TaskID )//注册按键响应事件 if ( HalKeyRead () == HAL_KEY_SW_5) { uint8 startOptions = ZCD_STARTOPT_CLEAR_STATE | ZCD_STARTOPT_CLEAR_CONFIGzb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions )zb_SystemReset()} #endif // HAL_KEY osal_set_event(task_id, ZB_ENTRY_EVENT)//在这里设置了一个进入事件,第一个参数是task_id 是任务的ID 号,因此我们可以在sapi 层的事件处理函数中找到这个进入事件是怎么处理的。 #endif } 在UINT16 SAPI_ProcessEvent( byte task_id, UINT16 events )函数中,找到 if ( events &ZB_ENTRY_EVENT ) { uint8 startOptions// Give indication to application of device startup #if ( SAPI_CB_FUNC ) zb_HandleOsalEvent( ZB_ENTRY_EVENT )#endif // LED off cancels HOLD_AUTO_START blink set in the stack HalLedSet (HAL_LED_4, HAL_LED_MODE_OFF)//为了方便观察实验现象,将第四个灯关闭。 zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions )if ( startOptions &ZCD_STARTOPT_AUTO_START ) { zb_StartRequest()} else { // blink leds and wait for external input to config and restart HalLedBlink(HAL_LED_2, 0, 50, 500)} return (events ^ ZB_ENTRY_EVENT )} 这个时候,程序就停在这里,只能看到LED_2 在闪烁。这个时候我们可以按下按键1,然后去找一下,事件处理函数中如何对此事件进行相应。找到UINT16 SAPI_ProcessEvent( byte task_id, UINT16 events )函数中的关于按键的处理: case KEY_CHANGE: #if ( SAPI_CB_FUNC ) zb_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys )#endif break进入到HandleKeys 函数中,找到 if ( keys &HAL_KEY_SW_1 ) { if ( myAppState == APP_INIT ) { zb_ReadConfiguration( ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType )//读取flash 中的设备类型 if ( logicalType != ZG_DEVICETYPE_ENDDEVICE ) { logicalType = ZG_DEVICETYPE_COORDINATOR//将设备类型改变为协调器类型 zb_WriteConfiguration(ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType)//将设备类型写入到flash 中 } zb_ReadConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions )//读取启动模式 startOptions = ZCD_STARTOPT_AUTO_START//将启动模式赋值为自动启动模式 zb_WriteConfiguration( ZCD_NV_STARTUP_OPTION, sizeof(uint8), &startOptions )//将启动模式存入到flash 中 zb_SystemReset()//系统重启设置。这时候flash 中,启动模式就变成了ZCD_STARTOPT_AUTO_START,系统重启以后,系统继续刚才讲过的过程,不同的是在进行到进入事件处理函数时,if ( startOptions &ZCD_STARTOPT_AUTO_START )的值成立,调用if 语句里面的zb_StartRequest() 函数即执行开始请求函数。 } 接下来进入到zb_StartRequest 函数中看一看,原代码如下: void zb_StartRequest() { uint8 logicalTypezb_ReadConfiguration( ZCD_NV_LOGICAL_TYPE, sizeof(uint8), &logicalType )//从flash 中读出设备类型。 // Check for bad combinations of compile flag definitions and device type setting. if ((logicalType >ZG_DEVICETYPE_ENDDEVICE) || //可以判断if 里面语句为0,执行else #if !ZG_BUILD_ENDDEVICE_TYPE // Only RTR or Coord possible. (logicalType == ZG_DEVICETYPE_ENDDEVICE) || #endif #if !ZG_BUILD_RTR_TYPE // Only End Device possible. (logicalType == ZG_DEVICETYPE_ROUTER) || (logicalType == ZG_DEVICETYPE_COORDINATOR) || #elif ZG_BUILD_RTRONLY_TYPE // Only RTR possible. (logicalType == ZG_DEVICETYPE_COORDINATOR) || #elif !ZG_BUILD_JOINING_TYPE // Only Coord possible. (logicalType == ZG_DEVICETYPE_ROUTER) || #endif (0)) { logicalType = ZB_INVALID_PARAMETERSAPI_SendCback(SAPICB_START_CNF, logicalType, 0)} else { logicalType = ZB_SUCCESS//将设备类型改为ZB_SUCCESS ZDOInitDevice(zgStartDelay)// 执行初始化设备函数。 } return} 那么接下来去初始化设备函数中看一下,右键,go to definition uint8 ZDOInitDevice( uint16 startDelay ) { uint8 networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE//首先更改了网络状态为初始化新网络状态 uint16 extendedDelay = 0if ( devState == DEV_HOLD ) { zgInitItems( FALSE )} ZDConfig_InitDescriptors()_NIB.CapabilityInfo = ZDO_Config_Node_Descriptor.CapabilityFlagsdevState = DEV_INIT// 设备状态改为初始化 ZDApp_LeaveCtrlInit()//离开控制初始化 ZDApp_LeaveCtrlStartup( &devState, &startDelay )//检查离开控制时的一些设置 if ( devState == DEV_HOLD ) { zgWriteStartupOptions( ZG_STARTUP_SET, ZCD_STARTOPT_DEFAULT_NETWORK_STATE )osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT )return ( ZDO_INITDEV_LEAVE_NOT_STARTED )} #if defined ( NV_RESTORE ) //NV_RESTORE 编译不通过,NV_RESTORE 主要是设置掉电再重新上电,参数是否保留 if ( HalKeyRead() == SW_BYPASS_NV ) networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATEelse { networkStateNV = ZDApp_ReadNetworkRestoreState()} if ( networkStateNV == ZDO_INITDEV_RESTORED_NETWORK_STATE ) { networkStateNV = ZDApp_RestoreNetworkState()} else { NLME_InitNV()NLME_SetDefaultNV()} #endif if ( networkStateNV == ZDO_INITDEV_NEW_NETWORK_STATE ) { ZDAppDetermineDeviceType()//确定设备类型 extendedDelay = (uint16)((NWK_START_DELAY + startDelay) + (osal_rand() &EXTENDED_JOINING_RANDOM_MASK))} ZDApp_SecInit( networkStateNV )// 初始化设备对象的安全 *** 作 ZDApp_NetworkInit( extendedDelay )//进行网络的初始化 NLME_SetBroadcastFilter( ZDO_Config_Node_Descriptor.CapabilityFlags )//网络层函数,代码看不到,看函数名字是设置广播地址掩码来支持广播过滤。不太明白这个。。。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/11301214.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-15
下一篇 2023-05-15

发表评论

登录后才能评论

评论列表(0条)

保存