我使用的协议栈版本及例子信息:
ZigBee2006\Texas Instruments\ZStack-1.4.3-1.2.1\Projects\zstack\Samples\SampleApp
OSAL作为操作系统抽象层,是整个Z-Stack运行的基础,用户自己建立的任务和应用程序都必须在此基础上运行,那我们知道整个Z-Stack协议就是用C语言编写的,既然使用C语言编写的,那程序的入口点就是main()函数,而且整个Z-Stack都只有一个main()函数入口,那我们的入口点也是main()函数,我们首先找到main()函数,在SampleApp这个工程文件列表中,我们可以看到ZMain文件,在展开该文件后,就可以看到有一个ZMain.c文件,通过文件名我们也可以看出来,里面应该包括main()函数,那我们首先看看自己添加的应用任务程序唉Z-Stack中的调用过程是怎样的:
(1).main() 执行(在ZMain.c中)
main() ---> osal_init_system()
(2). osal_init_system()调用osalInitTasks(),
(在OSAL.c中)
osal_init_system() ---> osalInitTasks()
(3). osalInitTasks()调用SampleApp_Init()
, (在OSAL_SampleApp.c中)
osalInitTasks() ---> SampleApp_Init()
在osalInitTasks()中实现了从MAC层到ZigBee设备应用层任务处理函数,而用户自己的初始化函数在最后,也就就优先级是最低的,taskID依次增加,taskID越高优先级也就越低。这在下面会看到具体的分析。
1. 首先,我们来看一下main()函数
ZSEG int main( void )
{
// Turn off interrupts 关闭所有的中断
osal_int_disable( INTS_ALL );
// Initialize HAL 初始化硬件抽象层
HAL_BOARD_INIT();
// Make sure supply voltage is high enough to run
//检测电压,以确保提供足够的电压,支持运行
zmain_vdd_check();
// Initialize stack memory 初始化内存中的堆
zmain_ram_init();
// Initialize board I/O 初始化板子上的I/O口
InitBoard( OB_COLD );
// Initialze HAL drivers 初始化硬件抽象层的驱动
HalDriverInit();
// Initialize NV System 初始化非易失性系统
osal_nv_init( NULL );
// Determine the extended address 确定扩展地址 也就是64位的IEEE地址
zmain_ext_addr();
// Initialize basic NV items 初始化基本的NV条目
zgInit();
// Initialize the MAC 初始化化MAC层
ZMacInit();
#ifndef NONWK
// Since the AF isn't a task, call it's initialization routine
afInit();
#endif
// Initialize the operating system 初始化化操作系统
osal_init_system();
// Allow interrupts 打开中断 允许中断
osal_int_enable( INTS_ALL );
// Final board initialization 最终的板载初始化
InitBoard( OB_READY );
// Display information about this device 显示设备的信息
zmain_dev_info();
/* Display the device info on the LCD */ 在LCD屏上显示设备信息
#ifdef LCD_SUPPORTED
zmain_lcd_init();
#endif
osal_start_system(); // No Return from here 没有返回值,启动操作系统
} // main()
注意: 整个main()函数按照一定的顺序的对整个系统进行初始化工作,其中比较重要的,也是和应用开发比较重要的有两个函数,一个是osal_init_system();函数,注释中写的是初始化操作系统,其中和我们相关的是它里面的osalInitTasks();,另一个是osal_start_system();
,所有的初始化后开始真正的启动操作系统。
下面我们就重点看一下osal_init_system();函数,
2. 操作系统初始化流程
2.1.osal_init_system()
byte osal_init_system( void )
{
// Initialize the Memory Allocation System
//初始化内存分配系统
osal_mem_init();
// Initialize the message queue
//初始化消息队列 任务之间的通信就是靠任务队列
osal_qHead = NULL;
#if defined( OSAL_TOTAL_MEM )
osal_msg_cnt = 0;
#endif
// Initialize the timers 初始化定时器
osalTimerInit();
// Initialize the Power Management System 初始化电源管理系统
osal_pwrmgr_init();
// Initialize the system tasks. 初始化系统任务,这个是我们重点关注的
osalInitTasks();
// Setup efficient search for the first free block of heap.设置有效的查找堆上的第一个空闲块
osal_mem_kick();
return ( ZSUCCESS );
}
注意:在这个函数中初始化了操作系统用到的重要信息,比如:内存,堆栈等,但我们重点关注的是初始化系统任务函数osalInitTasks();在这个函数中初始化了Z-Stack自己定义的各层的,和用户自己定义的任务。
2.2 任务初始化函数-------osalInitTasks();
void osalInitTasks( void )
{
uint8 taskID = 0; //任务ID
//osal_mem_alloc()该函数是OSAL中的内存管理函数,是一个存储分配函数,返回指向一个缓存的指针,参数是被分配缓存的大小,其tasksCnt的定义如下const
uint8 tasksCnt = sizeof( tasksArr ) / sizeof( tasksArr[0] );tasksEvents指向被分配的内存空间,这里注意tasksArr[]函数指针数组的联系是一一对应的。tasksEvents就是指向第一个被分配的任务的内存空间。
tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
,tasksCnt任务数 * 单个任务占的内存空间(4byte)
osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));
//下面就是Z-Stack协议栈中,从MAC层到ZDO层的初始化函数,其中的参数都是任务的ID,不过ID号是依次递增的,
macTaskInit( taskID++ ); //mac_ID = 0
nwk_init( taskID++ ); //nwk_ID = 1
Hal_Init( taskID++ ); //Hal_ID = 2
#if defined( MT_TASK )
MT_TaskInit( taskID++ ); //mt_ID = 3
#endif
APS_Init( taskID++ ); //APS_ID =4
ZDApp_Init( taskID++ );//ZDO_ID =5
SampleApp_Init( taskID ); //sapp_ID = 6 这里是用户创建的自己的任务的id,优先级最低
}
注意:任务初始化,就是为系统的各个任务分配存储空间,当然,这个空间初始化时为全0(NULL),然后为各任务分配taskID;这里的顺序要注意.系统主循环函数里tasksEvents[ idx]和tasksArr[ idx]的idx与这里taskID是一一对应关系。我们可以在OSAL_SampleApp.c文件中看到有下面的一个数组的定义,数组中的每个成员都是一个函数,这里的函数和上面的初始化顺序是一样的,也是和tasksEvents所指向的任务的内存地址一保持一致。在这个数组中我们仍然重点观注的是最后一个SampleApp_ProcessEvent,这个是用户应用层任务处理函数。
const pTaskEventHandlerFn tasksArr[] = {
macEventLoop,
nwk_event_loop,
Hal_ProcessEvent,
#if defined( MT_TASK )
MT_ProcessEvent,
#endif
APS_event_loop,
ZDApp_event_loop,
SampleApp_ProcessEvent
};
指针数组tasksEvents[ ]里面最终分别指向的是各任务存储空间
指针数组tasksArr[ ]里面最终分别指向的是各任务事件处理函数
这两个指针数组里面各元素的顺序要一一对应,因为后面需要相应任务调用相应事件处理函数.
由于MAC层和NWK层是不开源的,这里面的代码我们看不到,还是重点看一个用户的任何函数吧,
2.3. SampleApp_init()---用户自己定义的任务处理函数
void SampleApp_Init( uint8 task_id )
{
SampleApp_TaskID = task_id;//OSAL分配的任务ID,从前面可知这里是6.
SampleApp_NwkState = DEV_INIT;//设备状态的初始化,这里的DEV_INIT表示设备初始化并且没有任何的连接,这里在下面具体讲解
SampleApp_TransID = 0;//这是唯一的消息ID
// Device hardware initialization can be added here or in main() (Zmain.c).
// If the hardware is application specific - add it here.
// If the hardware is other parts of the device add it in main().
#if defined ( SOFT_START )
//编译器选项作为协调器时使用
// The "Demo" target is setup to have SOFT_START and HOLD_AUTO_START
// SOFT_START is a compile option that allows the device to start
// as a coordinator if one isn't found.
// We are looking at a jumper (defined in SampleAppHw.c) to be jumpered
// together - if they are - we will start up a coordinator. Otherwise,
// the device will start as a router.
// 检查跳线,决定作为协调器还是作为路由器
if ( readCoordinatorJumper() )
zgDeviceLogicalType = ZG_DEVICETYPE_COORDINATOR;
else
zgDeviceLogicalType = ZG_DEVICETYPE_ROUTER;
#endif // SOFT_START
#if defined ( HOLD_AUTO_START )
//编译器选项如果定义了HOLD_AUTO_STAT,则调用层的ZDOInitDevice,按照默认顺
//序网络中的第一个设备作为协调器,其他的设备作为子设备
// HOLD_AUTO_START is a compile option that will surpress ZDApp
// from starting the device and wait for the application to
// start the device.
ZDOInitDevice(0);
#endif
// Setup for the periodic message's destination address
// Broadcast to everyone
//下面的几行代码都是在设备传送数据的格式
SampleApp_Periodic_DstAddr.addrMode = (afAddrMode_t)AddrBroadcast;//发送模式(广播)
SampleApp_Periodic_DstAddr.endPoint = SAMPLEAPP_ENDPOINT; //指定的端点号EP20
SampleApp_Periodic_DstAddr.addr.shortAddr = 0xFFFF;//指定的网络短地址为广播地址
// Setup for the flash command's destination address - Group 1
//flash消息发送到组
SampleApp_Flash_DstAddr.addrMode = (afAddrMode_t)afAddrGroup;//组寻址
SampleApp_Flash_DstAddr.endPoint = SAMPLEAPP_ENDPOINT;//指定的端点号EP20
SampleApp_Flash_DstAddr.addr.shortAddr = SAMPLEAPP_FLASH_GROUP;//flash发送的组ID,#define
SAMPLEAPP_FLASH_GROUP 0x0001
// Fill out the endpoint description.
//定义设备用来通信的APS层的端点描述符
SampleApp_epDesc.endPoint = SAMPLEAPP_ENDPOINT;//EP号,EP=20
SampleApp_epDesc.task_id = &SampleApp_TaskID
SampleApp_epDesc.simpleDesc
= (SimpleDescriptionFormat_t *)&SampleApp_SimpleDesc;//SampleApp EP的简单描述符
SampleApp_epDesc.latencyReq = noLatencyReqs;
// Register the endpoint description with the AF
//向AF层注册EP描述符,应用程序中的每一个EP都必须使用该函数进行注册,告诉应用层有一个EP已经可以使用
afRegister( &SampleApp_epDesc );
// Register for all key events - This app will handle all key events
//注册所有的按键事件
RegisterForKeys( SampleApp_TaskID );
// By default, all devices start out in Group 1
SampleApp_Group.ID = 0x0001; //组号
osal_memcpy( SampleApp_Group.name, "Group 1", 7 );//组名
aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group );//把组添加到APS中
#if defined ( LCD_SUPPORTED )//如果定义胃LCD_SUPPORTED,在LCD上显示
HalLcdWriteString( "SampleApp", HAL_LCD_LINE_1 );
#endif
}
注意:在上面的程序中有这样一句话,SampleApp_NwkState = DEV_INIT;其中,SampleApp_NwkState是下面这样一个枚举类型,这句话主要是初始化应用设备的网络类型,设备类型的改变都要产生一个事件—ZDO_STATE_CHANGE,说明ZDO状态发生了改变。所以在设备初始化的时候一定要把它初始化为DEV_INIT状态。那么它就要去检测整个环境,看是否能重新建立或者加入存在的网络。但是有一种情况例外,就是当NV_RESTORE被设置的时候(NV_RESTORE是把信息保存在非易失存储器中),那么当设备断电或者某种意外重启时,由于网络状态存储在非易失存储器中,那么此时就只需要恢复其网络状态,而不需要重新建立或者加入网络了*/
typedef enum
{
DEV_HOLD, // Initialized - not started automatically
DEV_INIT, // Initialized - not connected to anything
DEV_NWK_DISC, // Discovering PAN's to join
DEV_NWK_JOINING, // Joining a PAN
DEV_NWK_REJOIN, // ReJoining a PAN, only for end devices
DEV_END_DEVICE_UNAUTH, // Joined but not yet authenticated by trust center
DEV_END_DEVICE, // Started as device after authentication
DEV_ROUTER, // Device joined, authenticated and is a router
DEV_COORD_STARTING, // Started as Zigbee Coordinator
DEV_ZB_COORD, // Started as Zigbee Coordinator
DEV_NWK_ORPHAN // Device has lost information about its parent..
} devStates_t;
上面的代码中还有一个很重要的数据结构,SampleApp_Periodic_DstAddr;它其实就是数据发送模式和地址等参数的设置,
typedef struct
{
union
{
uint16 shortAddr;
} addr;
afAddrMode_t addrMode;
byte endPoint;
} afAddrType_t;
这个结构体在上一篇文章解析AF_DataRequest函数中,有比较详细的介绍,可以参考那篇文章。在SamleAPP的例子中,应用层提供了两中发送数据的方式,一种是周期性发送,一种是Flash发送。 用户应用任务初始化大致是: 在osalInitTasks(
void )函数中添加应用初始化函数SampleApp_Init( taskID )。然后在应用初始化函数中,设置本应用发送数据的方式和目的地址寻址模式,登记注册本应用所用到的端点,以及配置相关发送模式所需的参数.
所有初始化结束以后开始进入osal_start_system(),也就是真正的开始启动操作系统.
这里不过只是把自己的理解和思路重新整
-
VxWorks各部分初始化流程
一)configAll.h中定义所有定置系统配置的宏 INCLUDED SOFTWARE FACILITIES:定义了基本组件: EXCLUDED FACILITIES:定义了扩充组件,缺省不包括: ...
-
Vue.js源码解析-Vue初始化流程
目录 前言 1. 初始化流程概述图.代码流程图 1.1 初始化流程概述 1.2 初始化代码执行流程图 2. 初始化相关代码分析 2.1 initGlobalAPI(Vue) 初始化Vue的全局静态AP ...
-
spring自动扫描、DispatcherServlet初始化流程、spring控制器Controller 过程剖析
spring自动扫描1.自动扫描解析器ComponentScanBeanDefinitionParser,从doScan开始扫描解析指定包路径下的类注解信息并注册到工厂容器中. 2.进入后findCa ...
-
【开源】OSharp3.3框架解说系列(7.1):初始化流程概述
OSharp是什么? OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现.与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现.依 ...
-
SpringMVC源码剖析(三)- DispatcherServlet的初始化流程
在我们第一次学Servlet编程,学Java Web的时候,还没有那么多框架.我们开发一个简单的功能要做的事情很简单,就是继承HttpServlet,根据需要重写一下doGet,doPost方法,跳转 ...
-
u-boot中nandflash初始化流程分析(转)
u-boot中nandflash初始化流程分析(转) 原文地址http://zhuairlunjj.blog.163.com/blog/static/80050945201092011249136/ ...
-
Raid1源代码分析--初始化流程
初始化流程代码量比较少,也比较简单.主要是run函数.(我阅读的代码的linux内核版本是2.6.32.61) 四.初始化流程分析 run函数顾名思义,很简单这就是在RAID1开始运行时调用,进行一些 ...
-
main之前初始化流程
main之前初始化流程 本文分别介绍Keil调用的ARMCC以及ARM-NONE-EABI-GCC两个编译器在main之前的操作: Keil MDK启动文件 总结一下MDK的启动流程: 1.系统初始化 ...
-
关于Flutter初始化流程,我必须告诉你的是...
1. 引言 最近在做性能优化的时候发现,在混合栈开发中,第一次启动Flutter页面的耗时总会是第二次启动Flutter页面耗时的两倍左右,这样给人感觉很不好.分析发现第一次启动Flutter页面会做 ...
随机推荐
-
javascript this在事件中的应用
this关键字在javascript中是非常强大的,但是如果你不清楚它是怎么工作的就很难使用它. function dosomething(){ this.style.color="#fff ...
-
由Qt4.x项目移植到Qt5.x需要注意的事项
The Transition from Qt 4.x to Qt 5 The transition from Qt 4.x to Qt 5 is not expected to be signific ...
-
tarjan算法(割点/割边/点连通分量/边连通分量/强连通分量)
tarjan算法是在dfs生成一颗dfs树的时候按照访问顺序的先后,为每个结点分配一个时间戳,然后再用low[u]表示结点能访问到的最小时间戳 以上的各种应用都是在此拓展而来的. 割点:如果一个图去掉 ...
-
Qt---自定义界面之 Style Sheet
这次讲Qt Style Sheet(QSS),QSS是一种与CSS类似的语言,实际上这两者几乎完全一样.既然谈到CSS我们就有必要说一下盒模型. 1. 盒模型(The Box Model) 在样式中, ...
-
【毕业原版】-《伦敦艺术大学毕业证书》UAL一模一样原件
☞伦敦艺术大学毕业证书[微/Q:865121257◆WeChat:CC6669834]UC毕业证书/联系人Alice[查看点击百度快照查看][留信网学历认证&博士&硕士&海归& ...
-
Hibernate的入门(概念1):
什么是持久化类? 持久化类的编写规则? 区分自然主键和代理主键? 主键生成策略?
-
Java程序员如何选择未来的职业路线
一.程序员的特性 技术出身的职场人特性很明显,与做市场.业务出身的职场人区别尤其明显.IT行业中常见的一些职场角色:老板.项目经理.产品经理.需求分析师.设计师.开发工程师.运维工程师等.开发工程师具 ...
-
Linksys E 刷Tomato shibby
前言 一直以来都用Linksys的无线路由器~因为它的稳定~多年来一直用Linksys自身的固件~因为之前没用它做什么特别的应用~所以一直用了下来~它的原厂固件的稳定性也从没让我操过心~近来要为用户提 ...
-
web程序打包详解
重要更新:鉴于很多小伙伴们说看不到图,我这边换了几个浏览器看了下,都看得到的,估计是网速问题,请耐心等待,另外,为了更好的方便大家学习,特此提供源码以及一个word文档,word文档就是本文内容 ...
-
ptrace注入型病毒“聊天剽窃手”分析
概述 “聊天剽窃手”Windseeker是一款间谍软件,它使用了ptrace进程注入技术,能够对微信和QQ的聊天记录进行监控. 软件安装后的桌面图标和启动界面如图所示: 行为分析 该应用首先获取手 ...