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

相关文章

Python判断for循环最后一次的6种方法

《Python判断for循环最后一次的6种方法》在Python中,通常我们不会直接判断for循环是否正在执行最后一次迭代,因为Python的for循环是基于可迭代对象的,它不知道也不关心迭代的内部状态... 目录1.使用enuhttp://www.chinasem.cnmerate()和len()来判断for

C#提取PDF表单数据的实现流程

《C#提取PDF表单数据的实现流程》PDF表单是一种常见的数据收集工具,广泛应用于调查问卷、业务合同等场景,凭借出色的跨平台兼容性和标准化特点,PDF表单在各行各业中得到了广泛应用,本文将探讨如何使用... 目录引言使用工具C# 提取多个PDF表单域的数据C# 提取特定PDF表单域的数据引言PDF表单是一

Java循环创建对象内存溢出的解决方法

《Java循环创建对象内存溢出的解决方法》在Java中,如果在循环中不当地创建大量对象而不及时释放内存,很容易导致内存溢出(OutOfMemoryError),所以本文给大家介绍了Java循环创建对象... 目录问题1. 解决方案2. 示例代码2.1 原始版本(可能导致内存溢出)2.2 修改后的版本问题在

PyCharm接入DeepSeek实现AI编程的操作流程

《PyCharm接入DeepSeek实现AI编程的操作流程》DeepSeek是一家专注于人工智能技术研发的公司,致力于开发高性能、低成本的AI模型,接下来,我们把DeepSeek接入到PyCharm中... 目录引言效果演示创建API key在PyCharm中下载Continue插件配置Continue引言

使用MongoDB进行数据存储的操作流程

《使用MongoDB进行数据存储的操作流程》在现代应用开发中,数据存储是一个至关重要的部分,随着数据量的增大和复杂性的增加,传统的关系型数据库有时难以应对高并发和大数据量的处理需求,MongoDB作为... 目录什么是MongoDB?MongoDB的优势使用MongoDB进行数据存储1. 安装MongoDB

Python实现NLP的完整流程介绍

《Python实现NLP的完整流程介绍》这篇文章主要为大家详细介绍了Python实现NLP的完整流程,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 编程安装和导入必要的库2. 文本数据准备3. 文本预处理3.1 小写化3.2 分词(Tokenizatio

SpringBoot使用minio进行文件管理的流程步骤

《SpringBoot使用minio进行文件管理的流程步骤》MinIO是一个高性能的对象存储系统,兼容AmazonS3API,该软件设计用于处理非结构化数据,如图片、视频、日志文件以及备份数据等,本文... 目录一、拉取minio镜像二、创建配置文件和上传文件的目录三、启动容器四、浏览器登录 minio五、

JAVA中while循环的使用与注意事项

《JAVA中while循环的使用与注意事项》:本文主要介绍while循环在编程中的应用,包括其基本结构、语句示例、适用场景以及注意事项,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录while循环1. 什么是while循环2. while循环的语句3.while循环的适用场景以及优势4. 注意

Java如何接收并解析HL7协议数据

《Java如何接收并解析HL7协议数据》文章主要介绍了HL7协议及其在医疗行业中的应用,详细描述了如何配置环境、接收和解析数据,以及与前端进行交互的实现方法,文章还分享了使用7Edit工具进行调试的经... 目录一、前言二、正文1、环境配置2、数据接收:HL7Monitor3、数据解析:HL7Busines

Nginx、Tomcat等项目部署问题以及解决流程

《Nginx、Tomcat等项目部署问题以及解决流程》本文总结了项目部署中常见的four类问题及其解决方法:Nginx未按预期显示结果、端口未开启、日志分析的重要性以及开发环境与生产环境运行结果不一致... 目录前言1. Nginx部署后未按预期显示结果1.1 查看Nginx的启动情况1.2 解决启动失败的