Simulink如何集成外部C/C++代码
Simulink集成外部C/C++代码方法综述 Simulink提供了多种集成外部C/C++代码的方法,以满足不同应用场景的需求。主要集成方式包括: C Caller模块:R2018b引入,适合简单函数调用,图形化配置自动解析头文件,无缝集成仿真和代码生成,适用于嵌入式系统开发。 C Function模块:R2020a引入,提供更高灵活性,支持数据预处理和多函数调用,适合复杂算法集成,基于S-Fu
在实际的工程应用和学术研究中,仅仅依靠Simulink自身的功能和模块往往难以满足需求。在这种情况下集成外部的功能、算法、模型等资源是最优的选择。
其中,基于C/C++语言的各类资源最为丰富,同时也拥有最优越的性能,还与底层同样基于C/C++的Simulink有着良好的兼容性,因此广泛应用在以下场景中:
- 重用既有算法/库
许多成熟项目中,已有用 C/C++ 写好的算法库、设备驱动或数学工具包,直接在 Simulink 中调用,可避免重复造轮子。 - 性能优化
关键计算模块(如大规模矩阵运算、信号滤波、FFT 等)用手写 C 实现通常比纯 Simulink block 或 MATLAB Function 更高效,尤其在实时仿真或部署到硬件时。 - 嵌入式目标代码生成
Simulink Coder 生成的代码如果要在目标微控制器/DSP 上运行,经常需要与厂商提供的外部库(HAL、RTOS 接口、外设驱动)链接。 - 接口真实硬件/仿真器
在硬件在环(HIL)测试中,经常要调用专有的实时驱动或仿真API,这些 API 多以 C 语言形式提供。 - 混合开发流程
工程团队中,算法工程师用 Simulink 快速建模、验证思路;软件工程师用 C 代码实现、调优。二者结合,可以大幅缩短开发周期、降低验证成本。
因此,Simulink 提供了一系列工具,可以将来自不同来源的现有组件和代码集成到 Simulink 中,以创建大规模模型。
Simulink 集成外部C/C++代码的方法主要有以下几种:
- C Caller
- C Function
- S-Function
- MATLAB Function + coder.ceval
- Stateflow + coder.ceval
1. C Caller
Simulink从R2018b版本开始, 增加 “C Caller” 模块,用于在 Simulink 模型中直接调用 C/C++ 函数,旨在简化与 Simulink 模型集成的过程,特别是对于不需要 S-function 复杂性的场景。
优点:
- 易用性:图形化配置,自动解析头文件,无需手动编写 S-Function。
- 集成性:与 Simulink 的仿真和代码生成工具无缝集成。
- 灵活性:支持标量、向量、矩阵和结构体等复杂数据类型。
- 代码生成支持:通过 Simulink Coder 自动将 C 代码嵌入目标系统,适合嵌入式应用。
- 验证支持:与 Simulink Coverage、Test 和 Design Verifier 兼容,便于测试和验证。
缺点:
- 功能有限:仅适合调用单一或少量 C 函数,无法实现复杂的动态系统逻辑(如需要状态更新的算法)。
- 数据类型限制:需要确保 C 函数的输入输出与 Simulink 的数据类型兼容,可能需要额外的数据转换。
- 调试复杂性:如果 C 代码出现错误,调试可能需要检查生成的 C 代码或使用外部调试工具。
适用场景:
- 简单函数调用:调用单一的 C 函数,如数学运算、滤波器或控制算法。
- 代码重用:集成现有的 C 代码库,避免重新开发。
- 嵌入式系统开发:在生成嵌入式代码时,确保特定 C 实现被包含在目标系统中。
- 快速原型设计:快速验证 C 代码在 Simulink 环境中的行为。
- 测试与验证:与 Simulink Test 或 Simulink Design Verifier 配合,验证外部 C 代码的功能。
配置流程:
- 准备 C 代码
编写或准备 C 源文件(.c)和头文件(.h),其中定义了需要调用的函数,这里以一个简单的求和函数myFunction为例:
// myFunction.h
#ifndef MYFUNCTION_H
#define MYFUNCTION_H
double myFunction(double input1, double input2);
#endif
// myFunction.c
#include "myFunction.h"
double myFunction(double input1, double input2) {
return input1 + input2; // 示例:返回两个输入之和
}
- 配置 Simulink 模型
打开Configuration Parameters (配置参数)> Simulation Target(仿真目标)
在 Simulation Target 面板中:
在 Source files (源文件)中添加 C 源文件(如 myFunction.c)。

在 Header files (头文件)字段中添加头文件(如 myFunction.h),也可以使用“基于源文件自动填充”功能。

指定包含路径(Include directories)和其他编译选项(如有需要)。
- 添加并配置C caller
打开 Simulink 库浏览器,导航到 Simulink > User-Defined Functions,拖放 C Caller 块到模型中。
点击标记1位置的刷新,Simulink 会自动解析头文件中定义的函数。接着从下拉菜单中选择要调用的函数(如 myFunction)

端口设定:
-
确认输入和输出端口与 C 函数的签名匹配。例如,myFunction 有两个 double 输入和一个 double 输出,Simulink 会自动生成两个输入端口和一个输出端口。
-
如果函数涉及复杂数据类型(如结构体),需要在头文件中定义,并在 Simulation Target 中正确映射。
- 仿真和验证
运行 Simulink 模型,验证 C Caller 块是否正确调用 C 函数并返回预期结果。

如果需要生成嵌入式代码,使用 Simulink Coder 配置目标硬件并生成代码,C Caller 块会自动将 C 代码嵌入生成的目标代码中。
2. C Function
C Function 是 Simulink 提供的一个 可视化编程与 C 语言结合的接口模块,首次引入于 R2020a版本,允许你直接在 Simulink 模型中编写、编译并运行 自定义 C 代码,而不需要额外生成 MEX 文件或编写 S-Function 模板。
C Function 模块的底层是基于 Simulink S-Function API 的封装,它帮你自动生成大部分 S-Function 框架,只保留给你编写的核心算法部分。
优点:
- 灵活性高:支持数据预后处理、多函数调用和持久数据管理,适合复杂集成。
- 自定义强:允许为仿真和代码生成指定不同代码,便于优化。
- 内存管理:支持分配/释放内存和初始化/终止代码。
- 集成性好:与 Simulink Coder 无缝配合,支持嵌入式部署。
- 代码重用:轻松集成现有 C 库,减少重复开发。
缺点:
- 配置复杂:相比 C Caller 块,需要更多手动配置和代码编写,学习曲线较陡。
- 功能限制:不适合动态系统(如连续状态),需转向 S-Function。
- 调试难度:自定义代码错误可能导致编译问题,需要检查 Diagnostic Viewer。
- 版本依赖:R2024a 前后配置略有差异,可能影响兼容性。
配置流程:
- 准备 C 代码
第一步同样是准备要集成的代码,这里使用与上文C caller案例中相同的代码。
- 配置 Simulink 模型
这里与上文一致,即在Configuration Parameters (配置参数)中配置代码文件。
- 添加并配置C Function
打开 Simulink 库浏览器,导航到 Simulink > User-Defined Functions,拖放 C Function 块到模型中。

如图所示:
-
在 C Function 模块参数对话框的 Simulation 选项卡下的输出一栏,编写模块在仿真期间执行的代码。
-
使用“端口和参数”表来定义块中代码中使用的符号。
-
仿真和验证
运行 Simulink 模型,验证 C Function块是否正确调用 C 函数并返回预期结果。

C Function块 与 C Caller 块的主要区别:
- C Caller 块:专用于调用单个 C 函数,配置简单,适合简单函数调用。自动解析头文件,无需手动编写预处理代码。
- C Function 块:用于调用可能需要修改的外部 C 算法,提供更多自定义选项,如数据处理、初始化和多函数调用。适用于复杂场景,但配置更复杂。
- 其他区别:对于动态系统(如连续状态或状态变化),推荐使用 S-Function 块而非 C Function 块。C Function 块更注重数据管理和自定义代码,而 C Caller 块强调快速集成。
3. S-Function
S-Function(System Function) 是 Simulink 的可编程模块接口,它允许用户用 C、C++、MATLAB、Fortran 等语言,扩展 Simulink 的功能,实现自定义模块。
S-Function 的核心是通过回调函数(callback methods)与 Simulink 交互,这些函数在仿真过程中被调用,以更新状态、计算输出等。
优点:
- 功能最强:支持动态端口、事件触发、复杂状态管理。
- 高性能:C MEX S-Function 的执行速度接近纯 C 代码。
- 硬件集成:方便调用底层驱动(SPI、CAN、传感器等)。
- 跨平台:一次实现,可在不同平台(Simulink 仿真 / Embedded Coder 生成代码)运行。
- 灵活数据接口:支持任意维度、数据类型、采样时间。
缺点:
- 开发成本高:必须熟悉 Simulink S-Function API。
- 调试难度大:需要用 mex 编译,调试要依赖外部调试器(如 gdb、Visual Studio)。
- 维护成本高:比 MATLAB Function/C Function 代码更难维护。
- 新手学习曲线陡:API 较多,生命周期管理复杂。
适用场景:
- 大型已有 C/C++ 库的接入(如信号处理库、控制算法库)
- 硬件驱动(传感器读写、总线通信)
- 特殊仿真逻辑(自定义事件调度、非标信号类型)
- 高性能计算(替换 MATLAB Function 模块,减少解释器开销)
配置流程:
- 编写 S-Function 文件
这里依旧以上文适用的代码为例,这里需要新建一个 mySfunc.c 文件,用来封装 myFunction:
#define S_FUNCTION_NAME mySfunc
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
#include "myFunction.h" // 引入你的函数声明
/* 初始化端口数量和属性 */
static void mdlInitializeSizes(SimStruct *S)
{
ssSetNumSFcnParams(S, 0); // 无参数
/* 输入端口 1 */
if (!ssSetNumInputPorts(S, 2)) return;
ssSetInputPortWidth(S, 0, 1); // 第1个输入端口:标量
ssSetInputPortWidth(S, 1, 1); // 第2个输入端口:标量
ssSetInputPortDirectFeedThrough(S, 0, 1);
ssSetInputPortDirectFeedThrough(S, 1, 1);
/* 输出端口 1 */
if (!ssSetNumOutputPorts(S, 1)) return;
ssSetOutputPortWidth(S, 0, 1); // 输出是标量
ssSetNumSampleTimes(S, 1);
}
/* 设置采样时间 */
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME); // 继承采样时间
ssSetOffsetTime(S, 0, 0.0);
}
/* 核心计算 */
static void mdlOutputs(SimStruct *S, int_T tid)
{
InputRealPtrsType u0 = ssGetInputPortRealSignalPtrs(S, 0);
InputRealPtrsType u1 = ssGetInputPortRealSignalPtrs(S, 1);
real_T *y = ssGetOutputPortRealSignal(S, 0);
/* 调用外部函数 */
*y = myFunction(*u0[0], *u1[0]);
}
/* 终止处理(此例无资源释放) */
static void mdlTerminate(SimStruct *S) {}
/* 选择正确的编译接口 */
#ifdef MATLAB_MEX_FILE
#include "simulink.c"
#else
#include "cg_sfun.h"
#endif
- 编译MEX文件
在 MATLAB 命令行执行(确保 .c 和 .h 文件在当前工作目录或路径中):
mex mySfunc.c myFunction.c

这样会生成:
mySfunc.mexw64
此文件就是Simulink可直接调用的模块实现。
- 在 Simulink 中调用
打开 Simulink Library Browser,添加 S-Function 模块(位于 User-Defined Functions → S-Function),双击打开模块,在 S-function name 中填 mySfunc,此时S function模块会根据代码创建相应输出输出端口。

运行 Simulink 模型,验证 S function 块是否正确实现 C 函数的功能并返回预期结果。
S-Function是一种非常强大的方法,其拥有多种创建方式与接口等级,例如:Level-1 S-Function、Level-2 S-Function、C MEX S-Function、MATLAB S-Function等,本文仅做简单介绍,后续会推出专题进一步介绍。
4. MATLAB Function + coder.ceval
MATLAB Function 模块是 Simulink 提供的一个可以直接在模型中用 MATLAB 语言编写算法的模块。
它的作用类似于 MATLAB 脚本,但有以下特性:
- 可以生成高性能的 C 代码(需要 Simulink Coder)。
- 可以在 MATLAB 代码里调用外部 C 函数(通过
coder.ceval)。 - 支持输入输出信号映射到端口,直接参与仿真。
coder.ceval 是 MATLAB Coder 提供的一个接口,用于在 MATLAB 代码生成过程中调用外部 C/C++ 函数。
coder.ceval('函数名', 参数1, 参数2, ...)
'函数名'必须是字符串,与外部 C 文件中的函数名一致。- 参数必须是 C 类型可接受的变量(标量、指针、数组)。
- 如果 C 函数有返回值,需要用
coder.ceval的返回值接收。 - 只能在 代码生成 过程中使用(普通 MATLAB 运行时需要提供 MEX 或者模拟实现)。
优点:
- 比 S-Function 开发简单,代码嵌入在 Simulink 模块中。
- 调用外部 C 代码无需自己写 S-Function 框架。
- 可直接生成嵌入式 C 代码。
- 对小型外部 C 库的集成非常方便。
缺点:
- 对动态端口、复杂状态管理支持不如 S-Function。
- 必须有 Simulink Coder(或 MATLAB Coder)才能调用外部 C 代码。
- 外部 C 文件路径和头文件需要额外配置,否则生成代码会失败。
适用场景:
需要使用到matlab function模块的场景。
配置方法:
- 准备C代码
这里依旧使用上文的myFunction函数代码作为示例,确保代码文件位于工作路径下。
与C caller和C Function一致,在Configuration Parameters (配置参数)中添加代码文件。
- 配置MATLAB Function
添加MATLAB Function模块,使用coder.ceval调用myFunction:
function y = callMyFunction(u1, u2)
%#codegen % 启用代码生成
% 声明外部函数
coder.cinclude('myFunction.h');
% 初始化输出变量 (假设返回 double 标量)
y = 0; % double 标量
% 调用外部函数
y = coder.ceval('myFunction', u1, u2);
end
- 仿真和验证
运行仿真,验证MATLAB Function + coder.ceval能否正确实现 C 函数的功能并返回预期结果。


5.Stateflow + coder.ceval
Stateflow 是 MATLAB/Simulink 的一个状态机与流程图建模工具,用于描述离散事件驱动逻辑、模式切换、控制流程等。
在 Stateflow 中使用 coder.ceval,可以直接调用外部 C/C++ 函数,让状态机逻辑与已有的底层算法库无缝集成。
优势:
- 结构清晰:状态机逻辑 + 底层算法分离。
- 性能高效:C 代码直接生成嵌入式目标代码,无额外解释开销。
- 可维护性好:C 代码可单独调试、单元测试,Stateflow 只管理状态转移。
- 硬件可移植:同一状态机可切换不同平台的底层驱动实现。
缺点:
- 类型与大小必须已知:
coder.ceval调用时,传入和返回的变量必须在 Stateflow 中预定义类型(可用 Data 定义)。 - 只能在代码生成模式下使用:必须启用 Embedded Coder / Simulink Coder。
- C 函数声明必须可见:需要在
#include语句中声明函数原型,或者通过 Custom Code 设置包含头文件。 - 内存管理:不能直接在 C 函数里用
malloc返回指针给 Stateflow,需在 Stateflow 侧分配内存并传指针。
配置方法:
- 准备 C 代码
与上文介绍的C caller和C Function一致,在Configuration Parameters (配置参数)中添加代码文件。
- 配置 Stateflow
在simulink中添加chart,创建状态。
通过在状态的entry 动作或transition 动作中使用coder.ceval调用C函数:
y = 0;
y = coder.ceval('myFunction', a, b);
- 仿真验证
运行仿真,验证Stateflow + coder.ceval能否正确实现 C 函数的功能并返回预期结果。

总结
本文介绍了Simulink 集成外部C/C++代码最常见的5种方法,这里将各类方法的特点总结如下:
| 方法 | 典型场景 | 优点 | 缺点 | 开发难度 |
|---|---|---|---|---|
| C Caller | 已有外部 C 函数(.c/.h),直接调用并在模型中使用 | 图形化直接映射 C 函数输入/输出;无需编写接口代码 | 只能调用已存在的函数;逻辑必须在外部实现,无法在模块内写逻辑 | 低 |
| C Function | 在 Simulink 模块内直接写 C 代码(算法、控制逻辑等) | 代码和模型绑定;可读性高;无需额外文件 | 不适合大型代码;可移植性较差 | 中 |
| S-Function | 高度自定义接口、非标数据类型、复杂状态机或自定义求解器 | 最灵活;可访问底层 API;支持多语言 | 编写复杂,需实现多回调函数 | 高 |
| MATLAB Function + coder.ceval | 已有 MATLAB 算法,需要在其中调用 C 函数 | 结合 MATLAB 语法和 C 代码;易调试 | C 调用嵌在 MATLAB 中,生成代码依赖 coder 规则 | 中 |
| Stateflow + coder.ceval | 状态机逻辑中需要调用外部 C 函数 | 将外部 C 调用直接嵌入状态转移逻辑;易维护状态逻辑 | 对非状态机逻辑冗余;调试稍复杂 | 中 |
更多推荐



所有评论(0)