从零开始simulink自定义代码生成----自定义硬件驱动库文件(3)

前言

在很早的时候,做过一些Simulink自定义硬件驱动库的相关探索,但是后面没有继续更新下去了。现在有空打算再重新整理一下。本文接着之前的那篇文章继续编写DO的C mex函数和tlc文件

C mex文件

首先配置s-function名称和level


#define S_FUNCTION_NAME  s12xep_dio_dout
#define S_FUNCTION_LEVEL 2

该名称需要与之前设计界面时填入的名称保持一致

level配置为2,可以扩展的功能更多。

必须要包含simstruc.h头文件,以调用API函数及结构

/*
 * Need to include simstruc.h for the definition of the SimStruct and
 * its associated macro definitions.
 */
#include "simstruc.h" 

定义参数枚举及获取参数的宏定义

/* 
 *  DIOPORT_GROUP    - Port Group A/B 
 *  BIT_NUM          - Bit Num for Control
 */

enum {
    DIOPORT_GROUP=0, 
    BIT_NUM, 
    N_PARAMS
};

#define DIOPORT(S)          (mxGetScalar(ssGetSFcnParam(S, DIOPORT_GROUP)))
#define BITARRAY_ARG(S)     (            ssGetSFcnParam(S, BIT_NUM))


这个宏定义和s-function中的参数对应,用来获取参数值

ssGetSFcnParam 获取指向控件中参数的指针, mxGetScalar获取指针指向地址的数值

mdlInitializeSizes

设置或获取输入和输出端口的数量、宽度以及一些其他信息


/* Function: mdlInitializeSizes ===============================================
 * Abstract:
 *   Initialize the sizes array
 */
static void mdlInitializeSizes(SimStruct *S)
{
    int nBits;
    
    /* Set and Check parameter count  */
    ssSetNumSFcnParams(S, N_PARAMS);
    if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) return;
    ssSetSFcnParamNotTunable(S, 0);//设置模块参数值在仿真过程中不可变
    ssSetSFcnParamNotTunable(S, 1);//设置模块参数值在仿真过程中不可变

    nBits  = mxGetNumberOfElements(BITNUM(S));//获取对象中元素个数

    /* Single input port of width equal to nBits */
    if (!ssSetNumInputPorts( S, 1)) return;//设置输入端口个数为1
    if (!ssSetNumOutputPorts(S, 1)) return;//设置输出端口个数为1
    

      
         ssSetInputPortWidth(    S, 0, nBits);
         ssSetInputPortDataType( S, 0, SS_BOOLEAN);
         ssSetOutputPortWidth(   S, 0, nBits);
         ssSetOutputPortDataType(S, 0, SS_BOOLEAN);
  
    
    ssSetInputPortRequiredContiguous(S, 0, 1);//设置输入端口号为0的端口输入值必须为连续
    ssSetInputPortDirectFeedThrough( S, 0, 1);//设置输入端口直接馈通状态
    /* No output port of width equal to nBits */
    
    /* sample times */
    ssSetNumSampleTimes(S, 1 );
    //其中numSampleTimes > 0。这告诉Simulink你的s函数有基于块的样本时间。
    //Simulink调用mdlInitializeSampleTimes,它反过来设置示例时间。
    /* options */
    ssSetOptions(S, (SS_OPTION_EXCEPTION_FREE_CODE |
                     SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME));
    //SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME 用于禁止S-Function块继承常数样例时间。
} /* end mdlInitializeSizes */


使用ssSetNumSFcnParams函数设置参数个数

使用ssGetNumSFcnParams获取参数个数,与界面中设置的参数个数校验

使用ssSetNumInputPorts函数设置输入端口个数

使用ssSetNumOutputPorts设置输出端口个数,0表示第一个端口

使用ssSetInputPortWidth设置输入端口的宽度,0表示第一个端口

使用ssSetInputPortDataType设置输入端口的数据类型,输出同理

使用ssSetNumSampleTimes设置采样时间个数,具体采样时间在后面配置

mdlInitializeSampleTimes

设置采样时间

/* Function: mdlInitializeSampleTimes =========================================
 * Abstract:
 *    Initialize the sample times array.
 */
static void mdlInitializeSampleTimes(SimStruct *S)
{
    ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);//引用模型的采样时间
    ssSetOffsetTime(S, 0, 0.0);
    
} /* end mdlInitializeSampleTimes */

mdlOutputs

此处没有添加代码

/* Function: mdlOutputs =======================================================
 * Abstract:
 *   Compute the outputs of the S-function.
 */
static void mdlOutputs(SimStruct *S, int_T tid)
{

} /* end mdlOutputs */


mdlTerminate

此处没有添加代码


/* Function: mdlTerminate =====================================================
 * Abstract:
 *    Called when the simulation is terminated.
 */
static void mdlTerminate(SimStruct *S)
{
} /* end mdlTerminate */


mdlRTW

在函数 mdlRTW 中设置模块参数,模块 tlc描述文件通过获取模块参数值来生成不同的模块代码


#define MDL_RTW
static void mdlRTW(SimStruct *S)
{
    uint8_T   dioPort     = (uint8_T) DIOPORT(S);
    uint16_T *bits        = (uint16_T *) mxGetData(BITNUM(S));

    /* Write out parameters for this block.*/
    if (!ssWriteRTWParamSettings(S, 2, 
                                 SSWRITE_VALUE_DTYPE_NUM,"DIOPort",
                                 &dioPort,DTINFO(SS_UINT8, COMPLEX_NO),                              
                                 
                                 SSWRITE_VALUE_DTYPE_VECT, "Bits",
                                 bits,
                                 mxGetNumberOfElements(BITNUM(S)),
                                 DTINFO(SS_UINT16, COMPLEX_NO)
                                 )) {
        return; /* An error occurred which will be reported by SL */
    }
}

ssWriteRTWParamSettings函数的用法如下:

int_T ssWriteRTWParameters(SimStruct *S, int_T nParams, int_T 
paramType, const char_T *paramName, const char_T *stringInfo, 
...)

nParams表示需要传递的参数个数,paramType表示参数类型,paramName表示参数名称,stringInfo一般省略。

此处通过RTW传递数据给TLC,一个传递两个参数,第一个数据为Uint8类型的数值,表示PORT A/B的索引,注意此处传递的为地址

第二个数据为bit位置,传递的是数据向量,有三个参数,第一个参数为首地址,第二个参数为长度,第三个参数为数据类型

具体的paramType参数类型见下表:



c文件结尾

/*==============================================*
* Enforce use of inlined S-function             * 
* (e.g. must have TLC file s12xep_dio_dout.tlc) *
*===============================================*/

#ifdef    MATLAB_MEX_FILE  /* Is this file being compiled as a MEX-file?    */
# include "simulink.c"     /* MEX-file interface mechanism                  */
#else                      /* Prevent usage by RTW if TLC file is not found */
# error "Attempted use non-inlined S-function s12xep_dio_dout.c"
#endif

结尾必须要加入该段,mex命令编译时,MATLAB_MEX_FILE自动被定义

编译c文件

编写好c文件后,使用mex命令编译c文件,可以命令行,也可以做成m脚本

mex s12xep_dio_dout.c


成功之后会生成.mexw64文件

tlc文件

tlc文件主要用来自定义生成的代码

%implements  s12xep_dio_dout "C"

使用implements指定TLC语言类型,此处设置为C语言

%include "driver_utils.tlc"

该TLC文件中包含了一些公用的文件

Start函数

%% Function: Start ==========================================================
%%
%% Purpose:
%%      Port A/B Digital Input initialization code.
%%
%function Start(block, system) Output
 
    /* S-Function "s12xep_dio_dout" initialization Block: %<Name> */
    %%
    %% Select DIO Port A or Port B and enable the selected Bits   
    %assign nPars       = SIZE(SFcnParamSettings.Bits,1)
    %assign port = getPort(SFcnParamSettings.DIOPort)
    %assign portId = CAST("Number", SFcnParamSettings.DIOPort)
    %assign nextChannel = 0 
    %% 

    %if ISEQUAL(portId,1)
          %% Configure Pins 0-3 of PORTA as outputs.
          %assign ddr = "DDRA"      
    %else
          %assign ddr = "DDRB"        
    %endif

      %foreach idx=nPars
        %assign bitIdx = CAST("Number",SFcnParamSettings.Bits[idx])
        %<ddr> =  %<ddr>  |  (0x1 << %<bitIdx>);  /* Select bit to be output */
        %<port> = %<port> | (0x1 << %<bitIdx>); /* Initial state */     
        %assign nextChannel = nextChannel+1
      %endforeach
 
  
%endfunction

通过SIZE函数获取bit个数,此处只可能为1

通过getPort函数获取PORT名称,此处为PORTA或PORTB

通过CAST函数获取对应PORT参数中的Number,应该是从1开始的。

通过DDRA/B对应bit设置为1确定输出方向,通过PORTA/B配置输出电平高低,此处默认高电平

Outputs函数

%% Function: Outputs ==========================================================
%%
%% Purpose:
%%      Code generation rules for mdlOutputs function.
%%
%function Outputs(block, system) Output
  
    /* S-Function "s12xep_dio_dout" Block: %<Name> */

    %assign port        = getPort(SFcnParamSettings.DIOPort)
    %assign nPars       = SIZE(SFcnParamSettings.Bits, 1)
    %assign nextChannel = 0 
    %%
   
      %foreach idx=nPars
        %assign u      = LibBlockInputSignal(0, "", "", %<nextChannel>)
        %assign bitIdx = CAST("Number",SFcnParamSettings.Bits[idx])
        %<port> = (%<port> & ~(1 << %<bitIdx>)) | ((%<u>) << %<bitIdx>); /* Clear the bit and set it to the input val */
        %assign nextChannel = nextChannel+1
    %endforeach

 
%% 
%endfunction

通过LibBlockInputSignal获取输入参数的值,然后对对应的bit进行set

模型及生成的代码

增加一个DO模块,实际放到1000ms task中运行,关于task的建立后面再讲

初始化:



  /* S-Function "s12xep_dio_dout" initialization Block: <S1>/DO1 */
  DDRB = DDRB | (0x1 << 1);            /* Select bit to be output */
  PORTB = PORTB | (0x1 << 1);          /* Initial state */

实际运行函数:


void
 task_1000ms(void)
{
  /* Output and update for function-call system: '<Root>/Function-Call Subsystem' */

  /* Delay: '<S1>/Delay' */
  LED_value = tsk_test_DW.Delay_DSTATE;

  /* S-Function (s12xep_dio_dout): '<S1>/DO' */

  /* S-Function "s12xep_dio_dout" Block: <S1>/DO */
  PORTB = (PORTB & ~(1 << 1)) | ((LED_value) << 1);
                                 /* Clear the bit and set it to the input val */


  /* Update for Delay: '<S1>/Delay' incorporates:
   *  Logic: '<S1>/Logical Operator'
   */
  tsk_test_DW.Delay_DSTATE = !LED_value;
}

总结

以上,一个简单的自定义DO输出硬件驱动库就完成了。后面有空会更新其他模块。

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赞哥哥s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值