当前位置:首页 >> 信息与通信 >>

TI-Zstack的OSAL & Message讲解


TI-Zstack 的 OSAL & Message 讲解
奥版和 Cyril3 分别发了关于 OSAL 专题讲座。我也把自己的心得笔记发出 来给大家互相印证参考。

一. Z-Stack 的 OSAL
void osal_start_system( void ) 所有应用程序,无论是自己写的最简单的测试程序还是复杂的 OSAL 操

作 系统,都必须从 main( )来入口。所谓的 OS 操作系统,我们不妨这样想像:自己 写一个最简单的 main( ),里面就一句打印 “Hello, World” .如果需要加入 Key, LED 这样的输入输出功能,那么就需要扩充 main( ),加入 Key, LED 的驱动,如果要实 现多线程调度, 就要加入 Timer 驱动等等。 其实操作系统就是这么来的, 简单吧。 一般在 OS 中都会有个死循环,在这个循环中去处理各种各样的事件。在 Zstack OSAL 中,这个死循环就存在于 osal_start_system()这个函数中。下面来详 细分析在这个死循环中到底在做哪些事情。 OSAL 的“心跳” 在 OSAL 的死循环中,各个事件只是在某些特定的情况下发生,如果 OSAL 一刻不停去轮询去处理这些应用程序,迟早会累死(热量,功耗,寿命…) ,这 样做是完全没有必要的。 所以这里就引入了心跳的概念, 也就是 OS 的时钟节奏。 在 Zstack OSAL 中这个节奏定义为 1ms, 由 8 bits HW_TIMER4 来控制,当然这 些都可以由程序员来修改, 后面就以系统的默认值来讲述。 void InitBoard( byte 在 level )这个函数中有下面这段代码就是在定义系统的心跳 Timer。 HalTimerConfig (OSAL_TIMER,HAL_TIMER_MODE_CTC, HAL_TIMER_CHANNEL_SINGLE, HAL_TIMER_CH_MODE_OUTPUT_COMPARE, OnboardTimerIntEnable,Onboard_TimerCallBack); 在 OSAL 的 Timer 定义好了以后,就要启动 Timer, 至于如何启动 Timer, 请 自行查阅 2430 Spec, 我这里想说的是,在一步步跟踪源码到死循环开始,都没 有发现启动 OSAL Timer 的代码, 最后通过观察 Timer 相关的控制寄存器, 发现, 在网络层初始化函数 nwk_init( taskID++ )执行完毕后 Timer 启动了,也就是说在 网络层初始化函数中有启动 Timer 的语句,因为网络层初始化是不开源的,无从 去看源代码验证,总之,Timer 启动了就好。 每当 1ms 心跳来临时,Timer4 的中断标志置位,这样在 OSAL 的死循环中 检测到这个标志置位后,就去轮询处理各事件。没有检测到这个标志位则继续死 循环。在死循环的开始有调用 Hal_ProcessPoll()这条语句,实际上就是在查询中 断标志并作相应的处理。 OSAL 的“心跳”来临后的处理 上节提到 Hal_ProcessPoll()这条语句,实际上就是在查询中断标志并作相应 的处理。那么当 1ms 心跳来临时,我们跟踪进这个函数看看它到底干了些什么。 当判断到中断标志,表明 1ms 心跳来临了,就去调用 Timer4 相应的回调函 数。 这个回调函数由 HalTimerConfig()的最后一个参数来定义, 请回看上节, OSAL Timer 的回调函数就是 Onboard_TimerCallBack(),一步步跟进,最终调用 osalTimerUpdate()这个函数。在这个函数中会去轮询 Timer 事件链表。 Timer 事件链表是下面这样一个结构,next 指向下一个 Timer 事件,timeout

值表明本 Timer 事件还需要 timeout 个心跳才需要被处理, 因为此处心跳是 1ms, 所以也就是说还需要 timeout 个 ms 才处理。所谓的处理也就是检测 timeout 是否 小于 1ms,如果小于 1ms, 则发出 event_flag 这个消息到消息队列,这个消息隶属 于 task_id 这个任务。如果大于 1ms,说明该 Timer 事件还不到处理的时候,则 Timeout = Timeout-1,然后继续耐心等待下一次心跳。注:Timer 事件链表的维护 是通过 osal_start_timerEx()这个函数来实现的。 typedef struct { void *next; UINT16 timeout; UINT16 event_flag; byte task_id; } osalTimerRec_t; 消息发出后的处理 上节讲到在心跳中发出任务的事件消息到消息队列。 那么这个消息由谁来处 理?回头再看 osal_start_system( )中的死循环,有检测消息队列的语句,当发现 有消息时,判断该消息隶属于哪个任务就去调用对应于该任务的消息处理函数。 各任务的消息处理函数是在 tasksArr[]这个常量数组中定义。这个数组中定义的 消息处理函数和任务初始化函数中的任务必须一一对应。 节电模式 细心的同学会发现在死循环体的后面有调用 osal_pwrmgr_powerconserve() 这样一条语句。 从名字及注释来看, 属于节电模式的调用。 此处不详细列举代码, 只讲其工作原理。 上面章节讲到 1ms 心跳来临时去轮询各事件 Timer 是否需要处理。 这里心跳 很快(1ms),各事件的 Timeout 很慢(往往成百上千)。譬如 Key 检测的 Timer 事件 的 Timeout 是 100,意思是说 100ms 才去检测一次是否有 Key 按下。假如说 Key 检测的 Timout 在各 Timer 事件中 Timeout 最小,那么也就是说有 99 次心跳都不 会有事件需要处理,但是死循环依然在跑,在做无用功,为了解决这个问题,就 加入了节电模式。 在 osal_pwrmgr_powerconserve()这个函数中会检测 Timer 时间链表中 Timeout 最小的值,假设为 next, 然后设定 CPU 进入休眠模式 next 个毫秒。休眠 时间到了苏醒过来立即就会有 Timer 事件需要处理,这样就可以达到省电的目 的。 小结 到此为止,OSAL 神秘面纱已完全揭开,为了巩固知识,下面以 Key 为例讲 述从 Key 按下到 Key 消息被处理的整个过程。 首先在 Key 的初始化过程中会调用下面这条语句:osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE), 这条语句的功 能就是将检测 Key 的这个事件放入 Timer 事件链表。这个事件隶属于 Hal_Task, Timeout 是 HAL_KEY_POLLING_VALUE。 当 1ms 心跳来临时,判断 timeout 是否小于 1,如果不小于,则 timeout=timeout-1 并等待下一次心跳。如果小于 1,则发出 HAL_KEY_EVENT 这个消息到消息队列,然后调用 Hal_Task 的事件处理函数 Hal_ProcessEvent() 在处理这个消息的过程中调用函数 HalKeyPoll()。 处理 HAL_KEY_EVENT 消息,

这个函数检测当前有无按键, 如果有按键并且和上次按键值不同则认为有新的按 键按下并发出相应的按键消息。 在上面这个过程完成后,必须通过 osal_start_timerEx()这个函数将 Key 检测 事件继续放入 Timer 事件链表,以便后面心跳时能检测到该事件,也就是说每 100ms 都会扫描看有无按键按下。

二.消息详解
消息结构:消息头结构+消息数据结构(Key)

消息空间分配函数中秘密 以 Key 消息为例,在调用 byte * osal_msg_allocate( uint16 len )这个函数给 Key 消息分配空间的时候,并不是分配了 len 大小的空间,而是分配了 len+消息 头大小的空间,也就是说预留出了消息头空间以便后面对消息头进行操作。但是 请注意消息空间指针没有指向消息头,而是指向消息数据。这样的做法可以说是 编程上的技巧,为后面“-1”操作埋下伏笔。 “-1”的奥秘 在给消息头赋值时有这样一条语句: OSAL_MSG_ID( msg_ptr ) = destination_task; OSAL_MSG_ID 是个宏,定义如下: #define OSAL_MSG_ID(msg_ptr) ((osal_msg_hdr_t *) (msg_ptr) - 1)->dest_id 很多人对这个宏定义中的-1 感到迷惑, 我们来分析: 上节提到消息空间指针 指向消息数据,也就是这里的 msg_ptr. 而(osal_msg_hdr_t *) (msg_ptr)这条语句 的含义是将该指针强制转换为消息头结构的指针, 这样的话, 后指针将向上移 -1 动消息头结构大小的偏移量,也就是恰好指向消息头结构处,而且因为是强制为 消息头结构指针,自然就找到消息头中的 dest_id 成员,并将 destination_task 赋 值给它。 这样做的好处是效率高, 坏处是不好理解, 当然理解了也就没有坏处了。 消息队列:当一个新消息封装好了以后,就要加入消息队列。由以下语句来 完成。 osal_msg_enqueue( &osal_qHead, msg_ptr );

此处的 osal_qHead 就是消息队列的头指针。 消息队列是由头指针和消息构成,消息又分为两部分:消息头(蓝色)和消 息数据(黄色)构成。所有的消息,消息头结构是完全相同的,但是消息数据部 分,其大小可按任务需要进行定义。

事件数组:消息操作完成后,还需要向系统发个通知,也就是说通知系统某 某任务有消息来了,你去处理一下。事件数组和任务是一一对应的,就是说每个 任务都对应事件数组中的一个单元。对于 Key 来说,就是往事件数组中对应与 Hal 任务的单元中放入 SYS_EVENT_MSG。 可以这样理解: 一个小区 100 户, 每户一个邮箱, 那么小区就有 100 个任务, 100 个邮箱构成事件数组。消息是包裹,事件就是包裹通知单,包裹通知单送达 某户的邮箱,这户人家查看邮箱,发现有包裹通知单,就去领出包裹,领出包裹 后拆开其实就是解析消息后再决定是吃掉它,用掉它,回覆它还是丢掉它等等。 消息的解析:正如上面包裹的例子一样,收到包裹后你一定要打开包裹看看 是什么东西再决定下步的行动,反应到消息上,就是消息的解析,实际上是封装 的反过程。 还是以 Key 为例:Hal 任务的事件处理函数是 Hal_ProcessEvent() ,在这个 函数中收到 SYS_EVENT_MSG 后会调用 osal_msg_receive(Hal_TaskID);这条语 句来进行解析,以下不在叙述。


相关文章:
TI-Zstack的OSAL & Message讲解
TI-Zstack的OSAL & Message讲解_信息与通信_工程科技_专业资料。TI-Zstack的OSAL & Message讲解TI-Zstack 的 OSAL & Message 讲解奥版和 Cyril3 分别发了关于...
Zstack OSAL详解
Zstack OSAL 详解 2010-08-16 13:33 Zstack OSAL 详解 1. void osal_start_system( void )λ 所有应用程序,无论是自己写的最简单的测试程序还是复杂的 ...
飞比开发板学习教程,经典教程
(); // Setup to send message again osal_start_timerEx( GenericApp_TaskID...首先着眼于 TI 公司提供的学习例程的讲解,这其中不但包括 Zstack 2007 的例程...
ZStack OSAL中文说明
ZStack OSAL中文说明_计算机软件及应用_IT/计算机_专业资料。如有错误请多多指...Zstack OSAL详解 2页 1下载券 我心目中的Zstack OSAL ... 5页 免费 ...
奥特曼大哥 之 串口通讯的经典讲解
奥特曼大哥 之 串口通讯的经典讲解_信息与通信_工程...(四)”中,其实已经利用 TI 提供的基本库,从 零...我心目中的 Zstack OSAL & Message(作者:ssls18...
从零开始学习Zstack之8
从零开始学习Zstack之8_调查/报告_表格/模板_实用...首先来看看 TI 究竟有哪些例子: 可以看出其例子是...(byte)osal_strlen( theMessageData ) + 1, (...
OSAL的工作原理
挪威半导体公司 Chipcon( 目前已经被 TI 公司收购)...即 ZStack 协议栈中的 HAL_KEY_SW_1和 HAL_KEY...PeriodicMessage(); // 重新启动定时器 osal_start...
zigbee组网程序解读
osal_mem_init(); // Initialize the message ...if ( ZSTACK_ROUTER_BUILD ) { // NOTE: first...//解析 clusterID 这个消息 inMsg.srcAddr.addrMode...
ZigBee源码程序及解释
osal_mem_init(); // Initialize the message ...zgDefaultStartingScanDurati on, beaconOrder, super...if ( ZSTACK_ROUTER_BUILD ) { // NOTE: first...
OSAL系统框架专题(触发事件三种方式的详细介绍)
OSAL系统框架专题(触发事件三种方式的详细介绍)_计算机硬件及网络_IT/计算机_专业...在 TI 官方 zstack 20 06 中有 4 个例子,其中一个叫 GenericApp 最基本的...
更多相关标签:
zstack osal | messagequeue使用讲解 | zstack | zstack协议栈 | zstack官网 | zstack协议栈下载 | zstack开发指南 | zstack cc2530 2.5.1a |