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 生成步骤
- 在模型上右键 → Code Generation → Generate Code。或者按Ctrl+B。
- 等进度条跑完。第一次生成会慢一些,因为要检查模型。
- 打开生成的文件夹,你会看到一堆文件。
核心文件就这几个:
模型名.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工程中。步骤很简单:
- 把上面那些.c和.h文件复制到你的工程目录。
- 在main.c里包含头文件。
- 在初始化时调用
模型名_initialize()。 - 在PWM中断里调用
模型名_step()。
注意:生成的代码默认是单精度浮点(float)。如果你的MCU不支持硬件浮点,编译时会报错。解决办法是在模型设置里把数据类型改成单精度,或者用软件浮点库。
4.4 避坑指南——我踩过的雷
最后分享几个实战中容易翻车的地方:
- 输入输出类型不匹配:模型里的信号类型必须跟实际ADC、PWM的位宽一致。我曾经因为uint16和int16没对齐,调试了整整一天。
- 全局变量太多:生成的代码默认会把所有信号都变成全局变量。建议在模型里用Data Store Memory或者Bus对象来管理,不然代码可读性很差。
- 忘了加看门狗:生成的代码里没有看门狗刷新。如果你在中断里调用step函数,记得在主循环里喂狗。
好了,这一章的内容就到这。你按我说的步骤操作一遍,应该能顺利生成第一份嵌入式C代码。下一章咱们聊聊怎么把生成的代码跟实际硬件对接——那才是真正有意思的部分。