
前段时间有读者朋友问代码生成的例子,说他正在做EPB的仿真模型,但总是和应用串不到一块去,我这里就从一个简单EPB控制模型来谈谈simulink代码生成,总结代码生成方法的同时也顺便回答他的问题。
1.EPB模型准备
用来举例说明的模型很简单,就是EPB(电子驻车,Electrical Parking Brake)中的手动夹紧功能,即驾驶员操作EPB按钮实现电子卡钳夹紧从而驻车的功能。
如果你开过车,应该会知道EPB就是下面这玩意。

EPB的功能很多,我们只拿其中最简单的EPB Switch Apply功能来说明,要使电子卡钳夹紧,同时满足下面四个条件即可:
(1)EPB未处于工厂转毂模式;
(2)EPB开关被拉起;
(3)EPB卡钳锁止功能正常;
(4)车辆静止;
分别用四个flag信号表示这四个状态量:

虽然只有简单四个信号,在实际开发中每个信号又要再根据很多条件做判断,比如工厂转毂模式是一种整车厂用来检测驻车制动力是否合格的测试模式,要根据车速轮速做综合判断;又例如车辆静止的判断条件,并不是简单的车速为0,而是要判断四个轮速传感器的脉冲信号;这里就不做详细展开。
根据以上的触发条件可以建立该功能的Simulink模型:

2.代码生成前的准备
打开Model Settings会话框,在Solver选项中,选择Fixed-Step(固定步长)和discrete(非连续解算器),这主要是因为我们面向的主要是嵌入式开发,而嵌入式开发的板子都是靠晶振或者外部时钟电路来计时的,采样时间一般是固定的。

在Code Generation选项中,System target file(生成目标文件)选ert.tlc,即embedded coder嵌入式代码生成。

在Report选项中,勾选Create code generation report和Open report automatically,生成代码后可以方便查看相关报告及详细代码。

3.代码生成及优化
上面准备工作完毕后,点击下图蓝色Generate Code按钮或者快捷键Ctrl+B,即可自动生成代码。

生成的代码如下,结构类似S-function:
初始化(EPB_model_20201123_initialize)→ 程序执行(EPB_model_20201123_step)→ 程序终止 (EPB_model_20201123_terminate)
备注:terminate在实际开发中用不到,因为程序在板子上都是循环跑的,不会终止,这里主要是仿真时间选择了有限值。

截取主要的代码看一下,是不是看起来很不舒服?这是因为设置没有进行优化,生成的代码都是按照simulink内部默认方式进行变量命名和算法转写,不是我们熟悉的C语言表示方法。
35 /* Model step function */
36 void EPB_model_20201123_step(void)
37 {
38 /* Outport: '<Root>/EPB_Caliper_apply_request_f' incorporates:
39 * Constant: '<S1>/Constant'
40 * Constant: '<S2>/Constant'
41 * Inport: '<Root>/EPB_RollerTest_Status_f'
42 * Inport: '<Root>/EPB_Switch_Status_f'
43 * Inport: '<Root>/EPB_lockfuntion_Status_f'
44 * Inport: '<Root>/Vehicle_Standstill_Status_f'
45 * Logic: '<Root>/Logical Operator'
46 * RelationalOperator: '<S1>/Compare'
47 * RelationalOperator: '<S2>/Compare'
48 */
49 EPB_model_20201123_Y.EPB_Caliper_apply_request_f =
50 ((EPB_model_20201123_U.EPB_RollerTest_Status_f == 0.0) &&
51 EPB_model_20201123_U.EPB_Switch_Status_f &&
52 (EPB_model_20201123_U.EPB_lockfuntion_Status_f == 0.0) &&
53 EPB_model_20201123_U.Vehicle_Standstill_Status_f);
54
55 /* Matfile logging */
56 rt_UpdateTXYLogVars(EPB_model_20201123_M->rtwLogInfo,
57 (&EPB_model_20201123_M->Timing.taskTime0));
58
59 /* signal main to stop simulation */
60 { /* Sample time: [0.2s, 0.0s] */
61 if ((rtmGetTFinal(EPB_model_20201123_M)!=-1) &&
62 !((rtmGetTFinal(EPB_model_20201123_M)-
63 EPB_model_20201123_M->Timing.taskTime0) >
64 EPB_model_20201123_M->Timing.taskTime0 * (DBL_EPSILON))) {
65 rtmSetErrorStatus(EPB_model_20201123_M, "Simulation finished");
66 }
67 }
那该怎么优化设置?
首先可以优化信号线,在信号线的properties菜单中,将要定义的变量填入Signal name,然后在Code Generation选项中选择ExportedGlobal(定义为全局变量)

修改完的模型就变成如下的样子:

生成的代码可以看到已经出现我们熟悉的变量定义,主要的代码也变得清爽易读

52 EPB_Caliper_apply_request_f = ((EPB_RollerTest_Status_f == 0.0) &&
53 EPB_Switch_Status_f && (EPB_lockfuntion_Status_f == 0.0) &&
54 Vehicle_Standstill_Status_f);
55
56 /* Outport: '<Root>/EPB_Caliper_apply_request_f' */
57 EPB_model_20201123_Y.EPB_Caliper_apply_request_f_j =
58 EPB_Caliper_apply_request_f;
其次可以单独管理变量,在M文件中批量定义程序用到的变量并初始化,这里变量不多就直接在命令窗口演示,Initial value设置为0,Storage class设置为ExportedGlobal:

然后在模型中绑定已经在workspace中定义的变量,信号线的properties中勾选Signal name must resolve to Simulink signal object:

修改后的信号线旁边会出现叉子标志,表示信号绑定成功:

4.代码生成后能干什么?
本来到上面本文就结束了,但之前和刚接触MBD开发流程的童鞋交流过程中,发现大家对整个开发过程不是很熟悉,或者说不知道生成代码后能做什么,这里简单介绍下MBD开发。
MBD是Model Based Design基于模型开发的缩写,本质上是将原本文字描述的技术需求文档以及测试说明文档转化成便于理解的可视化模型,将重复且易出错的代码编写过程交付给计算机软件完成,按照事先定义好的编程规则自动生成代码,目的是提高开发效率,减少错误。

自动生成的代码一般需要导入到芯片厂家开发的专用IDE(集成开发环境)中,进行检查编译后,下载到指定硬件中即可完成嵌入式开发。