RTA-OS基于早期ETAS操作系统的成熟技术,迄今为止,已在全球超过3.5亿个ECU中使用。RTA-OS是一个可静态配置的抢占式实时操作系统(RTOS),它常被用于资源受限但有着高性能要求的方案中。内核的实现不仅遵循了AUTOSAR R3.x、R4.0、R4.1、R4.2、R4.3的开放标准(包含了早期OSEK操作系统标准),还提供了许多RTA-OS所独有的附加功能。
文章首先介绍了RTA-OS诞生的整个过程,并粗略的介绍了不同阶段的系统特性,然后根据一个RTA-OA整体开发过程实例,介绍了RTA-OS基本的特性内容。因为整个RTA-OS是黑盒的,最终最终编译生成的静态链接库(不会像ISOLAR一样生成静态代码)。
目录
概述
开发过程
Counter配置
ISR配置
Task配置
Resources配置
Schedule Table配置
Application Modes配置
RTA-OS编译
概述
RTA-OS诞生的过程如下图所示。
OSEK是一个欧洲汽车行业标准,致力于为汽车电子产品提供操作系统,定义相应的接口。该项目的全称是OS-EK/VDX。OSEK是由德语中的一个短语组成的首字母缩略词,其翻译过来为汽车电子系统的开放系统和相应接口。VDX脱胎于法国的一个标准(Vehicle Distributed eXecutive),现已与OSEK合并。如果您想了解更多关于OSEK标准,点击这里可以传送官网。
OSEK OS是OSEK标准中最成熟、应用最广泛的一种操作系统。OSEK OS曾应用于所有类型的汽车ECU,从动力总成、底盘和车身以及多媒体设备。OSEK OS提供了以下几种操作系统功能。
- Tasks:任务是OSEKOS系统的主要组成部分。与其他一些操作系统不同,OSEK中的任务不需要自调度(也就是说,不需要将任务体放在无限循环中)。OSEK操作系统中有四种类型的任务:
- BCC1:具有唯一优先级并且不可被多次激活的基本任务。这是最简单的任务形式,非常适合于硬实时系统。一旦任务被激活,它必须运行并终止,然后才能再次激活。这种类型的任务不能在执行过程中暂停自身以等待事件。
- BCC2:基本任务之间可以共享同一个优先级,并且任务可以被多次激活,任务可以在运行的过程中被激活,激活被保留并等待运行终止之后来响应此次被保留的激活。这种类型的任务同样不能在执行过程中暂停自身以等待事件。
- ECC1:具有唯一优先级的扩展任务。允许扩展任务在执行期间等待事件(即任务可以自我挂起)。但是,不可被多次被激活,任务必须具有唯一的优先级。
- ECC2:具有共享优先级的扩展任务。扩展任务允许挂起等待事件,并且可以与系统中的其他任务共享优先级。在这方面,它们与BCC2任务类似。但是,与BCC2任务不同,扩展任务不能具有排队激活。
- Scheduling:任务可以被抢占或者非抢占的调度,合作式的调度策略也可以被方便的构建。
- Interrupts:中断允许操作系统与异步的外部传感器进行交互。OSEK操作系统中有两种类型的中断:
- 第1类中断不由OS处理。
- 第2类中断由操作系统处理,并且可以与操作系统交互。
- Resources:资源是简单的二进制信号量,允许您在任务和中断之间共享的临界区上提供互斥。资源由操作系统使用优先级天花板协议(占有资源的任务优先级将高于所有有机会访问此资源的任务)管理,该协议保证免于死锁和优先级反转等问题。
- Counters and Alarms:计数器和警报用于提供任务的周期性(和非周期性)调度。
- Debugging Support:OS提供了两种编译方式,标准的编译方式提供了最小资源与错误为目标,扩展的编译方式提供了广泛的错误检测工具,调试提供了符合OSEK ORTI标准的符号,第三方调试器可以基于此实时显示任务状态。
AUTOSAR(汽车开放系统架构)是一个开放的、标准化的汽车软件架构,由全球汽车制造商、供应商和工具开发商共同开发。AUTOSAR为基本软件模块(BSW)提供规范,例如操作系统、通信驱动程序、内存驱动程序和其他微控制器抽象。AUTOSAR标准还定义了一个基于组件的架构模型。该模型定义了一个虚拟功能总线(VFB),定义了一个抽象的应用软件组件(SW-Cs)之间的通信。VFB允许SW-Cs独立于底层硬件使其可在不同的ECU之间移植,并可在多个汽车项目中重复使用。VFB抽象由AUTOSAR运行时环境(RTE)封装。RTE提供了SW-Cs和BSW之间的“粘合剂”。对于AUTOSAR更多详细的内容点击这里跳转官网。
AUTOSAR OS是OSEK OS的扩展。AUTOSAR OS包含OSEK OS的所有功能,并添加了一些新功能,这些新功能分为四个可扩展性等级,如下所示:
- Scalability Class 1:在OSEK OS的基础上增加:
- Schedule Tables:在对多种相同与不同周期的任务和事件进行调度时,调度表提供了比OSEK Alarms的一种更简单的替代方法。每个调度表可以作为一个单独的表进行管理,并且可以在运行时在不同的表之间进行切换,使您可以轻松地构建模态系统。
- Software Counter Interface:操作系统和计数器之间的交互已经标准化(在OSEK是供应商特定的)
- Stack Monitoring:添加了额外的调试支持,以帮助处理堆栈故障。
- Scalability Class 2:在SC1的基础上增加了:
- Schedule Table Synchronization:调度表可以与全局的时钟进行同步。
- Timing Protection:增加保护以防止任务和中断执行太长或太频繁。
- Scalability Class 3:在SC1的基础上增加了Memory Protection与Service Protection。
- Scalability Class 4:是SC1与SC2的集合。
RTA-OS 5.6.3支持Scalability Class1-4的所有AUTOSAR OS R3.x/4.x功能。它还支持AUTOSAR多核操作系统规范中描述的多核应用,包括iOC(操作系统间应用通信)机制。并且由于AUTOSAR OS基于OSEK OS,因此它向后兼容现有的基于OSEK操作系统的应用程序,即,为OSEK OS编写的应用程序将在很大程度上运行于AUTOSAR操作系统而无需修改。
RTA-OS增加了以下一些特性:
- Time Monitoring:时间监控用于在运行时测量任务和Category2 lSR的执行时间,并根据预先配置的执行时间来检查是否符合要求。
- Enhanced Stack Monitoring:增强的堆栈监视提供额外的可能性,以帮助您调试堆栈问题。
- RTA-TRACE Integration:使用ETASRTA-TRACE实时操作系统分析和可视化工具,观察操作系统正在做什么。
- User control of hardware:用户可以透过OS直接对硬件完成控制。
- Predictable run-time overheads:可预测的运行时开销。
- Graphical offline configuration editor:支持通过AUTOSAR XML来配置OS。
- Easy integration into your build process:只需要一个命令行工具来生成OS代码。
- Highly scalable kernel architecture:使用离线工具配置生成适合您的OS代码。
开发过程
本章简要概述了如何使用RTA-OS。该过程包括以下步骤:
- 使用rtaoscfg配置要使用的操作系统的功能。
- 使用rtaosgen生成自定义的RTA-OS内核库与相应符合AUTOSAR规范的头文件。
- 编写使用操作系统的程序代码,包括参照生成的Samples完成回调函数的实现。
- 编译程序代码并与RTA-OS库链接,生成可执行文件。
- 在你的目标板卡上运行你的应用程序。
RTA-OS是静态配置的,这意味着你需要的每一个任务和中断都必须在配置时声明,以及任何临界段、同步点、计数器等。RTA-OS包括rtaoscfg,这是一个图形化配置编辑器,用于配置RTA-OS应用程序。rtaoscfg接受任何AUTOSARXML文件作为输入,并允许您编辑配置的特定于操作系统的部分。rtaoscfg os配置工具如下图所示。
要创建新的配置时,点击File-New Project即可以创建一个新的项目。
在下面的弹出窗口中依次填入AR Package Name、ECU Configuration Name、OS Configuration Name、Release信息,注意这块填入的内容要尽可能与导入的osNeeds.arxml保持一致,否则会在导入的过程中遇到问题。
此时,在RTA-OS主界面的OS Configuration界面中将会显示新工程的所有配置项,如下图所示。
我们先完成General中的配置,部分配置如下图所示。本书示例使用SC1等级的OS,并使能Startup、Shutdown与Error钩子(Hook)函数,便于OS的调试。
其次,需要选择OS所运行的目标芯片,这边可以根据您具体板卡的情况选择。
由于AUTOSAR中提出了运行时环境(RTE)的概念,先前也已经完成了运行实体RE向操作系统任务Task的映射,并完成了RTE Generation阶段的生成。其中,RTE生成的osNeeds.arxml描述文件包含了与操作系统相关的配置信息,将这个描述文件直接导入刚才创建的RTA-OS工程即可完成OS中与用户任务相关的大部分配置。下面介绍基于RTE的OS配置方法。
首先,保存当前的.rtaos,保存名为CsdnTest。切换到Project Files菜单,并将刚才新建工程生成的arxml文件重名了为CsdnTest.arxml。
切换到Project Files菜单,右键点击OS工程文件CsdnTest.rtaos→Add Existing File,如下图所示。选择添加先前所述的OsNeeds.arxml文件即可。
添加完成之后如下图。
此时,再切换到OS Configuration界面,将看到大部分与用户任务相关的配置信息已导入进来,如下图所示。
下面我们针对几个重要的操作系统配置来详细说明。
Counter配置
如果您在ISOLAR-A中进行RTE配置时未定义OS Counter具体实现相关的属性,那么就需要进行添加。我们将Counter的计数基准配置为1ms,即将Rte_TickCounter中的Seconds Per Tick设置为0.001,并将Ticks Per Base设置为1,如图下图所示。
Counter分为Software Counters和Hardware Counters,Software Counters可以通过程序调用OS提供的API进行增加,硬件计数器则更加复杂,计数值保存在外部硬件设备中。RTA-OS使用特殊的回调函数来设置请求的Ticks数,取消请求,获取当前的计数值和计数器的状态,并通知已经超过了预设的Ticks数。本文中的Counter为Software的,它的计数器驱动模型如下。
我们可以选择一个周期的中断(GTM的回调函数)来增加Counter的Ticks,也可以在一个周期任务做这个事情。
//Using a periodic interrupt to tick a software counter
#include <Os.h>
ISR(HandleTimerInterrupt) {
DismissTimerInterrupt();
IncrementCounter(TimeCounter);
}
//Using a periodic Task to tick a software counter
#include <Os.h>
TASK(Critical){
...
if (Error) {
IncrementCounter(ErrorCounter);
}
...
TerminateTask();
}
在调度表启动之前,应确保Counter已经启动,这里以Gpt举例,使用Gpt_EnableNotification使能回调函数,然后Gpt_StartTimer启动定时器,这样定时器就会以我们设定的周期来回调一个函数,在这个函数里我们可以调用IncrementCounter来增加系统Counter的值。
ISR配置
由于ISR是与具体实现密切相关的部分,而且还与微控制器相关,所以也需要另行配置。可以右键点击ISRs→New新建一个中断服务函数,其名字必须对应MCAL中的中断服务函数名。这里以产生OS Tick的通用定时器(GPT)中断配置为例进行说明。这里新建一个名为GTMATOM0SR0_ISR的ISR,将其配置为二类中断(CATEGORY_2),定义一个优先级(Priority),并需要选择一个中
断向量号(Address/Vector),这里根据实际的工程代码实现,选择对应的中断源:GTM ATOM 0 Shared SR0 (SRC_GTMATOM00)。
中断的优先级IPL(Interrupt Priority Level)为0代表的是所有Task的优先级,中断的均为1或者更大,下图为第2类中断处理状态图。
每个中断的实现在MCAL中IRQ对应的不同外设中断静态代码中实现,比如CANSR0_ISR在Can_Irq.c中实现。
Task配置
Task的配置在导入OsNeeds.arxml的过程中已经包含了,如下图是ASW_OsTask_100ms任务的配置。任务的优先级为33,可激活的次数为1(不可重复激活),此任务的调度是完全抢占模式,任务使用了RTE_RESOURCE资源。
下图是任务的状态切换图,可以看出Task可以调用WaitEvent()等待一个事件从而进入到WAITING状态,所有任务一开始都是SUSPENDED状态,只有被激活之后才进入READY状态,根据优先级排队进入RUNNING状态。
下图是抢占式的任务调度,可以看出每次抢占都涉及到了上下文之间的切换,虽然这会消耗时间和空间上的资源,但为了保证操作系统整体的实时性这是值得的,但是不建议有大量相同优先级的任务,这不仅会增加资源的消耗,并且任务执行的顺序也是无法预测的。
程序需要实现Task的实体,下面的代码包含了BCC 和ECC TASK的实现例子。
//Example A Basic Task
#include <Os.h>
TASK(BCC_Task)
{
do_something();
/* Task must finish with TerminateTask() or equivalent. */
TerminateTask();
}
//Example Extended Task Waiting for Events
#include <Os.h>
TASK(ECC_Task)
{
InitializeTheTask();
while (WaitEvent(SomeEvent)==E_OK)
{
do_something();
ClearEvent(SomeEvent);
}
/* Task never terminates. */
}
每一个任务的实体在RTE生成代码的以任务名称命名的.c文件中定义。
Resources配置
Resources的配置在导入OsNeeds.arxml的过程中已经包含了,资源用于提供对共享数据或硬件资源的访问的互斥。任务和ISR可以共享任意数量的资源。所有GetResource()和ReleaseResource()调用都必须正确嵌套。RTA-OS提供三种资源,Standard resources、Linked resources和Internal resources。我们使用的资源是Standard的,另外两种这里就不详述了,有兴趣的读者可以自行在《RTA-OS User Guide.pdf》中查找相关的说明。
下面是使用资源的例子。
#include <Os.h>
TASK(Task1) {
...
GetResource(Resource1);
/* Critical section. */
ReleaseResource(Resource1);
...
TerminateTask();
}
Schedule Table配置
我们可以利用Alarms来相对容易地构建需要周期性和非周期性行为的系统,但是Alarms的一个局限性是每个Alarm只能执行一个操作。如果您需要构建一个系统,在该系统中,任务激活具有阶段性序列,并保证每个序列在时间上有一定的Offset,那么您需要非常小心如何启动和停止警报。调度表能帮我们轻松的实现在这类系统中的任务调度,Schedule Table的配置在导入OsNeeds.arxml的过程中已经包含了,Schedule Table的驱动计数器为Rte_TickCounter,定义成了重复模式,且其持续时间为3000ms。如下图。
Schedule Table中每一个终结点的之间都相差1 tick,并且没有Initial Offset和Final Delay,所以一共产生了3000个终结点,在每个终结点定义了若干操作,下图中在第一个终结点激活了8个任务。
下图是两个不同起点的调度表执行情况,调度表的Initial Offset为4,Final Delay是10。
调度表支持三种同步策略,分别是None、Implicit、Explicit。其中前两种RTA-OS不做任何事情来确保同步,Implicit这种同步是通过将调度表的Duration配置成与定时器MaxAllowedValue一致且使用StartScheduleTableAbs()绝对起点来启动调度表保证的同步。而Explicit则需要两个计数器,首先需要的是驱动器计数器,用于驱动调度表判断终结点位置的。其次需要操作系统外部的同步计数器,RTA-OS被告知同步计数器的值,并使用它来同步调度表。
启动一个调度表有三种不同的方法,第一种使用如下接口在绝对点启动调度表。
/* Start Schedule Table Tbl when the counter reaches tick 6 */
StartScheduleTableAbs(Tbl, 6);
下图为对应调度表的执行情况。
第二种启动方法是以相对当下的Counter计数值的偏移来作为起点,接口如下。
/* Start Schedule Table Tbl 6 ticks from now */
StartScheduleTableRel(Tbl, 6);
Example 10.3: Using StartScheduleTableRel()
下图为对应的调度表执行情况。
最后一种为同步启动,同步启动分为两个步骤,第一是使用StartScheduleTableSynchron函数告知调度表进入WAITING状态,第二个使用SyncScheduleTable告知外部的定时器当前的值。
/* Start Schedule Table when the synchronization count is
provided */
StartScheduleTableSynchron(ExplicitlySynchronizedTable);
/* Table now waits */
...
SyncScheduleTable(ExplicitlySynchronizedTable,42);
/* Table processes first expiry point Duration-42+InitialOffset
ticks from now */
下图为同步启动调度表的执行情况。
调度表的停止调用StopScheduleTable停止调度表。
调度表的启动和停止在RTE生成的Rte_Lib.c中Rte_Start与Rte_Stop进行调用。
Application Modes配置
RTA-OS可以在不同的Application Modes下启动。模式是与应用程序的特定功能模式相对应的完整应用程序功能的集合或子集。Application Modes与一组任务、警报和调度表相关联,当操作系统启动时,这些任务、警报和调度表会自动启动。这样就可以实现我们可以在一个版本程序中实现不同类型功能(也可以匹配不同的硬件设计,高低边输出,高低有效输入等)。这里我们配置成OSDEFAULTAPPMODE。下图为将ASW_OsTask_100ms任务添加到以OSDEFAULTAPPMODE启动的OS自启动项。
在启动系统之前,需要先初始化目标硬件,然后才能运行RTA-OS,除非所有内容都位于内存中正确的位置,否则RTA-OS将无法正常工作。可以使用Shutdown0S()调用随时停止RTA-OS。下面是一个启动OS的例子。
OS_MAIN(){
InitializeTarget();
/* Use RTA-OS to initialize interrupts */
Os_InitializeVectorTable();
StartOS(OSDEFAULTAPPMODE);
/* Never reach here */
}
实际会在EcuM中的静态代码EcuM_Prv.c中的EcuM_Prv_StartOS函数根据配置的应用模式来启动OS。
RTA-OS编译
RTA-OS工具可以直接调用编译器对OS相关代码进行编译,生成静态链接库供程序使用。在完成
了OS所有配置后,可以切换到Builder→Setup界面,对生成文件的路径、包含的头文件等进行设置后,工具会自动生成Build脚本。
我们首先选择基本的配置项如下。
然后我们配置项目输出的,这里默认的是工程文件的相对路径,一般不需要改动。
下面我们可以选择生成一些Samples。ErrorHook提供了ErrorHook函数的模板,我们在使能了错误回调之后,就可以在函数中相应的一些错误处写一些错误处理逻辑。
Includes提供了符合AUTOSAR规范的头文件,当我们在将OS集成到符合AUTOSAR规范的程序时可以使用其提供的头文件,其中介绍以下几个。下面是提供的头文件与AUTOSAR头文件之间的关系。
我们在这里简要介绍几个头文件包含的内容。
- Compiler.h:Os_Compiler_Cfg.h文件包含了一组标记数据与代码大类的宏定义,用于声明符合AUTOSAR规范的函数,Compiler_Cfg.h包含了具体需要系统集成商集成的代码标记宏定义,于声明符合AUTOSAR规范的函数,例如FUNC(void, OS_ERRORHOOK_CODE) ErrorHook(StatusType Error),OS_ERRORHOOK_CODE就在此文件中定义。Compiler.h文件包含上述两个文件,同样包含AUTOSAR规范必要的宏定义,包括#define FUNC(rettype, memclass) rettype memclass等。
- MemMap.h:Os_MemMap.h按功能提供了系统集成商集成代码的Section开始和结束的宏定义,系统集成商可以在这个基础上增加#Param指令与链接文件配合将代码或数据放置到指定的存储位置。
Applications\HelloWorld例程包含了一些空的错误回调函数我们在集成的过程中参考使用,例如以下。
/* ------------------------------------------------------------------------- */
/* This is called during StartOS() and before RTA-OS starts the scheduler.
* It is the safest place to enable interrupt sources that have been initialized
* before StartOS() was called. */
FUNC(void, OS_APPL_CODE) StartupHook(void) {
TargetEnableInterrupts();
}
/* ------------------------------------------------------------------------- */
FUNC(void, OS_APPL_CODE) ShutdownHook(StatusType Error) {
/* End of example */
}
/* ------------------------------------------------------------------------- */
FUNC(Os_StopwatchTickType, OS_APPL_CODE) Os_Cbk_GetStopwatch(void) {
return (Os_StopwatchTickType)STM_TIM0.U;
}
具体的配置如下。
然后为了编译能够正常通过,我们需要包含一些头文件,包括Std_Types.h与Compiler.h,前者包含一些标准类型的定义,后者包含代码数据存储位置信息。
配置完成后,切换到Builder→Build界面,点击Build Now按钮即可开始OS工程的编译,如下图所示。过程中RTA-OS将调用编译器完成OS相关代码的编译。
十六宿舍 原创作品,转载必须标注原文链接。
©2023 Yang Li. All rights reserved.
欢迎关注 『十六宿舍』,大家喜欢的话,给个👍,更多关于嵌入式相关技术的内容持续更新中。