运行Zigbee例程Word文档下载推荐.docx
《运行Zigbee例程Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《运行Zigbee例程Word文档下载推荐.docx(14页珍藏版)》请在冰豆网上搜索。
按键操作几乎在每个例程中都会用到,故此处以按键驱动的修改为例,演示HAL的修改。
先了解下Ti和无线龙扩展板的不同之处。
Ti的CC2430EB原理图在Ti文档SWRU133.pdf(位于SWRU133.zip中)。
Page29是按键电路的原理图,如图1
图1(左上角是元件图)
CC2430EB的按键其实是摇杆,上下左右四个方向和电阻网络相连,通过放大电路送到CC2430的P0.6脚,经AD采样后判断摇杆摆向哪个方向,按键编号为SW1~SW4摇杆也可像普通按键一样按下,产生一个直流电平变化,接到P0.5脚,按键编号为SW5。
除此之外,还有一个独立按键连到P0.1脚,按键编号为SW6。
无线龙的开发板则是用六个独立按键,上下左右四个按键和电阻网络相连,接P0.6,由AD采样得出是哪个键被按下。
还有两个按键OK、Cancel分别直接和P0.5、P0.4相连。
由于Ti和无线龙上下左右四个按键的电阻网络有差异,AD采样值有所不同,要予以修改。
还有修改SW5、SW6的读取为的无线龙地OK、Cancel两个按键。
要修改的文件为hal_key.c,要修改的部分宏定义、uint8HalKeyRead()、voidHalKeyPoll()。
修改SW6的引脚定义,行156中的HAL_KEY_BIT1改为HAL_KEY_BIT4
156:
#defineHAL_KEY_SW_6_BITHAL_KEY_BIT4
修改uint8HalKeyRead()中的SW5、SW6有关的内容,注释掉以下语句
#ifdefined(HAL_KEY_SW_6_ENABLE)
if(!
(HAL_KEY_SW_6_PORT&
HAL_KEY_SW_6_BIT))/*Keyisactivelow*/
{
keys|=HAL_KEY_SW_6;
}
#endif
#ifdefined(HAL_KEY_SW_5_ENABLE)
if(HAL_KEY_SW_5_PORT&
HAL_KEY_SW_5_BIT)/*Keyisactivehigh*/
keys|=HAL_KEY_SW_5;
在对应位置添加
if(P0_5==0)
{
keys|=0x04;
}
if(P0_4==0)
keys|=0x20;
修改用于判断哪个方向键被按下的P0.6采样值,do{}while中的条件语句注释掉,取之以下内容
if((adc>
=0x55)&
&
(adc<
=0x70))
ksave0|=HAL_KEY_UP;
elseif((adc>
=0x40)&
=0x50))
ksave0|=HAL_KEY_DOWN;
=0x18)&
=0x30))
ksave0|=HAL_KEY_LEFT;
elseif(adc<
=10)
ksave0|=HAL_KEY_RIGHT;
else
修改voidHalKeyPoll()中的有关的内容,修改同HalKeyRead()。
再把voidHalKeyEnterSleep(void)中所有内容注释掉,将uint8HalKeyExitSleep(void)中的
HAL_KEY_SW_5_INP|=HAL_KEY_SW_5_BIT;
/*Setpininputmodetotri-state*/
注释掉,以上就完成了按键有关Hal修改。
二、Zigbee工程设置
下面将以运行一个工程<
GenericApp>
为例,介绍Zigbee工程设置。
CC2430的开发环境为IAR,相信接触过MSP430的朋友对其不会陌生,有关Zigbee工程的设置实际上就是通过IAR的工程设置完成的。
打开<
Projects/zstack/Samples/GenericApp/CC2430DB>
下的GenericApp.eww。
打开工程后,界面如图2
图2
同一个工程中包含的多个project,根据开发板的类型(EB和DB)、Zigbee网络中的节点类型(Coordinator、Router和EndDevice)一共有六个project,我们只需用到CoordinatorEB和EndDeviceEB这两个project。
先把project选择为CoordinatorEB,在GenericApp-CoordinatorEB上右击,选择Options…进入工程设置界面,选择C/C++Compiler->
Preprocessor,设置预编译项。
去掉LCD_SUPPORTED=DEBUG,HAL_UART在GenericApp中可加可不加,如图3所示
图3
通过条件编译选项设定是否添加相关的硬件驱动和调试选项,去掉LCD_SUPPORTED=DEBUG的原因是Ti的开发板和无线龙开发板的LCD接口不一样,而我们又没有修改相关Hal文件,无法使用LCD显示,加入HAL_UART的原因是我们将使用串口作为调试信息的输出。
三、Zigbee工程启动流程解析
用户相关的应用程序代码是在App类的文件当中,其与Zigbee是如何关联起来的呢?
如何调配处理器时间去处理应用程序、响应外设和进行无线收发呢?
Ti协议栈通过了一个小型的操作系统实现了复杂多样的事件处理。
CC2430具有128KBROM、8KBRAM,如此大的空间为SOC(SystemOnChip)提供了有力保障。
操作系统的API介绍在《OSALAPI_F8W-2003-0002_》中,在这个操作系统中提供了消息管理、任务同步、定时器管理、中断管理、任务管理、内存管理、电院管理和非易失内存管理等功能。
用户的应用程序要以任务(task)的方式注册到系统中,一个task对应于自己的事件处理函数(eventprocessor)。
task的添加,event和task的关联,事件处理函数和对应task的关联将在以下的流程解析中讲解。
1、main()
编译工程(快捷键F7),将模块连接到PC,调试工程(快捷键Ctrl+D)。
进入到函数的入口——main()函数,位于ZMainGroup的ZMain.c中。
在main()中完成了相关硬件和软件的初始化,作为重点,我们要关注的是
190:
osal_init_system();
和
206:
osal_start_system();
//NoReturnfromhere
我们先来看osal_init_system()。
在osal_init_system右击,选择Gotodefinitionofosal_init_system,追踪其定义(注意“追踪”的方法,下文所述的追踪都是指用这个方法)。
2、osal_init_system()
osal_init_system()定义在OSALGroup的OSAL.c中,进行了操作系统相关的初始化工作,需要重点关注的是
927:
osalInitTasks();
追踪其定义。
3、osalInitTasks()和添加task到OS
osalInitTasks()定义在AppGroup的OSAL_GenericApp.c中,函数内容如下
voidosalInitTasks(void)
uint8taskID=0;
tasksEvents=(uint16*)osal_mem_alloc(sizeof(uint16)*tasksCnt);
osal_memset(tasksEvents,0,(sizeof(uint16)*tasksCnt));
macTaskInit(taskID++);
nwk_init(taskID++);
Hal_Init(taskID++);
#ifdefined(MT_TASK)
MT_TaskInit(taskID++);
APS_Init(taskID++);
ZDApp_Init(taskID++);
GenericApp_Init(taskID);
在这个函数中,我们看到了将task添加到系统的过程,每添加一个task,taskID就加1。
最后一句GenericApp_Init()正是关注的重点,追踪它。
4、GenericApp_Init()和注册event到task中
GenericApp_Init()位于GenericApp.c中,完成了endPoint的设定和注册、按键事件的注册,即把相关的enevt和相关的task进行了关联,当发生这些event时,会由和相应task关联的事件处理函数进行处理。
到此,已经了解了如何添加task、如何关联event和task,那么task和事件处理函数的关联又是如何进行的呢?
先找找GenericApp_Init对应的事件处理函数,解决这个问题,要回到ZMain.c中main()里的osal_start_system(),追踪它吧!
5、osal_start_system()和task及事件处理函数的关联
osal_start_system()定义在OSAL.c中。
osal_start_system()中最有可能是执行事件处理函数的就是行977了
977:
events=(tasksArr[idx])(idx,events);
tasksArr是函数数组,idx代表task的id,events代表相应的事件,猜测正确吗,追踪它。
在OSAL_GenericApp.c中找到了tasksArr的定义,内容如下
constpTaskEventHandlerFntasksArr[]={
macEventLoop,
nwk_event_loop,
Hal_ProcessEvent,
MT_ProcessEvent,
APS_event_loop,
ZDApp_event_loop,
GenericApp_ProcessEvent
};
如果追踪里面的数组成员,它们都是事件处理函数,注意,里面的成员顺序和osalInitTasks()中task的添加顺序是一样的,即task的ID和其对应的事件处理函数在tasksArr中的序号是一样的,这样在调用(tasksArr[idx])(idx,events)时,完成了task和对应的事件处理函数的关联。
6、事件处理
上面我们了解了task和对应事件处理函数关联方式,我们重点分析下用户自定义task的事件处理函数GenericApp_ProcessEvent()。
通过行233
233:
if(events&
SYS_EVENT_MSG)
和行300
300:
GENERICAPP_SEND_MSG_EVT)
可知,起码有两类事件:
SYS_EVENT_MSG和GENERICAPP_SEND_MSG_EVT,显然,SYS_EVENT_MSG是协议栈已经定义好的系统事件,而GENERICAPP_SEND_MSG_EVT就是用户自定义的事件了。
事件号是一个16bit的常量,使用独热码(one-hotcode)编码,方便进行event的提取,这样一个task中最多可以有16个event,SYS_EVENT_MSG已经占用了0x8000,故自定义的事件只能有16个。
由于事件号使用独热码,故事件的提取和清除可以用简单的位操作指令实现。
事件提取events&
GENERICAPP_SEND_MSG_EVT,事件清除events^GENERICAPP_SEND_MSG_EVT。
系统事件包函了各种系统消息(message),系统事件中的消息号是一8bit常量,定义在ZComDef.h中,
322:
#defineSPI_INCOMING_ZTOOL_PORT0x21//RawdatafromZToolPort(notimplemented)
323:
#defineSPI_INCOMING_ZAPP_DATA0x22//RawdatafromtheZAPPport(seeserialApp.c)
324:
#defineMT_SYS_APP_MSG0x23//RawdatafromanMTSysmessage
325:
#defineMT_SYS_APP_RSP_MSG0x24//RawdataoutputforanMTSysmessage
326:
327:
#defineAF_DATA_CONFIRM_CMD0xFD//Dataconfirmation
328:
#defineAF_INCOMING_MSG_CMD0x1A//IncomingMSGtypemessage
329:
#defineAF_INCOMING_KVP_CMD0x1B//IncomingKVPtypemessage
330:
#defineAF_INCOMING_GRP_KVP_CMD0x1C//IncomingGroupKVPtypemessage
331:
332:
#defineKEY_CHANGE0xC0//KeyEvents
333:
334:
#defineZDO_NEW_DSTADDR0xD0//ZDOhasreceivedanewDstAddrforthisapp
335:
#defineZDO_STATE_CHANGE0xD1//ZDOhaschangedthedevice'
snetworkstate
336:
#defineZDO_MATCH_DESC_RSP_SENT0xD2//ZDOmatchdescriptorresponsewassent
337:
#defineZDO_CB_MSG0xD3//ZDOincomingmessagecallback
用户可以自定义系统事件的消息范围为0xE0~0xFF,现只针对SYS_EVENT_MSG中的两个处理函数进行说明。
AF_INCOMING_MSG_CMD:
当模块接收到属于自己的无线数据信息时,就会触发AF_INCOMING_MSG_CMD消息,由相关的处理函数处理接收到的信息。
ZDO_STATE_CHANGE:
当网络状态改变时就会触发此消息,其在这个工程中的作用是,当节点建立网络(作为协调器)、加入网络(作为终端)时,运行
osal_start_timerEx(GenericApp_TaskID,
GENERICAPP_SEND_MSG_EVT,
GENERICAPP_SEND_MSG_TIMEOUT);
osal_start_timerEx()的作用是启动一系统定时器,当其溢出(GENERICAPP_SEND_MSG_TIMEOUT)时,会触发task(GenericApp_TaskID)的事件(GENERICAPP_SEND_MSG_EVT)。
又看到事件GENERICAPP_SEND_MSG_EVT了,它是用户定义的事件,分析之。
if(events&
//Send"
the"
message
GenericApp_SendTheMessage();
//Setuptosendmessageagain
osal_start_timerEx(GenericApp_TaskID,
//returnunprocessedevents
return(events^GENERICAPP_SEND_MSG_EVT);
以上就是GENERICAPP_SEND_MSG_EVT的处理函数,先分析总的流程,GenericApp_SendTheMessage()发送信息,osal_start_timerEx()重新启动一系统定时器,同样是指向task(GenericApp_TaskID)的事件(GENERICAPP_SEND_MSG_EVT),返回时要注意清除当前事件(events^GENERICAPP_SEND_MSG_EVT),否则会反复处理同一个事件,陷入死循环。
四、Zigbee应用模型
对数据进行收发还需对Zigbee的应用模型有所了解。
Profile和Endpoint是Zigbee应用层中有关应用模型的,概念,先给出这两个概念的官方的定义:
ApplicationProfilesareanagreementonaseriesofmessagesdefininganapplicationspace(forexample,“HomeAutomation”or“SmartEnergy”)
EndpointsarealogicalextensionaddedtoasingleZigBeeradiowhichpermitssupportformultipleapplications,addressedbytheEndpointnumber(1-240)
KeyRelationships:
Maximumof240EndpointsperZigBeeDevice(Endpoint0isreservedtodescribethegenericdevicecapabilitiesandEndpoint255isreservedforbroadcastingtoallendpoints,Endpoints241-254arereservedforfutureuse)
OneApplicationProfiledescribedperEndpoint。
我个人的理解是:
某个具体的应用(application)的相关操作被定义成一个profile,profile和应用层的结合通过endpoint来实现,一个zigbee设备有240个endpoint。
在GenericApp_Init(bytetask_id)中有以下相关的定义
GenericApp_epDesc.endPoint=GENERICAPP_ENDPOINT;
GenericApp_epDesc.task_id=&
GenericApp_TaskID;
GenericApp_epDesc.simpleDesc
=(SimpleDescriptionFormat_t*)&
GenericApp_SimpleDesc;
GenericApp_epDesc.latencyReq=noLatencyReqs;
追踪相关的变量可知使用类型endPointDesc_t描述endpoint的类型,其定义在AF.h中
typedefstruct
byteendPoint;
byte*task_id;
//PointertolocationoftheApplicationtaskID.
SimpleDescriptionFormat_t*simpleDesc;
afNetworkLatencyReq_tlatencyReq;
}endPointDesc_t;
需要进一步了解的是SimpleDescriptionFormat_t,其定义也在AF.h中
byteEndPoint;
uint16AppProfId;
uint16AppDeviceId;
byteAppDevVer:
4;
byteReserved:
//AF_V1_SUPPORTusesforAppFlags:
4.
byteAppNumInClusters;
cId_t*pAppInClusterList;
byteAppNumOutClusters;
cId_t*pAppOutClusterList;
}SimpleDescriptionFormat_t;
里面有AppProfId,即为ProfileId,可以从这个结构中看到,profile中有In、Out两种类型的Cluster,Cluster此处可理解为操作,每类Cluster对应着一个ClusterList,list中的每个成员对应一种操作,GenericApp的GenericApp_SimpleDesc为
constSimpleDescriptionFormat_tGenericApp_SimpleDesc=
GENERICAPP_ENDPOINT,//intEndpoint;
GENERICAPP_PROFID,//uint16AppProfId[2];
GENERICAPP_DEVICEID,//uint16AppDeviceId[2];
GENERICAPP_DEVICE_VERSION,//intAppDevVer:
GENERICAPP_FLAGS,//intAppFlags:
GENERICAPP_MAX_CLUSTERS