MBD软件开发之调度方式

基于Simulink的模型化+自动代码生成的开发方式,在汽车电子行业正在逐渐演变成开发的标准配置。采用模型化的方式来描述控制算法,无论是可读性、可维护性、可移植性、测试验证的便利性等方面,相比于从前手工C代码都有长足的进步。说Simulink+Coder是推动人类进步的又一重要阶梯应不为过。

Simulink本质上也是门编程语言,只是用可视化的框图和连线方式替代了原来的程序语句,降低了编程门槛。即使没什么C代码经验的新手工程师,用不了几天也能上手。不过虽然是图形化建模,但最终还是要到生成代码的层级,手工代码时的架构和技巧在这里并未消失,只是换了个面貌存在,一般的编程概念和技巧在模型化开发中仍然非常有用。对于一些之前没写过代码,上手开发就是Simulink的工程师朋友来说,在这方面有时会有所欠缺。我自己在做基于模型的开发之前就没有积累足够的手工代码经验,好在Simulink+Coder工具足够鲁棒并且易用,基本上能解决大部分问题。不过要把模型建得更有效率,更优化,还需要一些更深入的考量。这里想就一个大的控制算法模型中,各种不同的任务调度方法这个话题量个人能力讨论一二,抛砖引玉,有不足的还请大牛指正。

嵌入式系统中,从应用层的视角总体来看,任务(Task)分很多类,比如说:

  • 周期性任务:这是最常见的,按固定频率周期性执行的任务。根据需要可能有多个不同频率的,比如一个系统中分别有 1ms, 5ms, 10ms, 20ms, 50ms, 100ms, 1000ms任务
  • 一次性任务:比如控制器的上电初始化任务,下电After Run任务
  • 中断任务:实时性要求特别高的,受中断调度的任务,比如电机矢量控制
  • 事件型任务:比如收到某帧CAN消息,再比如发动机控制中跟随曲轴转角位置触发的任务(虽然可能也是中断机制实现,但我觉得算是事件型的)

然后从系统层级的角度来看,任务可以按触发源头分级:

  • 硬件触发,例如硬件中断
  • 底层嵌入式操作系统(OS)触发
  • 应用层自身的任务调用,这个后面细聊

回到Simulink本身,如果不加任何调度,生成的嵌入式代码后,实质上就是个单/多周期的周期性任务(代码中会生成一个每个步长调用的函数 rtOneStep,其中执行整个模型代码)。

以上扯了这么多,谈到的任务的其实都是比较“根源”的任务。无论是采用<A:应用层模型+底层手工代码集成>的方式,还是<B:底层Simulink化封装的完全模型化开发>的方式,应用层的算法反正都要以某种形式的接口,塞到上述某个任务里。A方式一般是底层软件包给定一个函数接口,比如 void function_10ms(),然后把应用层生成的代码填在里面。B方式对A做了个封装,利用S-Function+tlc在模型层面自动填进去,如下图所示,这里借用一张自家公司(澳特卡 eCartronic)的图片,其它类似封装工具包有国外的MotoHawk, OpenECU等。

然后,在这一个根任务之下,还会有细分的功能模块,这不同的功能模块也在时间上、顺序上变化出各种各样复杂的调度需求来。不加调度情况下,就是靠信号流驱动,Simulink根据信号先后依赖关系自行判断执行顺序。但是,不加调度有可能会导致低效的模型,比如说,

  • 很多更新频率要求不高的信号,比如100ms甚至1s计算更新一次就足够的功能模块,不加区分放在一个比较快的5ms 10ms任务里去跑
  • 一些只在特定工况或者模式下需要计算的功能模块,不加调度变成每个步长都在算,偏偏这个算法里又是查表、又是各种数组计算的,白白浪费很多CPU资源

最后,都会导致整个控制器CPU负载率过高。各大主机厂对于各种ECU规范里,都会规定一个CPU最高负载率的限制。那么CPU负载率不达标怎么办,换更高计算能力的芯片,这可都是血淋淋的成本啊。如果在各个功能单元的调度都能仔细考虑一下,软件优化妥妥的。

下面列列我认为常见的几种基本调度方法,主要针对应用层,根据实际需要几种方法组合和嵌套,可以实现比较复杂的调度要求。

时间周期分频:

周期性任务是最常见的,基本上,按照触发源层级分,硬件中断触发的周期最精确,说10ms就是准的10ms;底层操作系统触发的,可能准也可能不准,取决于操作系统的调度机制、任务本身的优先级,以及CPU的负载率;而到了应用层再分频的,本质上就是一个任务里的条件语句分支而已,即所谓的“软分频”,一旦源任务没了所有分频任务就都没了。

总而言之,在执行周期的精确性上,依次递减;在建模实现的方便性上来说,逐步递增。

应用层的周期软分频方法,例如可以利用Stateflow把一个基频分成其它不同的周期:

执行顺序调度:

简单的一个执行顺序调度,用Simulink和Stateflow分别实现:

1分N调度:

把执行顺序的调度稍加变化,就是1分N的调度,用Simulink和Stateflow分别实现(Stateflow还顺便加了一个小技巧,可以控制每个子功能调度的开关):

N合1调度:

N合1的调度比较简单,就是把几个Function Call用Mux合起来。

交互式/多次迭代调度:

这种调度形式实际上我不太能给出一个合适的命名,在MATLAB Help中经常出现,同时在建模规范中进行调度和计算分离的推荐用法。这种形式算是终级大杀器了,因为可以在Stateflow里可以自由地搭各种复杂的逻辑,包括在一个步长多次迭代同一计算过程的算法等。只要Stateflow玩得转,基本想怎么调度都可以。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

电力电子空间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值