[Simulink] 从Simulink S函数的使用

Simulink S函数的使用

这部分涉及到对Simulink建模的控制,从而生成我们可读、可进一步编写或修改的代码。因此,建模时需要遵循一定的建模规范,并进行对应的检查,例如:ISO26262、MAAB、MISRA C等检查,MAAB的建模规范中给出很多建模注意事项,为了满足代码的可读性,可以利用Simlink的mpt数据类型建立对应的数据字典。
这部分不是这个学习笔记的重点,这里先挖坑。

Simulink C Mex S Function

制作C MEX S函数,代码如下:

#define S_FUNCTION_NAME digital_in /* Defines and Includes */
#define S_FUNCTION_LEVEL 2

#include "simstruc.h"

enum{
    Pin_idx = 0,
    N_Para
};

#define Pin(S) mxGetScalar(ssGetSFcnParam(S,Pin_idx))

static void mdlInitializeSizes(SimStruct *S)
{
    ssSetNumSFcnParams(S, N_Para);
    if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
        return; /* Parameter mismatch reported by the Simulink engine*/
    }
    
    ssSetSFcnParamNotTunable(S,0);

    if (!ssSetNumInputPorts(S, 1)) return;
    ssSetInputPortWidth(S, 0, 1);
    ssSetInputPortDirectFeedThrough(S, 0, 1);
    
    if (!ssSetNumOutputPorts(S,0)) return; /*ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);*/
    
    ssSetNumSampleTimes(S, 1);

    /* Take care when specifying exception free code - see sfuntmpl.doc */
    ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
    }
static void mdlInitializeSampleTimes(SimStruct *S)
{
    ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
    ssSetOffsetTime(S, 0, 0.0);
}
static void mdlOutputs(SimStruct *S, int_T tid)
{
}
static void mdlTerminate(SimStruct *S){}

#define MDL_RTW
static void mdlRTW(SimStruct *S){
    int8_T pin_number = Pin(S);
    if(!ssWriteRTWParamSettings(S,N_Para,
            SSWRITE_VALUE_DTYPE_NUM,"PIN", &pin_number, DTINFO(SS_INT8, COMPLEX_NO) ))
    return;
    
}



#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */
#include "simulink.c" /* MEX-file interface mechanism */
#else
#include "cg_sfun.h" /* Code generation registration function */
#endif

上述代码中,两个地方注意一下:

enum{
    Pin_idx = 0,
    N_Para
};

其中,

  • enum枚举了两个变量Pin_idxN_Para,其中初始化Pin_idx = 0,则N_Para就初始化为1,恰好与变量个数相对应。
  • #define Pin(S) mxGetScalar(ssGetSFcnParam(S,Pin_idx))获取参数,获取到的是具体的值,这里需要将S-Function中的Parameters对应好。[Simulink] 从Simulink S函数的使用_第1张图片
  • ssWriteRTWParamSettings这个是要进行参数的传递,需要注意的是最终RTW认的是用户设定的参数名,而不是上图中的S-function parameters填写的参数,即:
if(!ssWriteRTWParamSettings(S,N_Para,
            SSWRITE_VALUE_DTYPE_NUM,"PIN", &pin_number, DTINFO(SS_INT8, COMPLEX_NO) ))
    return;

最终的tlc控制.c文件的时候,用的是"PIN"这个参数名,而不是Pin。

digital_in.c --> digital_in.mexw64

我使用的是windows 64位的电脑,所以生成的是digital_in.mexw64的文件,windows 32位则生成digital_in.mexw32的文件。
在Command Windows中键入:

mex digital_in.c

编写digital_in.tlc

编写tlc文件要实现的目的是:
将Simulink模块与最终的代码实现有效地结合,即生成可以直接进行编译的代码。
这里的tlc主要实现:

  • 在model.c文件的Includes部分加入#include "reg52.h",在model_step()函数中加入P% = %;
  • 为了说明如何生成用户自定义的文件,这里生成一个GPIOconfig.c文件,里面什么也没有。
    代码如下:
%implements digital_in "C"



%%assign u = LibBlockInputSignal(0, "","",0)

%function BlockTypeSetup(block, system) void
	%% 初始化程序写入到ert_main.c中?
	%openfile tmpBuf
		#include "reg52.h"
	%closefile tmpBuf
	%assign srcFile = LibGetModelDotCFile()
    %warning srcFile
	%
    
    %openfile tmpBuf
	
    %closefile tmpBuf
    %assign cFile = LibCreateSourceFile("Source","Custom","GPIOconfig")
    %
%endfunction

%function BlockInstanceSetup(block, system) void
%endfunction

%function Outputs(block, system) Output
%assign u = LibBlockInputSignal(0, "","",0)
%assign Pin = CAST("Number", SFcnParamSettings.PIN)
P% = %;
%endfunction

在这里插入图片描述

建立S-function模块

建立S-function模块,并Mask掉,参数如下:
[Simulink] 从Simulink S函数的使用_第2张图片
在S-function模块中填入
[Simulink] 从Simulink S函数的使用_第3张图片
因为这里没有对digital_in.c中的mdlOuputs函数进行内容的填写,所以仿真时是没有任何输出的。

配置环境

配置ert.tlc,设置为Fixed-step,进行自动代码生成,生成后的代码中标黄的地方为用户自定义的部分
[Simulink] 从Simulink S函数的使用_第4张图片

编译

在编译之前,对ert_main.c进行修改:

  • 删除掉#include /* This ert_main.c example uses printf/fflush */
  • int_T main(int_T argc, const char *argv[])修改为int_T main(void),同时删除(void)(argc); (void)(argv);
  • 删除下图中部分:
    在这里插入图片描述
  • rt_OneStep()放到 while (rtmGetErrorStatus(LED_demo_M) == (NULL))
  • rt_OneStep()的/* Enable interrupts here */部分添加一个时钟中断

上面的修改其实可以看出,要做一个合适的底层驱动,其实最好是自定义main文件

这里给一个自己修改的ert_main.c

/*
 * File: ert_main.c
 *
 * Code generated for Simulink model 'LED'.
 *
 * Model version                  : 1.1
 * Simulink Coder version         : 9.0 (R2018b) 24-May-2018
 * C/C++ source code generated on : Fri Apr 12 10:31:58 2019
 *
 * Target selection: ert.tlc
 * Embedded hardware selection: Intel->x86-64 (Windows64)
 * Code generation objectives: Unspecified
 * Validation result: Not run
 */

#include 
#include "LED.h"                       /* Model's header file */
#include "rtwtypes.h"
/*#include "reg52.h"

/*#define led_blink P0

/*
 * Associating rt_OneStep with a real-time clock or interrupt service routine
 * is what makes the generated code "real-time".  The function rt_OneStep is
 * always associated with the base rate of the model.  Subrates are managed
 * by the base rate from inside the generated code.  Enabling/disabling
 * interrupts and floating point context switches are target specific.  This
 * example code indicates where these should take place relative to executing
 * the generated code step function.  Overrun behavior should be tailored to
 * your application needs.  This example simply sets an error status in the
 * real-time model and returns from rt_OneStep.
 */
 
typedef unsigned int u16;	

void delay(u16 i)
{
	while(i--);	
}

void rt_OneStep(void);
void rt_OneStep(void)
{
  static boolean_T OverrunFlag = false;

  /* Disable interrupts here */

  /* Check for overrun */
  if (OverrunFlag) {
    rtmSetErrorStatus(LED_M, "Overrun");
    return;
  }

  OverrunFlag = true;

  /* Save FPU context here (if necessary) */
  /* Re-enable timer or interrupt here */
  /* Set model inputs here */

  /* Step the model */
  LED_step();

  /* Get model outputs here */
	/*led_blink = LED_Y.Out1;
  /* Indicate task complete */
  OverrunFlag = false;

  /* Disable interrupts here */
  /* Restore FPU context here (if necessary) */
  /* Enable interrupts here */
	delay(100000);
}

/*
 * The example "main" function illustrates what is required by your
 * application code to initialize, execute, and terminate the generated code.
 * Attaching rt_OneStep to a real-time clock is target specific.  This example
 * illustrates how you do this relative to initializing the model.
 */
int_T main(void)
{

  /* Initialize model */
  LED_initialize();

  /* Attach rt_OneStep to a timer or interrupt service routine with
   * period 1.0 seconds (the model's base sample time) here.  The
   * call syntax for rt_OneStep is
   *
   rt_OneStep();
   */
	
	
  while (rtmGetErrorStatus(LED_M) == (NULL)) {
    /*  Perform other application tasks here */
		rt_OneStep();
  }
	
  /* Disable rt_OneStep() here */

  /* Terminate model */
  LED_terminate();
  return 0;
}

/*
 * File trailer for generated code.
 *
 * [EOF]
 */

接下来就是可以keil进行编译,生成hex文件,并通过烧录软件烧写程序了,这部分就不说了。

总结

上面的过程,实际上是利用S函数和tlc来控制代码的过程,实际上S函数还可以实现C代码的嵌入需求,好像 C Caller这个模块也行。

你可能感兴趣的:(Simulink)