本节摘要:预置模块装不下的算法,用自定义模块接进框图。本节讲清三条路线的归属——MATLAB System 块(纯 MATLAB 逻辑快速接入)、Level-2 MATLAB S-Function(完整模块语义)、C MEX S-Function(复用既有 C 代码并支持代码生成),并用编码器解码 C 代码接入的全程演示选型与实施。自定义模块的可生成性约束与配套义务(诊断、测试、规范适配)一并交代。
自定义模块的三条路线,按"你要什么"分流。只是算法逻辑,MATLAB 表达顺手? MATLAB System 块:写一个对象类,声明输入输出与离散状态,框架替你处理调度——半小时接入,代价是不支持连续状态、代码生成有语言子集限制。需要完整模块语义(连续与离散状态并存、过零、多速率)而逻辑可以用 MATLAB 表达? Level-2 MATLAB S-Function:回调方法齐全,语义完整,仍是脚本开发速度。核心资产是既有 C 代码(厂家库、驱动、遗产算法)或对生成代码有硬需求? C MEX S-Function:直接包装 C 源码,配合目标语言编译器(TLC)可定制生成。
主线的解码 C 代码毫无悬念走第三条:资产在 C、需要进生成代码、接口清晰。若它是个纯 MATLAB 的滑动平均滤波,选第一条;若它需要与连续模型紧密耦合的状态插值,选第二条。路线跟着资产与需求走,不跟着熟练度走——这是选型的第一原则。

厂家的解码代码接口长这样:decoder_init(counts_per_rev) 初始化,decoder_step(int16 pulse_count) 每节拍调用,返回转速与故障码,内部维护若干静态状态。接入 C MEX S-Function 的五步:
/* 第 1 步:S 函数主入口——声明回调表 */ #define S_FUNCTION_NAME decoder_sfun #define S_FUNCTION_LEVEL 2 #include "simstruc.h" static void mdlInitializeSizes(SimStruct *S) { ssSetNumSFcnParams(S, 1); /* 参数:每转计数 */ ssSetNumInputPorts(S, 1); ssSetInputPortWidth(S, 0, 1); ssSetInputPortDataType(S, 0, SS_INT16); ssSetNumOutputPorts(S, 2); /* 转速 + 故障码 */ ssSetNumDiscStates(S, 4); /* 解码器内部状态 4 个 */ ssSetNumSampleTimes(S, 1); /* 单速率:离散节拍 */ } static void mdlInitializeSampleTimes(SimStruct *S) { ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME); /* 继承节拍 */ } static void mdlOutputs(SimStruct *S, int_T tid) { /* 第 2 步:调用厂家函数——静态状态经 ssGetDiscStates 映射 */ real_T speed; uint8_T fault; decoder_getstate(ssGetDiscStates(S)); decoder_step((int16_T) *ssGetInputPortRealSignal(S,0), &speed, &fault); ssGetOutputPortRealSignal(S,0)[0] = speed; ssGetOutputPortRealSignal(S,1)[0] = (real_T) fault; } /* 第 3 步:mdlUpdate 更新离散状态;第 4 步:TLC 定制生成; 第 5 步:编译后接进模型,用厂家测试向量做单元验证 */
五步里最容易被低估的是第 5 步:自定义模块的单元验证。厂家给了测试向量(脉冲序列对应的标准转速输出),把它们做成一个测试 harness——激励源进 S-Function 与"厂家参考实现"双路对比,断言一致。这段 harness 顺手成为第 7 章覆盖率与回归测试的一部分:自定义模块从此不是验证的盲区。
自定义模块混进模型后,四项配套义务要认领。诊断信息:mdlCheckParameters 里校验参数合法性,错误信息写人话——"每转计数必须大于 0"而不是运行时的段错误。规范检查适配:团队规范里"信号必须命名""接口必须有总线对象"这类条款,自定义模块一样要过;检查器对黑盒模块的检查能力有限,义务落在作者头上——写清接口文档,补齐数据接口定义。可生成性验证:8.1 节的"探路"流程对自定义模块同样适用,TLC 写不写取决于生成需求,但"能不能生成"必须验证而不是假设。版本溯源:厂家代码的版本号进模块注释与第 6 章的实验记录——升级厂家库时,所有引用它的模型能被一次性找出来。
MATLAB System 块走的是轻量路线,义务同款但工具不同:参数校验写在属性的 validateImpl 里、诊断写在 assertImpl 里、可生成性看语言子集文档。两者的分工记一句话:语义轻量选 System 块,语义完整或 C 资产选 S-Function,义务一条不少。
⚠️ 常见坑:在 S-Function 里偷偷调用了仿真专用的全局状态(直接读写工作区变量、printf 之外的文件操作),仿真时岁月静好,代码生成时全面崩坏。边界纪律:S-Function 的世界只有输入、输出、参数与状态,其他一切走接口。
代码接进来了,接着把成熟设计沉淀下去:下一站,自定义库与封装。