2. 模型准备与代码生成配置

Embedded Coder 的代码生成并不是“点一下 Build”这么简单。它要求模型在进入生成流程之前,已经具备一套工程化的约束:求解器与步长决定了代码的时间语义,数据类型与采样时间决定了代码的数值语义,模型引用与配置集决定了代码的工程组织方式。本节围绕这四类准备展开,目标是让模型在生成前就处于“可编译、可追溯、可复用”的状态。

求解器与步长:先确定代码的时间语义

Embedded Coder 只支持定步长(Fixed-step)求解器,因为生成代码需要一个确定性的执行节拍。在 Configuration Parameters → Solver 中,需要把 Type 设为 Fixed-step,Solver 通常选择 discrete (no continuous states),前提是模型中没有连续状态。若模型包含连续环节,则必须选择定步长积分方法(如 ode1、ode4),并明确其精度代价。

步长 Fixed-step size 是代码时间语义的核心参数。它必须与模型中最快的采样周期匹配:若模型存在 1 ms 的采样任务,则基础步长不能大于 1 ms,否则该任务会被“跳过”或被迫降频。工程上常见的做法是令基础步长等于最快任务的周期,例如 0.001,再通过多速率机制让慢任务按整数倍分频执行。步长一旦确定,就应写入配置集并纳入版本管理,因为它会直接影响生成代码中定时器中断的配置。

判断依据:若模型在仿真中使用了变步长求解器,生成代码前必须切换为定步长,并重新验证仿真结果与定步长结果的一致性,避免因步长变化导致控制效果偏移。

数据类型:从 double 默认到嵌入式可落地的类型体系

Simulink 默认大量使用 double,而嵌入式目标往往只有 32 位甚至 16 位算力,且可能没有硬件浮点单元。因此,代码生成前必须完成数据类型收敛。核心操作包括:

  • 在 Configuration Parameters → Hardware Implementation 中设置目标处理器的字长、字节序、char 是否有符号等,这些参数会决定 int、long 等类型的实际位宽。
  • 为信号、参数和状态显式指定数据类型,优先使用 single、int16、uint8、boolean 等固定宽度类型,避免依赖平台相关的 int。
  • 使用 Data Type Assistant 或 Simulink.NumericType 定义定点类型,明确 Signedness、Word length、Fraction length,并检查溢出与精度。

一个常见的教学示例是:将原本为 double 的电机电流采样信号改为 int16,量程 ±20 A,对应比例因子 0.001 A/LSB,则 20 A 对应 20000,落在 int16 的 ±32767 范围内,既保留 1 mA 分辨率,又避免浮点运算。这类换算应在模型准备阶段完成,而不是等到生成代码后再手工修改。

采样时间:多速率模型的节拍一致性

采样时间决定了模块何时执行。代码生成要求所有采样时间都能用基础步长的整数倍表达,否则会触发“采样时间不是基础步长整数倍”的报错。准备阶段应做三件事:

  1. 用 Sample Time Legend 或 Sample Time Colors 检查模型中是否存在未指定采样时间的模块,尤其是 Constant、From、Goto 等容易被忽略的模块。
  2. 将多速率模型中的慢任务周期设为快任务周期的整数倍,例如快任务 1 ms、慢任务 10 ms,避免出现 1.5 ms 这类非整数倍关系。
  3. 对需要异步执行的子系统,考虑使用 Function-Call Subsystem 或导出函数,而不是依赖隐式的采样时间继承。

采样时间配置完成后,生成代码中会出现对应的 step 函数或周期任务入口,其调用周期与模型中的采样时间一一对应。若采样时间混乱,生成代码的任务调度也会随之混乱,这是工程中最常见的返工原因之一。

模型引用与配置集:工程组织的两条主线

当模型规模增大时,单模型难以维护。模型引用(Model Reference)允许把子系统独立为被引用模型,各自拥有独立的配置集,并支持增量编译与独立测试。准备阶段需要明确:哪些部分作为引用模型、哪些作为库、哪些保留在主模型中。引用模型的接口应通过 Inport/Outport 或 Model 模块的端口显式定义,避免使用 Data Store Memory 跨模型共享数据,因为后者会破坏代码的模块边界。

配置集(Configuration Set)则负责把求解器、硬件、代码生成、优化等设置集中管理。工程上建议:

  • 为“仿真验证”和“代码生成”分别建立配置集,前者可用变步长、double,后者必须定步长、固定宽度类型。
  • 将配置集保存为 .m 脚本或 Simulink.ConfigSet 对象,纳入版本控制,避免手工点击造成的配置漂移。
  • 使用 Configuration Parameters → Code Generation → System target file 选择 ert.tlc,并检查 Language 为 C。

下面这张图把本节的关键准备项组织成一条从模型到可生成代码的配置链,便于对照检查。

模型准备与代码生成配置链 模型准备 求解器 Fixed-step / discrete 步长 0.001 s(示例) 数据类型 int16 / single / boolean 采样时间 1 ms / 10 ms 整数倍 配置集管理 仿真配置集 变步长 / double 代码生成配置集 ert.tlc / C / 定步长 硬件实现 字长 / 字节序 / char 版本控制 .m 脚本 / ConfigSet 对象 模型引用组织 主模型 调度 / 顶层接口 引用模型 A 独立配置集 / 增量编译 引用模型 B 独立测试 / 复用 接口边界 Inport / Outport 显式定义 可生成代码的工程化模型 → 进入 Build 流程

准备完成后的检查清单

在点击 Build 之前,建议按以下顺序做一次快速核查,这些检查项直接对应本节四类准备:

检查项 期望状态 常见问题
求解器类型 Fixed-step 仍为 Variable-step,Build 报错
基础步长 等于最快任务周期 步长大于最快任务,任务被降频
数据类型 无隐式 double 扩散 增益、查表等模块仍输出 double
采样时间 均为基础步长整数倍 存在 1.5 ms 等非整数倍周期
配置集 代码生成配置集已激活 误用仿真配置集,生成结果不符合预期
模型引用接口 端口显式、无跨模型 Data Store 接口隐式,生成代码耦合严重

完成上述准备后,模型才真正具备“可生成代码”的工程条件。求解器与步长给出时间基准,数据类型与采样时间给出数值与节拍约束,模型引用与配置集给出工程组织方式。这四者共同构成 Embedded Coder 代码生成的输入前提,任何一项缺失或配置不当,都会在后续生成阶段以报错、警告或难以定位的运行时问题暴露出来。