当前位置:文档之家› OSAL实验1-OSAL架构预览

OSAL实验1-OSAL架构预览

无锡谷雨电子有限公司

OSAL实验一

OSAL架构预览

https://www.doczj.com/doc/ac637071.html,

2014/3/28

OSAL是协议栈的躯干,可以理解为一个超级简单的操作系统

目录

1前言 (2)

2必要条件 (2)

3原理图 (2)

4编程参考 (2)

5准备工作 (2)

6 OSAL架构预览 (9)

6.1 OSAL 运行原理 (10)

6.2 OSAL 实现 (11)

1前言

经过前面基础实验的学习,我们已经十分了解CC2530芯片了。当然这并不是我们的目的,它仅仅是我们学习Zstack协议栈过程必经历地一个过程。今天开始,我们就要来学习OSAL了,尽管现在对osal还非常陌生,但是请相信,在学习完我们的OSAL教程后,OSAL 的结构甚至Zstack协议栈的结构,你也会七八分的把握。并且会了解它是如何运行的。

2必要条件

A硬件

1、SmartRF系列开发板,CC2540或者CC2541

2、CC-Debugger仿真器

B软件

1、IAR for 8051开发环境,版本:8.10

2、Flash Programmer固件烧写软件。

3、Source Insight3.5代码阅读软件

3原理图

在基础实验里的原理图展示,在这里已经不适用了,因为OSAL还包括一层hal,硬件抽象层,硬件抽象层里已经做好了开发板上的各种外设驱动。我们只需调用相关函数即可。

4编程参考

OSAL实验中,我们需要参考的有两份文档,通过阅读这个两分文档助于我们对相关函数的理解。

1、HAL Driver API.pdf(位于协议栈安装目录Texas

Instruments\ZStack-CC2530-2.5.1a\Document),对于英语比较好一点的读者可以直接阅读这份英文资料,这个是由TI提供的。还有一份中文文档在我们提供资料的目录下CC2530开发板资料\0开始:入手开发套件\协议栈中document中文件中文翻译\HAL 驱动API(中).pdf

2、OSAL API.pdf(位于协议栈安装目录Texas Instruments\ZStack-CC2530-2.5.1a\Documents),

同样我们提供资料的目录下CC2530开发板资料\0开始:入手开发套件\协议栈中document中文件中文翻译\OSAL API(中).pdf也是一份中文文档

5准备工作

这时我们第一次分析比较庞大的源码,因此分析工具的选择显的十分重要,我们这里使用Source Insight3.5来阅读分析源码,这一节,我们使用SourceInsight3.5来创建一个代码阅

读工程。SourceInsight3.5工具软件在CC2530开发板资料\0开始:入手开发套件\0、软件工具文件夹内。安装在这里省略,不做详细说明。

5.1 新建一个Source Insight工程

打开SI软件,选择Project->New Project..

输入想要创建的SI工程名称,随便填写一个就好;在保存路径中设定为你想要保存的位置,然后单击OK。

然后会出现寻则要包含的源码对话框,单击Browse..定位到我们的【实验1 OSAL架构预览】里的【OSAL实验】文件夹单击确认,然后在单击OK

源码路径设置之后,然后需要选择包含哪些源码,一般是包含所有源码,所以选择Add Tree,这时就会跳出一个对话框,提示当前找到了多少个源文件,选择OK,然后Close。

这样,一个Source Insight工程就创建ok了,我们来打开刚才创建的工程,选择Project->Open Project

选择刚才创建的工程名:SimpleOsal,然后单击OK,就打开了这个工程。

工程打开后,可以通过右边的资源管理器,找到要阅读的源码即可。

6 OSAL架构预览

我们已经做好了所有准备工作,只缺仔细阅读以下代码。在分析代码之前,我先说明一下。刚才我们有SI创建的工程文件的源位于CC2530开发板资料\2中级;OSAL学习指南\OSAL实验1 OSAL架构预览文件夹下的OSAL_OS文件夹下的代码。这个代码是经过整理的OSAL操作系统的源码,是不包含ZStack协议栈的任何东西的。我们这样做的目的为让OSAL与ZStack 的分开,有助于我们分析OSAL的运行机制。我们这样做是有理论依据的,原因是这样的。ZStack协议栈是寄存在OSAL上运行ZIGBEE协议,所以将ZIGBEE的东西去掉是没有问题的。这样做反而带来了好处。

1.内容单一、专业化对我们学习OSAL十分有益

2.使OSAL与zigbee的两个东西分开,不会混乱

OSAL是系统抽象层的缩写,负责所有任务函数的调度,消息事件的管理等。

Hal是硬件抽象层的缩写,相当于底层硬件驱动,例如LED、按键、ADC、UART、LCD 等均已封装成api函数,在OSAL的任务函数中调用即可。

OSAL消息事件的运用以及Hal层API函数的调用,均是我们的OSAL实验内容。而本位,只分析OSAL架构,从main函数着手,一步一步的讲清楚各部分的运行原理。

首先打开OSAL实验的IAR工程。位置:【实验 1 OSAL架构预览\OSAL实验

\OSAL_OS\TestOSAL.eww】

然后双击打开IAR工程文件:TestOSAL.eww

在IAR工程workspace窗口中显示了OSAL工作的目录。注意这里目录并不是我们OSAL_OS文件夹下的目录。这里的目录是我们自己创建的IAR工作组,只是和我工程文件夹下的目录同名而已,这是个习惯。

6.1 OSAL 运行原理

OSAL 既然称之为操作系统抽象,那么她一定具用嵌入式操作系统的一些特点。嵌入式操作系统一般含有以下几个部分

1.系统的任务,系统管理的基本对像。有用户任务,系统任务之分。

2.系统时钟。这个是操作系统的核心,是任务间切换基本依据。

3.任务间的同步与通信。这是各个任务间协作完成大的工作的前提。

4.动态内存管理。

下面我们一步一步地分析OSAL是如何实现上面的功能的。

OSAL中任务可以分为两类,一类是软件任务,一类是硬件任务(这是笔者自己人为分类,肯定不准确,但是为书写方便)。硬件任务主要是管理板上的外设,比如按键,LED。而软件任务主要是由有用户自己定义,功能可以是各种各样的。

OSAL 又是如何管理系统中的任务的呢?其实OSAL 中有一个任务注册表(笔者自己称为描述方便),即tasksArr。这是个函数指针数组,用来存放各个系统函数。且每个任务都有一个任务号,相当于任务的一名字,但是这个任务号与任务函数在任务注表的位置相同。所以通过任务号就可以轻松地访问各个任务了。tasksCnt用于记录当前一共有多少个任务。

OSAL中的系统时钟。第个操作系统都会有自己的时钟,特别是那实时很强的嵌入式操作系统,它可以理解为人类的心脏,也称之为“心跳”。你想如果一个人没有了心跳就等于死亡,所以操作系统的心跳是十分重要的。它是任务切换的依据。OSAL 中的心跳是1ms,

即1000次/秒。它是以定时器产生精确的时间间隔产生的。在OSAL中任务的切换就是以这个心跳个数来确定的。声明一下,OSAL 是一个不可录夺内核系统,一个时间片轮转的系统。所以说在OSAL中如果想让任务不断的运行,就要给这个任务分配一个激活时间,当规定的时间到了以后,该任务就会被唤醒,执行完之后再次睡眠。在OSAL中Hal_ProcessEvent硬件任务就是有这样的特性。那么OSAL 又是如何实现的呢?原来它给这个任务分配了一个任务时间控制块,任务时间控制块它就是一个线性链表中的一节点。这个节点的详细说明查找源代码中osalTimeRec_t的数据类型。当规定的时间到时,OSAL就会对这个时间控制块链表进行遍历(从表头到表尾),对节点的timeout-1,如表发现timeout = 0,就会向任务的邮箱(将在任务间的同步与通信讲解)发送一个通知。OSAL在查询邮箱时发现有通知就会去调用相应的任务函数去处理这个通知。

任务间的同步与通信。这也是操作系统主要的组成部分,它是各个任务合力完成一个任务的基础。那么在OSAL又是如何实现这个的呢?原来OSAL在进行任务注册的时候,OSAL 根据注册表中任务个数,为每个任务创建一个邮箱。各任务邮箱地址也是根据任务任务号来确定的。在OSAL中邮箱是存在tasksEvents中,taskEvents含有邮箱个数与任务数相等。邮箱在OSAL中有举足轻重力量。任务间的通信就是靠邮箱来完成的。下我们就说说邮箱的作用。当一个任务要向另一个任务发一条消息,它就会创建一个消息并放到消息队列中,同时接收任务的邮箱发送一个通知。接收任务发现邮箱里有通知,它就会去消息队列中找到自己的消息,并打开查看是什么消息,然后根据消息的内容去做相应的事。这样它们之间就完成了一次配合。

OSAL 与ucos-II、FreeRTOS等嵌入式操作系统相比,几乎都不能被称为一个操作系统。虽然OSAL是一个小小小小的操作系统,但小而全,它也是有动态内存管理的。OSAL将系统的堆分成两个区域,一个是小区域,一个是大区域。根据OSAL_MEM_Alloc函数提出的需求,在动态内存分一个指定长度的连续地址空间,并调整指针指向。OSAL_MEM_FREE负责将不用内存归还给系统。

上面通过系统任务、系统时钟、任务间的同步与通信、动态内存管理四个方面讲述了OSAL微型操作系统的运行的机制。下面将在有上述理论基础之上,解说OSAL是如何实现的。

6.2 OSAL 实现

在6中打开的IAR工程中,仔细观察左边workspace窗口中,发现有一个user->Appmain.c 的文件,这是OSAL 操作系统启动的入口文件,即main函数就被定义在这里。user->app.c 这个文件中定义了用户任务相关的代码。双击打开Appmian.c,我们会发现main函数。

这是程序上电后首先会运行的地方。如下图,通过仿真器单步调试时,首先会到这里。

Main函数如下图,104行启动了osal大循环,在此之前,全部是硬件或者软件的相关初始

化工作,例如初始化LCD、ADC、UART等外外设,以及初始化OSAL的相关运行环境,最后开始无线循环,我们可以跟踪最后的osal_start_system()函数。

osal_start_system()位于OSAL.c中,函数很简单(无需理会宏定义),一个无限循环的for语句,然后就是osal_run_system(),在osal_start_system函数体的下面便是osal_run_system函数。

在osal_run_system函数中,暂时请关注下图中三个地方,1是hal层,也就是硬件驱动层的轮询,例如UART、SPI等,这个比较好理解,而后面的2和3就有osal的消息事件有关系了,请看3中的tasksArr数据,注意他是函数指针数据,由typedef 重定义的数据类型定义,读者可以查看其原型定义。taskArr根据6.1的描述,它应该是一个任务注册表,里面存放的是各个任务的指针。tasksArr函数指针原型为:

他带有两个参数,一个是task_id,另外一个是event,所有的实体函数都会这样声明和定义。那tasksArr在什么地方定义的呢。

请在Source Insight中,将光标停留在tasksArr上几秒,就会先显示声明处。

这时又回到了之前的app.c源文件中。在tasksArr里直接赋值了2个任务函数,这里可以赋

多个任务函数。在osal_run_system函数中,一旦检测到某个任务函数有事件发生,就会通过tasksArr函数指针来调用具体的任务函数,并且向他输入发送的event事件。这样,在任务函数中,就可以处理当前发生的事件了。我们继续向前走,看看与我们用户相关的任务函数,也就是AppTest_ProcessEvent函数。

AppTest_ProcessEvent任务函数接收到event消息,就会被启动。下面就是AppTest_ProcessEvent函数的定义。由定义可知,根据不同的通知,从消息队列中查询自己的消息,并根据消息的类别分别进行不同的操作。图中只进行了KEY_CHANGE事件处理,当然也可以进行其它的事件处理。SYS_EVENT_MSG系统事件里包括很多消息。

接下来,我们来仔细看一看Hal_ProcessEvent任务为什么会向App_ProcessEvent任务发送KEY_CHANGED的事件?在osalInitTasks初始化函数中调用了App_Init函数,此函数的定义如下图所示,这些函数被定义在用户文件app.c中。由App_Init的函数的定义中发现,此函数调用了RegisterForKeys函数,并将任务的ID做为参数。从名子上就可以看出一个大概,它是一个按键注册函数。当Hal_ProcessEvent发现有按键改变,就可以通过AppTest_TaskID,向该任务发送消息了与通知了。这样App_ProcessEvent就可以处理KEY_CHANGED事件了。

现在问题来了,osalInitTasks函数在什么地方被调用了?通过函数的名子发现它根初始化有关,所以我们就在main函数中查看。因为main函数是程序的入口,在启动之前它调用了一些初始硬件与软件相关的初始化函数。但在main函数中并同有找到osalInitTasks,请不要灰心,main函数中有一个osal_init_system初始化函数,进入该函数后,就可以发现osalInitTasks函数了。这样逻辑就被串起来了,形成了一条线,所有的问题都可以得到解答了。

接下来再来看看系统的时钟,这也是操作系统的主要组成部分。没有时钟的操作系统是不存在的,这个就像是数学讲的时钟是操作系统的充分而非必要条件,有操作系统能推出它一定有时钟,但有时钟不一定能推出它就是操作系统。废话多了,言归正传。OSAL的系统时钟有点简单,不进行任务时间控制块的更新,说白了它就是一个最基本的定时器,只提供精确的时间间隔,其它什么都不做。而任务时间控制块的更新是OSAL自己完成,下面就通过代码来讲解它的实例。

刚才我分析上面任务函数与调度与通信时候我们讲到了一个osal_run_system函数(见下图),它里面有一个osalTimerUpdate函数。它肯定是任务时间控制块有关的函数。我们不防进去看看,看看它是做什么工作的,osalTimerUpdate的函数内部已经贴出见下图。

果不其然,它真的是OSAL系统时钟与任务时间控制块的时间管理函数。它工作的情况是这样的。它首先获得MAC层的定时器2的320us的计数值,然后通过与上一次的计数值进行相减,即可得到已经过去了多长时间。获得320us的计数值并不直观,要将它转换成ms级的数值。指令tmp = (ticks320us*8) + remUSTick; 和CONVERT_320US_TO_MS_ELAPSED_REMAINDER( tmp, elapsedMSec, remUsTicks );两个指令就是将320us计数转成ms数值。读者可能有点疑惑,为什么320us计数要乘8呢,不瞒大家我第一次看到也有这么疑惑。经过一番地思考终于弄清楚了。原来是这样的,我先将320us 的计数值转换成us,即320*ticks320us。然后再将数值转成ms,即320*ticks320us/1000,

经化简等于ticks320us*8/25。而CONVERT_320US_TO_MS_ELAPSED_REMAINDER宏就是拿ticks320us*8得到的数值与25进行相除与相余。现在已经是豁然开朗了,elapseMSec就是ms数值,有了毫秒就可以对系统时钟与任务时间控制块进行更新了。这样在死循环中不断的调用osal_run_system,也就不断地调用osalTimeUpdate,这样系统时钟与作务控制块就会不断地得到更新。

上面是对OSAL框架与运行地机理进行了分析,由于短短地几页不能完全说明OSAL,希望读者根据源码自己也分析一遍,就样会更加的生刻。在以后的编程中遇到什么问题都可以到源码中查找答案。刚接触OSAL可能会有点陌生,但请不要着急,多阅读源码,多看多接触,你会觉得osal是一个非常实用的系统。

最后,做一个简单的实验,来感受一下OSAL操作系统的实用性。在打开的IAR 工程中对TestOSAL工程进行编译。通过CC-Debug将程序烧录到芯片中,然后点击全速运行。出现在LCD上的是一些输出信息。首先显示的是各功能模块初始化完提示和系统开机以来运行的时间。如下图所示

相关主题
文本预览
相关文档 最新文档