ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

TC3XX上AUTOSAR OS任务与事件配置实战:从Task到Event

TC3XX上AUTOSAR OS任务与事件配置实战:从Task到Event 第一次在TC3XX上把裸机轮询迁到AUTOSAR OS最磨人的不是编译错误而是你明明照着模板配了一个Task系统却一动不动你给它加了一个Event任务反而连初始调度都跑不起来。我第一次做这种迁移时问题几乎全出在Task类型、Event挂载位置和优先级这些最基础的对象上。这篇文章把我实际配置Vector MICROSAR的完整过程梳理出来从Task到Event再到TC3XX上的调试方法尽量不让后来者再走我那些弯路。1. 项目概述为什么是 MICROSAR 与 TC3XX 这套组合1.1 什么是 MICROSAR它和 AUTOSAR OS 是什么关系先说结论MICROSAR是Vector提供的一套商用AUTOSAR Classic协议栈软件它不是一个单独模块而是一整个产品家族包括MICROSAR OS、MICROSAR RTE、MICROSAR CAN/CAN FD、MICROSAR Diagnostics等。本文用到的主要是其中的MICROSAR OS也就是AUTOSAR规范里的操作系统模块。AUTOSAR OS本质上是从OSEK/VDX OS演进过来的保留了OSEK的任务调度、中断处理、资源管理机制又增加了Schedule Table、多核Multi-Core、Timing Protection、Memory Protection、OS Applications等新特性。和Linux这种“跑起来再说”的系统不一样AUTOSAR OS的一切调度对象都是静态定义的任务、事件、计数器、调度表在配置阶段全部固定下来。所以你会看到这么个现象真正能跑的代码没写几行配置工具里的点选反而占了大头。MICROSAR OS的价值在于它把这些规范落地成了可以商用的代码并且和Vector的配置工具链主要是DaVinci Configurator Pro深度绑定。你在图形界面里配好Task和Event它直接生成对应的C代码和配置表省去了自己手搓OS对象的痛苦。说句实在话如果你硬要用符合AUTOSAR规范的OS但不用Vector这套工具工作量完全不在一个量级。1.2 TC3XX 在车载项目中的位置英飞凌TC3XX是AURIX系列的第二代产品典型型号有TC367、TC377、TC387、TC397等采用TriCore架构单芯片上放了多个TriCore核心比如TC387是三核TC397是三核/六核的衍生。在主流的域控制器、动力域、底盘域、ADAS控制器里TC3XX出现频率非常高不是没有原因的算力够用适合做多核软件架构下的OS部署自带硬件MPU能和AUTOSAR OS的内存保护机制配合起来拦野指针有成熟的编译与调试生态TASKING、HighTec、PLS UDE、Lauterbach都支持得不错周边外设资源多CAN、LIN、ETH、ADC、GTM这些能满足整车控制的基本盘。做AUTOSAR底软选TC3XX做载体是一个非常标准的搭配。不是因为别的芯片不行而是当你遇到问题时社区方案、官方例程、Vector相关配置案例都最多这对排错来说太重要了。1.3 这篇实战解决什么问题适合谁看这篇文章聚焦两件事在MICROSAR里配置Task以及在Task上配置Event并让它们真正跑起来。它会覆盖从概念、环境准备、配置步骤、代码填充到调试排错的完整链路。适合以下读者从裸机或FreeRTOS转向AUTOSAR OS的嵌入式工程师已经拿到一个MICROSAR工程但不知道怎么定义Task和Event的新人正在做TC3XX平台底软、准备把多核任务落地的开发人员。如果你是完全零基础建议先补一下AUTOSAR的整体分层和OSEK的基本概念否则后面很多配置项会显得“不知道为什么存在”。2. 动手之前先弄清机制Task 与 Event 在 AUTOSAR OS 里的真实身份2.1 Task 的两种类型以及为什么你经常用 Extended TaskAUTOSAR OS把任务分成两类Basic Task基本任务和Extended Task扩展任务。新手最容易犯的一个错就是以为所有任务都能等Event。不是的。Basic Task执行完自己主动结束TerminateTask不能WaitEvent没有Waiting状态。它的生命周期非常“直肠子”激活-就绪-运行-结束。Extended Task除了Basic Task的状态之外多了Waiting状态可以调用WaitEvent挂起自己等待别处SetEvent唤醒。你可以把Basic Task理解成一个一次性函数调用跑完就结束把Extended Task理解成一个带阻塞能力的循环能在没有消息来的时候让出CPU。实际工程里周期任务、事件驱动任务绝大多数都配成Extended Task因为谁也不希望一个高优先级任务紧紧霸占CPU空转等数据。2.2 任务状态机从创建到运行再到等待AUTOSAR OS任务有四种状态状态含义进入方式Running正在CPU上执行调度器从Ready队列选出Ready已就绪但没获得CPU激活、抢占、事件触发、等待条件满足Waiting等待某事件发生扩展任务调用WaitEventSuspended任务未激活或已结束初始状态、TerminateTask后调度器只有在某个任务主动让出CPU、或者被更高优先级任务抢占时才可能换下一个任务跑。这里有个隐蔽点如果把任务配置成FULL可抢占类型那么高优先级任务激活后可以立刻抢占低优先级任务如果配置成NON不可抢占哪怕高优先级已经就绪低优先级也要先跑到主动结束或者遇到调度点。这个状态机不是考概念用的。你在调试时看到的“任务不动了”本质上就是任务停在了错误的状态比如该Running却卡在Waiting该Ready却因为优先级问题永远排不上。2.3 Event 的定位它是挂在 Extended Task 上的信号牌严格讲Event并不独立存在于系统里它必须挂载到某个Extended Task上。每个事件本质上是EventMaskType里的一个位也就是一个32位的掩码值。Event的典型工作流是任务A正在Running调用WaitEvent(Event_X)后进入Waiting任务B或者其他中断调用SetEvent(Task_A_ID, Event_X)系统把任务A对应的事件位置1任务A进入Ready状态等待调度任务A再次获得CPU通过GetEvent读取掩码判断是不是自己等的事件然后调用ClearEvent清掉标志位。所以Event不是“邮箱”不带数据它更像一块“信号牌”只管通知“某件事发生了你可以往下走了”。如果你需要同时传数据得配合RTE或共享内存做。2.4 调度优先级和抢占策略怎么影响行为AUTOSAR OS的优先级是在配置阶段写死的。MICROSAR的实践里我见到的绝大多数工程都是“数值越大优先级越高”但这个不绝对所以拿到新工程千万别靠猜。我自己的习惯是建立一张Task清单把优先级、周期、类型、事件、所在核全部列出来配置前先评审一遍。抢占策略要特别注意它会导致一种很反直觉的现象低优先级任务如果配了不可抢占它在一定窗口内会挡住所有高优先级任务。这在时间分析时非常关键尤其是TC3XX多核场景下核间调度没有全局时间片的概念全靠优先级和抢占策略维持确定性。3. 环境准备Vector 工具链与 TC3XX 工程的搭建要点3.1 一套能跑起来的工具链清单我实际用的环境清单大致如下不一定是最新胜在稳定工具/组件用途DaVinci Configurator ProMICROSAR模块的图形化配置DaVinci Developer软件组件和RTE配置如果你涉及RTE的话MICROSAR OS产品包OS的库文件、生成文件模板TASKING VX-toolset for TriCore编译/链接PLS UDE 或 Lauterbach TRACE32调试、Trace、查看寄存器TC387/TC397最小系统板跑系统验证这里有个容易忽略的环节MICROSAR版本和TASKING版本是有匹配关系的。我踩过一次很深的坑项目从MICROSAR老版本升到新版本后编译器还停留在旧版本结果任务上下文切换的汇编文件编译出来行为异常调度器偶发卡死。后来查Vector Release Note才发现它对编译器版本有明确要求。3.2 MICROSAR 工程的模块关系一个典型的TC3XX底软工程里模块大概是这样分工的MCAL层Vector或英飞凌MCAL负责驱动芯片外设BSW层MICROSAR OS负责调度MICROSAR COM/CAN负责通信EcuM/BswM负责启动和模式管理RTE层可选生成软件组件之间通信的中转应用层你真正写的任务函数。Task和Event配置集中在OS模块里它会生成一堆Os开头的文件包括Os_Cfg.c、Os_Cfg.h、Os_Lcfg.c这些。后面我会专门讲怎么看这份生成代码。3.3 从复位到 StartOS任务是什么时候开始有机会跑的TC3XX上电后并非直接进用户main。整个启动链路是启动代码初始化CPU CSR、栈指针、看门狗调用EcuM_Init初始化必要的BSW模块经过一系列模式切换后最终调用StartOS把AUTOSAR OS调度跑起来StartOS内部会启动配置成Autostart的任务然后进入调度循环。不少新手拿到模板工程直接把代码写在main里不调用StartOS结果OS服务全部返回错误。记住StartOS之前的阶段OS API不能乱调StartOS之后main里你的用户代码才有机会被OS调度执行。4. Task 配置实战在 DaVinci Configurator 里搭出第一个任务4.1 创建 OsApplication 并绑定 Core在DaVinci Configurator Pro里Task不是散落在OS根节点下的它属于某个OsApplication。OsApplication是AUTOSAR里一个重要的隔离边界一个OsApplication通常绑定到一个Core。我的配置步骤是在DaVinci Configurator Pro的OS模块下新建OsApplication比如命名App_Core0把App_Core0的CoreReference指向TC3XX的Core 0在App_Core0下面新建任务相关对象。这里注意TC3XX是多核芯片每个Core可以独立运行一个OS Application。如果你打算把任务分布到Core0和Core1需要分别建立OsApplication。同一个任务不能同时属于两个App跨核任务通信要么走SetEvent这种核间事件机制要么走RTE的跨核通信配置上都要显式打开跨核访问权限。4.2 创建 OsTask关键属性逐个讲在OsApplication下右键新建OsTask会看到一大堆属性。别被吓到核心就这几个配置项推荐/实践值说明Task TypeEXTENDED如果需要WaitEvent必须选扩展任务ScheduleFULL允许抢占NON即不可抢占Priority根据任务重要性分配数值大的优先级高按工具提示确认Activation1任务被连续激活的最大次数AutostartFALSE/TRUE启动OS后自动进入Ready的任务配置为TRUEStack Size2048字节起步TC3XX上根据函数调用深度调整Event后续添加只能添加在扩展任务上关于Activation我多说一句。它表示任务最多能被激活多少次比如Activation1意味着任务还没结束之前你不能再次调用ActivateTask激活它否则会返回错误码。实际项目里我习惯把它设成1用调度周期或事件来控制任务运行节奏避免任务重入导致数据竞争。Stack Size这个值很多初学者喜欢填512觉得没事。AUTOSAR任务的函数调用深度加上中断上下文消耗很容易超过这个数。TC3XX上出现栈溢出不一定立刻崩可能只是某个局部变量被踩坏导致任务行为随机。建议起步2048等整个模块稳定了再调优。4.3 任务函数的代码写法配置完成后DaVinci会生成一个任务函数的“空壳”你需要到相应的C文件里实现它。基本任务的写法TASK(Task_1ms_Trigger) { /* 做一些周期性的触发动作 */ SetEvent(Task_10ms_Com, Event_Tick10ms); /* 基本任务必须主动结束 */ TerminateTask(); }扩展任务的写法通常是死循环加WaitEventTASK(Task_10ms_Com) { EventMaskType eventMask 0u; while (1) { /* 等待某个事件 */ WaitEvent(Event_Tick10ms); /* 读取实际的事件掩码判断是哪个事件唤醒了自己 */ (void)GetEvent(Task_10ms_Com, eventMask); if ((eventMask Event_Tick10ms) ! 0u) { /* 清掉事件再干活 */ ClearEvent(Event_Tick10ms); /* 读取底层信号更新RTE数据 */ ReadCanSignal(); UpdateOutputData(); } } }注意扩展任务里不能随便写return退出否则系统调度状态会被破坏。如果任务真的不再需要了可以调用ChainTask或者TerminateTask但这在周期型扩展任务里非常少见。4.4 多核 TC3XX 上的任务归属TC3XX的多核特性意味着选择任务放在哪个核直接决定它能访问哪些外设、能不能看到哪个内存区域。比如GTM或CCU6定时器分配给Core0管理那对应的处理任务最好放在Core0。跨核访问虽然允许但每多一次跨核就有同步和资源竞争风险。在配置阶段我一般会把任务的核归属和外设归属画成一张表避免后面写代码时一个任务跑在Core1却去访问一个只能在Core0上下文里安全操作的外设寄存器。AUTOSAR OS本身不会替你检查这种访问是否合理它只管调度。5. Event 配置实战让任务在等待中被唤醒5.1 事件和任务之间靠 Mask 沟通在AUTOSAR OS里Event和Task的绑定方式是你在某个Extended Task下面创建一个Event对象系统会为它分配一个掩码位。多个事件挂在同一个任务上时每个事件的掩码必须不同否则无法区分。我习惯的事件掩码规划Event_A0x00000001Event_B0x00000002Event_C0x00000004不要用0xFFFFFFFF这种“全置位”的掩码来代表某一个具体事件一旦和别的位冲突判断逻辑就会错乱。5.2 在 DaVinci Configurator 里配置 Event在目标OsTask必须是EXTENDED类型下新建OsEvent配置项主要包括配置项实践值Event NameEvent_Tick10msEvent Mask0x00000001或由工具自动分配所属TaskTask_10ms_Com生成后系统会把事件掩码和相关宏定义写进Os_Cfg.h你在用户代码里直接用事件名称就行不用手写魔法数。5.3 一组可以实际使用的 Task Event 代码假设这样一个场景一个1ms任务负责扫描硬件信号每累计10次就通知10ms通信任务去发送报文。用Event实现的骨架如下任务一TASK(Task_1ms_Trigger) { static uint32_t tickCount 0u; tickCount; if (tickCount 10u) { tickCount 0u; (void)SetEvent(Task_10ms_Com, Event_Tick10ms); } TerminateTask(); }任务二TASK(Task_10ms_Com) { EventMaskType curEventMask 0u; while (1) { WaitEvent(Event_Tick10ms); (void)GetEvent(Task_10ms_Com, curEventMask); if ((curEventMask Event_Tick10ms) ! 0u) { ClearEvent(Event_Tick10ms); /* 组装CAN报文并发送 */ SendCanFrame(); } } }这里要特别提醒WaitEvent返回后尽量先用GetEvent确认是哪一位被置位再处理。虽然很多场景下一个任务只等一个事件但一旦后面加了别的Event不清掩码就往下走很可能会误处理。5.4 配置阶段最容易错的三处第一任务类型不是EXTENDED。你配了Event但任务本身是Basic Task系统约束不允许WaitEvent生成的代码里API会返回错误甚至配置工具直接报错。第二事件挂错了任务。Event M是挂在任务A名下的你却用SetEvent(Task_B, Event_M)如果任务B上并没有这个Event这个调用会失败。AUTOSAR里SetEvent的第一个参数是目标任务ID第二个参数是这个目标任务上确实存在的事件掩码。第三ClearEvent的时机不对。收到事件后如果先清位再读掩码可能什么都读不到如果在WaitEvent之前就清位则可能把之前已经置位的事件清掉导致任务永远等不到自己真正想要的那一次唤醒。6. 生成代码怎么看从 Os_Cfg.c 读懂任务与事件6.1 生成的配置文件里藏了哪些信息DaVinci Configurator生成代码后打开Os_Cfg.c你能看到类似这样的静态配置数据每个任务的优先级、调度策略、栈指针、任务函数入口每个事件的掩码每个OS Application对应的Core编号系统启动时Autostart任务列表。这相当于一份“调度器初始化清单”把你在图形界面里点的每一下都翻译成了C语言常量。我曾见过同事改代码时直接在Os_Cfg.c里手改优先级结果下次重新生成代码时被覆盖改了个寂寞。正确做法永远是回配置工具里改再重新生成。6.2 如何在运行期确认任务真的在按预期跑任务跑没跑状态对不对最直接的办法是用调试器挂上去看任务状态变量。MICROSAR OS内部会为每个任务维护一个状态表你可以用工具查看当前任务在Running还是Waiting。如果任务一直Suspended说明它从未被激活如果一直Waiting说明它等的事件一直没到。想更简单点可以在任务里加运行计数变量volatile uint32_t g_task10msRunCount 0u; TASK(Task_10ms_Com) { /* ... */ g_task10msRunCount; }调试时直接看这个计数是否每秒增大约100次。这种手段在系统联调时非常管用能快速判断是调度没起来还是任务被谁堵住了。7. 基于 TC3XX 调试时我踩过的几个坑7.1 任务栈溢出现象诡异在TC3XX上跑AUTOSAR OS任务栈最大的特点是它不仅在任务运行时使用某个中断嵌套发生时也可能消耗当前任务的栈。如果把栈配得太小系统不会像Windows那样弹窗告诉你“栈溢出”而是运行一段时间后任务突然陷入Trap或者某个局部变量莫名被改写。排查手段先看Trap寄存器确认是不是Context Management类型的Trap如果现象随机优先把嫌疑任务的栈从1024加到2048再看能否复现。很多“灵异问题”最后都只是栈不够大。7.2 Event 设置了但任务收不到这是我最常被问的问题之一。现象是SetEvent明明返回了OK目标任务却一直卡在Waiting不动。排查链路分三步确认目标任务是EXTENDED确认SetEvent使用的任务ID和事件掩码确实属于目标任务确认这个任务的事件掩码没有因为跨核、跨OS Application被禁止访问。最后一条特别隐蔽。TC3XX多核工程里如果你在Core1上SetEvent去唤醒Core0上的任务而两边配置里没有允许跨核访问这个事件可能根本不会生效。我后来习惯在配置阶段就检查每个OsApplication的跨核访问设置不要等到联调时才查。7.3 高优先级任务把系统拖死如果两个任务配置成不同的优先级高优先级任务又恰好是死循环那么低优先级任务可能永远得不到CPU。有些同事会选择把高优先级任务每次循环里加个等待比如等待一个Event这看似解决了问题实际是把调度压力转给了事件源。更好的做法是重新梳理任务周期和依赖关系把不依赖紧耦合数据的任务放到低优先级把真正有硬实时要求的中断服务和紧周期任务放到高优先级。AUTOSAR OS不只靠优先级还可以用调度表或计数器让系统在时间上更均匀不要盲目堆高优先级任务。7.4 工具链版本和生成代码不兼容MICROSAR和编译器、调试器的版本匹配非常严格。我遇到过TASKING编译器一个小版本升级后编译生成的代码在中断返回时上下文恢复异常。后来发现是Vector该版本还没有官方适配那个编译器小版本。建议是每次更新工具链前先去Vector官网看Release Note里的兼容性矩阵别只盯着新功能。旧工程尽量保持工具链冻结除非有明确需求否则不折腾。最后分享一点我的个人操作习惯在实际项目里我习惯把所有Task和Event先做成一张Excel清单列出任务名、优先级、调度方式、周期、是否Autostart、所属Core、栈大小、事件名、事件掩码。配置前先评审这张表配置时照着填配置完再做一次交叉检查。这样做的好处是多核、多模块协同开发时不会你加一个Task我加一个Event最后谁也不知道谁的优先级更高、谁和谁的掩码撞了。AUTOSAR OS的静态特性决定了前期规划比后期调试便宜得多这张表就是整个调度体系的地基。
返回列表