
《协议栈ZStack协议栈基础和数据传输实验》.doc
10页花了好久写的...感觉还不错的呢...如果看,请细看...Mua~Z-Stack协议栈基础和数据传输实验一、实验目的 终端节点将数据无线发送到协调器,协调器通过串口将数据发送到PC端,并在屏幕上显示出来串口优化把有线串口传输改为无线蓝牙传输 二、实验平台 硬件:2个zigbee节点,1个编译器,1根方口转USB数据线,一个蓝牙模块 软件:实验基于SampleApp工程进行 三、实验步骤1. 串口初始化代码2. 发送部分代码3. 接收部分代码 四、协议栈基础 做实验之前先了解一点关于协议栈的基础知识吧~ 什么是协议栈?我们知道使用Zigbee一般都要进行组网、传输数据可想而知其中的代码数量是非常庞大的,如果我们每次使用zigbee都需要自己写所以代码的话,会非常麻烦因此就有了协议栈可以说它是一个小型的操作系统,把很多通信、组网之类的代码都封装起来了我们要做的只是通过调用函数来实现我们的目的 来看一下协议栈的工作流程图(图1)然后我会对照流程图对协议栈进行简单的分析 图1 我们就从流程图的“开始”开始分析吧~ 打开工程文件SampleApp,main函数是程序执行的开始,我们要先找到它。
Main函数在ZMAin文件夹的ZMain.c下,打开它,找到main函数 main(); 浏览一下main函数可以看到一开始都是各种初始化函数,即对应流程图中的“各种初始化函数”初始化中我们需要注意的是“osal_init_system();”初始化操作系统函数等一下会对它进行说明继续看下去,“osal_start_system();”这是执行操作系统函数,对应流程中的“运行操作系统”注意这个函数进去之后是不会再返回的总结main函数就是初始化和执行操作系统两个部分 我们再来分析一下“osal_init_system();”这个函数,它的功能是初始化操作系统我们go to definition看一下这个函数的代码 osal_init_system(); 浏览这个函数我们可以看到其中依旧是各种初始化函数重点观察“osalInitTasks();”这个函数,函数功能是初始化任务系统,继续go to definition,查看该函数 osalInitTask(); 通过注释我们可以知道这个函数也是拿来初始化的,可以里面的代码有点难以理解......这里我们需要先知道一点,后面会提到,这里先说明下。
额,因为这个是我自己的理解,所以部分描述起来可能不是很专业,能懂这个意思就好了,以后专业起来了再回来修改......协议栈采用任务机制,然后使用轮询的方式处理任务就是说在空闲的时候它从优先级高的任务开始,一个个检查是否有任务要处理,有则处理这个任务,没有则继续循环检测 好嘞~就是这样!那么再来看这个函数,它的作用就是按“任务”的优先级给它们发一个ID号,发的同时呢又对这个任务进行初始化需要注意的是任务优先级越高,它的ID号越小!然后上面那些我们全都不用考虑,需要考虑的是最后两个函数(原来我们能操作的优先级最低呀......)嗯...感觉go to definition好久了...就不继续看下去啦,之后再详细解读这两个函数吧~ 这样子初始化的函数算是解释完了,我们回到main函数,继续看下一个函数“osal_start_system();”执行操作系统函数!来来来,继续go to definition找到它本尊 osal_start_system(); 嗯哼,找到“osal_run_system();”我们继续...... osal_run_system(); 这里就是我之前说的轮询的地方啦~这里就说下我的理解吧......但是不确定对不对......大致思想应该是对的...... 先把工作分成两部分,一部分是任务请求,有任务请求了就把相应标志位置1。
另一部分就是我们看到的这个函数在函数开头读一下任务请求的寄存器(也许不是寄存器,就那个意思),然后从最高优先级依次检索是不是有任务请求只要有任务请求,就进入处理任务请求部分(就是“if (idx < tasksCnt)”这个if语句里面的内容),没有则继续循环处理任务请求部分中需要注意两点:1. 它在把高优先级任务处理完之后会继续检测是否还有任务请求,直到把使用任务请求处理完毕2. 处理完一个任务之后它会清除该任务的标志位 咳,不知道你们有没有看懂......然后这里面的重点函数呢就是“events = (tasksArr[idx])( idx, events );”这一句先看一看tasksArr[]这个数组的定义 pTaskEventHandlerFn tasksArr[] 有没有发现它和函数osalInitTasks();在同一个文件里面!有没有发现它就在osalInitTasks()这个函数的上面!再仔细看一看,有没有发现它定义成员变量名的顺序和下面初始化函数的顺序是一样的!这样说估计还是云里雾里的吧...(因为写到这里我也还是没有完全懂...)我再说明白点......之前不是说每个任务都有一个ID号嘛,优先级从0开始的,而数组里面第一位的索引号也是0,就是说任务ID号和数组索引号相对应,那么利用任务ID号就可以在数组里面找到相应的任务。
还有很神奇的一点,至少我是怎么觉得的...之前看这行代码感觉非常难以理解......“events = (tasksArr[idx])( idx, events ); ”刚刚想明白的,原来那个数组的类型是一个函数!也就是说通过任务ID找到相应进行任务处理的函数!这样你们有没有明白?不懂留言......有人看吗?笑...... 终于,协议栈的分析工作完成了......会不会觉得很乱?看看下面的流程图再来回顾整理一下吧~ 图2 五、实验过程和说明 1. 串口初始化代码 ①在协议栈中,用户自己添加代码的地方基本为App这个文件夹打开其中的文件SampleApp.c,在INCLUDES部分添加代码 #include "MT_UART.h" 图3 操作说明:协议栈中关于串口封装的文件有两个,一个是HAL->Target->CC2530EB->Drivers->hal_uart.c,另一个是MT->MT_UART.c这两个文件有什么区别呢?打开文件研究一下首先,在MT_UART.c中有include “hal_uart.h”,所以写头文件只要写”MT_UART.h”即可然后,仔细看代码,分析hal_uart.c这个文件只要是对不同串口类型的相应操作进行选择,MT_UART.c这个文件则是对任意串口的操作。
就是说MT_UART.c这个文件更底层一点 ②同样SampleApp.c文件中,找到函数void SampleApp_Init( uint8 task_id ),在其中加入串口初始化代码 /************串口初始化******************/ MT_UartInit(); //串口初始化 MT_UartRegisterTaskID(task_id); //登记任务号 图4 操作说明:串口初始化就不说了登记任务号就是把串口事件通过task_id登记在SampleApp_Init();中之前我们有提到说SampleApp_Init();函数很重要,就是分配ID号,它还是优先级最低的那个把这个函数的ID号给串口就是告诉串口我是在这个函数里面初始化的,相应的我的任务优先级是最低的...... ③更改串口初始化配置 在上图所示的MT_UartInit();处go to definition,进入MT_UartInit()函数(如图5)找到其中的MT_UART_DEFAULT_BAUDRATE,go to definition后将波特率设置为115200(如图6)。
回到图3位置,找到MT_UART_DEFAULT_OVERFLOW,go to definition将参数设为FALSE(如图7) 图5图6 图7 操作说明:修改波特率就不解释啦 #define MT_UART_DEFAULT_OVERFLOW FALSE 这行代码是打开串口流控的意思因为我们串口通讯是两根线的,必须把它关闭 2. 发送部分代码 ①打开SampleApp.c,找到SampleApp事件处理函数SampleApp_ProcessEvent() 补充一点,我们可以在SampleApp下添加自己的事件,每个事件有自己的事件号事件号是16位的,但是每个事件号只允许占16位中的1位,也就是说最多有16个事件 我们先浏览一下代码,大致功能是分析传递进来的事件号,触发相应事件感觉这个和任务号处理模式还是挺像的我们需要关注的是“系统消息事件”被触发之后即 “if ( events & SYS_EVENT_MSG )”语句之后的部分先看第一行: MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID ); 这一行代码实现功能是获取系统消息数据。
我们可以自己去查看里面的定义afIncomingMSGPacket是包含整个消息内容的结构体类型 之后的选择语句则是根据消息中的信息对数据进行相应处理我们需要关注如下代码(图8) 图8 弄了半天,原来还在初始化......意思是网络状态发送变化时(其实就是打开网络),就对数据发送进行初始化看下这三个参数,第一个是任务号,不重复啦第二个是事件号,这个也说过啦,每个事件只占1位哦!第三个是设置时间,就是规定你多久发一次信息!这里我们预设SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT = 5000,这个值可以自行修改,数值单位是毫秒,就是说这个程序是5秒发送一次数据 ②设置发送内容,自动周期性发送在同一个函数下找到如下代码(图9) 图9 如果触发周期性数据发送部分(就是说5秒过去了,要发送信息了),就执行SampleApp_SendPeriodicMessage()这个函数这个函数是重点哦,里面放我们需要发送的数据继续go to definition...... 找到该函数后对该函数做如下修改(如图10) 图10 我们来看一下AF_DataRequest()这个函数,通过上下文我们就可以知道这个函数一定就是决定发送数据内容的啦。
我们需要关注的是其中第3、4、5个参数,第3个参数的作用是和接收方建立联系,这里定义SAMPLEAPP_PERIODIC_CLUSTERID=1,如果协调器收到一个数据包,获取里面的这个标号,为1则证明这个数据包是以周期性广播方式进来的第4个参数表示发送数据的长度,第5个参数为需要发送的数据的指针进行到这里发送数据部分已经结束啦~~~ 3. 接收部分代码 在SampleApp.c下找到函数“void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )”,在“case SAMPLEAPP_PERIODIC_CLUSTERID:”下面一行添加代码(如图11): HalUARTWrite(0, "I get data!\n", 12); HalUARTWrite(0, &pkt->cmd.Data[0], 。
