7天快速入门Zigbee:如何在协议栈中从零建立自己的任务

7天快速入门Zigbee:如何在协议栈中从零建立自己的任务

点击左上角的“关注”,定期更新Zigbee最新资讯,总有你想要的信息!

目录

  1. 概述
  2. Zigbee协议栈的运行机制
  3. 建立自己的任务并添加到系统中
  4. 在系统中使用自己的任务

1 概述

  这篇文章主要是要让大家了解清楚如何在Zigbee协议栈当中从零建立起自己的任务。
  什么叫从零建立起自己的任务呢?从零建立起自己的任务就是将Zigbee协议栈当中的应用层任务部分的代码全部删除自己重写。
  为什么要重新建立自己的任务呢?第一,因为协议栈的应用层任务代码写的十分的杂乱,与其在它的任务代码当中添加自己的代码,不如重新写一份,让应用层的代码变得更加的清晰、可控。第二,自己从零写一遍应用层的代码可以更好的认识整个Zigbee协议栈的结构。第三,少了协议栈官方繁杂的代码,可以更加清晰的将自己的功能模块化。

2 Zigbee协议栈的运行机制

  Zigbee协议栈的运行机制类似于非抢占式实时操作系统,是一种基于事件驱动的轮询式操作系统。其基本原理与我们平时写的嵌入式程序是一样的。
  平时我们写的嵌入式程序基本的框架都是先写“初始化程序部分”,然后再在while循环里面加入我们要添加的任务轮询,当然程序中还有许多中断服务,这些就构成了我们的一个嵌入式程序基本框架。
嵌入式程序基本框架:

void main(void)
{
  // 初始化程序
  ……
  while(1)
  {
    // 任务轮询
    ……
  }
}

void xxx_ISR(void)
{
	// 中断处理
	……
}

  Zigbee协议栈的运行机制和上面我们说到的普通嵌入式程序框架基本是一样的。首先我们先来找到Zigbee协议栈的“main函数”在哪里。“main函数”在“ZMain.c”文件当中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z3yNDXDQ-1605513407102)(https://i.imgur.com/kzLXCcE.png)]
Zigbee协议栈中的main函数内容:

int main( void )
{
  // 初始化程序
  ……
  // 任务轮询
  osal_start_system();
}

  我们可以看到,在Zigbee协议栈的“main函数”当中,也是和上面的“嵌入式程序基本框架”一样,在“osal_start_system();”函数前面的全部都是初始化函数,初始化各种驱动。“osal_start_system();”函数里面是一个for(;;)语句的无限循环。现在我们就先不看那些初始化程序,先来研究下这个无限循环里面是怎么实现任务的调度的。
  我们先把循环代码贴在下面,去掉一些现在对我们研究任务调度无用的宏定义,让代码变得更整洁一些。

void osal_run_system( void )
{
  uint8 idx = 0;

  osalTimeUpdate();	// 不用管
  Hal_ProcessPoll();	// 不用管

  // 查询“任务列表”里有无任务有事件发生要处理
  do {
    if (tasksEvents[idx])  // Task is highest priority that is ready.
    {
      break;
    }
  } while (++idx < tasksCnt);

  // 处理任务的事件
  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);

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

    HAL_ENTER_CRITICAL_SECTION(intState);
    tasksEvents[idx] |= events;  // Add back unprocessed events to the current task.
    HAL_EXIT_CRITICAL_SECTION(intState);
  }
}

  从上面的这一段代码我们可以看到,整个任务轮询代码就在处理两件事情,“查询有无任务有事件发生”和“处理任务的事件”。那么怎么产生任务事件呢?这时候就有两个常用的函数:uint8 osal_set_event( uint8 task_id, uint16 event_flag )uint8 osal_start_timerEx( uint8 taskID, uint16 event_id, uint16 timeout_value ),这两个函数都是用来通知任务有事件发生。当任务有事件发生时,会在相对应的“事件处理函数”中进行处理。

3 建立自己的任务并添加到系统中

3.1 建立自己的代码文件
  建立自己的代码文件“Gateway.c”和“Gateway.h”文件到“Z-Stack Mesh 1.0.0\Project\zstack\Sample\GenericApp\Source”文件夹中,然后将文件添加到工程中(右键点击Workspace框中的“App”文件夹->Add->Add File,找到自己的代码文件,然后把它们添加进工程)。
3.2 屏蔽掉源码原来的应用层文件
  屏蔽方法:右键点击Workspace框中的“GenericApp.c”文件->“Option”->打勾弹框左上角的“Exclude from build”,这样编译器在编译工程时便不会将此文件编译在内;同理屏蔽掉“GenericApp.h”文件。
  操作完成后APP文件夹应如下图所示:
操作完成后APP文件夹应如上所示
3.3 建立自己的任务初始化函数和任务处理函数
  模仿“GenericApp.c”中的源码在“Gateway.c”文件中建立自己的任务初始化函数和任务处理函数。

-------------------------------------------- Gateway.c ---------------------------------------
#include "OSAL.h"
#include "AF.h"
#include "ZDApp.h"
#include "ZDObject.h"
#include "ZDProfile.h"
>
#include "GenericApp.h"
#include "DebugTrace.h"if !defined( WIN32 ) || defined( ZBIT )
  #include "OnBoard.h"
#endif
#include "Gateway.h"

// 任务初始化函数
void Gateway_Init( uint8 task_id )
{
  
}
// 任务处理函数
uint16 Gateway_ProcessEvent ( uint8 task_id, uint16 events )
{
  return 0;
}
-------------------------------------------- Gateway.h ---------------------------------------
#ifndef __GATEWAY_H
#define __GATEWAY_H

#ifdef __cplusplus
extern "C"
{
#endif

void Gateway_Init( uint8 task_id );
uint16 Gateway_ProcessEvent ( uint8 task_id, uint16 events );

#ifdef __cplusplus
}
#endif
#endif

3.4 将自己的任务初始化函数和任务处理函数添加到协议栈系统的运行机制中
  在“OSAL_GenericApp.c”文件中添加自己的任务初始化函数和任务处理函数到系统的运行机制中。
1) 添加新函数的头文件:

//#include "GenericApp.h"
#include "Gateway.h"

2)添加任务初始化函数:
这里写图片描述
3)添加任务处理函数:
这里写图片描述
  按下编译键,0错误0警告。到此我们自己的任务就建立完成了。

4 在系统中使用自己的任务

  我们新建的任务已经加入到Zigbee协议栈中了,那么该如何使用我们的任务呢?
  我们可以利用我们上面提到过的uint8 osal_set_event( uint8 task_id, uint16 event_flag )uint8 osal_ start_timerEx( uint8 taskID, uint16 event_id, uint16 timeout_value )这两个函数做一个闪烁LED灯的功能来演示如何使用自己的任务。
  先在“Gateway.h”中定义“LED灯闪烁事件”号:#define EVENT_FLASH_LED 0x0001。然后在“Gateway.c”中的任务初始化函数中利用osal_set_event()函数通知自己的任务有“LED灯闪烁事件”发生。

------------------------------------------ Gateway.c ------------------------------------------
static void Init_IndicatorLight(void)
{
  // P0_7,LED1,低电平亮,高电平灭
  P0SEL &= ~(1<<7);     // 通用IO口
  P0DIR |= (1<<7);      // IO口方向输出
  P0_7 = 1;             // LED灯灭
}

void Gateway_Init( uint8 task_id )
{
  g_gateway_taskid = task_id;
  // 初始化LED灯
  Init_IndicatorLight();
  // 通知g_gateway_taskid任务有LED灯闪烁事件发生
  osal_set_event(g_gateway_taskid, EVENT_FLASH_LED);
}

  然后在自己的任务处理函数中处理该事件。在处理函数中定时500毫秒通知g_gateway_taskid任务有LED灯闪烁事件发生,这样LED灯就会每秒亮灭一次,达到闪烁LED灯的效果。

uint16 Gateway_ProcessEvent( uint8 task_id, uint16 events )
{
  // 收到LED灯闪烁事件
  if ( events & EVENT_FLASH_LED )
  {
    // 置反LED灯
    if(P0_7==1)
    {
      P0_7 = 0;
    }
    else
    {
      P0_7 = 1;
    }
    // 定时500毫秒后通知g_gateway_taskid任务有LED灯闪烁事件发生
	osal_start_timerEx(g_gateway_taskid, EVENT_FLASH_LED, 500);
	return (events ^ EVENT_FLASH_LED);
  }

  return 0;
}

  按下编译按钮,0错误0警告,然后再将程序烧录到CC2530设备中,我们就可以看到1秒钟闪烁一次的LED灯了。
  软件源码的下载地址在下面的评论区有给出。
大家的支持就是我分享技术的动力,希望大家需转载时能附上原作者的博客:https://blog.csdn.net/u012993936,谢谢。



--- End ---
你可能还想看:

> Zigbee进阶:功能模块
> 免费的Zigbee抓包神器!比Ubiqua还好用!


文章都看完了,随手点个赞吧~
在这里插入图片描述
↓↓↓ ↓↓↓

Zigbee入门开发 Zigbee是一种新型的短距离无线通信技术。其特点是低功耗,低成本,组网灵活。Zigbee协议由zigbee联盟指定,包括应用层,和网络层,其下层采用IEEE802.15.4协议。 无论是学习zigbee技术,还是利用zigbee技术开发产品,都需要较好的掌握zigbee协议,并比较深入的了解IEEE802.15.4协议.然而仅zigbee协议就接近400页,而IEEE802.15.4协议多达600页,全部是英文。如果从来没有接触过无线通信的开发,要迅速掌握这么多内容确实有一些难度,笔者考虑到广大急切进入zigbee的同仁者,结合自己开发总结出的理解协议的特定方法,从开发的角度,阐述协议内容,以达到抛砖引玉的效果,同时和广大同仁交流,共同提高,并希望zigbee技术能够在国发扬光大。 协议从功能实现来讲,ZigBee协议层共包括物理层(又称实体层)、MAC层、数据链接层、网络层和应用支持层五个主要层次。在标准制定的分工上,ZigBee协议层是由ZigBee联盟和IEEE802.15.4的任务小组共同完成的。其,物理层(又称实体层)、MAC层、数据链接层,以及传输过程的资料加密机制等都是由IEEE所主导的。网络层和应用支持层则由ZigBee联盟来完成。IEEE802.15.4小组与ZigBee联盟共同针对ZigBee协议栈的发展进行研究,而未来还能依据系统客户的要求来修正其所需的应用界面。如图1所示: Zigbee协议整体架构 作为理解协议,从开发者的角度来讲,这样学习协议是比较费时间的,也较难掌握。笔者从自己长期开发的经验来看,对无线通信,最重要的就是在发送端根据用户的要求,把数据能够扔出去,并且是扔到指定的设备,在接收端,能够把发送到该设备的数据捡起来。并根据用户要求的作特定的处理。如果考虑在一个无线网路的话,就得首先建立网络,其他得设备加入网络。最后是才是一些其他的问题,如设备离开网络,设备重新加入网络,等等。 这样从通信过程去理解协议,将会使得协议的内容非常明了,也可以很好的知道利用协议开发产品,达到事半功倍的效果。 本人将根据这样的思路,分五期具体讲述协议内容: 第一期:设备建立网络过程 第二期:设备加入网络过程 第三期:数据发送过程 第四期:数据接收过程 第五期:其他的问题 zigbee设备建立网络过程 在一个zigbee网络,只有协调器(coordinator)设备可以建立网络,在建立网络过程,所有的实现过程都是通过原语实现的(具体在程序,如用c语言,就是调用一个一个函数,或者是一个一个任务来实现(不要刻意最求这句话的准确性,本来好的协议就是只要达到这样的结果,不管具体实现)),首先协调器设备的应用层调用NLME_NETWORK_FORMATION.request原语,发出建立网络请求,网络层收到这个原语,就要求MAC层执行信道能量扫描(在IEEEE802.15.4协议规定,在2.4G频段,共有16个信道,每个信道的带宽为5M,深入理解,可以看协议和通信原理的相关资料)。这调用MLME_SCAN.request,主要找到信道能量低于设定能量值的信道,并且标注这些信道是可用信道,下 一步就在可用信道执行活动情况扫描(active scan),就是在可接受的信道
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jesse_嘉伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值