计数器以tick为单位记录操作系统中发生了多少“事情”。滴答是一个抽象的单位。
这是由你来决定你想要一个滴答的意思。
可以这样定义tick:
•时间Time,例如毫秒,微秒,分钟等,然后计数器告诉你已经过去了多少时间。
•旋转Rotation,例如以度或分钟为单位,在这种情况下,计数器会告诉你物体旋转了多少。
•按钮按下Button Presses,在这种情况下,计数器会告诉你按钮被按了多少次。
•错误Errors,在这种情况下,计数器计算错误发生的频率。
一个ISR(有时是一个任务)用于驱动一个计数器。驱动程序负责进行正确的RTA-OS API调用来“tick”计数器,或者告诉RTA-OS计数器已经“tick”到一个必需的值。
每个计数器都有4个强制性属性:
Name 是计数器的名称。RTA-OS使用与计数器具有相同名称的标识符为每个计数器创建一个句柄。
Type 定义计数器模型。AUTOSAR提供两种型号
软件Software计数器是由操作系统内部维护的计数值。User需要提供一个计数器驱动程序,告诉RTA-OS将计数器增加一个tick。在6.2.1节中提供了进一步的细节。
硬件Hardware计数器是由外设维护的计数值。User需要提供一个计数器驱动程序,该驱动程序告诉操作系统何时已经过了请求的tick数。操作系统还将要求您的驱动程序支持支持RTA-OS用于在运行时管理外围设备的回调例程的实现。章节6.2.2提供了进一步的细节。
当需要相对较低的分辨率(例如一毫秒或更高)时,软件计数器就足够了。当需要非常高的分辨率(例如在微秒范围内),或者需要将RTA-OS中的任务调度精确地同步到外部源(例如TPU或全局(网络)时间源)时,应该使用硬件计数器。
Maximum Value定义计数器的最大计数值。在达到最大允许值后,所有计数器在刻度上自动归零。对于16位计数器将为65535(216−1),对于32位计数器将为4294967295(232−1)。这对应于AUTOSAR OS计数器属性MAXALLOWEDVALUE。端口的最大可能计数器值由文件Os.h中TickType类型的大小决定。
集成指南:对于硬件计数器,必须确保最大匹配值+1等于外围设备的模量。
Minimum Cycle最小周期定义在为Alarm或者schedule table偏移量设置周期值时允许的最短刻度数。在大多数情况下,希望它是1个刻度。但是,如果希望构建的系统在计数器上强制执行最小的警报间隔,那么可以选择更大的值。这对应于AUTOSAR OS计数器属性MINCYCLE。
Ticks per base是一个属性,可用于定义计数器上每个刻度所需的基础计数器驱动刻度的数量。可以为这个属性赋任何值,因为RTA-OS不使用它。这对应于AUTOSAR操作系统属性TICKSPERBASE。
还有一个附加的可选属性:
Seconds Per Tick以秒为单位定义计数器滴答的持续时间。如果想要使用AUTOSAR OS提供的刻度/时间转换功能,则应该定义此选项。进一步的细节在第6.5节中给出。
图6.1 声明一个计数器
RTA-OS不控制任何硬件来提供计数器驱动程序。这使得RTA-OS非常容易与任何tick源集成,例如计时器滴答,错误计数,按钮按压,TPU外设等。这意味着需要为在RTA-OS中声明的每个计数器提供驱动程序,并将其接口到操作系统。
驱动程序和计数器之间的接口取决于计数器的类型:
Software Counters由API调用递增。
Hardware Counters该计数值保存在外部硬件外围设备中。应用程序必须提供一个更复杂的驱动程序,该驱动程序告诉RTA-OS请求的滴答数已经过了。RTA-OS使用特殊的回调来设置请求的tick数,取消请求,获取当前计数值并获取计数器的状态。
对于每个软件计数器,需要提供提供刻度的驱动程序。RTA-OS在StartOS()期间将所有软件计数器初始化为零并向上计数。
软件计数器驱动模型在AUTOSAR OS中标准化,如图6.2所示。
图6.2 Ticked Counter Driver Model
实现软件计数器:
使用API调用IncrementCounter(CounterID)来增加RTA-OS中保存的计数器值。当向MAXALLOWEDVALUE中添加1时,软件计数器会自动归零。
可以从应用程序代码中的大多数地方调用IncrementCounter(CounterID)。计数器最常见的用途之一是为RTA-OS提供基于Alarm或Schedule table激活任务的时间基础。在这种情况下,需要提供一个周期性计时器中断,在每次到期时调用IncrementCounter(CounterID)。
例6.1展示了毫秒中断如何驱动一个名为TimeCounter的计数器。
#include <Os.h>
ISR(HandleTimerInterrupt) {
DismissTimerInterrupt();
IncrementCounter(TimeCounter);
}
Example 6.1: Using a periodic interrupt to tick a software counter
软件计数器的另一个常见用途是作为容错系统的一部分,在超过错误阈值时需要采取某些操作。软件计数器可用于记录错误的数量,然后可以使用警报来触发恢复操作(例如,激活错误恢复任务)。
例6.2展示了名为Critical的任务如何在名为ErrorCounter的计数器上记录错误。
#include <Os.h>
TASK(Critical){
...
if (Error) {
IncrementCounter(ErrorCounter);
}
...
TerminateTask();
}
Example 6.2: Using a periodic Task to tick a software counter
静态计数器接口Static Counter Interface:
由于AUTOSAR API调用将计数器的名称作为参数,这意味着RTA-OS必须在更新OS数据结构之前在内部取消对参数的引用。这也意味着编译器需要在进入时将参数压入堆栈。
然而,通常情况下,在构建时就知道将从何处计时哪个计数器。可能需要从中断处理程序驱动计数器。
RTA-OS认识到这一点,并为配置文件中声明的每个计数器生成一个专用的API调用Os_IncrementCounter_<CounterID>()(其中CounterID是计数器的名称)。
集成指南: API调用Os_IncrementCounter_<CounterID>()不一定可移植到其他AUTOSAR操作系统实现。
例如,考虑一个包含两个计数器的应用程序:一个名为TimeCounter,另一个名为AngularCounter。rtaosgen将生成例6.3中所示的两个API调用。
为定时器和角中断提供服务的中断处理程序必须调用这些API调用。
例6.4展示了这些中断处理程序。
#include <Os.h>
ISR(HandleTimerInterrupt) {
ServiceTimerInterrupt();
Os_IncrementCounter_TimeCounter();
}
ISR(HandleAngularInterrupt) {
ServiceAngularInterrupt();
Os_IncrementCounter_AngularCounter();
}
Example 6.4: Interrupt Handlers for Example 6.3
#include <Os.h>
ISR(MillisecondInterrupt) {
ServiceTimerInterrupt();
Os_IncrementCounter_Counter1();
Os_IncrementCounter_Counter2();
...
Os_IncrementCounter_CounterN();
}
Example 6.5: Making multiple calls to the static software counter interface
如果您有多个软件计数器,您需要以相同的速率tick,可以在处理程序中进行多个Os_IncrementCounter_<CounterID>()调用,如例6.5所示
对于声明的每个计数器,都有一个Os_IncrementCounter_<CounterID>() API调用。这些静态API调用比AUTOSAR IncrementCounter(<CounterID>) API调用更快,使用更少的RAM,因为调用不需要参数,也不需要计算出哪个计数器被选中。user应该决定哪个版本适合您的应用程序,并相应地进行选择。
对于每个硬件计数器,User需要提供调用RTA-OS的硬件计数器驱动程序和RTA-OS使用的一组回调。与软件计数器一样,RTA-OS提供了一个定义良好的接口,用于将高级计数器驱动程序连接到操作系统。
集成指南:AUTOSAR OS标准没有指定处理硬件计数器的标准API调用。如果要将应用程序从另一个操作系统移植到RTA-OS,则可能需要更改硬件计数器驱动程序API调用。
对于每个硬件计数器,RTA-OS知道该计数器驱动的下一个动作是什么,是使警报过期,还是处理调度表上的过期点,或者两者兼而有之。RTA-OS还知道在发生这种情况之前需要经过多少滴答。这被称为匹配值。
图6.3 Advanced Counter Driver Model
当使用软件计数器时,驱动程序会在每次计时结束时告诉RTA-OS。RTA-OS在内部对刻度进行计数,当达到匹配值时,采取操作。然后RTA-OS计算下一个匹配值并重复该过程。
相比之下,当使用硬件计数器时,RTA-OS通过回调函数告诉驱动程序何时需要进行下一个操作。外设计算请求的滴答数,并在正确的滴答数耗尽时生成中断。在中断处理程序中,可以调用Os_AdvanceCounter_CounterID() API来告诉RTA-OS处理CounterID上的下一个操作。RTA-OS这样做,然后重复这个过程。
通常,User将使用中断来驱动软件和硬件计数器。对于软件计数器,无论RTA-OS是否有任何事情要做,每个计数器滴答都会发生中断。对于硬件计数器,只有当RTA-OS需要做某事时才会发生中断。这意味着硬件计数器将中断干扰减少到所需的绝对最小值。
Advancing Hardware Counters
User使用API调用Os_AdvanceCounter_<CounterID>()来告诉RTA-OS匹配值已经达到。
集成指南:User负责编写调用Os_AdvanceCounter_<CounterID>()的驱动程序,并确保在正确的时间采取下一个操作。
Os_AdvanceCounter_<CounterID>() API调用导致下一个警报和/或到期点被处理,并将通过调用User提供的回调来设置下一个匹配值,或者,如果没有操作要做(即计数器上没有活动警报或计划表),取消来自驱动程序的中断。关于编写硬件计数器驱动程序的更多详细信息可以在后面章节中找到。
Callback Functions
对于软件计数器,RTA-OS在内部计算经过的滴答数。这意味着RTA-OS总是知道当前的计数器值,在下一次到期之前有多少个滴答等等。
对于硬件计数器,外设计算经过的滴答数。这意味着RTA-OS必须告诉硬件计数器它需要计数多少个节拍,并且必须询问外设当前的计数值,到下一个到期的节拍数等。
这种交互是使用回调函数在RTA-OS和您想用作计数器驱动程序的任何类型的硬件外设之间进行接口的。回调函数的确切功能性取决于用作硬件计数器驱动程序的外设。
然而,通过一个简短的概述,需要四个回调:
Os_Cbk_Set_<CounterID>()
这个回调为下一个动作到期时发生的中断设置状态。回调函数被传递一个计数器的绝对值,在这个计数器上应该发生一个动作。对于计数器,这个回调在两种不同的情况下使用:
1. 开始 Starting
当Schedule Table或Alarm在计数器上启动时,设置初始中断源。
2. 重置 Resetting
缩短到下一个计数器到期的时间。这是必要的,例如,当下一个中断超过100个滴答时,做一个SetRelAlarm(WakeUp, 100)调用。
AlarmBaseType Info;
GetAlarmBase(Alarm2, &Info);
MaxValue = Info.maxallowedvalue;
BaseTicks = Info.ticksperbase;
MinCycle = Info.mincycle;
Example 6.6: Using GetAlarmBase() to read static counter attributes
Os_Cbk_State_<CounterID>()
此回调返回计数器上的下一个操作是否挂起,以及(通常)在达到匹配值之前剩余的tick数。
Os_Cbk_Now_<CounterID>()
此回调需要返回外部计数器的当前值。这用于GetCounterValue() API调用。
Os_Cbk_Cancel_<CounterID>()
这个回调必须为计数器清除任何挂起的中断,并确保中断不能成为挂起,直到一个Os_Cbk_Set_<CounterID>()调用进行。如果不取消计数器上的所有警报和/或停止由计数器驱动的计划表,则不需要此调用。
集成指南:硬件计数器可用于多核系统。RTA-OS将确保State, Set和Cancel回调只在拥有计数器的核心上被调用。这意味着User可以从拥有计数器的不同核心启动alarm和ScheduleTables, RTA-OS将向计数器的核心发送消息,告诉它调用适当的回调。
RTA-OS API调用GetAlarmBase()总是返回配置的计数器值。GetAlarmBase()的结构如例6.6所示。
配置的值也可以以符号常量的形式访问,如下所示。
• OSMAXALLOWEDVALUE_<CounterID>
• OSTICKSPERBASE_<CounterID>
• OSMINCYCLE_<CounterID>
因此,上面的例6.6也可以写成例6.7:
MaxValue = OSMAXALLOWEDVALUE_Alarm2;
BaseTicks = OSTICKSPERBASE_Alarm2;
MinCycle = OSMINCYCLE_Alarm2;
Example 6.7: Using macros to read static counter attributes
如果创建了一个名为SystemCounter的计数器,那么在AUTOSAR OS中可以通过省略末尾的_CounterID来使用简短的宏形式访问相关的计数器属性:
OSMAXALLOWEDVALUE_SystemCounter ➔ OSMAXALLOWEDVALUE
OSTICKSPERBASE_SystemCounter ➔ OSTICKSPERBASE
OSMINCYCLE_SystemCounter ➔ OSMINCYCLE
RTA-OS为SystemCounter生成两种形式的宏,User可以使用任何一种版本。SystemCounter还提供了一个额外的宏来获取计数器滴答的持续时间(以纳秒为单位),称为OSTICKDURATION。
集成指导:生成一个有意义的OSTICKDURATION宏需要配置计数器属性“Seconds Per Tick”。
应用程序需要能够在运行时读取计数器的当前值。例如,User可能想知道错误计数器记录了多少错误,按钮被按了多少次,或者经过了多少时间。
计数器的当前值可以在运行时通过调用GetCounterValue() API来读取,如例6.8所示。
TickType HowMany;
GetCounterValue(ButtonPresses,&HowMany);
Example 8.8: Using GetCounterValue()
当使用GetCounterValue()时,应该意识到:
•计数器从MAXALLOWEDVALUE到零,因此计算需要补偿。Counters wrap around from MAXALLOWEDVALUE to zero, so the calculation needs to compensate for the wrap
•抢占可以在调用返回时发生,这意味着当您恢复时,' Now '的值将是旧的
•当使用硬件计数器时,当调用返回时,计数器驱动程序仍将递增。即使没有发生抢 占,立即执行的计算也将基于旧数据
如果需要执行一个简单的计算来计算自上次读取值以来计数器经过了多少次计时,那么可以通过使用GetElapsedCounterValue() API调用来避免这种潜在的竞争条件。该调用将先前读取的计数器值作为输入,并计算已经过的刻度,包括对计数器包装的补偿。计算发生在操作系统级别(即禁用中断),因此不会受到抢占效应的影响。
示例6.9显示了如何使用此特性来度量任务的端到端(响应)时间。
#include <Os.h>
TickType Start;
ISR(CaptureTrigger){
/* Dismiss interrupt */
GetCounterValue(TimeCounter,&Start);
...
ActivateTask(GenerateResponse);
}
TASK(GenerateResponse){
TickType Finish;
CalculateValue();
WriteToDevice();
GetElapsedCounterValue(TimeCounter,&Start,&Finish);
...
TerminateTask();
}
Example 6.9: Using GetElapsedCounterValue()
如果计数器正在计算时间刻度(如例6.9),那么这在AUTOSAR OS中被称为“自由运行计时器free running timer”。这种类型的计数器没有什么特别之处——它与任何其他类型的计数器相同——唯一的区别是计数器是由计时器滴答源驱动的。
自由运行计时器功能的预期用途是在运行时测量短、高准确度的持续时间。如果需要这样做,那么可能需要使用硬件计数器来获得所需的计数器分辨率。
通常将计数器用作操作系统的时基参考。对于编写的大多数应用程序,事件的相对定时将是由系统需求决定的实时值。很可能会根据实时值、纳秒、毫秒等来考虑系统配置,而不是使用更抽象的tick(滴答)概念。
如果计数器配置参数“秒每滴答”已经配置,那么RTA操作系统生成宏供使用,以在滴答和实时单位之间转换。
可移植性注意事项:AUTOSAR OS声明Tick到时间的转换仅适用于硬件计数器。但是,该特性通常对软件和硬件计数器都有用,并且AUTOSAR XML配置语言支持对这两种类型的计数器进行配置。
在RTA-OS中,这种异常通过为软件和硬件计数器提供刻度到时间的转换来解决。但是,应该注意,其他AUTOSAR OS实现不一定支持为软件计数器提供这些宏。
提供了以下Tick转换为时间的宏:
• OS_TICKS2NS_CounterID(ticks) converts ticks to nanoseconds
• OS_TICKS2US_CounterID(ticks) converts ticks to microseconds
• OS_TICKS2MS_CounterID(ticks) converts ticks to milliseconds
• OS_TICKS2SEC_CounterID(ticks) converts ticks to seconds
这些宏返回的值是PhysicalTimeType,而不是TickType,可能会使用这些宏的API调用使这些值,因此需要将它们转换为适当的类型。
例6.10展示了如何在应用程序代码中使用这些宏来使用静态定义的“timeout”值来模拟超时。
#define TIMEOUT_MS 100 /* Set a timeout to be 100ms */
TickType TimeoutInTicks;
TimeoutInTicks =
(TickType)((PhysicalTimeType)TIMEOUT_MS/OS_TICKS2MS_TimeCounter(1));
SetRelAlarm(TimeoutAlarm, TimeoutInTicks, 0);
Example 6.10: Programming an alarm with time rather than ticks (1)
RTA-OS将尽可能使用整数乘法或除法生成这些宏。然而,对于某些滴答率,有必要在计算中使用浮点数,以保持准确性。当这些宏被传递给编译时已知的固定值时,编译器通常会自己执行计算并嵌入整型结果。如果传递的值是一个变量,那么编译器将不得不在运行时生成使用浮点计算的代码。如果这在应用程序中可能是一个问题,应该检查文件Os_Cfg.h以检查宏的代码。
除了这些宏之外,RTA-OS还生成一个名为otickduration_ <CounterID>的宏,该宏以纳秒为单位返回计数器滴答的持续时间,因此,如果您希望编写固定时间的警报,即使更改底层计数器滴答率,它也非常有用。例6.11展示了如何使用otickduration_ <CounterID>宏对例6.10进行修改。这个版本提供了稍好的性能,因为单个tick的持续时间不需要在运行时计算。
#define TIMEOUT_NS 100000000 /* Set a timeout to be 100ms */
TickType TimeoutInTicks;
TimeoutInTicks = (TickType)(TIMEOUT_NS/OSTICKDURATION_TimeCounter);
SetRelAlarm(TimeoutAlarm, TimeoutInTicks, 0);
Example 6.11: Programming an alarm with time rather than ticks (2)
可移植性注意事项: OSTICKDURATION_<CounterID>宏是由RTA-OS提供的,不是AUTOSAR OS标准的一部分。这些宏的使用不能移植到其他实现中。
•计数器用于注册一些tick源的计数。
•计数器可以是软件计数器也可以是硬件计数器。User需要为配置的计数器类型提供私有驱动程序。
参考文档:
[1] RTA-OS V6.1.3 User Guide
[2] Specification of Operating System AUTOSAR Release 4.2.2
更多文章
会员权益: (点击可进入)谈思实验室VIP会员
END
微信入群
谈思实验室专注智能汽车信息安全、预期功能安全、自动驾驶、以太网等汽车创新技术,为汽车行业提供最优质的学习交流服务,并依托强大的产业及专家资源,致力于打造汽车产业一流高效的商务平台。
每年谈思实验室举办数十场线上线下品牌活动,拥有数十个智能汽车创新技术的精品专题社群,覆盖BMW、Daimler、PSA、Audi、Volvo、Nissan、广汽、一汽、上汽、蔚来等近百家国内国际领先的汽车厂商专家,已经服务上万名智能汽车行业上下游产业链从业者。专属社群有:信息安全、功能安全、自动驾驶、TARA、渗透测试、SOTIF、WP.29、以太网、物联网安全等,现专题社群仍然开放,入满即止。
扫描二维码添加微信,根据提示,可以进入有意向的专题交流群,享受最新资讯及与业内专家互动机会。
谈思实验室,为汽车科技赋能,推动产业创新发展!