ZSTACK协议栈--OSAL主循环流程

2023-11-22 22:38
文章标签 流程 协议 循环 osal zstack

本文主要是介绍ZSTACK协议栈--OSAL主循环流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

OSAL系统主循环函数:

void osal_start_system( void )
{
#if !defined ( ZBIT ) //不知道是什么东西
for(;;)
// Forever Loop
#endif
{
uint8 idx = 0;

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

//轮询TIMER与UART

//--------------------------
//执行循环语句:tasksEvents[idx]是一个指针变量,指向存放任务idx的存储空间,初始化时由
//osal_memset()设为0,只要不为空类型NULL,
//即有相对应任务事件发生,就break跳出循环体,通过下面的程序进行任务事件处理。
//如果为空,执行判断语句,即idx自增,再返回轮询有无各层的任务事件发生。如果
//执行完循环语句都没有检测到有事件发生,idx=7,进入睡眠。(对于本例子来说,任务数组里只有七个任务,tasksEvents[0]~tasksEvents[6],tasksEvents[6]就是用户自已添加的任务,idx随着用户添加任务的增多而增大

do {
if (tasksEvents[idx])
// Task is highest priority that is ready.
{
break;
}
} while (++idx < tasksCnt); //tasksCnt=7(针对本例子,随着用户应用任务增多而增大)
//-------------------------
if (idx < tasksCnt)
{
uint16 events;
halIntState_t intState;
//中断位状态

HAL_ENTER_CRITICAL_SECTION(intState); //中断临界状态:保存先前中断状态,然后关中断
events = tasksEvents[idx];
//uint16 events;对应有事件发生的任务的数组
tasksEvents[idx] = 0;
// Clear the Events for this task. NULL
HAL_EXIT_CRITICAL_SECTION(intState);
//跳出中断临界状态:恢复先前中断状态

events = (tasksArr[idx])( idx, events ); //调用相对应的任务事件处理函数处理,各类事件处理函
//数M(task_id,event)返回的都是这个任务未被处理的事件

HAL_ENTER_CRITICAL_SECTION(intState);
tasksEvents[idx] |= events;
// Add back unprocessed events to the current task.
//把刚才返回未处理的任务事件添加加当前任务中再进行处理
//(跳出此if(idx < tasksCnt)循环再进行if (tasksEvents[idx])判断并处理)
HAL_EXIT_CRITICAL_SECTION(intState);
}
#if defined( POWER_SAVING )
else
// Complete pass through all task events with no activity?
{
osal_pwrmgr_powerconserve();
// Put the processor/system into sleep
}
#endif
}
}

说明:

(1)OSAL调用Hal_ProcessPoll(); 来轮询UART与TIMER,涉及HAL层,晚点总结.

(2)HAL_ENTER_CRITICAL_SECTION(intState); 与HAL_EXIT_CRITICAL_SECTION(intState); 见1;

(3)events = tasksEvents[idx]; tasksEvents[idx] = 0; 见2;

(4) events = (tasksArr[idx])( idx, events ); 见3;

(5)tasksEvents[idx] |= events; 见4;

//--------------------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------------------

1、HAL_ENTER_CRITICAL_SECTION(intState)与HAL_EXIT_CRITICAL_SECTION(intState)

定义在hal_mcu.h中,如下:

/* ---------------------------------------------------
* Interrupt Macros
* ----------------------------------------------------
*/

#define HAL_ENABLE_INTERRUPTS() st( EA = 1; ) //开中断
#define HAL_DISABLE_INTERRUPTS() st( EA = 0; ) //关中断
#define HAL_INTERRUPTS_ARE_ENABLED() (EA)

typedef unsigned char halIntState_t; //中断位状态

//中断临界状态:把原先中断状态EA赋给X,然后关中断;以便后面可以恢复原先的中断状态 #define HAL_ENTER_CRITICAL_SECTION(x) st( x = EA; HAL_DISABLE_INTERRUPTS(); )

//跳出上面的中断临界状态,恢复先前的中断状态 #define HAL_EXIT_CRITICAL_SECTION(x) st( EA = x; )

//中断临界状态以及跳出临界状态的全过程:
#define HAL_CRITICAL_STATEMENT(x) st( halIntState_t s; HAL_ENTER_CRITICAL_SECTION(s); x; HAL_EXIT_CRITICAL_SECTION(s); )

st()函数,定义在hal_defs.h中:

* This macro is for use by other macros to form a fully valid C statement.
* Without this, the if/else conditionals could show unexpected behavior.

#define st(x) do { x } while (__LINE__ == -1)

//--------------------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------------------

2、events = tasksEvents[idx]; tasksEvents[idx] = 0;

个人认为,tasksEvents[ ]是一个指针数组(但是在协议栈里对这个“数组”的定义我还没有找到,只是在OSAL_SampleApp.c下有这么一个声明:uint16 *tasksEvents;以及系统任务初始函数下对tasksEvents的定义),内部各元素都是指向uint16类型变量的指针变量(对于本例子来说内部各元素即tasksEvents[0]~tasksEvents[6];tasksEvents[0]指向任务1的存储空间……),对events的声明也是 uint16 events;。这里把有事件发生的任务idx的存储空间的地址赋给events。然后清除任务idx的事件 tasksEvents[idx] = 0,(指针变量值为NULL).

//--------------------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------------------

3、 events = (tasksArr[idx])( idx, events ); ——任务idx的事件处理函数

----------------------------

首先看下对tasksArr[ ]的定义:
const pTaskEventHandlerFn tasksArr[ ] = //数组tasksArr[ ]有7个元素(MT defined)
{
macEventLoop, //MAC层任务事件处理函数
nwk_event_loop, //NWK层任务事件处理函数
Hal_ProcessEvent, //PHY层事件处理函数
#if defined( MT_TASK )
MT_ProcessEvent, //MT任务事件处理函数
#endif
APS_event_loop, //APS层任务事件处理函数
ZDApp_event_loop, //ZDO任务事件处理函数
SampleApp_ProcessEvent //用户应用任务事件处理函数
};

----------------------------

再来看下对pTaskEventHandlerFn 的定义:(OSAL_Tasks.h中)

typedef unsigned short (*pTaskEventHandlerFn)( unsigned char task_id, unsigned short event );

//前面有个typedef,把pTaskEventHandlerFn声明成一种函数指针类型的别名
//这种函数指针指向M(task_id,event)这种类型的函数(即各任务的事件处理函数)
//如果用这个别名来生成一个指向函数的指针X,则当X获得函数的地址后,就可以像调用
//原来函数一样来使用这个函数指针X(task_id,event),如上面的pTaskEventHandlerFn tasksArr[ ]
//因此tasksArr[ ]是一个指针数组,其内部的各元素都是指针,从OSAL_SampleApp.c中对tasksArr[ ]
//的定义我们可以知道macEventLoop,nwk_event_loop一直到SampleApp_ProcessEvent这些元素都是
//指针变量,最终都是指向M(task_id,event)这种类型的函数,也就是各层的事件处理函数。

//另外我觉得,就像 pTaskEventHandlerFn tasksArr[ ];之后可以通过tasksArr[ ]直接
//调用tasksArr[ ](task_id,event),也可以通过tasksArr[ ]里面的元素直接调用
//macEventLoop(task_id,event)...SampleApp_ProcessEvent(task_id,event),而这些任务事件处理
//函数都是uint16类型的.因此osal_start_system()里可以events = (tasksArr[idx])( idx, events );events也是uint16类型的。……………………语法上来解释我自己都一团雾水了,个人想法,不一定正确~
//typedef unsigned short uint16;
//typedef unsigned char uint8;

----------------------------

这里events = (tasksArr[idx])( idx, events ); 是因为每一个任务的事件处理函数返回的都是这个任务没有被处理完成的事件。比如说用户应用任务的事件处理函数:SampleApp_ProcessEvent( uint8 task_id, uint16 events ),如下:

每个任务都有不同的事件,因此在进行事件的处理前要进行判断,对于SampleApp

大事件1_接收系统消息:(a)_按键小事件(包含了组发送flash消息事件和进组退组事件)

(b)_接收数据小事件

(c)_设备网络状态变化小事件

大事件2_向外发送消息:(a)_发送 periodic 消息

下面的SampleApp_ProcessEvent()就是通过上面的顺序对用户添加的应用任务进行事件的轮询:

uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
afIncomingMSGPacket_t *MSGpkt; //接收到的消息

/*如果大事件是接收系统消息*/ //则接收系统消息再进行判断
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID ); //接收属于本应用任务SampleApp的消息(SampleApp_TaskID标记。
while ( MSGpkt ) //接收到

{

switch ( MSGpkt->hdr.event ) //系统消息的进一步判断

{

// Received when a key is pressed

/*小事件:按键事件*/
// 如果一个OSAL任务已经被登记注册,那么任何键盘事件都将接受一个KEY_CHANGE事件信息。
case KEY_CHANGE: //#define KEY_CHANGE 0xC0 --Key Events
SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
break;
//执行具体的按键处理函数,定义在sampleAPP.c中


// Received when a messages is received (OTA:over the air) for this endpoint

/*小事件:接收数据事件*/
// 接收数据事件,调用函数AF_DataRequest()接收数据
case AF_INCOMING_MSG_CMD:
// #define AF_INCOMING_MSG_CMD 0x 1A --Incoming MSG type message

SampleApp_MessageMSGCB( MSGpkt ); //调用回调函数对收到的数据进行处理
break;


// Received whenever the device changes state in the network
/*小事件:设备网络状态变化事件*/
// 只要网络状态发生改变,就通过ZDO_STATE_CHANGE事件通知所有的任务,注意,是所有任务都会收到这消息。
case ZDO_STATE_CHANGE: //#define ZDO_STATE_CHANGE 0xD1 --ZDO has changed the device's network state
SampleApp_NwkState = (devStates_t)(MSGpkt->hdr.status); //获取设备当前状态
if ( (SampleApp_NwkState == DEV_ZB_COORD)
|| (SampleApp_NwkState == DEV_ROUTER)
|| (SampleApp_NwkState == DEV_END_DEVICE) )
{
// Start sending the periodic message in a regular interval.
/*按一定间隔启动定时器*/
//这个定时器是为发送周期信息设置的,我觉得因为在这个例子中,用户自己添加的任务,只有两个事件是用于向外发送消息的,一个是发送flash闪烁消息,属于组寻址,而另一个是发送periodic周期消息,属于广播;这里是一个设备的网络状态发生了变化,必须要告诉同一网络中的其它设备,因此要进行广播通知其它设备…发送的消息中应该会包括本设备的类型。……不知道这样理解对不对~管它的,以后会明白的~

//更新:这个定时器只是为发送周期信息开启的,设备启动初始化后从这里开始触发第一个周期信息的发送,然后周而复始下去.
osal_start_timerEx( SampleApp_TaskID,
SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT );
}
else
{
// Device is no longer in the network
}
break;

default:
break;
}

// Release the memory
//以上把收到系统消息这个大事件处理完了,释放消息占用的内存

osal_msg_deallocate( (uint8 *)MSGpkt );

// Next - if one is available
/*指针指向下一个"已接收到的”[程序在while ( MSGpkt )内]放在缓冲区的待处理的事件,与
// SampleApp_ProcessEvent处理多个事件相对应,返回while ( MSGpkt )重新处理事件,
//直到缓冲区没有等待处理事件为止。*/
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );
}

// return unprocessed events
// 两者相异或,返回未处理的事件,
return到osal_start_system()下的events = (tasksArr[idx])( idx, events )语句中,重新在osal_start_system()下轮询再进入此函数进行处理。

return (events ^ SYS_EVENT_MSG);
}
//--------------------------

// Send a message out - This event is generated by a timer
/* 如果大事件是向外发送信息,该事件由定时器产生*/
// (setup in SampleApp_Init()).

if ( events & SAMPLEAPP_SEND_PERIODIC_MSG_EVT ) //发送周期消息事件
{
// Send the periodic message
SampleApp_SendPeriodicMessage();

// Setup to send message again in normal period (+ a little jitter)

//这里为任务SampleApp_TaskID的事件SAMPLEAPP_SEND_PERIODIC_MSG_EVT设定一个定时器,定时时间为 (SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF)),当时间一到,该运行的任务将被通报有事件发生。
osal_start_timerEx( SampleApp_TaskID, SAMPLEAPP_SEND_PERIODIC_MSG_EVT,
(SAMPLEAPP_SEND_PERIODIC_MSG_TIMEOUT + (osal_rand() & 0x00FF)) );

// return unprocessed events
// 两者相异或,返回未处理的事件,同样是返回到osal_start_system()下的events = (tasksArr[idx])( idx, events )语句中。

return (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT);
}

// Discard unknown events 丢弃未知事件(为什么没有发送flash消息的事件!!!???今天发现发送flash消息事件是包含在按键事件里面的~soga)
return 0;
}

//--------------------------------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------------------------------

4、tasksEvents[idx] |= events;

意思是好理解的,返回未处理的事件到任务idx中。但从语法上面来分析,tasksEvents[idx] 已被清0,我实在不知道是存储空间地址上面的或,还是内容上面的或……不钻了,以后再看看~

这篇关于ZSTACK协议栈--OSAL主循环流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/413404

相关文章

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

【Linux】应用层http协议

一、HTTP协议 1.1 简要介绍一下HTTP        我们在网络的应用层中可以自己定义协议,但是,已经有大佬定义了一些现成的,非常好用的应用层协议,供我们直接使用,HTTP(超文本传输协议)就是其中之一。        在互联网世界中,HTTP(超文本传输协议)是一个至关重要的协议,他定义了客户端(如浏览器)与服务器之间如何进行通信,以交换或者传输超文本(比如HTML文档)。

poj3750约瑟夫环,循环队列

Description 有N个小孩围成一圈,给他们从1开始依次编号,现指定从第W个开始报数,报到第S个时,该小孩出列,然后从下一个小孩开始报数,仍是报到S个出列,如此重复下去,直到所有的小孩都出列(总人数不足S个时将循环报数),求小孩出列的顺序。 Input 第一行输入小孩的人数N(N<=64) 接下来每行输入一个小孩的名字(人名不超过15个字符) 最后一行输入W,S (W < N),用

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除

【Go】go连接clickhouse使用TCP协议

离开你是傻是对是错 是看破是软弱 这结果是爱是恨或者是什么 如果是种解脱 怎么会还有眷恋在我心窝 那么爱你为什么                      🎵 黄品源/莫文蔚《那么爱你为什么》 package mainimport ("context""fmt""log""time""github.com/ClickHouse/clickhouse-go/v2")func main(

2024.9.8 TCP/IP协议学习笔记

1.所谓的层就是数据交换的深度,电脑点对点就是单层,物理层,加上集线器还是物理层,加上交换机就变成链路层了,有地址表,路由器就到了第三层网络层,每个端口都有一个mac地址 2.A 给 C 发数据包,怎么知道是否要通过路由器转发呢?答案:子网 3.将源 IP 与目的 IP 分别同这个子网掩码进行与运算****,相等则是在一个子网,不相等就是在不同子网 4.A 如何知道,哪个设备是路由器?答案:在 A

火语言RPA流程组件介绍--浏览网页

🚩【组件功能】:浏览器打开指定网址或本地html文件 配置预览 配置说明 网址URL 支持T或# 默认FLOW输入项 输入需要打开的网址URL 超时时间 支持T或# 打开网页超时时间 执行后后等待时间(ms) 支持T或# 当前组件执行完成后继续等待的时间 UserAgent 支持T或# User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器

Modbus-RTU协议

一、协议概述 Modbus-RTU(Remote Terminal Unit)是一种基于主从架构的通信协议,采用二进制数据表示,消息中的每个8位字节含有两个4位十六进制字符。它主要通过RS-485、RS-232、RS-422等物理接口实现数据的传输,传输距离远、抗干扰能力强、通信效率高。 二、报文结构 一个标准的Modbus-RTU报文通常包含以下部分: 地址域:单个字节,表示从站设备