cc2530入门 与串口中断处理_cc2530串口通信中断

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

如果你需要这些资料,可以戳这里获取

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

信道    :  2.4GHz的射频频段被分为16个独立的信道。每一个设备都有一个默认的信道集(DEFAULT_CHANLIST)。协调器扫描自己的默认信道并选择噪声最小的信道作为自己所建的网络信道。设备节点和路由器也要扫描默认信道集并选择信道上已经存在的网络加入。

PANID :  PANID指网络编号,用于区分不同的网络设备,PANID值与ZDAPP_CONFIG_PAN_ID的值设定有关。如果协调器的ZDAPP_CONFIG_PAN_ID设置为0xFFFF,则协调器将产生一个随机的PANID,如果路由器和终端节点的ZDAPP_CONFIG_PAN_ID设置为0xFFFF,路由器和终端节点将会在自己默认信道上随机的选择一个网络加入,网络协调器的PANID即为自己的PANID。如果协调器的ZDAPP_CONFIG_PAN_ID设置为非0xFFFF值,则协调器根据自己的网络长地址(IEEE地址)或ZDAPP_CONFIG_PAN_ID随机产生PANID的值。不同的是如果路由器和终端节点的ZDAPP_CONFIG_PAN_ID 的值设置为非0xFFFF,则会以ZDAPP_CONFIG_PAN_ID值作为PANID。如果协调器的值设为小于等于0x3FFF的有效值,协调器就会以这个特定的PANID值建立网络,但是如果在默认信道上已经有了该PANID值的网络存在,则协调器会继续搜寻其它的PANID,直到找到不冲突的网络为止,这样就可能产生一个问题如果协调器在默认信道上发生PANID冲突而更换PANID,终端节点并不知道协调器已经更换了PANID,还会继续加入到PANID为ZDAPP_CONFIG_PAN_ID值的网络中。

协议栈的文件包层次结构:

App:应用层目录,这是用户创建各种不同工程的区域,在这个目录中包含了应用层的内容和
这个项目的主要内容,在协议中一般是以操作系统的任务实现的
 HAL:硬件层目录,包含有与硬件相关的配置和驱动及操作函数
 MAC:MAC层目录,包含了MAC层的参数配置文件及其MAC的LIB库的函数接口文件
 MT:实现通过串口可控制各层,并与各层进行直接交互
 NWK:网络层目录,包含网络层配置参数文件网络层库的函数接口文件及APS层库的函数接口
 OSAL:协议栈的操作系统
 Profile:AF(Application framework应用框架)层目录

 Security:安全层目录,包含安全层处理函数,比如加密函数等
 Services:地址处理函数目录,包括地址模式的定义及地址处理函数
 Tools: 工程配置目录,包括空间划分及Z-Stack相关配置信息
 ZDO:ZDO目录
 ZMac:MAC层目录,包括MAC层参数配置及MAC层LIB库函数回调处理函数
 ZMain:主函数目录,包括入口函数及硬件配置文件
 Output:输出文件目录,由IAR IDE自动生成

1. 系统初始化

系统启动代码需要完成初始化硬件平台和软件架构所需要的各个模块,位操作系统的运行做好准备工作,主要分为初始化系统时钟, 检测芯片的工作电压,初始化堆栈,初始化各个硬件模块,初始化flash存储,形成芯片MAC地址,初始化非易失变量,初始化MAC层协议,初始化应用帧协议,初始化操作从系统等。

启动代码为操作系统的执行做好准备工作后,就开始执行操作系统入口程序,并由此彻底将控制权交给操作系统。其实操作系统的实体只有一行代码:

osal_start_system(); // No Return from here

根据这句代码的注释,即本函数不会返回,也就是说它是一个死循环,永远不可能执行完。这个函数就是轮转查询操作系统的主体部分,它所做的就是不断的查询每个任务是否有事件发生,如果发生,则执行相应的函数,如果没有发生,就查询下一个任务。

函数的主体部分代码如下:

for(;😉  // Forever Loop   无限循环

#endif

{

uint8 idx = 0;

osalTimeUpdate();

Hal_ProcessPoll();  // This replaces MT_SerialPoll() and osal_check_timer().

do {

if (tasksEvents[idx])  // Task is highest priority that is ready.   准备好具有最高优先权的任务

{

break;

}

} while (++idx < tasksCnt);//得到了待处理的具有最高优先级的任务索引号idx

if (idx < tasksCnt)//确认本次有任务需要处理

{

uint16 events;

halIntState_t intState;

//进入/退出临界区,来提取出需要处理的任务中的事件

HAL_ENTER_CRITICAL_SECTION(intState);

events = tasksEvents[idx];

tasksEvents[idx] = 0;  // Clear the Events for this task.  清除任务中的事件

HAL_EXIT_CRITICAL_SECTION(intState);

//通过指针调用执行对应的任务处理函数

events = (tasksArr[idx])( idx, events );

//进入/退出临界区,保存尚未处理的事件

HAL_ENTER_CRITICAL_SECTION(intState);

tasksEvents[idx] |= events;  // Add back unprocessed events to the current task. //增加下一个任务

HAL_EXIT_CRITICAL_SECTION(intState);

//本次事件处理函数执行完,继续下一个循环

}

操作系统专门分配了所有任务时间的taskEvents[]这样一个数组,每一个单元对应存放着一个任务的所有事件。在这个函数中,首先通过一个do-while循环来遍历tasksEvents[],找到第一个具有事件的任务(即具有待处理事件的优先级最高的任务,因为序号低的优先级高),然后跳出循环此时就得到了有事件待处理的具有最高优先级的任务序号idx,然后通过events = tasksEvents[idx]语句,将这个当前具有最高优先级任务的时间取出,接着就调出(tasksArr[idx])(idx,events)函数来执行具体的处理函数了。下面以例子GeneralApp为例看一下taskArr函数数组(这个数组在Osal_GeneralApp.c中,前缀Osal表明这是和操作系统接口文件,Osal_start_system()函数中通过函数指针(task[idx])(idx,events)调用具体的相应任务处理函数)。

项目GeneralApp中的tasksArr函数数组代码如下:

const pTaskEventHandlerFn tasksArr[] = {

macEventLoop,                                    //MAC层事件处理进程

nwk_event_loop,                                  //网络层事件处理进程

Hal_ProcessEvent,                                //物理层事件处理进程

#if defined( MT_TASK )

MT_ProcessEvent,                                //调试任务处理进程

#endif

APS_event_loop,                                  //APS层事件处理进程,可选

#if defined ( ZIGBEE_FRAGMENTATION )

APSF_ProcessEvent,

#endif

ZDApp_event_loop,                             //ZDApp层事件处理进程

#if defined ( ZIGBEE_FREQ_AGILITY ) || defined ( ZIGBEE_PANID_CONFLICT )

ZDNwkMgr_event_loop,

#endif

GenericApp_ProcessEvent                 
  //用户应用层处理进程,用户自己生成

};

这些任务的优先级从高到低,即MAC层任务优先级最高。 一般情况下无需修改这些函数,只需按照自己的需求编写应用层的任务及事件处理函数即可。一般情况下,用户只需在工程里外加三个文件就可以完成一个项目,一个是主文件,存放具体的任务事件处理函数(如:SampleApp_ProcessEvent);一个是这个主文件的头文件(以Osal开头),是专门存放任务处理函数数组tasksArr[]的文件。对于GeneralApp来说,主文件是GeneralApp.c,头文件是GeneralApp.h,操作系统接口文件是Osal_GeneralApp.c;

系统开始的时候的初始化流程:

根据上图,执行完自定义事件
SampleApp_ProcessEvent()函数(注:这里的自定义事件是自己根据需求定义的,不明白的话先不用理会)后
并不代表主函数结束,程序将会一
直待在osal_start_system()进行
任务轮询

Zigbee组网流程:
协调器的组网,终端设备和路由设备发现网络以及加入网络

  1. Z-Stack 由 main()函数开始执行
  2. osal_init_system()函数,执行操作系统初始化
  3. 进入osalInitTasks()函数,执行操作系统任务初始化
  4. ZDApp层,初始化 ,执行ZDApp_init函数后,如果是协调器将建立网络
    ,如果是终端设备将加入网络。
  5. 执行ZDOInitDevice()函数,执行设备初始化
  6. 执行ZDApp_NetworkInit函数,完成网络初始化
  7. ZDApp_event_loop()函数
  8. 执行ZDO_StartDevice()函数,启动设备

协调器和路由器组建网络
 if ( ZG_BUILD_COORDINATOR_TYPE && logicalType ==
NODETYPE_COORDINATOR ) //当设备作为协调器时,执行这个
条件语句。
执行NLME_NetworkFormationRequest()向网络层发送网络形成
请求,当网络层建立网络后,将给予 ZDO层反馈信息;
接着去执行ZDApp层的 ZDO_NetworkFormationConfirmCB()
函数
 if ( ZG_BUILD_JOINING_TYPE && (logicalType ==
NODETYPE_ROUTER || logicalType == NODETYPE_DEVICE) )
//当为终端设备或路由时,执行这个条件语句,发出加入网络请求,
// 继而转到ZDO_NetworkDiscoveryConfirmCB()函数

Zstack协议栈函数
 向操作系统分配任务事件片函数
uint8 osal_start_timerEx( uint8 taskID, uint16 event_id,
uint16 timeout_value )
这个定时器只是为发送周期信息开启的,设备启动初始化后从
这里开始触发第一个周期信息的发送,然后周而复始下去

Zigbee数据通信
 Zigbee发送数据时调用函数AF_DataRequest()
 若Zigbee接收到数据,则进入自定义事件
SampleApp_ProcessEvent()后,会触发能接收及处理数
据包的函数SampleApp_MessageMSGCB()

发送数据函数AF_DataRequest
afStatus_t AF_DataRequest(
afAddrType_t *dstAddr,//发送目的地址+端点地址和传送模式
endPointDesc_t *srcEP,//源终端的描述(如:操作系统任务ID)
uint16 cID, //用于接收器识别的标号(簇ID)
uint16 len, //发送数据长度
uint8 *buf, //发送数据缓冲区(即发送的数据)
uint8 *transID, //任务ID号
uint8 options, //有效位掩码的发送选项
uint8 radius //最大传送跳数
);

afIncomingMSGPacket_t
typedef struct {
osal_event_hdr_t hdr; /* OSAL Message header */
uint16 groupId; /*组号(若未设置则为0)*/
uint16 clusterId; /*用于识别的标号(簇ID),应该与数据发送函数中的簇ID一致*/
afAddrType_t srcAddr;
uint16 macDestAddr; /* MAC header destination short address */
uint8 endPoint; /*端点号*/
uint8 wasBroadcast; /*判断是否为广播地址,若是返回TRUE*/
uint8 LinkQuality; /* The link quality of the received data frame */
uint8 correlation; /* The raw correlation value of the received data frame */
int8 rssi; /* 接收到的射频功率单位dBm */
uint8 SecurityUse; /* deprecated */
uint32 timestamp; /* receipt timestamp from MAC */
afMSGCommandFormat_t cmd; /* 接收到的数据 */
} afIncomingMSGPacket_t;

Zstack简单串口通信调试:
 在SampleApp.c添加头文件
#include “MT_UART.h”
#include “MT_APP.h”
#include “MT.h”
 在SampleApp_Init()函数开始部分编写如下代码:
/***********串口初始化************/
MT_UartInit();//初始化
MT_UartRegisterTaskID(task_id);//登记任务号
HalUARTWrite(0,“Hello World\n”,12);
 进入MT_UartInit()函数,修改MT_UART_DEFAULT_BAUDRATE为
HAL_UART_BR_115200
MT_UART_DEFAULT_OVERFLOW 为FALSE
 工程名->右键->Options->C/C++ Compiler->Preprocessor->Defined
symbols:
修改为ZTOOL_P1 xMT_TASK xMT_SYS_FUNC xMT_ZDO_FUNC
xLCD_SUPPORTED=DEBUG
接通串口调试助手,进行串口测试

Zigbee的通讯方式
主要有三种:广播、点播、组播
 广播就是网络中任意一节点设备发出广播数据,网络中其它的任
意节点都能收到
 点播(也叫点对点)就是网络中任意一节点对另一个已知网络地
址(即短地址)的节点进行数据发送的过程
 组播(也叫组网)就是网络中所有节点设备被分组后,网络中任
意组的任意一节点都可以对某一已知组号(包扩自身所属的组号)
的组进行数据发送的过程

在这里只给出单播实现的例子,以SimpleApp为例,其它两个方式原理和这个差不多,读者可自行设置:

参数配置
注:以下代码都需要进行粘贴(在SampleApp.c中)
文件开头定义 afAddrType_t Point_To_Point_DstAddr;
SampleApp_Init中添加参数配置
Point_To_Point_DstAddr.addrMode = (afAddrMode_t)Addr16Bit; //点播
Point_To_Point_DstAddr.endPoint = SAMPLEAPP_ENDPOINT;
Point_To_Point_DstAddr.addr.shortAddr = 0x0000; //发给协调器

添加点对点发送函数
文件后面添加:
void SampleApp_SendPointToPointMessage( void )
{
uint8 data[10]={0,1,2,3,4,5,6,7,8,9};
if ( AF_DataRequest( &Point_To_Point_DstAddr, &SampleApp_epDesc,
SAMPLEAPP_Point_To_Point_CLUSTERID,
10,
data,
&SampleApp_TransID,
AF_DISCV_ROUTE,
AF_DEFAULT_RADIUS ) == afStatus_SUCCESS )
{
}
else
{
// Error occurred in request to send.
}
}

修改参数
 在SampleApp.h中,添加 #define SAMPLEAPP_Point_To_Point_CLUSTERID 5
 SampleApp_ClusterList列表里面添加SAMPLEAPP_Point_To_Point_CLUSTERID,如:
const cId_t SampleApp_ClusterList[SAMPLEAPP_MAX_CLUSTERS] =
{
SAMPLEAPP_PERIODIC_CLUSTERID,
SAMPLEAPP_FLASH_CLUSTERID,
SAMPLEAPP_Point_To_Point_CLUSTERID
};
修改SAMPLEAPP_MAX_CLUSTERS 值为 3
(该部分修改内容可选)
 在if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT )中,替换
SampleApp_SendPeriodicMessage()为
SampleApp_SendPointToPointMessage(),后面添加“LED1 闪烁提示”代码
HalLedBlink( HAL_LED_1, 2,50, 500 );

修改参数
 修改SampleApp_MessageMSGCB中簇ID为
SAMPLEAPP_Point_To_Point_CLUSTERID,并添加如下代码:
HalUARTWrite(0,“I get data\n”,11); //提示接收到数据
for(i=0;i<10;i++)
HalUARTWrite(0,&asc_16[pkt->cmd.Data[i]],1);//ASCII码发给PC机
HalUARTWrite(0,“\n”,1); // 回车换行
定义变量:
uint8 asc_16[16]={‘0’,‘1’,‘2’,‘3’,‘4’,‘5’,‘6’,‘7’,‘8’,‘9’,‘A’,‘B’,‘C’,‘D’,‘E’,‘F’},i;

修改参数
 屏蔽掉(SampleApp_NwkState == DEV_ZB_COORD),如:
if ( /*(SampleApp_NwkState == DEV_ZB_COORD)|| */(SampleApp_NwkState ==
DEV_ROUTER) || (SampleApp_NwkState == DEV_END_DEVICE) )
 找到SampleApp_MessageMSGCB( MSGpkt );
后面添加“LED1 闪烁提示”代码 HalLedBlink( HAL_LED_1, 2,50, 500 );
 在文件开头声明void SampleApp_SendPointToPointMessage(void);
分别以协调器、终端或路由器的方式下载到2-3个设备中,连接串口
注:HalLedBlink( HAL_LED_1, 2,50, 500 ); //LED1 闪烁提示 进行指示
单播通信时,收发双方的端点号必须相同。到这里单播通信方式就实现了。

再此说一下串口:

1.在z_stack中,可以通过预编译和#define来选择是用DMA方式或中断方式进行数据传输。

首先打开hal_board_cfg.h文件,确定有以下语句:

/* Set to TRUE enable DMA usage, FALSE disable it */
#ifndef HAL_DMA
#define HAL_DMA TRUE
#endif

这段语句定义了编译器编译有关DMA的函数

这里引用一下:
任一权,刘童   的 协议栈中的MT包实现UART的发送与接收   
http://www.docin.com/p-658995029.html

Zigbee协议栈已经把使用串口的条件准备好了,从应用角度讲,利用协议栈现有平台即ZStack自带的MT包来实现自己的串口应用,只需对协议栈做一些修改就可以实现URAT发送与接收的功能。

关键函数的解析

MT_UartInit () ①

这是为UART串口传输数据而初始化的MT函数包,在其中定义了串口的波特率,最大接收发送数据量,传输模式等相关配置。在这里的各项参数不需要改变,保持系统默认即可。

MT_UartProcessZToolData() ②

这是MT程序包中URAT接收数据的代码,在这里面需要修改的地方是,在数据接收完毕后需要添加一个回车,便于我们区分不同传输数据,具体的代码为UartRxBuf.RxBuf[count]=‘\n’;此段代码需要添加在count++;后,这里注意一下count,一会还会用到。

hal_board_cfg.h ③

首先说明一下,串口接收发送数据的方式有两种:一种是中断模式,另一种是DMA模式,这里是使用中断模式。而在hal_board_cfg.h中,DMA模式的优先级要高于中断模式的,这里最简单的解决方案就是将DMA的那段预编译注释掉,即仅留一种模式——中断。

void Sampleapp_Init ④

这是应用层函数初始化,即对我们自己的具体应用进行初始化,在这里面需要添加两段代码 MT_UartInit() 和 MT_UartRegisterTaskID(SampleApp_TaskID);这两端代码要添加在Sampl Init()中。

MT_UartInit()

MT_UartRegisterTaskID(SampleApp_TaskID); ⑤

Sampleapp_ProcessEvent ⑥

case SPI_INCOMING_ZTOOL_PORT UartRxComCallBack()

代码功能及执行流程

至此,串口发送接受数据的已经全部修改完毕,可以将程序下载到板子上,利用PC机上的程序调试助手向2530发送数据,在程序调试助手和板子上的液晶屏上分别显示。我们在回顾一下代码的执行顺序

  1. 首先进行各种初始化,这里就不再详解
  2. 程序进去osal_start_system,开始进行轮询有没有要处理的事件
  3. 这时如果从串口发送一个数据,发生中断,(这里我们利用的中断模式)

在OSAL框架中,Hal_ProcessPoll()函数是在一个死循环中,所以每过一定的时间就会执行到。在Z-Stack OSAL中这个时种节奏定义是1ms,Hal_ProcessPoll()的作用是检测中断标志位,由于串口已经接收到数据,所以进入中断处理,具体处理过程如下HalUARTPoll(); ( HalUARTPollISR(); ( static uint16 HalUARTRxAvailISR(void)

(然后就进入了MT_UartProcessZToolData;(详解如下) (数据接收完毕后又进入轮询,由于sampleapp中已经不为空了,所以就会执行sampleapp中的操作,在Sampleapp中的

case SPI_INCOMING_ZTOOL_PORT:

UartRxComCallBack();

break;

这段代码将被执行,UartRxComCallBack();中的代码如上述红字表示,完成我们设定的相应功能。

实验数据

利用程序调试助手测试一下程序的收发速率(波特率为115200)

附:涉及的代码:

① void MT_UartInit ()

{

halUARTCfg_t uartConfig;

/* Initialize APP ID */

App_TaskID = 0;

/* UART Configuration */

uartConfig.configured = TRUE;

uartConfig.baudRate = MT_UART_DEFAULT_BAUDRATE;

uartConfig.flowControl = FALSE;

uartConfig.flowControlThreshold = MT_UART_DEFAULT_THRESHOLD;

uartConfig.rx.maxBufSize = MT_UART_DEFAULT_MAX_RX_BUFF;

uartConfig.tx.maxBufSize = MT_UART_DEFAULT_MAX_TX_BUFF;

uartConfig.idleTimeout = MT_UART_DEFAULT_IDLE_TIMEOUT;

uartConfig.intEnable = TRUE;

#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

/* Start UART */

#if defined (MT_UART_DEFAULT_PORT)

HalUARTOpen (MT_UART_DEFAULT_PORT, uartConfig);

#else

/* Silence IAR compiler warning */

(void)uartConfig;

#endif

/* Initialize for ZApp */

#if defined (ZAPP_P1) || defined (ZAPP_P2)

/* Default max bytes that ZAPP can take */

MT_UartMaxZAppBufLen = 1;

MT_UartZAppRxStatus = MT_UART_ZAPP_RX_READY;

#endif

}

void MT_UartProcessZToolData ( uint8 port, uint8 event )

{

osal_event_hdr_t *msg_ptr;

uint8 ch;

count = 35;

(void)event; // Intentionally unreferenced parameter

while (Hal_UART_RxBufLen(port))

{

HalUARTRead (port, ch, 1);

if((ch == ’ ') (count 31))//帧头

{

UartRxBuf.RxBuf[0] = ch;

count = 1;

}

else if((ch == ‘*’) || (count == 31))//帧尾

{

UartRxBuf.RxBuf[count] = ch;

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

如果你需要这些资料,可以戳这里获取

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

nt8 event )

{

osal_event_hdr_t *msg_ptr;

uint8 ch;

count = 35;

(void)event; // Intentionally unreferenced parameter

while (Hal_UART_RxBufLen(port))

{

HalUARTRead (port, ch, 1);

if((ch == ’ ') (count 31))//帧头

{

UartRxBuf.RxBuf[0] = ch;

count = 1;

}

else if((ch == ‘*’) || (count == 31))//帧尾

{

UartRxBuf.RxBuf[count] = ch;

收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
[外链图片转存中…(img-J9e8nNoc-1715765788259)]
[外链图片转存中…(img-cTzHFTDi-1715765788259)]

如果你需要这些资料,可以戳这里获取

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值