没有合适的资源?快使用搜索试试~ 我知道了~
AVRX实时操作系统使用教程
需积分: 10 16 下载量 77 浏览量
2010-03-22
23:30:48
上传
评论
收藏 107KB DOC 举报
温馨提示
试读
16页
AVR微处理器是Atmel公司开发的8位嵌入式RISC处理器,它具有高性能、高保密性、低功耗、非易失性等优点,而且程序存储器和数据存储器可独立编址,并具有独立访问的哈佛结构。AVR单片机内核有丰富的指令集,通过32个通用寄存器直接与逻辑运算单元相连接,允许在一个周期内一条单一指令访问两个独立的寄存器,这样的结构使代码的执行效率比传统的复杂指令集微处理器快了将近lO倍
资源推荐
资源详情
资源评论
AVR 单片机的 RTOS-AVRX
引 言
随着技术的发展,嵌入式系统的设计及应用对人们的生活产生了很大的影响,并将逐渐改变人们未来的生活方式。
在特定的操作系统上开发应用程序,可以使开发人员忽略掉很多底层硬件细节,使得应用程序调试更方便、易于维护 、
开发周期缩短并且降低开发成本,因而嵌入式操作系统深得开发人员的青睐。
AVR 微处理器是 Atmel 公司开发的 8 位嵌入式 RISC 处理器,它具有高性能、高保密性、低功耗、非易失性等优点,
而且程序存储器和数据存储器可独立编址,并具有独立访问的哈佛结构。AVR 单片机内核有丰富的指令集,通过 32
个通用寄存器直接与逻辑运算单元相连接,允许在一个周期内一条单一指令访问两个独立的寄存器,这样的结构使代
码的执行效率比传统的复杂指令集微处理器快了将近 lO 倍。
AVRX 是由 lbarello 编写的源码公开的嵌入式操作系统,它专门针对 AVR 系列单片机的 RTOS,具有免费和可以修
改的特点。它的缺点是由于做为一种专用的操作系统很难移植到其他平台上。
1 AVRX 系统的特点
AVRX 做为 AVR 专用 RTOS 有如下的特点:
◆完全支持占先式、优先级驱动的任务调度算法;
◆16 个优先级,相同的优先级的任务采用 Round-robin 调度算法轮流执行;
◆信号量可以用于信号传递、同步和互斥信号量,支持阻塞和非阻塞语法;
◆任务之间可以用消息队列相互传递信息,接收和确认消息可以用阻塞和非阻塞调用;
◆在中断子程序中,大部分非阻塞的中断服务程序可以使用;
◆支持单个定时器的时间队列管理,任何进程都可以设置一个定时器,并且任何一个任务都可以等待定时器时间到 ;
◆支持单步调试运行着的进程;
◆程序空间小,包含所有功能的版本占用 700~1000 字(words);
◆快速,若 10MHz 的晶振速律,内核扫描中断速律为 1Khz(1ms),中断服务包括返回用 211 周期占用处理器
20%(211/1000)左右的资源。
◆与 AvrX 内核定时器/计数器有关的一些事务可以用 AVRX 写成任务级代码。
1.1 任务
AVRX2.6 为了支持 c 语言,保存了所有的 32 个寄存器。最小的上下文是 32 个寄存器、SREG 和 PC,总共 35 个
字节。AvrxInitTask()函数给所有的寄存器初始化为 0X00。只有进程上下文保存在任务堆栈中,所有其他的使用(包括
内核和中断)保存在内核堆栈。这样降低了第一个中断的上下文切换和进入内核 API 的 SRAM 消耗。随后的中断(如果
允许中断嵌套)嵌入内核堆栈,API 不进行上下文切换。
保持任务的信息(栈指针或上下文指针)存于进程 ID 块中(PID),PID 用 6 字节 SRAM 构成 。额外的 SRAM 是进
程队列,状态位和优先级的字节指针。
AvrXInitTask
AvrXRunTask
AvrXSuspend
AvrXResume
AvrXTaskExit
AvrXTerminate
AvrXHalt /*AvrXHalt:cli
rjmp AvrXHalt*/
1.2 信号量
信号量是 SRAM 指针,它们有三种状态:PEND、WAITING 和 DONE。当一个进程被一个信号量阻塞时,它处于
WAITING 状态,多个任务可以排队等候一个信号量。在后一种情况下,信号量可以看作互斥信号量。提供的 API 函
数如下:
AvrXSetSemaphore
AvrXIntsetSemaphore
AvrXWaitSemaphore
AvrXTestSemaphore
AvrXIntTestSemaphore
AvrxResetSemaphore
1.3 定时器
定时器控制块(TCB)长度为 4(或 6)个字节。它们管理一个 16 位计数值。定时器队列管理器管理一个分类的定时器队
列,每个都调整为所有计数器的和到其延时需要的值。提供的 API 函数如下:
AvrXStartTimer
AvrXTimerHandler
AvrXCancelTimer
AvrXWaitTimer//阻塞函数
AvrXTestTimer
AvrXDelay//阻塞函数
在 AvrX2.6 中,有个额外的可变的定时器队列块,TimerMessageBlock。定时器的消息用在了 MessageTimer 的列程中。
简言之,定时器一到点,一个消息就被排列到消息队列中。任务以等消息队列的方法来等侯多个事件。
AvrXStartTimerMessage //阻塞函数
AvrXCancelTimerMessage
1.4 消息队列
消息队列用消息控制块(MCB)做为队列首地址。任何进程、中断处理函数和多个进程都可以等待消息。MCB 的长度
是 2 或 4 个字节,消息可以认为是灵活性更大的信号量。提供的 API 函数如下:
AvrXSendMessage
AvrXIntSendMessage
AvrXRecvMessage
AvrXWaitMessage
AvrXAckMessage
AvrXTestMessage
AvrXWaitMessageAck
1.5 单步运行支持
通过重新汇编内核 AVRX,可以允许和禁止单步运行的支持。单步运行可以通过编译内核库时定义下面的变量:
#define SIGNALSTEPSUPPORT。在能够单步运行以前,进程必须先暂停。有两种方法实现:一是仅仅初始化进程但
不使能;二是用目标进程识别码调用 AvrXSuspend,一旦目标进程挂起,调试 SPI 就能使用了。提供的 API 函数有:
AvrxStepNext
AvrXSin-gleStepNext
1.6 系统对象
AVRX 是围绕系统对象的概念而构建的。系统对象包括一个链接和其后面的 0 个或者若干个字节的数据信号量。进
程对象可以根据运行队列和信号量排队。计数器控制块只能根据计数器队列排队。消息控制块只能在消息队列排队。
进程根据嵌入对象的信号量等待这些对象。进程堆栈中可用的 SRAM 是限制系统规模的主要因素,每个进程都需要至
少 10~35 字节的空间来存储进程上下文。对中断进程只要使用 AvrX 的语句就无需额外堆栈。堆栈可在 64k 的 SRAM
任何地方开始。至少该内核的最好表现是在片内 SRAM。提供的 API 函数如下:
AvrXSetObjectSamaphore
AvrXIntObjectSamaphore
AvrXResetObiectSamaphore
AvrXWaitObjectSamaphore
AvrXTestObjectSamaphore
AvrXIntTestObjectSamaphore
1.7 系统堆栈
AVRX 需要足够大的堆栈来处理所有可能的中断嵌套。每次进入内核将会把 10~35 字节压进堆栈(标准上下文和返
回地址),中断处理可能压进去更多。AVRX 的 API 会临时压入 2 个以上的字节。GCC 或者汇编代码定义于 SRAM 的
顶部,保证 AVRX 的堆栈在有效 SRAM 空间之内是设计者的工作。
原理:
AvrX 完全支持占先式、优先级驱动的任务调度算法。口水话。
术语: PID - 进程识别码,它是一个数据结构,包含了用来执行一个进程的所有信息。
任务切换:
任务切换逻辑的大部分是在_Prolog 和_Epilog 这两个程序中实现的,它用到了三个数据结构:
_RunQueue 指向运行队列中的第一个 PID 的指针
_Running 指向正在运行的 PID 的指针
_SysLevel 系统进入内核的次数计数器,-1 = 进程堆栈
_SysLevel 指示了系统正在运行的是用户代码还是内核代码。如果_SysLevel = -1,当前的堆栈是任务堆栈。当有中断
发生,或者用户代码调用内核 API 时,_Prolog 就首先保存当前的运行环境到堆栈,然后使_SysLevel 加一,接下来如
果_SysLevel == 0 就切换到内核堆栈上去。后来的再发生中断或者是内核 API 调用时,_Prolog 将会把运行环境保存在
内核堆栈上并对_SysLevel 加一。切换到内核堆栈的过程包括读取当前 SP(堆栈指针)并将其存储到_Running 所指向的
PID。然后向 SP 中装载指向内核堆栈栈顶的指针。_Epilog 展开内核堆栈,它先弹出先前的运行环境,把_SysLevel 减
一 。 当 _SysLevel 变 为 负 数 时 , 就 必 须 切换 到进 程 堆 栈 上 去 , 这 时 环 境 切 换 才 真 正 发 生 : 如 果 _Running ==
_RunQueue,就意味着没有切换运行环境的必要,_Epilog 就简单的恢复个寄存器的内容(通过指针_RunQueue)。如
果_Running 和_RunQueue 不同的话,说明有高优先级的任务变为运行态等待运行了(不管是正在队列中的,或者是当
前的任务给阻塞,比如被从_RunQueue 中移除)。在这种情况下,_Epilog 仍然是仅仅通过_RunQueue 队首的指针来恢
复运行环境。第三种情况是如果 _RunQueue == 0,或者为空,这意味着所有的任务都被阻塞了,CPU 处在空闲状态。
在这种情况下,_Epilog 转向运行一个叫做空闲任务的特殊的任务,当前,空闲任务所做的事情就只是把 CPU 置于休
眠状态,等待中断的唤醒。软件系统的设计者可能会选择创建一个低优先级的任务,该任务永远不会被阻塞或者调用
空闲任务,它可以暂停 CPU 或者做任何你想做的事。只要它永远不会被阻塞,则系统内部的空闲任务就永远不会被执
行。
Queue 队列
处在运行态的进程会被按照优先级反序放入_RunQueue 队列中,优先级为 0 的将放在队列的头部。如果有多个进程
拥有相同的优先级,则以进程的先后顺序运行。随着进程的执行和阻塞,他们将被重新置于相同优先级任务列表的尾
部,从而进行有效的循环打鸣调度。 一个较低优先级的进程不能中断一个较高优先级的进程。_Epilog 只是简单的从
_RunQueue 的头部取出一个进程来运行。然而,如果一个较低优先级的进程占用了较高优先级的进程需要的资源,比
方说一个用来控制硬件访问的信号量,则较高优先级的进程将会阻塞低优先级的进程直到该资源可被访问为止。
一旦一个进程开始运行,它将一直运行直到被较高优先级的进程占先,或者是被较高优先级的进程阻塞等待某种
资源。
Suspending a process 挂起一个进程
因为一个“正在运行”的进程可能被一个信号量阻塞,而信号量不被内核所知,所以挂起一个进程不是简单把它从运
行队列中移除。而是用 AvrXSuspend 先标记一个准备挂起的进程,然后尝试把它从运行队列中移除。如果成功了 ,
AvrXSuspend 将标记该进程为“SUSPEND”并返回,如果没有成功,它将简单返回,稍后当该进程符合条件准备插入运
行队列时,如果它标记为准备挂起,则通过_QueuePid 手续不在将其列入队列,并把它标记为“SUSPEND”。根据定义,
当_QueuePid 试图把一个任务推入运行队列中时,该任务不能正在等待任何资源,并且与之关联的信号量将被清零。
Blocked Tasks 阻塞的任务
一个任务,不管它是被挂起或者是在等待某种信号量,都是被阻塞。换一句话说,就是没有被放入运行队列。因
为 AvrX 是计划为小系统用的,所以只有一条前向链表。由于只需要一个指针,这种方式为系统节省了宝贵的 SRAM
空间。当要插入或者删除队列中的某个元素时,AvrX 遍历队列寻找需要删除的对象或者是插入点。一般来讲,所有的
队列(运行队列,定时队列,信号量,消息队列以及消息)都支持多元素排队等待。这样一来,一个信号量变成了一
个互斥量(Mutex):如果它被一个进程所使用,所有其他的进程都排队等待它被使用者释放,然后队列头部的进程
就可以从等待队列中释放出来。消息队列可以是多个消息等待一个接收者,或者是多个接收者等待一个消息。运行队
列可以有很多的运行态任务排队等待运行。随着任务的阻塞或者退出或者被挂起,则运行队列中的下一个进程将把其
运行环境交换到 CPU 中继续执行。(非作者注:阻塞的任务由运行阻塞函数实现的。)
Timer Queue 定时队列
定时队列是个比较特殊的情况。它是一个排过序的链表且链表中的超时设定都是调整过的,这样中断处理过程就只
需要递减链表中第一个元素的计数器即可。当第一个元素的计数器变为 0 时,处理过程就设定该元素中的信号量。如
果有一个任务在等待此信号量,则该任务就被放入运行队列中。TimerMessages 是特殊的情况,TimerControlBlok 不是
直接发信号给任务,而是进入一个消息队列中。TimerHandler 可以作为一个单独的任务运行,定时器可以作为消息。
这实际上是个相当强大的功能但是并没有被采用,因为它要花费两次环境切换(一次是定时器处理任务,另一次是那
个被通知进入运行的任务)并且还要占用额外的任务环境(~40 字节的 SRAM)。 AvrX 可以在 512 字节的 SRAM 上
轻松运行 3~4 个任务:使用其中的一个任务做定时器句柄似乎太过于浪费。
Fifo Support 支持 FIFO
AvrXFifo 支持只是简单的字节性的 FIFO。FIFO 是通过申请字节数组,用一个正确类型的 Const 型指针映射到数组
上生成的。FIFO 可以是静态的也可以是自动的,或者是从堆中申请的。FIFO 包括一些信号量,用来实现中断处理过
程和任务之间的同步。AvrXSerialIO 例程举例说明 FIFO 是怎样使用的。
怎样开始
开始的最简单的方式是找一个例程或者是测试程序和内核一起编译。当核实该例程或测试代码可以正常工作后,去
掉例程文件,重名名文件并添加进你自己的代码。
顶层的文件一般需要包含下列部分:
AvrXTimerHandler 中断句柄(处理程序)
一个或者多个内部或外部任务定义
CPU 复位程序或者 main(void)
为系统运行必须准备的所有任务必须执行最后一条设置。至少包含一些的条目,其中前三项都是由 C 编译器帮你完成
的。
设置硬件堆栈指针
清零 SRAM
清零所有寄存器
初始化任务结构(AvrXInitTask or AvrXRunTask)
初始化硬件(Timer0 or 1, ports, serialio, 其他)
由 Epilog()跳转
必须由启动代码的最后指令 Epilog()是跳转到任务。它将启动系统调度过程,切换运行环境到_RunQueue 中的第一
个任务上。如果 monitor 被包含在你的程序中,它应该拥有最高优先级(0),并且第一个被运行。
典型的用户代码应该独立放在各自的文件中,仅仅任务控制块(TCB)需要导入到顶层文件中。AvrX2.6 中,宏
AVRX_EXTERNALTASK 完成了导入工作。请参考头文件(avrx.inc 或 avrx.h)获得宏工作的具体细节。
启动代码是在由 C 运行时库建立的堆栈或者是你用汇编语言建立的堆栈上运行的。这个堆栈地址基本上就是 AvrX
用来做内核堆栈的地方。因此,main()中所做的一切或者是复位程序都是在内核环境中运行的。在退出复位代码前至
少要准备一个任务用 AvrXRunTask()来运行。如果没有准备任务,Epilog()将会发现运行队列为空,就会使系统永久进
入空闲任务。
尽管我还没有这样做,但是从空闲模式启动并且有一个中断处理程序通过 AvrXRunTask()来创建一个任务是有可能
的。更多的情况应该是运行你所有的任务,让他们做一些他们需要的初始化工作并通过某种东西( Timer,semaphore or
message)来阻塞之。
可选的素材
在最小化下考虑,AvrX 只是简单地支持任务初始化和信号量。单步,定时器队列管理,消息队列管理都是可选的
服务,如果不需要的话可以被裁减掉。没有这些服务的内核是非常小的。这里给出不同内核功能下的粗略代码大小。
基本的任务调度和信号量队列管理: ~670 字节(约 670 字节)
定时器队列管理: ~236
消息队列管理: 48
杂项(单步,高级任务调度): 200
调试监视器: ~1300 字节
Fifo 支持(用 C 编写) ~300bytes
不带调试监视器的总代码大小(对 GCC 版 AvrX 而言)为约 600 个字(1200 字节),占 8515 代码空间的 14%
定时器支持包含 AvrX 在你的应用中是毫无道理的。(非作者注:AvrX 的内核用 T0 以 1Khz(1ms)速律产生循环打鸣调
度,一般来说应用中可用内核定时器。所以 T0 不得作为他用。)定时器队列管理仅是多任务竞争调度时延的一种实现
机制而已。对简单的应用而言,可以简单地用实时中断发送一个信号量并用一个进程封装所有依赖时间要素。在每一
个时间片到来时,该进程就运行,作业并有必要时置位其他的信号量来通知相应进程准备运行。另一种方式是发送消
息。如果你的定时要求适当,一个简单的任务,甚至是中断处理程序,可能在代码空间和处理周期上都更加有效。(非
作者注:若对定时有特殊要求可见列程 Timers1.c,Timers1.c。)
消息队列管理之所以这么小是因为很多功能都已经在基本的任务和信号量模块中实现了。消息队列是个从信号量
队列引申出的简单的概念。
任务结构
尽管不是绝对必要,一个任务一般是一个由入口,初始化和一个死循环组成的子程序。死循环中一般包括阻塞或
等 待 一 个 信 号 量 。 它 有 可 能 是 显 式 地 调 用 AvrXWaitSemaphore , 或 者 是 非 显 式 的 调 用 AvrXWaitTimer 或
AvrXWaitMessage。后两种方式实际上被定时器结构或者消息结构嵌入的信号量给阻塞了。
剩余15页未读,继续阅读
资源评论
radiums_2
- 粉丝: 0
- 资源: 1
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功