实现FreeRTOS系统傻瓜编程汇编.docx
《实现FreeRTOS系统傻瓜编程汇编.docx》由会员分享,可在线阅读,更多相关《实现FreeRTOS系统傻瓜编程汇编.docx(19页珍藏版)》请在冰豆网上搜索。
实现FreeRTOS系统傻瓜编程汇编
傻瓜实现FreeRTOS系统
如果你
1. 希望修改FreeRTOS源代码
2. 移植实时内核到另一个微控制器或者原型板(prototypingboard)
3. 第一次接触FreeRTOS,希望得到关于它们在操作和实现上的更多信息
这些文档会有用。
本文档分为两个章节:
1. 基本原理和RTOS概念
包括多任务的背景信息和基本实时概念,这是为初学者准备的(isintendedforbeginners)
2. 从底向上(fromthebottomup)解释实时内核源代码
FreeRTOS实时内核已经移植到许多不同的微控制器架构下。
这份文档是以AtmelAVR为范例,因为:
1. AVR架构简单
2. 有免费可用的开发工具WinAVR(GCC)developmenttools.
3. 非常便宜的原型板STK500prototypingboard
在本文的最后,还一步一步地详细描述了一个完整的上下文切换(contextswitch)。
RTOS基本原理
多任务
调度
上下文切换
实时应用
实时调度
这一节提供一个关于实时和多任务概念的简介。
读下一节之前必须理解这些概念。
多任务(Multitasking)
在一个操作系统内部,内核[kernel]是最核心的部件。
像Linux那样的操作系统使用的内核,从表面上看(seemingly),允许用户并发(simultaneously)访问计算机。
多个用户似乎(apparently)可以并行(concurrently)执行多个程序。
在操作系统的控制下,每个正在执行的程序就是一个任务[task]。
如果一个操作系统能够以这种方法执行多个任务,这就叫做多任务[multitasking].
多任务操作系统的使用可以简化应用程序的设计:
1. 操作系统的多任务和任务间通信的机制允许复杂的应用程序被分成一系列更小的和更多的可以管理的任务。
2. (程序的)划分(partitioning)让软件测试更容易,团队工作分解(workbreakdownwithinteams),也有利于代码复用。
3. 复杂的定时和先后顺序的细节可以从应用程序代码中删除。
(因为)这成为操作系统的职责。
多任务Vs并发
传统的(conventional)的处理器同时只能执行一个任务。
但通过快速的任务切换,一个多任务操作系统可以使它看起来(appear)好像每个任务并行执行一样。
这可以下面的示意图来描述(depicted)。
它显示了有关(withrespectto)时间的3个任务的执行模式。
任务名用颜色标注出来,写在左手边。
时间从左到右增加,相应的颜色的线条显示该任务在某个特殊时间正在执行。
上面的图演示的是用户所觉察到的并行执行模式,下面的图是实际的多任务执行模式。
----所有可用的任务都好像在执行,但实际上在任何一个时刻都只有一个任务在执行
调度
调度器(scheduler)是内核中负责决定在某个特殊时间哪个任务应该执行的部分。
内核可以在任务的生命期(lifetime)挂起(suspend)/恢复(resume)一个任务许多次。
调度策略(schedulingpolicy)是调度器用来决定哪个任务在哪个时间点执行的算法。
一个(非实时)多用户系统的策略很可能分配(allow)给每个任务一个"公平"(fair)的处理器时间片(proportionofprocessortime)。
用在实时系统/嵌入式系统的策略稍后再描述。
除了被RTOS内核无意的挂起外,一个任务还可以自己挂起自己。
如果一个任务想延迟一段固定的时间(也就是sleep),或者等待(也就是block)某个资源可用(比如一个串口),或者等待一个事件出现(比如一个键按下)。
一个阻塞或者睡眠的任务是不能执行的,不会为它分配任何处理时间。
上图中提到的编号:
1) Task1正在运行
2) 内核挂起Task1
3) 恢复任务Task2
4) Task2正在执行,为独占访问(exclusiveaccess),它锁定一个处理器外设
5) 内核挂起Task2
6) 恢复Task3
7) Task3试图访问同样的处理器外设,发现它被锁定,Task3不能继续,所以自己挂起自己。
8) 内核恢复Task1
………….
9) 接下来(thenexttime),Task2在9处执行。
它完成了对处理器外设的访问,所以解锁它
10) 再下来,Task3在10处执行。
它发现现在可以访问处理器外设了,于是开始执行,直到被内核挂起。
上下文切换
跟任何其他程序一样,一个任务执行时,它使用处理器/微控制器的寄存器,访问RAMROM。
这些资源(处理器的寄存器,stack等)一起组成任务的执行上下文(thetaskexecutioncontext).
一个任务是一个连续有序的代码片断。
它并不知道它将何时被内核挂起或者恢复,甚至不知道这些事情(挂起或者恢复)在什么时候已经发生了。
下面考查(Consider)的这个例子是用来求两个处理器中的寄存器值之和,该任务在执行1条指令后就立即被挂起。
-->任务将要执行ADD指令时,被挂起
-->先前的指令已经把数取到寄存器(Reg1,Reg2)中了,而这些寄存器(Reg1,Reg2)将要被ADD指令用到。
当这个任务被恢复后,ADD就是要执行的第1条指令。
这个任务不知道是否有另一个的任务会在中间时期修改Reg1或者Reg2)
当这个任务挂起时,其他任务继续执行,可能会修改处理器寄存器的值。
在恢复之后,这个任务也不知道处理器的寄存器被修改过(altered).如果它使用这个修改过的值,就会导致计算的和的结果不正确。
为了避免这类错误,必须保证,在恢复一个任务之后,其上下文环境跟即将挂起前是一样的。
操作系统内核有责任通过在任务挂起前保存其上下文来确保这种状况。
当任务恢复时,保存的上下文就被操作系统内核恢复到先前的执行情况。
保存一个被挂起的任务的上下文并在任务恢复时恢复其上下文的这个处理过程就叫做上下文切换(contextswitching)
实时应用
实时操作系统(RTOS's)通过同样的原理达到多任务的目的。
但他们的目标与那些非实时系统相比是很不一样的。
不同的目标影响到不同的调度策略。
实时/嵌入式系统设计成提供一个对真实世界的事件的及时响应(timelyresponse)。
出现在真实世界中的事件可能有一个时间限制(deadline),在此期限之前,实时/嵌入式系统必须给出响应,RTOS调度策略必须确保时间限制是恰当的(met).
为了达到这个目的,软件工程师必须首先为每个任务设置一个优先级(priority)。
RTOS的调度策略只是简单地确认能被执行的最高优先级别的任务是分配了处理时间的任务(thetaskgivenprocessingtime)。
这可能要求在相同优先级的任务之间公平的共享处理时间,如果他们准备并发运行的话。
代码示例:
最基本的例子是一个由键盘和LCD组成的实时系统。
用户必须在合理的时段为每个键盘按下取得视觉反馈(visualfeedback).。
如果用户不能在这时段看到键盘按下已经被接受,软件产品将会很难使用(beawkwardtouse)。
如果最长的接受期是100ms,那么在0到100ms的响应可被接受。
这个功能可以用一个下面这样的结构的独立(autonomous)任务实现:
voidvKeyHandlerTask(void*pvParameters)
{
//键盘处理是一个连续的过程。
就像大多实时任务那样,这个任务
//也是用一个无限循环实现的。
for(;;)
{
[Suspendwaitingforakeypress]
[Processthekeypress]
}
}
现在假设实时系统也执行一个依赖数字滤波输入的控制功能。
这个输入必须被取样(sampled),滤波(filtered),并且每2ms执行一次控制循环。
为了让滤波器正常操作,取样的时间规律(thetemporalregularity)必须精确到0.5ms。
这个功能可以用下面这个结构的独立任务实现:
voidvControlTask(void*pvParameters)
{
for(;;)
{
[Suspendwaitingfor2mssincethestartoftheprevious
cycle]
[Sampletheinput]
[Filterthesampledinput]
[Performcontrolalgorithm]
[Outputresult]
}
}
软件工程师必须设置控制任务为最高的优先级,因为:
1. 控制任务的时间限制(deadline)比键盘处理任务的要严格;
2. 控制任务错过最后期限(deadline)的后果比键盘处理任务要严重。
下面将演示这些任务是如何被实时操作系统调度的.
实时调度
下面的图演示前面定义的那些任务是如何被时实操作系统调度的。
RTOS自己已经建立了一个任务----idletask---它只在没有其他任务执行的时候才被执行。
RTOSidletask总是处于可以执行的状态(注:
也就是它不可能会因为等待什么外设资源而被阻塞,而是处于一种随时待命的状态).
上图中:
1. 在最开始,我们的两个任务都不能被执行-vControlTask等待合适(correct)的时间开始新的控制循环,vKeyHandlerTask等待键盘按下。
处理器时间分配给RTOS的idletask.
2. 在t1时刻,一个键盘按下(事件)出现.VKeyHandlerTask任务现在可以执行,它比RTOS的idletask有更高的优先级,所以处理器时间给它。
3. 在t2时刻,vKeyHandlerTask已经完成了对按键的处理,并更新了LCD。
它不能继续,直到另一个键被按下,所以必须挂起它自己。
RTOSidletask又被恢复执行。
4. 在t3时刻,一个定时器事件预示(indicates),可以执行下一个控制循环了。
VControlTask现在可以执行,作为最高优先级的任务被立刻分配(scheduled)到处理器时间。
5. 在t3和t4之间,当vControlTask任务还在执行的时候,一个键按下。
VKeyHandlerTask不能被执行,因为它没有vControlTask的优先级高。
不能分配(scheduled)到任何处理器时间。
6. 在t4时刻,vControlTask完成了控制循环的处理,不能够重新开始,直到下一个时间事件出现,所以它自己挂起自己。
而vKeyHandlerTask现在是最高优先级的任务,可以运行了,所以,为了处理先前的键盘事件,分配(scheduled)到了处理器时间.
7. 在t5时刻,键盘已经被处理。
VkeyHandlerTask为了等待下一个键盘事件,自己挂起自己。
现在,我们的两个任务再度不能执行了。
RTOSidletask分配到处理器时间。
8. 在t5和t6之间,一个定时器事件被处理,但是没有更多的键盘事件出现。
9. 下一个键盘按下出现在t6时刻,但在vKeyHandler