中国汽车工程师之家--聚集了汽车行业80%专业人士 

论坛口号:知无不言,言无不尽!QQ:542334618 

本站手机访问:直接在浏览器中输入本站域名即可 

  • 148查看
  • 0回复

[Simulink] MBD的Simulink使用技巧⑦:自动生成代码的集成方法

[复制链接]

该用户从未签到

发表于 2-3-2024 09:52:47 | 显示全部楼层 |阅读模式

汽车零部件采购、销售通信录       填写你的培训需求,我们帮你找      招募汽车专业培训老师


全文约3500字,你将看到以下内容:

    构建Step函数的接口函数

    MATLAB Utilities代码的集成
    共用代码的集成生成代码的最终打包导出

autoMBD最近发布了《autoMBD原创技术文章合集》
合集包含156页丰富的MBD入门基础和MBDT硬件支持包的使用还包含基于MBD的电机控制算法开源项目——AMBD-MC合集配备了丰富的视频讲解
和大量的模型、文档和软件资源

如何获取请参考@所有读者:autoMBD发布《autoMBD原创技术文章合集》。

本篇文章继续介绍MBD的Simulink使用技巧,主要包括如何将Simulink生成的代码与外部已有的代码进行集成。

点击以下链接,可以查看MBD的Simulink使用技巧系列的往期文章:
    MBD的Simulink使用技巧①:Simulink代码生成的基本概念MBD的Simulink使用技巧②:详解代码生成中的模型与代码MBD的Simulink使用技巧③:虚拟子系统与原子子系统的代码生成MBD的Simulink使用技巧④:详解生成代码的结构与代码的生成流程
    MBD的Simulink使用技巧⑤:详解自动代码生成的配置与优化
    MBD的Simulink使用技巧⑥:代码生成目标配置工具

特别提示:在本篇文章中使用到的PI控制器示例模型和接口函数示例代码,可以在autoMBD资源库的“临时资源分享”文件夹中找到(资源序号为tA25和tA26)。资源库链接的获取可以在《autoMBD原创技术文章合集》中找到(见文章开头)。

1  构建Step函数的接口函数

Step函数的集成方法在以往的文章中介绍过数次。在《MBD的Simulink使用技巧④:详解生成代码的结构与代码的生成流程》一文中,详细介绍了模型生成的三个入口函数(Model Entry-Point Functions):

    模型初始化函数

    模型Step函数
    模型终止函数

要集成Simulink生成的代码,最基本的方式就是合理调用这些入口函数,一般情况下,Step函数需要周期性被调用。

在文章合集的《第十七篇 把模型嵌入代码》中,详细展示集成Step函数的基本步骤。更多关于集成的细节可以阅读该文章,这里不再赘述。(获取文章合集的方法见文章开头)

这里仅展示最终集成的代码,如下所示(Motor_ISR(void)是周期中断服务函数,在Step函数的前后分别设置输入数据和输出数据):
voidMotor_ISR(void){/*Read ADC*/    ADC_DRV_GetChanResult(INST_ADCONV1,0,&adcResult[0][0]);/*V_REFSH*/    ADC_DRV_GetChanResult(INST_ADCONV1,1,&adcResult[0][1]);/*Pot*/    ADC_DRV_GetChanResult(INST_ADCONV1,2,&adcResult[0][2]);/*iA*/    ADC_DRV_GetChanResult(INST_ADCONV1,3,&adcResult[0][3]);/*Temp*/    ADC_DRV_GetChanResult(INST_ADCONV2,0,&adcResult[1][0]);/*V_REFSL*/    ADC_DRV_GetChanResult(INST_ADCONV2,1,&adcResult[1][1]);/*uDC*/    ADC_DRV_GetChanResult(INST_ADCONV2,2,&adcResult[1][2]);/*iB*/    ADC_DRV_GetChanResult(INST_ADCONV2,3,&adcResult[1][3]);/*iDC*/
/*Set model inputs here*/    FOC_Ctrl_CodeModel_U.ADCinput[0]=adcResult[0][2];    FOC_Ctrl_CodeModel_U.ADCinput[1]=adcResult[1][2];    FOC_Ctrl_CodeModel_U.ADCinput[2]=adcResult[1][3];    FOC_Ctrl_CodeModel_U.ADCinput[3]=adcResult[1][1];    FOC_Ctrl_CodeModel_U.ADCinput[4]=adcResult[0][1];    FOC_Ctrl_CodeModel_U.FaultSwitch=faultSwitch;    FOC_Ctrl_CodeModel_U.MotorSwitch=motorSwitch;
/*Calculate one-step for the model*/    rt_OneStep();
/*Get model output here*/    pwmDuty[0]=(uint16_t)(FULL_DUTY*FOC_Ctrl_CodeModel_Y.DUTY[0]);    pwmDuty[1]=(uint16_t)(FULL_DUTY*FOC_Ctrl_CodeModel_Y.DUTY[1]);    pwmDuty[2]=(uint16_t)(FULL_DUTY*FOC_Ctrl_CodeModel_Y.DUTY[2]); 28/*Set PWM duty*/    FTM_DRV_FastUpdatePwmChannels(INST_FLEXTIMER_PWM1, 3, pwmChannels, pwmDuty, true);
/*Clear FTM flag*/    FTM_DRV_ClearStatusFlags(3, FTM_TIME_OVER_FLOW_FLAG | FTM_RELOAD_FLAG);}在模型不复杂,模型的输入、输出数据不多的情况下,上述集成方法是有效可行的。但当模型变得复杂时,上述的方法就显得不太灵活,且效率不高。这时候构建专门的接口函数,来实现数据的传递,或者执行输入、输出的一些特定的动作(读ADC数据,或输出PWM信号,等),是一个较为常用且高效的方法。接口函数具体执行的内容需要开发者自己构建,但如何快速方便地将开发者构建的接口函数与模型生成的代码(Step函数)集成在一起呢?
在Simulink中,可以通过存储类(Storage Class)来实现这样的需求。

Tips:autoMBD目前还尚未系统介绍存储类,不了解存储类的读者也不用担心,后续会专门介绍存储类的相关概念。这里把它当作一种控制代码生成的方法即可。

下面以PI控制器示例模型为例,向读者展示如何通过存储类,为输入输出端口构建专门的接口函数。

MBD的Simulink使用技巧⑦:自动生成代码的集成方法w1.jpg

PI控制器示例模型 - From autoMBD

该模型想必很多读者都比较熟悉了,打开该模型后,首先在APPS中找到Embedded Coder工具,在Embedded Coder标签页中,点击Code Interface下拉菜单中的Individual Element Code Mappings选项,此时Property Inspector窗口出现,如下所示:

MBD的Simulink使用技巧⑦:自动生成代码的集成方法w2.jpg

打开Embedded Coder工具 - From autoMBD

MBD的Simulink使用技巧⑦:自动生成代码的集成方法w3.jpg

Individual Element Code Mappings - From autoMBD

然后在“Code Mapping - C”的窗口中,找到Inports下的两个输入端口,将两个端口的存储类(Storage Class)设置为“GetSet”,同时配置Headerfile为自己构建的接口函数的头文件名,本例中设置为:
    autoMBD_example_IO.h

MBD的Simulink使用技巧⑦:自动生成代码的集成方法w4.jpg

设置输入端口存储类 - From autoMBD

对Outports执行同样的操作,将存储类设置为“GetSet”,将Headerfile设置与输入相同。这里要注意,输出端口的Identifier不能为空,需要设置一个合适的名字,如下所示:

MBD的Simulink使用技巧⑦:自动生成代码的集成方法w5.jpg

设置输出端口存储类 - From autoMBD

设置完成后,点击“Build”生成代码,此时Step函数如下:
/* Model step function */void autoMBD_example_PI_noSubs_step(void){  real_T rtb_Err;
  /* Sum: '<Root>/Sum2' incorporates:   *  Inport: '<Root>/Feedback'   *  Inport: '<Root>/Req_Ctrl'   */  rtb_Err = get_Req_Ctrl() - get_Feedback();
  /* Outport: '<Root>/PI_Ctrl' incorporates:   *  DiscreteIntegrator: '<Root>/Discrete-Time Integrator'   *  Gain: '<Root>/Kp'   *  Sum: '<Root>/Sum1'   */  set_PI_Ctrl(2.0 * rtb_Err +              autoMBD_example_PI_noSubs_DW.DiscreteTimeIntegrator_DSTATE);
  /* Update for DiscreteIntegrator: '<Root>/Discrete-Time Integrator' incorporates:   *  Gain: '<Root>/Ki'   */  autoMBD_example_PI_noSubs_DW.DiscreteTimeIntegrator_DSTATE += 3.0 * rtb_Err *    0.001;}可以看到,输入端口Req_Ctrl和Feedback变成了Get接口函数:
    get_Req_Ctrl()
    get_Feedback()
而输出端口PI_Ctrl变成了Set接口函数:
    set_PI_Ctrl()
在生成的代码中,是没有这三个接口函数的具体实现的,只在“模型名_private.h”文件中包含了我们所设置的头文件,如下所示:/* Includes for objects with custom storage classes. */#include"autoMBD_example_IO.h"前文提到接口函数具体执行的内容需要开发者自己构建,这里给出上述接口函数的一个实现示例,供读者参考:/**************************************** * 文件名  :autoMBD_example_IO.c  * 作者    :autoMBD  * 创建时间:2022-11-20 ****************************************/
#include"autoMBD_example_IO.h"#include"Cpu.h"#include"rtwtypes.h"/* 在这里添加必要的头文件 */
/* get_Req_Ctrl接口函数,返回Req_Ctrl的值 */real_T get_Req_Ctrl(void){static real_T Req_Ctrl = 0;    Req_Ctrl += 1;return Req_Ctrl;}
/* get_Feedback接口函数,返回Feedback的值 */real_T get_Feedback(void){    real_T Feedback;/*Read ADC*/    ADC_DRV_GetChanResult(0,0,&Feedback);return Feedback;}
/* set_PI_Ctrl接口函数,根据输入参数,设置PWM占空比 */voidset_PI_Ctrl(real_T PI_Ctrl){    FTM_DRV_FastUpdatePwmChannels(0, 1, 0, PI_Ctrl, true);}接口函数的头文件如下:
/**************************************** * 文件名  :autoMBD_example_IO.h  * 作者    :autoMBD  * 创建时间:2022-11-20 ****************************************/#ifndef AUTOMBD_EXAMPLE_IO_H#define AUTOMBD_EXAMPLE_IO_H
real_T get_Req_Ctrl(void);real_T get_Feedback(void);voidset_PI_Ctrl(real_T PI_Ctrl);
#endifTips:上述示例代码仅展示接口函数如何构建,要使用这些接口函数,还需要完整的包含底层代码的工程。Tips:上述模型和示例代码可以在autoMBD资源库的“临时资源分享”文件夹中找到该模型,资源序号tA25。用户可以自由构建接口函数的实现内容和执行的任务,只要接口函数的名称一致,并且和模型生成的代码放在同一个工程内(生成的代码中已经自动包含了用户设置的头文件),Step函数就可以直接使用接口函数,而不需要开发者额外的任何操作。这种使用接口函数的集成方式,在复杂模型和大量数据输入输出的场景下,会十分高效和实用。

2  MATLAB Utilities代码的集成

在有的情况下,模型生成的代码,并不全部来自于模型。

部分代码是由MathWorks官方预先写好的,存放在MATLAB的安装目录下,模型生成的代码只是调用这些代码中的函数来实现功能。

这部分由MathWorks官方预先写好的源代码就是MATLAB Utilities代码。特别是当模型中使用了Simulink的特定功能时,可能会使用到MATLAB Utilities相关代码,例如连续模型求解器、AI算法、图像处理算法等。

读者可以在MATLAB安装目录下的这个位置找到MATLAB Utilities代码:
/* MATLAB Utilities代码路径 */~/MATLAB/simulink/include~/MATLAB/simulink/src
MBD的Simulink使用技巧⑦:自动生成代码的集成方法w6.jpg

MATLAB Utilities代码位置 - From autoMBD

下面以连续模型为例,向读者展示如何使用MATLAB Utilities相关代码。

使用到的模型依然是PI控制器模型,不过对该模型进行了修改,将原来的离散积分模块改为了连续积分模块,如下所示:

MBD的Simulink使用技巧⑦:自动生成代码的集成方法w7.jpg

PI控制器连续模型 - From autoMBD

同时配置模型支持连续时间(continuous time),如下所示:

MBD的Simulink使用技巧⑦:自动生成代码的集成方法w8.jpg

配置模型支持连续时间 - From autoMBD

Tips:上述连续模型可以在autoMBD资源库的“临时资源分享”文件夹中找到该模型,资源序号tA26。

Tips:不建议在嵌入式系统中使用连续模型,本次例程仅展示MATLAB Utilities相关代码的使用方法。

使用Embedded Coder重新生成代码,对比原本离散积分器生成的较为简单的代码,连续模型生成的代码变得非常复杂,如下所示(只截取了部分代码片段):
/* 连续积分模块生成的代码 *//* Model step function */void autoMBD_example_PI_Continous_step(void){  real_T rtb_Err;if (rtmIsMajorTimeStep(autoMBD_example_PI_Continous_M)) {/* set solver stop time */    rtsiSetSolverStopTime(&autoMBD_example_PI_Continous_M->solverInfo,                          ((autoMBD_example_PI_Continous_M->Timing.clockTick0+1)*      autoMBD_example_PI_Continous_M->Timing.stepSize0));  }                                    /* end MajorTimeStep */
/* Update absolute time of base rate at minor time step */if (rtmIsMinorTimeStep(autoMBD_example_PI_Continous_M)) {    autoMBD_example_PI_Continous_M->Timing.t[0] = rtsiGetT      (&autoMBD_example_PI_Continous_M->solverInfo);  }
/* Sum: '<Root>/Sum2' incorporates:   *  Inport: '<Root>/Feedback'   *  Inport: '<Root>/Req_Ctrl'   */  rtb_Err = autoMBD_example_PI_Continous_U.Req_Ctrl -    autoMBD_example_PI_Continous_U.Feedback;
/* Outport: '<Root>/PI_Ctrl' incorporates:   *  Gain: '<Root>/Kp'   *  Integrator: '<Root>/Integrator'   *  Sum: '<Root>/Sum1'   */  autoMBD_example_PI_Continous_Y.PI_Ctrl = 2.0 * rtb_Err +    autoMBD_example_PI_Continous_X.Integrator_CSTATE;
/* Gain: '<Root>/Ki' */  autoMBD_example_PI_Continous_B.Ki = 3.0 * rtb_Err;if (rtmIsMajorTimeStep(autoMBD_example_PI_Continous_M)) {    rt_ertODEUpdateContinuousStates(&autoMBD_example_PI_Continous_M->solverInfo);
/* Update absolute time for base rate *//* The "clockTick0" counts the number of times the code of this task has     * been executed. The absolute time is the multiplication of "clockTick0"     * and "Timing.stepSize0". Size of "clockTick0" ensures timer will not     * overflow during the application lifespan selected.     */    ++autoMBD_example_PI_Continous_M->Timing.clockTick0;    autoMBD_example_PI_Continous_M->Timing.t[0] = rtsiGetSolverStopTime      (&autoMBD_example_PI_Continous_M->solverInfo);
    {/* Update absolute timer for sample time: [0.001s, 0.0s] *//* The "clockTick1" counts the number of times the code of this task has       * been executed. The resolution of this integer timer is 0.001, which is the step size       * of the task. Size of "clockTick1" ensures timer will not overflow during the       * application lifespan selected.       */      autoMBD_example_PI_Continous_M->Timing.clockTick1++;    }  }                                    /* end MajorTimeStep */}可以看到,Step函数中调用很多Solver和Time相关的的API,这些API相关代码并不在生成的代码中。它们使用的便是MATLAB Utilities代码,在“模型名.h”头文件中,可以看到对MATLAB Utilities头文件的引用:
/* 连续积分模块生成的代码 *//* "模型名.h"中引用"rtw_continuous.h"和"rtw_solver.h" */
#ifndef autoMBD_example_PI_Continous_COMMON_INCLUDES_#define autoMBD_example_PI_Continous_COMMON_INCLUDES_#include"rtwtypes.h"#include"rtw_continuous.h"#include"rtw_solver.h"#endif
上述代码中头文件rtw_continuous.h和rtw_solver.h可在MATLAB安装目录下找到源文件。

要使用MATLAB Utilities相关代码,必须在代码中包含相应的头文件;同时在编译时,也要将源文件的路径添加到编译路径中,不然工程找不到相应文件,编译会报错。

3  共用代码的集成

实际开发过程中,我们可能会创建非常多不同的模型。细心的读者可能会发现,文章展示的很多示例模型都会生成相同头文件rtwtypes.h。

该文件内容相同,是共用的代码,多模型开发时只需要生成一个即可。在不同情况下,还可能存在其他的共用代码或文件。

可以通过配置“Shared location”功能,让所有共用代码生成在相同的位置,配置的方法如下所示:

MBD的Simulink使用技巧⑦:自动生成代码的集成方法w9.jpg

配置Shared Location - From autoMBD

配置好之后,共用代码生成的位置会改到“slprj/ert/_sharedutils/“目录下:

MBD的Simulink使用技巧⑦:自动生成代码的集成方法w10.jpg

共用代码位置 - From autoMBD

4  生成代码的最终打包导出

文章至此,已经介绍了多个方面的代码集成方法。

但还有一个问题未能解决:生成的代码存储位置比较混乱无序,生成的C代码源文件还和其他无关代码放在一起;特别是当使用了MATLAB Utilities代码和共用代码后,有多个位置都存放了代码。

在Simulink中,提供了代码打包导出的功能,可以将所有生成的代码打包放在一起,包括MATLAB Utilities代码和共用代码。使用方法如下:

MBD的Simulink使用技巧⑦:自动生成代码的集成方法w11.jpg

代码打包导出 - From autoMBD

生成的Zip压缩包中的内容如下:

MBD的Simulink使用技巧⑦:自动生成代码的集成方法w12.jpg

压缩包中的代码 - From autoMBD

生成的压缩包中,文件结构层级变得简洁了许多,并且只包含源代码,没有其他任何无关文件,非常方便后续集成开发的使用。



如果你觉得文章对你有帮助,欢迎关注,我会持续更新更多原创文章

与我交流讨论

由于新开通的公众号均不支持留言,欢迎给我发私信,我会及时回复的



版权归autoMBD所有,转载请注明作者和来源。

快速发帖

您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|手机版|小黑屋|Archiver|汽车工程师之家 ( 渝ICP备18012993号-1 )

GMT+8, 1-5-2024 13:42 , Processed in 0.323798 second(s), 30 queries .

Powered by Discuz! X3.5

© 2001-2013 Comsenz Inc.