ZigBeeCC2530无线PWM产生以及定时器应用
[复制链接]
本帖最后由 jsxykj1 于 2014-3-10 20:54 编辑
分享下PWM产生呼吸灯经验,顺便和大家一起继续学习无线,由于小弟新手不才,有不对的请多多原谅。首先谢谢合纵物联的产品和技术支持,官方群 305145401 。大家有兴趣的都来讨论ZIGBEE,主要是能够享受优质服务和技术支持。
先来跟大家介绍一个我的这个实验所需要达到的要求。
1.Zigbee协调器接收到上位机串口的数据后用协调器通过周期广播将数据发送到末节点。(这个串口我是通过改了的,如果要修改可以参考透传实验)
2.让末节点的接收事件产生后进入到我定义的PWM事件。(这个部分主要可以学习ZIGBEE的事件调用触发,以及定时器的寄存器配置。)
1.ZIGBEE协调器部分编程:
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);
148
/*一个一个地读,读完一个缓冲区就清 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 );
/*清申请的内存空间*/
149
}
/* Reset the state, send or discard the buffers at this point */
state = SOP_STATE; /*状态机一周期完成*/
…
…
…
简单看了一下代码,串口从 PC 机接收到信息会做如下处理:
1、 接收串口数据,判断起始码是否为 0xFE
2、 得到数据长度然后给数据包 pMsg 分配内存
3、 给数据包 pMsg 装数据
4、 打包成任务发给上层 OSAL 待处理
5、 释放数据包内存
我们要做的是简化再简化。流程变成:
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
150
18. pMsg->hdr.event = CMD_SERIAL_MSG;
19. pMsg->msg = (uint8*)(pMsg+1); // 把数据定位到结构体数据部分
20. pMsg->msg [0]= j; //给上层的数据第一个是长度
21. for(i=0;i
22. pMsg->msg [i+1]= buf;
23. osal_msg_send( App_TaskID, (byte *)pMsg ); //登记任务,发往上层
24. /* deallocate the msg */
25. osal_msg_deallocate ( (uint8 *)pMsg ); //释放内存
26. }
27. }
由网蜂提供的代码可以知道,数据包中数据部分的格式是:
datalen + data
到这里,数据接收的处理函数已经完成了,接下来我们要做的就是怎么在任
务中处理这个包内容呢?很简单,因为串口初始化是在 SampleApp 中进行的,
任务号也是 SampleApp 的 ID,所以当然是在 SampleApp.C 里面进行了。在
SampleApp.C 找到任务处理函数:
uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events ),加入下
面红色代码:
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,” ”,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. }
这是协调器通过串口事件将实现读取串口的信息周期给出去。修改根据网峰的还是很简单。
下面给大家粘出协调器的代码。这个基本没改过就当他是透传。
我们的协调器大概就是这么简单。
2.现在给大家在末节点先粘出代码。
这是我先给大家配置一个定时器1,由于定时器1是16位的所以我们在处理高八位和第八位需要自己给一个规定来转化,定时器34不需要因为是8位也有8位寄存器。
#define HI_UINT16(a) (((a) >> 8) & 0xFF)
#define LO_UINT16(a) ((a) & 0xFF) 16位高低转换
#define HI_UINT8(a) (((a) >> 4) & 0x0F)
#define LO_UINT8(a) ((a) & 0x0F) 8位高低转换
我在和一个朋友一起配置寄存器的时候会发现我们是否需要关了定时器再进行值得装载,在这里我是觉得是不需要的,不然每回关对电平会产生影响,之后他的效果就会很差。
所以我将前面的T1CTL=0;开始这句话删除了。
再我们来配置引脚,和优先级这里我们需要打开芯片手册,这个很简单在这,我的定时器1有两个备用脚我是用的2,因为我的串口P0.3和通道冲突了,所以在这我希望大家先将自己的引脚看好不然串口没效果了,会很麻烦。所以大家先配置PERCFG寄存器现在自己的外设优先级设置好这样我们就方便后面的应用。
我是选用的向上比较模式。这个通过我的值看看你们的芯片手册肯定就懂了,再就是PWM波也可以用模模式产生,只是我们配置操作不一样。在这里跟大家粘出其他模式代码
这个也是可以的大家可以试试。先说到这里关于接收串口后建立事件,不停的触发事件,下楼继续分享,要做作业了蛋疼。
赞赏
1
查看全部赞赏