文档章节

OSAL小记

idaren
 idaren
发布于 2016/06/09 21:06
字数 1784
阅读 217
收藏 7

0 预备

  • 端口 :在OSAL中,为每个子程序分配了一个端口号task_id。
  • 事件 :OSAL的运行是依赖事件进行的。每个事件都有对应的task_id,一个task_id可以对应多个事件。这些事件相当于一个个小的子线程。这些事件什么时候运行是由用户编程实现的。一般的,每个事件会有特定的标志位,这个标志位由特定的操作触发,比如在时间性事件中,当到达特定时间时,其标志位便会被置为1。通过查询这些标志位,我们可以决定是否运行某些事件。
HAL_ENTER_CRITICAL_SECTION(x)
HAL_EXIT_CRITICAL_SECTION(x)
  1. 是将定时器4的计数器EA的值保存起来,并置EA的值为0,相当于禁止了定时器4的中断
  2. 是设置EA的值为给定值,这两个函数经常配合使用。

1 Main函数

安装好的zigbee协议栈中有示例程序,路径在Texas Instruments\ZStack-CC2530-2.5.1a\Projects\zstack\Samples\SampleApp\CC2530DB中,打开路径下面的工程就可以在IAR中查看这个示例程序了。

我们将会一眼看到ZMain并顺手打开ZMain.c文件

看到主函数int main( void ),它在执行了一系列的初始化函数后,便会执行osal_start_system(),双击这个函数然后右键跳转到定义,于是我们看到了熟悉的永无止尽的循环

void osal_start_system( void )
{
#if !defined ( ZBIT ) && !defined ( UBIT )
  for(;;)  // Forever Loop
#endif
  {
    osal_run_system();
  }
}

当程序开始执行,它就会通过循环osal_run_system()不断的运行这个系统,而这个系统就是被称为OSAL的操作系统。于是我们的重点自然就落到了函数osal_run_system()上,不过在此之前,我们先来看osal初始化时做了些什么。

2 初始化OSAL

回到之前的主函数,在很多初始化函数中会看到osal_init_system(),这个就是osal系统的初始化函数。从osalInitTasks()进入,在这个函数的最后一行,就是SampleApp_Init(),这是示例程序在应用层的初始化函数,我们也可以将自己想要做的初始化操作函数放到这里来。从SampleApp_Init进入,就来到了SampleApp.c文件,这个文件位于

这里是应用层,而大部分的应用功能都写在了这里。

重新回到osalInitTasks()可以看到函数下的每个初始化的操作函数都分配了一个taskID,这个taskID相当于应用程序的端口号。因为其是8位的,因此最多能有255个端口号,其中包含了一些特殊的端口号。此处还申请了一个tasksEvents数组,每个taskID将对应其中的一个tasksEvents[x]

tasksEvents[x]是一个16位的标志位集合,一个事件使用一个标志位,一个tasksEvents[x]下可以有16个事件,当到达事件的发生时间时,会将这个标志位置为1。在示例程序中,在SampleApp.c中自定义了一个名为SAMPLEAPP_SEND_PERIODIC_MSG_EVT的事件,它使用了第一位的标志位,因此其值是0x0001。另外与osalInitTasks()在同一个文件中,有一个函数指针数组tasksArr[],这个数组与端口号一一对应,其作用在下文可以看到。

3 OSAL的Timer链表

osal维护了一个链表,其节点的数据结构如下

typedef struct
{
  void   *next;
  uint16 timeout;
  uint16 event_flag;
  uint8  task_id;
  uint16 reloadTimeout;
} osalTimerRec_t;

我们暂且叫它计划表。计划表中的每个节点包含5个数据,第一个是指针,指向下一个节点,第二个是执行时间,第三个是事件标志位,第四个是端口,第六个是重载时间。通过以下函数

uint8 osal_start_timerEx( uint8 taskID, uint16 event_id, uint16 timeout_value )
uint8 osal_start_reload_timer( uint8 taskID, uint16 event_id, uint16 timeout_value )

都可以将一个新的事件注册到计划表里面,不同的是后者为reloadTimeout赋了值,其值等于传进去的参数timeout_value,这个区别将在下文说到。

每次进入osal_run_system(),先执行更新时间的操作osalTimeUpdate(),这个函数先读取定时器2的计数(定时器2是专为mac层提供定时服务的),经过计算后将经过的时间(从上一次更新时间到当前的值)的毫秒数赋值给elapsedMSec,单位是毫秒。并判断elapsedMSec是否有值,即超过1ms的话就执行两个更新操作,分别是

 osalClockUpdate( elapsedMSec );
 osalTimerUpdate( elapsedMSec );

第一个函数更新了系统的时间值,确切说是系统的秒数以及系统的毫秒数。第二个函数遍历了链表即计划表。一个个节点遍历,首先会判断当前节点的timeout是否小于elapsedMSec,大于执行

timeout = timeout - elapsedMSec

小于,则说明当前节点代表的事件到了执行时间了,根据节点的task_id和event_flag可以找到在tasksEvents数组下的该节点的事件标志位,并将其置为1。接下来判断reloadTimeout是否大于0,大于0便将这个值赋值给timeout。这样事件标志就可以在下次到期时被触发,reloadTimeout决定了这个事件是周期事件还是仅仅只发生一次。

回到osal_run_system(),在osalTimeUpdate()之后,是Hal_ProcessPoll(),这个函数主要是去查询硬件上是否触发了其他事件。

4 对事件进行处理

通过上文已经可以知道,当我们将一个事件注册到链表计划表里面时,便可以在特定时间将这个事件对应的事件标志置为1,于是通过查询这个标志就可以知道事件是否应该发生了。而所有的事件的标志位其实都在tasksEvents数组中,这么多的事件,系统每次却只处理一个事件,所以都得排着队来处理。系统检索tasksEvents的每个元素是否有值,有则说明这个端口对应的事件至少有一个被触发,然后从tasksArr数组中选择端口对应的函数指针进行调用。比如示例程序中应用层的回调函数指针是SampleApp_ProcessEvent,而函数的主体则是位于应用层SamleApp.c文件中

uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )

回调时的传参分别是task_id端口号和events该端口号下的事件触发标志。示例程序在SampleApp_ProcessEvent中,第一步先检查定义的系统事件是否被触发,假如没有,就判断SAMPLEAPP_SEND_PERIODIC_MSG_EVT,一个示例程序定义的事件是否被触发,触发了就执行发送信号的操作。无论是哪种事件,最后都需要手动清零标志位,这就是为什么我们会看到类似这种写法,之所以return是因为一次只处理一个事件

return (events ^ SAMPLEAPP_SEND_PERIODIC_MSG_EVT);

总结一下,在应用层,可以自定义事件,并把它注册到链表中去,然后可以在回调函数中判断事件是否发生并提供关于事件的处理方法。

© 著作权归作者所有

共有 人打赏支持
idaren
粉丝 1
博文 16
码字总数 14680
作品 0
广州
程序员
私信 提问
加载中

评论(5)

Ho1den
Ho1den
@|-|
idaren
idaren

引用来自“临峰不畏”的评论

点赞,你是第一个写zigbee程序博文的。
谢谢不过应该不是第一个吧
临峰不畏
临峰不畏
点赞,你是第一个写zigbee程序博文的。
踏雪寻阳
踏雪寻阳
6666
daniel098
daniel098
yyuyy
eclipse 控制台输出了三行红字怎么回事?

eclipse控制台输出了下面这三行红字怎么回事? [WARN ][osal ] Could not enumerate processes (1) error=-1073738819 [WARN ][osal ] Could not add counter (null)\ for query [WARN ][os......

QGP-ROC
2015/01/03
1K
1
[ZigBee] 16、Zigbee协议栈应用(二)——基于OSAL的无线控制LED闪烁分析(下)

说在前面:上一篇介绍了无线LED闪烁实现的OSAL部分,本篇介绍如何实现无线数据收发及数据处理: 上一篇是用SI跟着流程查看源码,我个人认为以架构的思维去了解代码能让人更清晰 ::ZMain.c...

史迪奇2号
01/10
0
0
蓝牙4.0 BLE SimpleBLEPeripheral_添加新CHAR值及UUID

UUID, 就是用来唯一识别一个特征值的ID. handle,就是对应的attribute 的一个句柄。 所有对特征值的操作,都是通过对UUID 的搜索得到对应的handle之后,通过handle来操作特征值的。 添加新的...

lxllinux
10/19
0
0
Zigbee网络设备启动—基本问题说明

记录几个问题: *** 1、 有关设备的启动模式选项:(有待完善): 非自动启动模式 HOLDAUTOSTART:HOLDAUTOSTART is a compile option that will surpress ZDApp from starting the device and...

DB_Terrill
2014/07/23
0
0
[ZigBee] 15、Zigbee协议栈应用(一)——Zigbee协议栈介绍及简单例子(长文,OSAL及Zigbee入门知识)

1、Zigbee协议栈简介   协议是一系列的通信标准,通信双方需要按照这一标准进行正常的数据发射和接收。协议栈是协议的具体实现形式,通俗讲协议栈就是协议和用户之间的一个接口,开发人员通...

史迪奇2号
01/01
0
0

没有更多内容

加载失败,请刷新页面

加载更多

websocket 接口简单测试工具,webcoket实用测试工具

chorme 插件 Simple WebSocket Client 对开发来说够用了,能连互联网,也能连本地的。

SimonAt
14分钟前
1
0
Ext中 get、getDom、getCmp的区别

getDom方法能够得到文档中的DOM节点,该方法中包含一个参数,该参数可以是DOM节点的id、DOM节点对象或DOM节点对应的Ext元素(Element)等。 (与getElementById是一个效果) •Ext.onReady(funct...

休辞醉倒
21分钟前
1
0
springboot jpa 访问数据库

spring boot(五):spring data jpa的使用 SpringBoot非官方教程 | 第三篇:SpringBoot用JdbcTemplates访问Mysql

颖辉小居
36分钟前
4
0
Quartz + Oracle 分布式Job实现

简述 实现 依赖包 <dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.2.3</version><exclusions><exclusion><groupId>com.zax......

呼呼南风
42分钟前
5
0
崛起于Springboot2.X之集成规则引擎Drools(41)

1、创建Springboot项目,勾选Web模块和lombok插件 2、添加pom其他依赖 <dependency> <groupId>org.kie</groupId> <artifactId>kie-spring</artifactId> <version>7.11.0.Fin......

木九天
50分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部