萌新的Zigbee学习日记(3.4) 协议栈串口

原来是要昨天发的,结果写了一半去做拟真了,今天补上。

流程大致为:

1、串口初始化

2、登记任务号

3、串口发送

第一步:串口初始化串口初始化大家很熟悉,就是配置串口号、波特率、流控、校验位等等。 以前我们都是配置好寄存器然后使用。现在我们在 workspace下找到HAL\Target\CC2530EB\drivers的hal_uart.c 文件,我们可以看到里面已经包括了串口初始化、发送、接收等函数。

之后有一个MT层(Monitor and Test)

进入 MT_UartInit();,修改自己想要的初始化配置,进入函数后,发现代码如下。
1. void MT_UartInit ()
2. {
3. halUARTCfg_t uartConfig;
4. /* Initialize APP ID */
5. App_TaskID = 0;
6. /* UART Configuration */
7. uartConfig.configured = TRUE;
8. uartConfig.baudRate = MT_UART_DEFAULT_BAUDRATE;
9. uartConfig.flowControl = MT_UART_DEFAULT_OVERFLOW;
10. uartConfig.flowControlThreshold = MT_UART_DEFAULT_THRESHOLD;
11. uartConfig.rx.maxBufSize = MT_UART_DEFAULT_MAX_RX_BUFF;
12. uartConfig.tx.maxBufSize = MT_UART_DEFAULT_MAX_TX_BUFF;
13. uartConfig.idleTimeout = MT_UART_DEFAULT_IDLE_TIMEOUT;
14. uartConfig.intEnable = TRUE;
15.
16. #if defined (ZTOOL_P1) || defined (ZTOOL_P2)
17. uartConfig.callBackFunc = MT_UartProcessZToolData;
18. #elif defined (ZAPP_P1) || defined (ZAPP_P2)
19. uartConfig.callBackFunc = MT_UartProcessZAppData;
20. #else
21. uartConfig.callBackFunc = NULL;
22. #endif
23. /* Start UART */
24. #if defined (MT_UART_DEFAULT_PORT)
25. HalUARTOpen (MT_UART_DEFAULT_PORT, &uartConfig);
26. #else
27. /* Silence IAR compiler warning */
28. (void)uartConfig;
29. #endif
30. /* Initialize for ZApp */
31. #if defined (ZAPP_P1) || defined (ZAPP_P2)
32. /* Default max bytes that ZAPP can take */
33. MT_UartMaxZAppBufLen = 1;
34. MT_UartZAppRxStatus = MT_UART_ZAPP_RX_READY;
35. #endif
36. }
第 8 行:uartConfig.baudRate = MT_UART_DEFAULT_BAUDRATE;是配置波特率,
我们 go to definition of MT_UART_DEFAULT_BAUDRATE,
 可以看到:
#define MT_UART_DEFAULT_BAUDRATE HAL_UART_BR_38400
默认的波特率是 38400bps,现在我们修改成 115200bps,修改如下:
#define MT_UART_DEFAULT_BAUDRATE HAL_UART_BR_115200
第 9 行:uartConfig.flowControl = MT_UART_DEFAULT_OVERFLOW;语句是配置流控的,我们进入定义可以看到:
#define MT_UART_DEFAULT_OVERFLOW TRUE
默认是打开串口流控的,如果你是只连了 TX/RX 2 根线的方式务必关流控,像我们功能底板一样。
#define MT_UART_DEFAULT_OVERFLOW FALSE
注意:2 根线的通讯连接务必关流控,不然是永远收发不了信息的。
第 16~22 行:这个是预编译,根据预先定义的 ZTOOL 或者 ZAPP 选择不同的数据处理函数。后面的P1和 P2则是串口 0和串口1。我们用 ZTOOL,串口 0。我们可以在 option——C/C++ 的 CompilerPreprocessor 地
方加入。

第二步:登记任务号

在 SampleApp_Init();刚添加的串口初始化语句下面加入语句: MT_UartRegisterTaskID(task_id);//登记任务号

意思就是把串口事件通过 task_id 登记在 SampleApp_Init();里面。具体作用以后会提及。

第三步:串口发送

经过前面两个步骤,现在串口已经可以发送信息了。

我们在刚刚添加初始 化代码后面加入一条上电提示 Hello World 的语句。 HalUARTWrite(0,"Hello World\n",12); (串口 0,‘字符’,字符个数。)

再在预编译加入以下一些内容:

ZIGBEEPRO

ZTOOL_P1

MT_TASK

MT_SYS_FUNC

MT_ZDO_FUNC

提示:需要在 SampleApp.c 这个文件里加入头文件语句:#include "MT_UART.h"

连接 CC DEBUGGER 和 USB 转串口线,选择 CoordinatorEB-Pro,点解下载并 调试。全速运行,可以看到串口助手收到信息。

显示可以接收代码了,但是我们发现 Hello World 前面有一小段乱码。用十六进制显示,看到是 FE .. .. .. 这是 Z-stack MT 层 定义的串口发送格式,以 FE 开头。详细的以后内容会讲述。如果不想要的可以 在预编译地方把 MT 相关内容注释。

ZIGBEEPRO

ZTOOL_P1

xMT_TASK//xMT_TASK:表示没有定义 MT_TASK,也就是不定义了

xMT_SYS_FUNC

xMT_ZDO_FUNC

我们在协议栈里再做一个测试,在 osal_start_system()函数里 for(;;) 里加入:

HalUARTWrite(0,"Hello,WeBee\n",12);

下载运行后发现串口 不停地接收到 Hello WeBee。

这就证明了前一节的协议栈运行后 会在这个函数里不停地循环查询任务、执行任务。

实际应用中可千万不能有把串口发送函数弄到这个位置然后给 PC 发信息的想法,因为这破坏了协议栈任务轮 询的工作原则,相当于我们普通单片机不停用 Delay 延时函数一样,是极其低 的。

在协议栈串口初始化后

我们打开 SampleApp.C 文件,搜索找到函数:

void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )

 在 case SAMPLEAPP_PERIODIC_CLUSTERID:下面加入

HalUARTWrite(0,"I get data\n",11);//记得添加串口初始化等设置

1、选择 CoodinatorEB-Pro, 下载到开发板 1;(作为协调器串口跟电脑连接)

 2、选择 EndDeviceEB-Pro, 下载到开发板 2;(作为终端设备无线发送数据给协调 器)

给两块开发板上电,打开串口调试助手,可以看到大约 5S 中会收到 I get data 的内容。

下面是详细解释:

发送部分:(可以将这部分内容理解成是发送终端需要执行的)

1、登记事件,设置编号、发送时间等。

打开 SampleApp.C 文件,找到 SampleApp 事件处理函数:

uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )

找到函数里下面代码:

1、 // Received whenever the device changes state in the network
2、 case ZDO_STATE_CHANGE: //当网络状态改变,如从未连上到连上网络
3、 SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status);
4、 if ( (SampleApp_NwkState == DEV_ZB_COORD //协调器、路由器、
5、 | | (SampleApp_NwkState == DEV_ROUTER) //或者终端都执行
6、 | | (SampleApp_NwkState == DEV_END_DEVICE) )
7、 {
8、 // Start sending the periodic message in a regular interval.
9、 osal_start_timerEx(SampleApp_TaskID,
SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT ); }
10、 else
11、 {
12、 // Device is no longer in the network
13、 }
14、 break;

第 9 行:代码的关键部分。

这三个参数决定着周期性发送数据的命脉。 

SampleApp_TaskID:

任务 ID,函数开头定义了 SampleApp_TaskID = task_id;也就是 SampleApp 初 始化的任务 ID 号。

SAMPLEAPP_SEND_PERIODIC_MSG_EVT:
 // Application Events (OSAL) - These are bit weighted definitions.
#define SAMPLEAPP_SEND_PERIODIC_MSG_EVT 0x0001

同一个任务下可以有多个事件,这个是事件的号码。

我们可以定义自己的 事件,但是编号不能重复。

SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT:
 // Send Message Timeout Every 5 seconds
#define SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT 5000

事件重复执行的时间。这里以毫秒为单位,所以是 5s,也就是刚刚实验为什么隔约 5s 收到数据的原因。这里可以改你需要发送数据的时间间隔。

登记好事件后,看第二行代码可以知道如果网络一直连接的就不会再次进入 这个函数了,所以这个相当于初始化,只执行 1 次。

2、设置发送内容。自动周期性地发送 在同一个函数下面可以找到如下代码:

1、 // Send a message out - This event is generated by a timer
2、 // (setup in SampleApp_Init()).
3、 if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT )
4、 {
5、 // Send the periodic message
6、 SampleApp_SendPeriodicMessage();
7、 // Setup to send message again in normal period (+ a little jitter)
8、 osal_start_timerEx( SampleApp_TaskID,
SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
9、 (SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF)) );
10、 // return unprocessed events
11、 return (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT);
12、 }

第 3 行:判断 SAMPLEAPP_SEND_PERIODIC_MSG_EVT (0x0001)有没有发 生,如果有的就执行下面函数。

第 6 行:SampleApp_SendPeriodicMessage();是主要的代码,是我们编写需要发 送内容的地方,我们进入去做一些修改。先看源代码。

/**********************周期性发送数据函数*************************/
1、 void SampleApp_SendPeriodicMessage( void )
2、 {
3、 if(AF_DataRequest(&SampleApp_Periodic_DstAddr, &SampleApp_epDesc,
4、 SAMPLEAPP_PERIODIC_CLUSTERID,
5、 1,
6、 (uint8*)&SampleAppPeriodicCounter,
7、 &SampleApp_TransID,
8、 AF_DISCV_ROUTE,
9、 AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )
10、 {
11、 }
12、 else
123
13、 {
14、 // Error occurred in request to send.
15、 }
16、 }

第 4 行:SAMPLEAPP_PERIODIC_CLUSTERID 这是一个新东西,其定义为:

#define SAMPLEAPP_PERIODIC_CLUSTERID 1 定义的作用是和接收方建立联系,协调器收到这个标号,如果是 1,就 证明是由周期性广播方式发送过来的。

第 5 行:1 是数据长度。

第 6 行:(uint8*)&SampleAppPeriodicCounter 是要发送的内容。

知道代码功能后我们可以做以下修改,

uint8 data[10]={0,1,2,3,4,5,6,7,8,9};

10

data

为修改内容。添加数据 0~9, 发送字符数 10,内容是 data 数组。

/**********************周期性发送数据函数*************************/
1、 void SampleApp_SendPeriodicMessage( void )
2、 {
3、 uint8 data[10]={0,1,2,3,4,5,6,7,8,9};
4、 if(AF_DataRequest(&SampleApp_Periodic_DstAddr, &SampleApp_epDesc,
5、 SAMPLEAPP_PERIODIC_CLUSTERID,
6、 10,
7、 data, //指针方式
8、 &SampleApp_TransID,
9、 AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )
10、 {
11、 }
12、 else
13、 {
14、 // Error occurred in request to send.
15、 }
16、 }

至此,发送部分代码修改完成,上电后 CC2530 会以周期 5s 来广播式发送 数据 0~9。

接收部分:(可以将这部分内容理解成是连接电脑的协调器需要执行的)

接收部分需要完成 2 个任务:

1、读取接收到的数据;

2、把数据通过串口发送给 PC 机。

1、读取接收到的数据;

读取接收到的数据; 同样的在 uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events ) 事 件处理函数中找到代码:

// Received when a messages is received (OTA) for this endpoint
case AF_INCOMING_MSG_CMD:
 SampleApp_MessageMSGCB( MSGpkt );
 break;

其中 SampleApp_MessageMSGCB( MSGpkt );就是将接收到的数据包进行处理 的函数。我们进入此函数,代码如下:

1、 void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
2、 {
3、 uint16 flashTime;
4、 switch ( pkt->clusterId )
5、 {
6、 case SAMPLEAPP_PERIODIC_CLUSTERID:
7、 HalUARTWrite(0,"I get data\n",11); //提示收到数据
8、 break;
9、
10、 case SAMPLEAPP_FLASH_CLUSTERID:
11、 flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] );
12、 HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) );
13、 break;
}
}

第 6 行:读取发来的数据包的 ID 号,如果是:

SAMPLEAPP_PERIODIC_CLUSTERID 就执行里面函数,

这个编号就是我们前面发送定义的数据包 的 ID 号(TaskID),它的作用很明显了,如果收到是这个的话说明是我们自己定 义的周期性广播。

所有的数据和信息都在函数传入来的 afIncomingMSGPacket_t *pkt 里面,进入 afIncomingMSGPacket_t 的定义,它是一个结构体,内容如下:

typedef struct
{
 osal_event_hdr_t hdr; /* OSAL Message header */
125
 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 */
 afMSGCommandFormat_t cmd; /* Application Data */
} afIncomingMSGPacket_t;

里面包含了数据包的所有东西,长地址、短地址、RSSI 等.

afMSGCommandFormat_t cmd; /* Application Data */

里又是一个结构体

// Generalized MSG Command Format
typedef struct
{
 byte TransSeqNumber;
 uint16 DataLength; // Number of bytes in TransData
 byte *Data;
} afMSGCommandFormat_t;

 

2、把数据通过串口发送给 PC 机

1、void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
2、{
3、 uint16 flashTime;
4、 switch ( pkt->clusterId )
5、{
6、 case SAMPLEAPP_PERIODIC_CLUSTERID:
7、 HalUARTWrite(0,"I get data\n",11); //提示收到数据
8、 HalUARTWrite(0, &pkt->cmd.Data[0],10); //打印收到数据
9、 HalUARTWrite(0,"\n",1); // 回车换行
10、 break;
11、
12、 case SAMPLEAPP_FLASH_CLUSTERID:
13、 flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] );
14、 HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) );
15、 break;
}
}

分别选择 CoordinatorEB-Pro和 EndDeviceEB-Pro 编译后对应下载到协调器和终端 模块,协调器通过串口连接到电脑。最后终于收到数据。

乱码又来了,不用担心,我们用十六进制显示,发现数据是在的, 而且 0A 就是 ASCII 码的换行,这样就很清晰了,接收到的是 16 进制数据,我们 串口显示用 ASCII 当然乱码了。

1、void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
2、{
3、/*16 进制转 ASCII */
4、uint8 asc_16[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'},i;
5、uint16 flashTime;
6、 switch ( pkt->clusterId )
7、{
8、 case SAMPLEAPP_PERIODIC_CLUSTERID:
9、 HalUARTWrite(0,"I get data\n",11); //提示收到数据,十一个字符
10、 for(i=0;i<10;i++)//输出前10个字符
11、 HalUARTWrite(0,&asc_16[pkt->cmd.Data[i]],1); //打印收到数据
12、 HalUARTWrite(0," \n",1); // 回车换行
13、 break;
14、
15、 case SAMPLEAPP_FLASH_CLUSTERID:
16、 flashTime = BUILD_UINT16(pkt->cmd.Data[1], pkt->cmd.Data[2] );
17、 HalLedBlink( HAL_LED_4, 4, 50, (flashTime / 4) );
18、 break;
}
}

实际上ASCII的变化时因为前边函数的类为 uint8_t =unsinged char,其输出为字符型,计算机里字符都是用ASCII存储的。

最后.串口透传

实验依然使用我们熟悉的 SampleApp.eww 工程来进行。在前面我们曾做 过串口实验和数据无线传输,这次实验也算是前面 2 个实验的一个结合。不过 协议栈的串口接收有特定的格式,我们得了解一下它的传输机制。先理清我们 要实现这个功能的流程:

由于 2 台 PC 机所带的模块地位是相等的,所以两个模 块的程序流程也一样了:

1、ZigBee 模块接收到从 PC 机发送信息,然后无线发送出去

2、ZigBee 模块接收到其它 ZigBee 模块发来的信息,然后发送给 PC 机

1、 ZigBee 模块接收到从 PC 机发送信息,然后无线发送出去以前我们做的都是 CC2530 给 PC 机串口发信息,还没接触过 PC 机发送 给 CC2530,现在我们就来完成这个任务。其主要代码在 MT_UART.C 中。我们之前协议栈串口实验对串口初始化时候已经有所了解了。

我们在这个文件里找到串口初始化函数 void MT_UartInit (),找到下面代码:

#if defined (ZTOOL_P1) || defined (ZTOOL_P2)
 uartConfig.callBackFunc = MT_UartProcessZToolData;
#elif defined (ZAPP_P1) || defined (ZAPP_P2)
 uartConfig.callBackFunc = MT_UartProcessZAppData;
#else
 uartConfig.callBackFunc = NULL;
#endif

我们定义了 ZTOOL_P1,故协议栈数据处理的函数 MT_UartProcessZToolData, 进入这个函数定义。下边是对函数关键地方的解释。

/*******************************************************************
* @fn MT_UartProcessZToolData
*
* @brief | SOP | Data Length | CMD | Data | FCS |
* | 1 | 1 | 2 | 0-Len | 1 |
*
* Parses the data and determine either is SPI or just simply serial data
* then send the data to correct place (MT or APP)
*
* @param port - UART port
* event - Event that causes the callback
* @return None
****************************************************************/
/* 这个函数很长,具体说来就是把串口发来的数据包进行打包,校验,生
成一个消息,发给处理数据包的任务。如果你看过 MT 的文档,应该知道如
果用 ZTOOL 通过串口来沟通协议栈,那么发过来的串口数据具有以下格式:
0xFE, DataLength, CM0, CM1, Data payload, FCS

翻译: 0xFE:数据帧头
DataLength:Datapayload 的数据长度,以字节计,低字节在前;
CM0:命令低字节;
CM1:命令高字节;(ZTOOL 软件就是通过发送一系列命令给 MT 实现和协议栈交互)
Data payload:数据帧具体的数据,这个长度是可变的,但是要和DataLength 一致;
FCS :校验和,从 DataLength 字节开始到 Data payload 最后一个字节所有字节的异或按字节操作;

也就是说,如果 PC 机想通过串口发送信息给 CC2530,由于是使用默认的 串口函数,所以您必须按上面的格式发送,否则 CC2530 是收不到任何东西的, 这也是我们大家在调试串口接收时一直打圈的地方。尽管这个机制是非常完善 的,也能校验串口数据,但是很明显,我们需要的是 CC2530 能直接接收到串口 信息,然后一成不变的发成出去,相信你在聊 QQ 的时候也不希望在每句话前面 加 FE .. ..的特定字符吧,而且还要自己计算校验码。

下边先从了解函数入手:

void MT_UartProcessZToolData ( uint8 port, uint8 event )
{
 …
…
 while (Hal_UART_RxBufLen(port))
 /*查询缓冲区读信息,也成了这里信息是否接收完的标志*/
 {
 HalUARTRead (port, &ch, 1);
 /*一个一个地读,读完一个缓冲区就清 1 个了,?为什么这样呢,往下看*/
 switch (state)
/*用上状态机了*/
 {
 case SOP_STATE:
 if (ch == MT_UART_SOF) /* MT_UART_SOF 的值默认是 0xFE,所以数据必须 FE 格式开始发送才能进入下一个状态,不然永远在这里转圈*/
 state = LEN_STATE;
 break;
 case LEN_STATE:
 LEN_Token = ch;
 tempDataLen = 0;
 /* Allocate memory for the data */
 pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof
( mtOSALSerialData_t ) +MT_RPC_FRAME_HDR_SZ + LEN_Token );
/* 分配内存空间*/
if (pMsg) /* 如果分配成功*/
 {
 /* Fill up what we can */
 pMsg->hdr.event = CMD_SERIAL_MSG;
/* 注册事件号 CMD_SERIAL_MSG;,很有用*/
 pMsg->msg = (uint8*)(pMsg+1);
/*定位数据位置*/
 …
 …
 …
 /* Make sure it's correct */
 tmp=MT_UartCalcFCS((uint8*)&pMsg->msg[0], MT_RPC_FRAME_HDR_SZ+ LEN_Token);
if (tmp == FSC_Token) /*数据校验*/
 {
 osal_msg_send( App_TaskID, (byte *)pMsg );
/*把数据包发送到 OSAL 层,很很重要*/
 }
 else
 {
 /* deallocate the msg */
 osal_msg_deallocate ( (uint8 *)pMsg );
/*清申请的内存空间*/
 }
 /* Reset the state, send or discard the buffers at this point */
state = SOP_STATE; /*状态机一周期完成*/
 …
…
…

简单看了一下代码,串口从 PC 机接收到信息会做如下处理:

1、接收串口数据,判断起始码是否为 0xFE

2、得到数据长度然后给数据包 pMsg 分配内存

3、给数据包 pMsg 装数据

4、打包成任务发给上层 OSAL 待处理

5、释放数据包内存

webee简化后流程变成:

1、接收到数据

2、判断长度然后然后给数据包 pMsg 分配内存

3、打包发送给上层 OSAL 待处理

4、释放内存

简化的时候它去掉了起始位的判断。

1. void MT_UartProcessZToolData ( uint8 port, uint8 event )
2. {
3. uint8 flag=0,i,j=0; //flag 是判断有没有收到数据,j 记录数据长度
4. uint8 buf[128]; //串口 buffer 最大缓冲默认是 128,我们这里用 128.
5. (void)event; // Intentionally unreferenced parameter
6. while (Hal_UART_RxBufLen(port)) //检测串口数据是否接收完成
7. {
8. HalUARTRead (port,&buf[j], 1); //把数据接收放到 buf 中
9. j++; //记录字符数
10. flag=1; //已经从串口接收到信息
11. }
12. if(flag==1) //已经从串口接收到信息
13. { /* Allocate memory for the data */
14. //分配内存空间,为机构体内容+数据内容+1 个记录长度的数据
15. pMsg = (mtOSALSerialData_t *)osal_msg_allocate( sizeof
16. ( mtOSALSerialData_t )+j+1);
17. //事件号用原来的 CMD_SERIAL_MSG
18. pMsg->hdr.event = CMD_SERIAL_MSG;
19. pMsg->msg = (uint8*)(pMsg+1); // 把数据定位到结构体数据部分
20. pMsg->msg [0]= j; //给上层的数据第一个是长度
21. for(i=0;i<j;i++) //从第二个开始记录数据
22. pMsg->msg [i+1]= buf[i];
23. osal_msg_send( App_TaskID, (byte *)pMsg ); //登记任务,发往上层
24. /* deallocate the msg */
25. osal_msg_deallocate ( (uint8 *)pMsg ); //释放内存
26. }
27. }

数据包中数据部分的格式是: datalen + data 

/*这个大家了解TCP/IP , UDP ,计算机网络里知识的应该都知道,所以我有点不了解为什么把头部校验码去了。暂时先跟着学习,之后再继续思考。*/

因为串口初始化是在 SampleApp 中进行的, 任务号也是 SampleApp 的 ID,所以当然是在 SampleApp.C 里面进行了。在 SampleApp.C 找到任务处理函数:

uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events ),加入

case CMD_SERIAL_MSG: //串口收到数据后由 MT_UART 层传递过来的数据,用网蜂方法接收,编译时不定义 MT相关内容,
SampleApp_SerialCMD((mtOSALSerialData_t *)MSGpkt);
 break; 

最后代码:

uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
 afIncomingMSGPacket_t *MSGpkt;
 (void)task_id; // Intentionally unreferenced parameter
 if ( events & SYS_EVENT_MSG )
 {
 MSGpkt(afIncomingMSGPacket_t*)osal_msg_receive( SampleApp_TaskID );
 while ( MSGpkt )
{
 switch ( MSGpkt->hdr.event )
 {
 case CMD_SERIAL_MSG: //串口收到数据后由 MT_UART 层传递过来的
数据,用网蜂方法接收,编译时不定义 MT
相关内容,
SampleApp_SerialCMD((mtOSALSerialData_t *)MSGpkt);
 break; 

解释:串口收到信息后,事件号 CMD_SERIAL_MSG 就会被登记,便进入

case CMD_SERIAL_MSG:

执行 SampleApp_SerialCMD((mtOSALSerialData_t *)MSGpkt);大家是不是很奇怪怎么在协议栈里找不到这个函数,当然了,我们那边只把他打包了,然后登记任务,这个包是我们自己的,想怎么处理当然由自己来搞掂。大家应该想到这个函数应该要把信息无线发送出去吧,想到这个的话你的悟性还挺高的。

1. void SampleApp_SerialCMD(mtOSALSerialData_t *cmdMsg)
{
2. uint8 i,len,*str=NULL; //len 有用数据长度
3. str=cmdMsg->msg; //指向数据开头
4. len=*str; //msg 里的第 1 个字节代表后面的数据长度
5. /********打印出串口接收到的数据,用于提示*********/
6. for(i=1;i<=len;i++)
7. HalUARTWrite(0,str+i,1 );
8. HalUARTWrite(0,"\n",1 );//换行
9. /*******发送出去***参考网蜂 1 小时无线数据传输教*********/
10. if ( AF_DataRequest( &SampleApp_Periodic_DstAddr, &SampleApp_epDesc,
11. SAMPLEAPP_COM_CLUSTERID,//自己定义一个
12. len+1, // 数据长度
13. str, //数据内容
14. &SampleApp_TransID,
15. AF_DISCV_ROUTE,
16. AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )
17. {
18. }
19. else
20. {
21. // Error occurred in request to send.
22. }
23. }

SAMPLEAPP_COM_CLUSTERID 这个是自己定义的 ID,用于接收方判别。

到这里,CC2530 从串口接收到信息到转发出去已经完成了,我们可以先下 载程序到网蜂开发板,然后可以看到随便发什么都可以打印出来提示了。也就是说:

CMD_SERIAL_MSG:事件和 void SampleApp_SerialCMD (mtOSALSerialData_t *cmdMsg )函数已经被成功执行了。

2. ZigBe 模块接收到其它 ZigBee 模块发来的信息,然后发送给 PC 机

void SampleApp_MessageMSGCB( afIncomingMSGPacket_t *pkt )
{
 uint8 i,len;
 switch ( pkt->clusterId )
 {
 case SAMPLEAPP_COM_CLUSTERID: //如果是串口透传的信息
 len=pkt->cmd.Data[0];
 for(i=0;i<len;i++)
 HalUARTWrite(0,&pkt->cmd.Data[i+1],1);//发给 PC 机
 HalUARTWrite(0,"\n",1); // 回车换行
 break;
 }
}

选择了协调器、路 由器、或者终端编译时都要修改 options 。


版权声明:本文为qq_45204001原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。