ZIGBEE协议栈介绍
Zigbee是一种低功耗、低成本的无线通信协议,特别适用于物联网设备之间的通信。Zigbee协议栈是一组硬件和软件层级结构,用于支持Zigbee设备之间的通信。
Zigbee协议栈通常由以下几个层级组成:
1. 物理层(Physical Layer):负责处理物理无线通信的细节,例如频率、功率控制和数据传输速率等。物理层使用的是2.4GHz频段,并支持多种调制方式。
2. 媒介接入控制层(Medium Access Control, MAC):负责管理通信介质的访问,以便多个设备可以按照一定的规则共享通信介质。MAC层处理一些基本的网络控制功能,例如设备的寻址、帧结构定义和冲突避免。
3. 网络层(Network Layer):负责设备的路由和组网管理。网络层处理设备之间的寻址、路由选择和数据包转发等任务。它使用分层路由技术,可实现灵活的网络拓扑结构,如星形、网状和多跳网络等。
4. 应用层(Application Layer):负责定义应用层协议和数据格式,以便不同设备之间可以进行有效的通信。应用层定义了设备之间的命令、控制和数据交换等。
Zigbee协议栈的设计目标是提供低功耗、低数据速率和较短通信距离的通信解决方案。它广泛应用于家庭自动化、智能电网、工业自动化和物联网等领域,能够支持大规模设备的无缝连接和通信。
物理层
其中物理层可简单理解为,将一个设备的数据转化为电磁波信号之后,通过物理介质发送给另一个设备,再由另一个设备解读电磁波信号获取数据。
MAC层
MAC(媒体接入控制层)的作用是有序地利用网络通讯资源来进行可靠通信。
由于电磁波和物理介质的限制,同一物理资源在同一时刻只能被一个网络设备占用来发送数据,为控制多个网络设备发送数据。MAC层将设备划分成协调器和普通设备(路由器和终端),协调器产生并发送信标帧,普通设备根据所发出的信标帧和协调器同步,即可组建网络。
网络层(核心协议)
基于IEEE 802.15.4协议之上,主要负责多个设备之间的组网,其优点为支持更多设备进行组网、能够进行多通道通信服务、支持自组网;其次负责设备之间的控制指令和设备状态数据的传输,如温度监测、开关控制;还负责网络安全管理。
目前较多采用的是ZigBee 2007 (Pro)版本。
应用层(一套标准规范,即应用协议)
应用层规定了对象的属性和状态。如灯具备开关的属性,这个属性由开(1)和关(0)两种状态。ZigBee技术开发一般就是指基于ZigBee应用协议的技术开发。
为解决同一领域不同公司设备兼容性问题,ZigBee联盟根据不同领域(Profile)推出了不同的协议。
之后,为解决不同领域的兼容性问题,ZigBee联盟推出ZigBee3.0协议,提升了安全性和稳定性,是目前应用最为广泛的ZigBee应用协议。
TI 技术方案(Chipcon)
TI Z-Stack是ZigBee协议的代码实现, 是TI(德州仪器)针对 ZigBee 3.0 协议而开发的技术方案,也称为TI ZigBee 3.0 协议栈,包含应用层(Z-Stack 3.0)和内核层(2.7.1)。
本次项目采用TI Z-Stack3.0.1_2协议栈以及CC2530芯片进行学习开发,Z-Stack 3.0.1 是到目前为止最新版本。
Z-Stack 3.0官方下载地址 https://www.ti.com/tool/Z-STACK 。
ZigBee设备类型
操作系统的任务调度
任务调度涉及任务、任务池、优先级、轮询和系统调度周期。其中任务池为储存多个任务的缓冲区,适用于任务池的数据结构有多种,比如队列(先进先出)、堆栈(先进后出)和树状结构(遍历),这些数据结构的实现方式可以是静态的数组或者是动态的链表等;优先级为在同一时刻执行任务的等级划分;轮询为系统每隔一段时间在任务池中检查需要处理的任务;“每隔一段时间”指的就是操作系统调度周期。
简单的操作系统原理代码如下:
void main()
{
initLed();//初始化LED灯
taskListInit();//初始化任务池
addTask(2000, TASK_LED_ON);//往任务池中添加一个任务,即2s后打开LED
addTask(3000, TASK_LED_OFF);//往任务池中添加一个任务,即3s后关闭LED
//每隔1ms轮询1次
while(1) {
delayMs(1);//暂停1ms
polling();//轮询
}
}
OSAL(Operating System Abstraction Layer,系统抽象层)
可以通俗地理解为一个简化版的操作系统,为Z-Stack的正确运行提供了内存管理、中断管理和任务调度等基本功能。
ZMain.c文件,可以找到main()函数,其代码如下:
int main( void )
{
// Turn off interrupts
osal_int_disable( INTS_ALL ); // 关闭所有中断
// Initialization for board related stuff such as LEDs
HAL_BOARD_INIT(); // 初始化板载资源,比如PA、时钟源等
// Make sure supply voltage is high enough to run
zmain_vdd_check(); // 检测供电电压是否可以支撑芯片正常运行
// Initialize board I/O
InitBoard( OB_COLD ); // 初始化板载I/O,比如按键配置为输入
// Initialze HAL drivers
HalDriverInit(); // 初始化硬件适配层,比如串口、显示器等
// Initialize NV System
osal_nv_init( NULL ); // 初始化NV(芯片内部FLASH的一块空间)
// Initialize the MAC
ZMacInit(); // 初始化MAC层(数据链路层)
// Determine the extended address
zmain_ext_addr(); // 确定芯片的物理地址
#if defined ZCL_KEY_ESTABLISH
// Initialize the Certicom certificate information.
zmain_cert_init(); // 初始化认证信息
#endif
// Initialize basic NV items
zgInit(); // 初始化存储在NV中的协议栈全局信息,如网络启动方式等
#ifndef NONWK
// Since the AF isn't a task, call it's initialization routine
afInit(); // 初始化AF(射频)
#endif
// Initialize the operating system
osal_init_system(); // 初始化OSAL(操作系统抽象层)
// Allow interrupts
osal_int_enable( INTS_ALL ); // 使能所有中断
// Final board initialization
InitBoard( OB_READY ); // 初始化板载IO资源,比如按键
// Display information about this device
zmain_dev_info(); // 在显示器上显示设备物理地址
/* Display the device info on the LCD */
#ifdef LCD_SUPPORTED
zmain_lcd_init(); // 在显示器上显示设备信息,比如制造商等
#endif
#ifdef WDT_IN_PM1
/* If WDT is used, this is a good place to enable it. */
WatchDogEnable( WDTIMX ); // 启动看门狗功能
#endif
/* 进入系统轮询 */
osal_start_system(); // No Return from here
return 0; // Shouldn't get here.
} // main()
上述代码中有两个关键的函数,代码如下:
//初始化OSAL,包括初始化任务池
osal_init_system();
//轮询任务池
osal_start_system();
osal_init_system()函数代码如下:
uint8 osal_init_system( void )
{
#if !defined USE_ICALL && !defined OSAL_PORT2TIRTOS
// 初始化内存分配系统
osal_mem_init();
#endif /* !defined USE_ICALL && !defined OSAL_PORT2TIRTOS */
// 初始化消息队列
osal_qHead = NULL;
// 初始化OSAL定时器
osalTimerInit();
// 初始化电源管理系统
osal_pwrmgr_init();
#ifdef USE_ICALL
osal_prepare_svc_enroll();
#endif /* USE_ICALL */
// 初始化任务池
osalInitTasks();
#if !defined USE_ICALL && !defined OSAL_PORT2TIRTOS
// Setup efficient search for the first free block of heap.
osal_mem_kick();
#endif /* !defined USE_ICALL && !defined OSAL_PORT2TIRTOS */
#ifdef USE_ICALL
// Initialize variables used to track timing and provide OSAL timer service
osal_last_timestamp = (uint_least32_t) ICall_getTicks();
osal_tickperiod = (uint_least32_t) ICall_getTickPeriod();
osal_max_msecs = (uint_least32_t) ICall_getMaxMSecs();
/* Reduce ceiling considering potential latency */
osal_max_msecs -= 2;
#endif /* USE_ICALL */
return ( SUCCESS );
}
osal_start_system()函数代码如下:
void osal_start_system( void )
{
#ifdef USE_ICALL
/* Kick off timer service in order to allocate resources upfront.
* The first timeout is required to schedule next OSAL timer event
* as well. */
ICall_Errno errno = ICall_setTimer(1, osal_msec_timer_cback,
(void *) osal_msec_timer_seq,
&osal_timerid_msec_timer);
if (errno != ICALL_ERRNO_SUCCESS)
{
ICall_abort();
}
#endif /* USE_ICALL */
#if !defined ( ZBIT ) && !defined ( UBIT )
//主循环
for(;;)
#endif
{
//系统轮询调度
osal_run_system();
#ifdef USE_ICALL
ICall_wait(ICALL_TIMEOUT_FOREVER);
#endif /* USE_ICALL */
}
}
在osal_start_system()函数的主循环中,循环调用了 osal_run_system()函数,该函数主要工作轮询任务池。
void osal_run_system( void )
{
uint8 idx = 0;
/* 更新时间,并整理出到期的任务。系统的时钟周期是:320us */
osalTimeUpdate();
Hal_ProcessPoll();// 硬件适配层中断查询
do {
if (tasksEvents[idx])// 查看是否有任务需要处理
{
break;
}
} while (++idx < tasksCnt);// 轮询整个任务池
if (idx < tasksCnt)//循环结束后,如果idx < tasksCnt表示任务池有任务需要处理
{
uint16 events;
halIntState_t intState;
HAL_ENTER_CRITICAL_SECTION(intState);//关闭中断
events = tasksEvents[idx];//evets中保存了该任务中的待处理事件
tasksEvents[idx] = 0;//清空此任务中的所有待处理事件
HAL_EXIT_CRITICAL_SECTION(intState);//恢复中断
activeTaskID = idx;
events = (tasksArr[idx])( idx, events ); // 处理任务中的事件
activeTaskID = TASK_NO_TASK;
HAL_ENTER_CRITICAL_SECTION(intState);//关闭中断
tasksEvents[idx] |= events;//保存还没被处理的事件到任务中
HAL_EXIT_CRITICAL_SECTION(intState);//恢复中断
}
#if defined( POWER_SAVING ) && !defined(USE_ICALL)
else// Complete pass through all task events with no activity? {
osal_pwrmgr_powerconserve(); //如果没有任务需要处理则进入低功耗
}
#endif
/* Yield in case cooperative scheduling is being used. */
#if defined (configUSE_PREEMPTION)&&(configUSE_PREEMPTION == 0) {
osal_task_yield();
}
#endif
Z-Stack 3.0采用了独热码(one-hot code)的方式对事件类型(UINT16 events)进行编码。
假设events的值为0000 0000 0101 0101,其中的右起第1、3、5和7位为1,于是可以理解为事件集合events包含了用户事件A、C、E和G。可以得到events最多可以包含15种用户事件,和一种系统事件。
创建事件:在zcl_samplesw.h文件中定义一个用户事件,代码如下:
#define SAMPLEAPP_TEST_EVT 0x0040
处理事件:在zcl_samplesw.c文件中的应用层事件处理函数中添加相关的处理,代码如下:
if ( events & SAMPLEAPP_TEST_EVT )
{
printf("Hello World!\r\n");
//把events中的第3位清0,然后把这个值作为函数的返回值,表示events中的这个事件已经被处理了
return ( events ^ SAMPLEAPP_TEST_EVT );
}
触发事件:
在OSAL_Timers.h文件中,可以找到触发事件的API,函数声明如下:
uint8 osal_start_timerEx(uint8 task_id,uint16 event_id,uint32 timeout_value);
如果希望在触发事件的3s后处理刚才自定义的事件,可在应用层初始化函数zclSampleSw_Init()的末尾位置添加如下代码:
osal_start_timerEx(
zclSampleSw_TaskID,//标记本事件属于应用层任务
SAMPLEAPP_TEST_EVT,//标记本事件的类型
3000);//表示3000ms后才处理这个事件
//zclSampleSw_TaskID是一个全局变量,用于标记这个事件是属于应用层任务的。
动态内存的申请和释放
/**
* @fn osal_mem_alloc
*
* @brief 动态申请内存空间
*
* @param size - 申请多少个字节的内存空间
*
* @return 返回该内存空间的指针
*/
void *osal_mem_alloc( uint16 size );
/**
* @fn osal_mem_free
*
* @brief 动态释放内存空间
*
* @param ptr - 待释放的内存空间指针
*
*/
void osal_mem_free( void *ptr );
动态内存操作
/**
* @fn osal_memcpy
*
* @brief 把内存空间的内容复制到另一个内存空间中
*
* @param void* - 目标内存空间
* @param const void GENERIC * - 源内存空间
* @param unsigned int - 复制多少个字节
*
* @return
*/
void *osal_memcpy(void*, const void GENERIC *,unsigned int);
/**
* @fn osal_memset
*
* @brief 把内存空间的值设置为指定的值
*
* @param dest - 内存空间
* @param value - 指定的值
* @param len - 把从dest起的len个字节的存储空间的值设置为value
*
* @return
*/
extern void *osal_memset( void *dest, uint8 value, int len );
示例代码如下:
//事件:SAMPLEAPP_TEST_EVT
if ( events & SAMPLEAPP_TEST_EVT )
{
//字符串:”Hello World!\n”
char *str = "Hello World!\n";
//从堆空间中申请32个字节的内存空间
char *mem = osal_mem_alloc(32);
//如果申请成功
if (mem != NULL) {
//清零内存空间
osal_memset(mem, 0, 32);
//将字符串拷贝到内存空间中
osal_memcpy(mem, str, osal_strlen(str));
//打印内存空间内存
printf(mem);
//释放内存空间
osal_mem_free(mem);
}
//重新触发事件,3000毫秒后执行
osal_start_timerEx(zclSampleSw_TaskID, SAMPLEAPP_TEST_EVT, 3000);
//消除已经处理的事件,然后返回未处理的事件
return ( events ^ SAMPLEAPP_TEST_EVT );
}
内容来自善学坊,如有侵权,立即删除。
ZigBee 3.0 开发指南 (topthink.com)
/**********************************分割线**********************************文章来源:https://www.toymoban.com/news/detail-787928.html
后续章节内容:文章来源地址https://www.toymoban.com/news/detail-787928.html
二、ZIGBEE单播(完成)
三、ZIGBEE串口发送及数据上传(待完成)
到了这里,关于ZIGBEE协议栈通信——阿里云上传数据(STM32)(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!