开箱报告,Simulink Toolbox库模块使用指南(五)——S-Fuction模块(C MEX S-Function)
文章目录
前言
C MEX S-Function
算法原理
原始信号创建
编写S函数
仿真验证
Tips
分析和应用
总结
前言
见《开箱报告,Simulink Toolbox库模块使用指南(一)——powergui模块》
见《开箱报告,Simulink Toolbox库模块使用指南(二)——MATLAB Fuction模块》
见《开箱报告,Simulink Toolbox库模块使用指南(三)——Simscape 电路仿真模块》
见《开箱报告,Simulink Toolbox库模块使用指南(四)——S-Fuction模块》
C MEX S-Function
C MEX S-Function是使用C语言开发的一种S-Fuction,具备前一篇文章中讲解的S-Fuction的全部基本特性。它对应S-Fuction中的Level2类型,支持访问更广泛的 S-Function API 集,并支持代码生成。由于汽车电子工程与C语言密不可分,所以我们必须对C MEX S-Function重点关注。
Mathworks官方Help对该模块的说明如下所示。

本文以DFT算法为例,介绍如何利用C MEX S-Function搭建项目需求中高度自定义的信号解耦模块。
算法原理
傅里叶变换告诉我们,任何周期信号都可以分解为正弦波的叠加。具体的做法是:将被求解的原始信号,与目标频率的信号相乘,然后再积分,就得到了原始信号在该频率上的分量,公式如下:
原始信号:S;
目标频率信号:D_cos = cos(2pi*w*t);
目标频率信号:D_sin = sin(2pi*w*t);
目标信号实部:D_real = dot(S,D_cos)/N*2;
目标信号虚部:D_imag = dot(S,D_sin)/N*2;
目标信号模数:D_modl = sqrt(D_real^2 + D_imag^2);
原始信号创建
这里沿用前一篇文章中的电路方程模型,见《开箱报告,Simulink Toolbox库模块使用指南(四)——S-Fuction模块》
创建电压和电流信号如下:
t(S) = (0 : 4999)*0.0001;
I(A) = 4.235 + 0.035*sin(2pi * 50t + pi);
U(A) = 3.529 + 0.071*sin(2pi * 50t);
在Matlab的命令窗口中运行该动态方程,得到的电流和电压曲线,与前一篇文章一致,如下所示:


编写S函数
根据官方的Basic C MEX S-Function模板,写出的S函数完整代码如下:
#define S_FUNCTION_NAME DFT_CMexSfunc //函数名字,与C文件名一致
#define S_FUNCTION_LEVEL 2
#include "simstruc.h" //Matlab宏函数库static real_T Fs = 10e3; //信号采样频率,与信号源一致
static real_T L = 5e3; //信号采样点个数,两者根据Nyquist定理计算/* Function: mdlInitializeSizes ===============================================* Abstract:* The sizes information is used by Simulink to determine the S-function* block's characteristics (number of inputs, outputs, states, etc.).*/
static void mdlInitializeSizes(SimStruct *S)
{//一个算法参数Freq,目标解算频率ssSetNumSFcnParams(S, 1); /* Number of expected parameters */if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {/* Return if number of expected != number of actual parameters */return;}ssSetNumContStates(S, 0);ssSetNumDiscStates(S, 4); //离散状态的个数if (!ssSetNumInputPorts(S, 1)) return;ssSetInputPortWidth(S, 0, 1); //一个信号输入端口,信号维度1ssSetInputPortRequiredContiguous(S, 0, true); /*direct input signal access*//** Set direct feedthrough flag (1=yes, 0=no).* A port has direct feedthrough if the input is used in either* the mdlOutputs or mdlGetTimeOfNextVarHit functions.*/ssSetInputPortDirectFeedThrough(S, 0, 1);if (!ssSetNumOutputPorts(S, 1)) return;ssSetOutputPortWidth(S, 0, 1); //一个信号输出端口,信号维度1ssSetNumSampleTimes(S, 1);ssSetNumRWork(S, 0);ssSetNumIWork(S, 0);ssSetNumPWork(S, 0);ssSetNumModes(S, 0);ssSetNumNonsampledZCs(S, 0);/* Specify the operating point save/restore compliance to be same as a * built-in block */ssSetOperatingPointCompliance(S, USE_DEFAULT_OPERATING_POINT);ssSetOptions(S, 0);
}/* Function: mdlInitializeSampleTimes =========================================* Abstract:* This function is used to specify the sample time(s) for your* S-function. You must register the same number of sample times as* specified in ssSetNumSampleTimes.*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
// ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);ssSetSampleTime(S, 0, 0.001); //算法运行周期0.001s,不同于信号采样频率ssSetOffsetTime(S, 0, 0.0);}#define MDL_INITIALIZE_CONDITIONS /* Change to #undef to remove function */
#if defined(MDL_INITIALIZE_CONDITIONS)/* Function: mdlInitializeConditions ========================================* Abstract:* In this function, you should initialize the continuous and discrete* states for your S-function block. The initial states are placed* in the state vector, ssGetContStates(S) or ssGetRealDiscStates(S).* You can also perform any other initialization activities that your* S-function may require. Note, this routine will be called at the* start of simulation and if it is present in an enabled subsystem* configured to reset states, it will be call when the enabled subsystem* restarts execution to reset the states.*/static void mdlInitializeConditions(SimStruct *S){//离散状态赋初值real_T Count = 1;real_T t = 0;real_T cos_integ = 0;real_T sin_integ = 0;real_T *x0 = ssGetRealDiscStates(S);*x0++ = Count;*x0++ = t;*x0++ = cos_integ;*x0++ = sin_integ;}
#endif /* MDL_INITIALIZE_CONDITIONS */#define MDL_START /* Change to #undef to remove function */
#if defined(MDL_START) /* Function: mdlStart =======================================================* Abstract:* This function is called once at start of model execution. If you* have states that should be initialized once, this is the place* to do it.*/static void mdlStart(SimStruct *S){}
#endif /* MDL_START *//* Function: mdlOutputs =======================================================* Abstract:* In this function, you compute the outputs of your S-function* block.*/
static void mdlOutputs(SimStruct *S, int_T tid)
{real_T real = 0;real_T imag = 0;real_T modl = 0;real_T *y = ssGetOutputPortSignal(S,0);real_T *x = ssGetRealDiscStates(S);if(x[0]==L+1){real = x[2]/L*2;imag = x[3]/L*2;modl = sqrt(real*real + imag*imag);y[0] = modl; //解算结果输出}
}#define MDL_UPDATE /* Change to #undef to remove function */
#if defined(MDL_UPDATE)/* Function: mdlUpdate ======================================================* Abstract:* This function is called once for every major integration time step.* Discrete states are typically updated here, but this function is useful* for performing any tasks that should only take place once per* integration step.*/static void mdlUpdate(SimStruct *S, int_T tid){real_T Sr_cos;real_T Sr_sin;real_T T;real_T Freq = (real_T) *mxGetPr(ssGetSFcnParam(S,0));real_T *x = ssGetRealDiscStates(S);const real_T *u = (const real_T*) ssGetInputPortSignal(S,0);if(x[0]<=L){ Sr_cos = cos(2*3.14 * Freq*x[1]);Sr_sin = sin(2*3.14 * Freq*x[1]);x[2] = x[2] + u[0]*Sr_cos;x[3] = x[3] + u[0]*Sr_sin;x[0] = x[0] + 1;T = 1/Fs;x[1] = x[1] + T;}}
#endif /* MDL_UPDATE *//* Function: mdlTerminate =====================================================* Abstract:* In this function, you should perform any actions that are necessary* at the termination of a simulation. For example, if memory was* allocated in mdlStart, this is the place to free it.*/
static void mdlTerminate(SimStruct *S)
{
}
C代码编写好之后,用Matlab指令 'mex DFT_CMexSfunc.c' 进行编译。如果代码没有错误,编译成功后会看到如下提示:

仿真验证
将上述编写好的C MEX S-Fuction模块,放入Simulink模型中进行验证,如下所示:

运行上述模型,得到的电流和电压如上图所示,也与前一篇文章一致。
至此,可以证明该C MEX S-Fuction模块可以较好地求解,耦合信号中的自信号分量。
Tips
C MEX S-Fuction模块能够让使用者充分自由地开发Simulink模块,一方面是跨语言的程序开发方式,只需要include其他c文件的头文件,即可调用其中已开发和验证好的c函数。另一方面是大量的能够与Simulink引擎交互的的宏函数,使得开发人员有了更大的发挥空间。一些常用的,必须熟练掌握宏函数如下:
1.系统输入宏函数
ssSetNumInputPorts(S, 1)
ssSetInputPortWidth(S, 0, 1)
ssSetInputPortDirectFeedThrough(S, 0, 1)
real_T *u = (real_T*) ssGetInputPortSignal(S,0);
Temp = u[0];
2.系统参数宏函数
ssSetNumSFcnParams(S, 1);
real_T Pa1 = (real_T) *mxGetPr(ssGetSFcnParam(S,0));
//real_T Pa2 = (real_T) *mxGetPr(ssGetSFcnParam(S,1));
Temp = Pa1;
3.系统周期宏函数
ssSetSampleTime(S, 0, 0.001);
ssSetOffsetTime(S, 0, 0.0);
4.系统状态宏函数
ssSetNumDiscStates(S, 4);
real_T *x = ssGetRealDiscStates(S);
*x++ = a;
*x++ = b;
*x++ = c;
*x++ = d;
x[0] = x[0] + 1;
x[1] = x[1] + 1;
x[2] = x[2] + 1;
x[3] = x[3] + 1;
5.系统输出宏函数
ssSetNumOutputPorts(S, 1)
ssSetOutputPortWidth(S, 0, 1)
real_T *y = ssGetOutputPortSignal(S,0);
y[0] = a;
//y[1] = b;
//y[2] = c;
//y[3] = d;
分析和应用
C MEX S-Fuction是S-Fuction的一种,他们的相同点一样,不同点在于灵活度和自由度进一步延伸,功能进一步扩展。比如本文举例的DFT求解模块,是在原有DFT算法的基础上进一步裁剪,只求解目标期望频率上的信号分量,并且把原本算法中集中计算的几个向量积分、乘除、开方等运算分解到每一个运算周期中,变成单个变量的运算(需要时还可以灵活调整指定N个周期把算法执行完),以此通过延长运算时间来节省算法对单个周期硬件算力的开销,不仅能够保证整个系统的实时性能,还能大大提高算法求解得数据量,以此提高求解精度。同时本文举例的DFT求解求解算法也是以前开发的,经过验证的模块功能,利用C MEX S-Fuction提供的C函数调用机制,无缝衔接的移植了过来。
总结
以上就是本人在使用C MEX S-Fuction模块时,一些个人理解和分析的总结,首先介绍了该模块的背景知识,然后展示它的使用方法,最后分析了该模块的特点和适用场景。
后续还会分享另外几个最近总结的Simulink Toolbox库模块,欢迎评论区留言、点赞、收藏和关注,这些鼓励和支持都将成文本人持续分享的动力。
另外,上述例程使用的Demo工程,可以到笔者的主页查找和下载。
版权声明,原创文章,转载和引用请注明出处和链接,侵权必究!
相关文章:
开箱报告,Simulink Toolbox库模块使用指南(五)——S-Fuction模块(C MEX S-Function)
文章目录 前言 C MEX S-Function 算法原理 原始信号创建 编写S函数 仿真验证 Tips 分析和应用 总结 前言 见《开箱报告,Simulink Toolbox库模块使用指南(一)——powergui模块》 见《开箱报告,Simulink Toolbox库模块使用…...
摄像头的调用和视频识别
CV_tutorial3 摄像头调用实时播放保存视频 运动目标识别帧差法背景减除法 摄像头调用 创建视频捕捉对象:cv2.VideoCapture() 参数为视频设备的索引号,就一个摄像投的话写0默认; 或者是指定要读取视频的路径。 实时播放 import cv2 import …...
多通道分离与合并
目录 1.多通道分离split() 2.多通道合并merge() 3.Android JNI demo 1.多通道分离split() void cv::split ( InputArray m, OutputArrayOfArrays mv ) m:待分离的多通道图像。 mv:分离后的单通道图像,为向量vector形式。 2.多通道合并merge…...
JOJO的奇妙冒险
JOJO,我不想再做人了。 推荐一部动漫 JOJO的奇妙冒险 荒木飞吕彦创作的漫画 《JOJO的奇妙冒险》是由日本漫画家荒木飞吕彦所著漫画。漫画于1987年至2004年在集英社的少年漫画杂志少年JUMP上连载(1987年12号刊-2004年47号刊),2005年后在集英…...
LeetCode56.合并区间
这道题我想了一会儿,实在想不到比较好的算法,只能硬着头皮写了,然后不断的debug,经过我不懈的努力,最后还是AC,不过效率确实低。 我就是按照最直接的方法来,先把intervals数组按照第一个数star…...
【内推码:NTAMW6c】 MAXIEYE智驾科技2024校招启动啦
MAXIEYE智驾科技2024校招启动啦【内推码:NTAMW6c】 【招聘岗位超多!!公司食堂好吃!!】 算法类:感知算法工程师、SLAM算法工程师、规划控制算法工程师、目标及控制算法工程师、后处理算法工程师 软件类&a…...
Python框架【模板继承 、继承模板实战、类视图 、类视图的好处 、类视图使用场景、基于调度方法的类视图】(四)
👏作者简介:大家好,我是爱敲代码的小王,CSDN博客博主,Python小白 📕系列专栏:python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 📧如果文章知识点有错误…...
对于前端模块化的理解与总结(很全乎)
目录 模块化的好处 模块化的commonJS导入导出 暴露(导出)模块:module.exports value或exports.xxx value 导入模块——使用 es6模块化 方法一逐个导出 方法二默认导出 方法三 方法四 方法五 export 和import 同时存在 多个文件导出到一个文件后在相关文件…...
NewStarCTF 2022 web方向题解 wp
----------WEEK1---------- BUU NewStarCTF 公开赛赛道 WEEK1 [NotPHP] 先看题目,要传参加绕过。 分析一下代码:首先get一个datadata://test/plain,Wel…。然后key1和2用数组可以绕过。num2077a可以绕过弱类型。eval()中的php语句被#注释了,…...
WebGL矩阵变换库
目录 矩阵变换库: Matrix4对象所支持的方法和属性如表所示: 方法属性规范: 虽然平移、旋转、缩放等变换操作都可以用一个44的矩阵表示,但是在写WebGL程序的时候,手动计算每个矩阵很耗费时间。为了简化编程…...
block层:8. deadline调度器
deadline 源码基于5.10 0. 私有数据 struct deadline_data {/** run time data*//** requests (deadline_rq s) are present on both sort_list and fifo_list*/struct rb_root sort_list[2];struct list_head fifo_list[2];/** next in sort order. read, write or both ar…...
DTO,VO,PO的意义与他们之间的转换
DTO(Data Transfer Object):数据传输对象,这个概念来源于J2EE的设计模式,原来的目的是为了EJB的分布式应用提供粗粒度的数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负…...
Java 集合框架2
一、关于set接口的常用类 1.HashSet类 用来处理无序的单列数据,没有重复的元素,重复的元素算一个 i.构造方法 //HashSet类是set接口的子类,是无序的单列数据,没有重复的元素,重复的元素算一个 //HashSet的构造方法 //HashSet() …...
2024王道408数据结构P144 T16
2024王道408数据结构P144 T16 思考过程 首先看题目,要求我们把二叉树的叶子结点求出来并且用链表的方式存储,链接时用叶结点的右指针来存放单链表指针。我们很清楚可以看出来能用中序遍历递归的方式实现,因为第一个叶子结点在整棵树的最左下…...
【ARM Coresight 系列文章 22 -- linux frace 与 trace-cmd】
文章目录 ftrace 介绍trace-cmd 介绍trace-cmd 常用跟踪事件ftrace 与 trace-cmd 关系ftrace 编译依赖 ftrace 介绍 ftrace 是 Linux 内核中的一个跟踪工具,主要用于帮助开发者分析和调试内核的行为。ftrace 的名字来源于 “function tracer”,它最初是…...
MyBatis的一级缓存和二级缓存是怎么样的?
目录 1. 一级缓存 2. 一级缓存失效的几种情况 3. 二级缓存 4.二级缓存失效的情况 5. 二级缓存的相关配置 6. 缓存的查询顺序 MyBatis 的缓存共分为一级缓存和二级缓存。 1. 一级缓存 一级缓存是 SqlSession 级别的,通过同一个 SqlSession 查询到的数据会被缓…...
下载的文件被Windows 11 安全中心自动删除
今天从CSDN上下载了自己曾经上传的文件,但是浏览器下载完之后文件被Windows安全中心自动删除,说是带病毒。实际是没有病毒的,再说了即便有病毒也不应该直接删除啊,至少给用户一个保留或删除的选项。 研究了一番,可以暂…...
【Java List与数组】List<T>数组和数组List<T>的区别(124)
List数组:存储List的数组,即:数组中的元素是:List; 数组List:存储数组的List,即:List中的数据是类型的数组; 测试案例: import java.util.ArrayList; impor…...
Nuxt 菜鸟入门学习笔记四:静态资源
文章目录 public 目录assets 目录全局样式导入 Nuxt 官网地址: https://nuxt.com/ Nuxt 使用以下两个目录来处理 CSS、fonts 和图片等静态资源: public 目录 public 目录用作静态资产的公共服务器,可通过应用程序定义的 URL 公开获取。 换…...
C语言 - 结构体、结构体数组、结构体指针和结构体嵌套
结构体的意义 问题:学籍管理需要每个学生的下列数据:学号、姓名、性别、年龄、分数,请用 C 语言程序存储并处理一组学生的学籍。 单个学生学籍的数据结构: 学号(num): int 型姓名(…...
避坑指南:展锐平台Camera驱动移植中那些容易出错的配置项(以OV08A10为例)
展锐平台Camera驱动移植实战:OV08A10关键配置避坑手册 当你在展锐平台上移植OV08A10摄像头驱动时,是否遇到过这样的场景:所有配置看似正确,但摄像头就是无法正常工作?预览黑屏、图像异常或设备根本无法识别传感器——这…...
人工智能系统的测试:AI模型的可靠性与鲁棒性测试
在人工智能技术深度渗透各行业的当下,AI模型的可靠性与鲁棒性直接关乎业务安全与用户信任。对于软件测试从业者而言,突破传统测试思维,构建适配AI特性的测试体系,已成为保障AI系统高质量落地的核心任务。 一、AI模型可靠性与鲁棒…...
使用Taotoken后如何通过用量看板清晰掌握各模型API消耗情况
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 使用Taotoken后如何通过用量看板清晰掌握各模型API消耗情况 当你将多个大模型API的调用统一接入到Taotoken平台后,一个…...
Linux ISP驱动全流程解析:从V4L2框架到图像处理管线
1. 项目概述:从用户按下快门到ISP驱动当我们用手机或相机拍照时,屏幕上那个“咔嚓”的动画和瞬间生成的图片,背后是一场从物理世界到数字世界的精密“接力赛”。这场接力赛的第一棒是镜头和传感器,它们负责捕捉光线。但传感器输出…...
为什么92%的DeepSeek AWS部署失败?资深架构师拆解3大隐性成本陷阱与4步合规加固法
更多请点击: https://codechina.net 第一章:DeepSeek AWS部署教程 在AWS云平台上部署DeepSeek系列大语言模型(如DeepSeek-V2、DeepSeek-Coder)需兼顾计算性能、存储效率与网络低延迟。推荐使用g5.12xlarge或p4d.24xlarge实例类型…...
从PME消息到唤醒中断:图解Linux内核处理PCIe设备唤醒的完整链条与潜在陷阱
从PME消息到唤醒中断:图解Linux内核处理PCIe设备唤醒的完整链条与潜在陷阱 当一块NVMe SSD在深夜的服务器机柜中突然闪烁起状态灯,或是数据中心网卡因流量激增从节能模式苏醒时,PCIe总线上正上演着一场精密的电子芭蕾。这场唤醒仪式的核心角色…...
从74LS00与非门到74LS86异或门:手把手教你用面包板搭建数字电路基础实验(附波形分析)
从74LS00与非门到74LS86异或门:面包板上的数字电路实战指南 在电子技术的浩瀚海洋中,数字电路犹如一座连接现实与虚拟的桥梁。对于初学者而言,从理论到实践的跨越往往充满挑战——实验室里昂贵的设备、复杂的接线、固定的实验流程,…...
RTOS如何通过确定性调度与内存管理增强嵌入式系统安全可靠性
1. 项目概述:为什么我们需要关注实时操作系统的安全与可靠?在嵌入式、工业控制、汽车电子乃至航空航天这些领域里,系统一旦“死机”或“反应迟钝”,后果往往不是重启一下那么简单。轻则产线停摆、设备损坏,重则可能危及…...
从概率图到优化问题:信息矩阵、Hessian矩阵与协方差矩阵的内在统一
1. 概率图模型中的信息矩阵与协方差矩阵 我第一次接触信息矩阵是在做视觉SLAM项目时,当时被一堆矩阵运算绕得头晕。后来才发现,理解它们的关系就像拼乐高——每个零件都有明确的位置和作用。让我们从一个简单的因子图例子开始,看看这些矩阵如…...
使用curl命令直接测试taotoken api的连通性与基础功能
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 使用curl命令直接测试taotoken api的连通性与基础功能 基础教程类,面向需要快速验证或在不便安装SDK的环境中进行操作的…...
