和大量的模型、文档和软件资源 如何获取请参考@所有读者:autoMBD发布《autoMBD原创技术文章合集》。 本篇文章继续介绍MBD的Simulink使用技巧,主要包括如何将Simulink生成的代码与外部已有的代码进行集成。 点击以下链接,可以查看MBD的Simulink使用技巧系列的往期文章:
特别提示:在本篇文章中使用到的PI控制器示例模型和接口函数示例代码,可以在autoMBD资源库的“临时资源分享”文件夹中找到(资源序号为tA25和tA26)。资源库链接的获取可以在《autoMBD原创技术文章合集》中找到(见文章开头)。 1 构建Step函数的接口函数 Step函数的集成方法在以往的文章中介绍过数次。在《MBD的Simulink使用技巧④:详解生成代码的结构与代码的生成流程》一文中,详细介绍了模型生成的三个入口函数(Model Entry-Point Functions):
要集成Simulink生成的代码,最基本的方式就是合理调用这些入口函数,一般情况下,Step函数需要周期性被调用。 在文章合集的《第十七篇 把模型嵌入代码》中,详细展示集成Step函数的基本步骤。更多关于集成的细节可以阅读该文章,这里不再赘述。(获取文章合集的方法见文章开头) 这里仅展示最终集成的代码,如下所示(Motor_ISR(void)是周期中断服务函数,在Step函数的前后分别设置输入数据和输出数据): void Motor_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); } 在Simulink中,可以通过存储类(Storage Class)来实现这样的需求。 Tips:autoMBD目前还尚未系统介绍存储类,不了解存储类的读者也不用担心,后续会专门介绍存储类的相关概念。这里把它当作一种控制代码生成的方法即可。 下面以PI控制器示例模型为例,向读者展示如何通过存储类,为输入输出端口构建专门的接口函数。 PI控制器示例模型 - From autoMBD 该模型想必很多读者都比较熟悉了,打开该模型后,首先在APPS中找到Embedded Coder工具,在Embedded Coder标签页中,点击Code Interface下拉菜单中的Individual Element Code Mappings选项,此时Property Inspector窗口出现,如下所示: 打开Embedded Coder工具 - From autoMBD Individual Element Code Mappings - From autoMBD 然后在“Code Mapping - C”的窗口中,找到Inports下的两个输入端口,将两个端口的存储类(Storage Class)设置为“GetSet”,同时配置Headerfile为自己构建的接口函数的头文件名,本例中设置为:
设置输入端口存储类 - From autoMBD 对Outports执行同样的操作,将存储类设置为“GetSet”,将Headerfile设置与输入相同。这里要注意,输出端口的Identifier不能为空,需要设置一个合适的名字,如下所示: 设置输出端口存储类 - From autoMBD 设置完成后,点击“Build”生成代码,此时Step函数如下:
/* Includes for objects with custom storage classes. */ #include 'autoMBD_example_IO.h'
/**************************************** * 文件名 :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); void set_PI_Ctrl (real_T PI_Ctrl);
#endif 2 MATLAB Utilities代码的集成 在有的情况下,模型生成的代码,并不全部来自于模型。 部分代码是由MathWorks官方预先写好的,存放在MATLAB的安装目录下,模型生成的代码只是调用这些代码中的函数来实现功能。 这部分由MathWorks官方预先写好的源代码就是MATLAB Utilities代码。特别是当模型中使用了Simulink的特定功能时,可能会使用到MATLAB Utilities相关代码,例如连续模型求解器、AI算法、图像处理算法等。 读者可以在MATLAB安装目录下的这个位置找到MATLAB Utilities代码:
MATLAB Utilities代码位置 - From autoMBD 下面以连续模型为例,向读者展示如何使用MATLAB Utilities相关代码。 使用到的模型依然是PI控制器模型,不过对该模型进行了修改,将原来的离散积分模块改为了连续积分模块,如下所示: PI控制器连续模型 - From autoMBD 同时配置模型支持连续时间(continuous time),如下所示: 配置模型支持连续时间 - 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 */ }
上述代码中头文件rtw_continuous.h和rtw_solver.h可在MATLAB安装目录下找到源文件。 要使用MATLAB Utilities相关代码,必须在代码中包含相应的头文件;同时在编译时,也要将源文件的路径添加到编译路径中,不然工程找不到相应文件,编译会报错。 3 共用代码的集成 实际开发过程中,我们可能会创建非常多不同的模型。细心的读者可能会发现,文章展示的很多示例模型都会生成相同头文件rtwtypes.h。 该文件内容相同,是共用的代码,多模型开发时只需要生成一个即可。在不同情况下,还可能存在其他的共用代码或文件。 可以通过配置“Shared location”功能,让所有共用代码生成在相同的位置,配置的方法如下所示: 配置Shared Location - From autoMBD 配置好之后,共用代码生成的位置会改到“slprj/ert/_sharedutils/“目录下: 共用代码位置 - From autoMBD 4 生成代码的最终打包导出 文章至此,已经介绍了多个方面的代码集成方法。 但还有一个问题未能解决:生成的代码存储位置比较混乱无序,生成的C代码源文件还和其他无关代码放在一起;特别是当使用了MATLAB Utilities代码和共用代码后,有多个位置都存放了代码。 在Simulink中,提供了代码打包导出的功能,可以将所有生成的代码打包放在一起,包括MATLAB Utilities代码和共用代码。使用方法如下: 代码打包导出 - From autoMBD 生成的Zip压缩包中的内容如下: 压缩包中的代码 - From autoMBD 生成的压缩包中,文件结构层级变得简洁了许多,并且只包含源代码,没有其他任何无关文件,非常方便后续集成开发的使用。 |
|