4. 模型配置与代码生成:求解器设置、代码生成参数配置、生成嵌入式C代码初体验

好,咱们终于走到这一步了。说实话,很多工程师做电机仿真时,模型搭得漂漂亮亮,波形也好看,但一到要生成代码就卡住了。我当年第一次搞代码生成时,也折腾了好几天,生成的代码编译都过不了。嗯,今天咱们就把这块硬骨头啃下来。

4.1 求解器设置——别让仿真骗了你

先说说求解器。你想想看,电机控制是个连续系统,但我们的控制器是离散的。仿真时如果求解器没选对,结果可能跟实际差十万八千里。

我个人习惯这样设置:

  • 类型:选「Fixed-step」(固定步长)。变步长仿真虽然快,但生成的代码没法用。
  • 求解器:选「discrete (no continuous states)」。电机模型里如果有连续状态,就选「ode3」或「ode4」。我一般用ode4,精度够用。
  • 步长:这个很关键。电机控制通常用10kHz到20kHz的PWM频率,所以步长设成1e-4(100微秒)或5e-5(50微秒)。

重要提醒:步长必须跟实际控制器的采样周期一致。我在项目里见过有人仿真步长设1e-6,结果代码生成后跑在10kHz的控制器上,模型跟实测完全对不上。

具体操作路径:

Simulink 菜单 → Modeling → Model Settings → Solver
→ Type: Fixed-step
→ Solver: discrete (no continuous states)
→ Fixed-step size: 1e-4

4.2 代码生成参数配置——这些坑我都踩过

求解器设好了,接下来是代码生成配置。这部分选项很多,但真正关键的没几个。我按经验给你捋一遍。

4.2.1 系统目标文件

这个决定了生成代码的框架。选「ert.tlc」——Embedded Real-Time。为什么?因为它生成的代码是纯C,没有操作系统依赖,适合嵌入式MCU。

Code Generation → System target file: ert.tlc

4.2.2 语言与接口

语言选C。接口方面,我个人建议:

  • Generate code only:勾上。我们只要源文件,不要makefile那些东西。
  • Support floating-point numbers:如果你的MCU有FPU,选上。没有的话,考虑定点数——不过电机控制一般都用浮点。
  • Generate an example main program:不勾。这个main函数太简陋,我从来不用。

4.2.3 数据类型与优化

这里有个容易忽略的地方:

参数 推荐值 说明
Default parameter behavior Tunable 运行时可以调参,调试方便
Application type (IDE) ARM Cortex-M / TI C2000 / 其他 根据你的芯片选
Replace with custom code 根据需要添加 比如初始化外设的代码

小技巧:我习惯把「Default parameter behavior」设成Tunable。这样在调试时可以直接通过CAN或串口改PID参数,不用重新编译。量产时再改成Inlined。

4.3 生成嵌入式C代码——初体验

好了,配置都搞定了。咱们来生成第一份代码。

4.3.1 生成步骤

  1. 在模型上右键 → Code Generation → Generate Code。或者按Ctrl+B。
  2. 等进度条跑完。第一次生成会慢一些,因为要检查模型。
  3. 打开生成的文件夹,你会看到一堆文件。

核心文件就这几个:

模型名.c      — 主算法代码
模型名.h      — 头文件
模型名_private.h — 内部变量声明
模型名_types.h   — 数据类型定义
rtwtypes.h      — 基础类型定义

4.3.2 代码长什么样?

打开那个.c文件,你会看到类似这样的结构:

/* 模型初始化函数 */
void 模型名_initialize(void)
{
    /* 初始化状态变量 */
}

/* 模型步进函数 —— 这就是你的控制算法 */
void 模型名_step(void)
{
    /* 读取输入 */
    /* 执行控制算法 */
    /* 更新输出 */
}

/* 模型终止函数 */
void 模型名_terminate(void)
{
    /* 清理工作 */
}

嗯,是不是很眼熟?这就是嵌入式开发的标准模式。你只需要在定时器中断里调用模型名_step()就行了。

4.3.3 集成到你的工程

我曾经在一个无刷直流电机项目里,把生成的代码直接集成到STM32工程中。步骤很简单:

  1. 把上面那些.c和.h文件复制到你的工程目录。
  2. 在main.c里包含头文件。
  3. 在初始化时调用模型名_initialize()
  4. 在PWM中断里调用模型名_step()

注意:生成的代码默认是单精度浮点(float)。如果你的MCU不支持硬件浮点,编译时会报错。解决办法是在模型设置里把数据类型改成单精度,或者用软件浮点库。

4.4 避坑指南——我踩过的雷

最后分享几个实战中容易翻车的地方:

  • 输入输出类型不匹配:模型里的信号类型必须跟实际ADC、PWM的位宽一致。我曾经因为uint16和int16没对齐,调试了整整一天。
  • 全局变量太多:生成的代码默认会把所有信号都变成全局变量。建议在模型里用Data Store Memory或者Bus对象来管理,不然代码可读性很差。
  • 忘了加看门狗:生成的代码里没有看门狗刷新。如果你在中断里调用step函数,记得在主循环里喂狗。

好了,这一章的内容就到这。你按我说的步骤操作一遍,应该能顺利生成第一份嵌入式C代码。下一章咱们聊聊怎么把生成的代码跟实际硬件对接——那才是真正有意思的部分。