本文主要是介绍软件集成:Simulink与STM32联合开发,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
本文首先通过一个简单的Simulink模型生成代码,然后将代码copy到一个STM32工程中去编译软件。最后将软件下载到STM32F407中,通过串口输出Simulink计算的结果。
阅读本文需要有一定的STM32开发调试经验和Simulink代码生成经验。关于Simulink代码生成可以参考博主的专栏《Simulink代码生成》。
文章目录
- 1 问题引入
- 2 集成方案
- 3 建模与编程过程
- 3.1 Simulink模型搭建
- 3.2 代码生成配置
- 3.3 STM32工程搭建
- 3.4 STM32的main.c文件修改
- 3.4.1 #include "demo.h"
- 3.4.2 float Demo_Input;
- 3.4.3 demo_initialize()
- 3.4.4 Demo_Input = Demo_Input + 1;
- 3.4.5 demo_step();
- 3.4.6 printf()
- 3.5 软件编译、串口打印
- 4 多模型的集成
- 4.1 多模型集成方案
- 4.2 新增Demo2模型
- 4.3 修改Stm32工程
- 4.4 软件编译、串口打印
- 5 总结与注意点
1 问题引入
博主在《Simulink代码生成》专栏的博客中,通过将模型生成代码,然后研究C代码的逻辑和结构。但是,在汽车ECU软件开发的过程中,代码并不是最终产物,还需要将代码编译成可执行文件,刷入控制器中。过程如下示意图:
如果自己在家学习MBD,想要验证一下自己生成的代码到底能不能在硬件中跑起来,首先就需要一个目标硬件。这个硬件当然不能是公司项目用的TC27X以及刷新工具,因为价格太贵了,只为了学习MBD犯不着花这么大价钱。所以博主选择了创客们比较喜欢的STM32开发板来验证模型开发的结果,只需要一个STM32开发板和ST-LINK下载工具。
最后通过串口验证了Simulink生成的代码能在STM32中正确的跑起来,Simulink与STM32联合开发就算是成功了。
2 集成方案
第3章会通过一个例程,将Simulink生成的代码添加到STM32工程中进行编译。例程的方案如下:
1)在STM32的main.c主函数中定义一个全局变量Demo_Input,作为Simulink模型的输入接口;
2)在Simulink模型demo.slx中建模实现将Demo_Input输入接口乘以2,然后作为Demo_Output输出;
3)在信号线上定义输入和输出的Storage Class,配置Embedded Coder和目标硬件;
4)在main.c主函数中调用Simulink生成的step函数;
5)通过串口打印出Demo_Input和Demo_Output全局变量的数值,观察Simulink的代码是否成功的将输入放大了2倍;
3 建模与编程过程
本章节会详细地讲解博主从零开始建模和编程,直到控制器跑起来,并打印出正确的结果。
3.1 Simulink模型搭建
根据上一章的方案介绍,Simulink模型里面就是简单实现了将输入放大两倍的功能。具体搭建过程如下:
1)打开Simulink模型,建立一个Inport模块、一个Outport模块和一个Gain模块。将Gain模块的放大系数设置为2,并用信号线将三者连接。
2)右键点击Inport和Gain之间的信号线,点击properties,打开该信号线的属性面板。
2)将属性面板中的Signal Name填写为Demo_Input。
3)切换到Code Generation,将Storage Class选为ImportedExtern,然后点击OK。
4)对于输出的信号线也做2~3的操作,但是Storage Class要改成ExportedGlobal。
对于Storage Class可以参考以前的博客《Simulink代码生成: Storage Class配置》,博主不再细说。
5)OK以后可以看到信号线上出现了设置好的名字。
6)双击Inport模块,将Data Type设为single,模型的single类型在代码中对应的就是float类型。
3.2 代码生成配置
紧接着上一个小节。
1)打开Simulink配置,配置离散求解器、代码生成的目标文件,以及目标硬件。
关于Embedded Coder配置和目标硬件配置,这里不再赘述,可以参考博客《Simulink代码生成: Embedded Coder配置》和《Simulink代码生成:目标硬件配置》。
2)Ctrl + B生成代码后,可以简单地看一下接口和step函数。在demo.c文件中先是定义了输出接口Demo_Output变量,然后在step函数中将Demo_Inport乘以2再赋值给Demo_Output。
在demo_private.h头文件中,对输入接口Demo_Inport进行了外部声明。通过这种外部声明,demo.c中就可以引用main.c文件中定义的Demo_Inport。
3)在模型目录下会生成一个demo_ert_rtw文件夹,其中包含了生成的源文件和头文件。这1个源文件和5个头文件会在后文加入到STM32工程之中。
3.3 STM32工程搭建
1)首先从STM32开发板教程中拷贝一个《跑马灯实验》的工程出来,在此基础上修改其中的代码。博主这里用的是正点原子的STM32F407开发板及其配套教程。这里没有自己从头开始写主函数是因为移植stm32的库函数会比较复杂,不是本文的重点内容。
2)打开\HARDWARE文件夹,在其中新建一个demo文件夹,并把Simulink生成的1个源文件和5个头文件拷贝进去。
3)打开USER文件夹下的LED.uvprojx项目文件,把demo.c添加到工程目录中。
4)把demo模型生成的头文件路径添加到环境配置中。
3.4 STM32的main.c文件修改
将main.c文件修改如下
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"#include "demo.h" //包含demo.h头文件才能访问其中的函数和全局变量float Demo_Input; //定义demo.slx模型的输入端口int main(void)
{ Demo_Input = 0; //定义Demo_Input初始值为0delay_init(168); //初始化延时函数 uart_init(115200); //串口初始化波特率为115200demo_initialize(); //调用demo.slx模型的初始化函数while(1){Demo_Input = Demo_Input + 1; //Demo_Input每个周期加1demo_step(); //调用demo.slx模型生成的step函数printf("Demo_Input = %f;Demo_Output = %f;\r\n\r\n",Demo_Input,Demo_Output);//串口打印Demo_Input,Demo_Outputdelay_ms(1000); //延时1000ms}
}
这里对代码做一些简单的解释:
3.4.1 #include “demo.h”
#include "demo.h"是为了让main.c文件能够通过demo的头文件访问到demo.c的函数。因为在后面的代码中,main函数中要调用demo_step函数和Demo_Output全局变量。
3.4.2 float Demo_Input;
在main.c中定义了全局变量Demo_Input,使得demo.c可以通过demo_private.h中访问到。这样的话,Demo_Input全局变量就传递到了demo.c中进行计算了。
3.4.3 demo_initialize()
在while(1)循环之前先调用一下demo_initialize();初始化函数,将Demo_Output初始化为0.
3.4.4 Demo_Input = Demo_Input + 1;
让每个while循环中,Demo_Input的值加1。
3.4.5 demo_step();
调用了step函数,对Demo_Input的数值做乘以2的计算,再赋值给Demo_Output。那么通过模型的放大2倍,Demo_Output应该一直是Demo_Input的两倍。也就是说,Demo_Input的输出值是0,1,2,3…,Demo_Output的输出值是0,2,4,6…。
3.4.6 printf()
printf把输入Demo_Input和输出Demo_Output通过串口打印到电脑上。
3.5 软件编译、串口打印
将STM32工程重新编译,并通过ST-LINK下载到硬件中。
通过串口,可以打印出输入和输出的值:
从图中可以看出,串口打印的输入每隔1秒钟会加1,输出一直是输入的2倍。从而验证了模型的2倍增益效果已经体现在整个STM32工程中了。
4 多模型的集成
4.1 多模型集成方案
在汽车软件开发中,应用层控制策略通常都比较复杂,所以一般会将项目分成很多个子模型进行分布式开发。对于多模型的集成,一般会有两种集成方案:
- 先将多个模型通过模型引用,或者手动组合成一个模型,然后生成一个模型的代码,再拿去编译。
- 将多个模型分别生成代码,然后将各个模型的代码放到一起编译。
博主比较推荐第二种方案,因为完整地生成一个集成式模型的代码,需要花费很多时间,降低了效率。所以本章节主要演示一下如何将多个模型分别生成代码,然后放到STM32项目环境中去编译。
4.2 新增Demo2模型
在第三章地基础上,新增一个demo2模型,将demo模型的输出接口demo_Output再次乘以2,再输出到STM32的main.c文件中。
与demo.slx模型一样,配置好模型的输入输出接口但是接口名称和demo.slx中的不一样,数据类型、Storage Class配置都和demo.slx中完全相同。
然后将模型生成代码,并拷贝到STM32工程中。
4.3 修改Stm32工程
1)首先将demo2.c文件加入Stm32的项目中。
2)修改main.c文件,包括以下几方面。
- 包含demo2.h头文件,以便调用demo2.c中定义的Demo2_Output全局变量;
- 在while循环之前调用demo2_initialize();初始化函数;
- 在while循环中调用demo2_step();的step函数;
- 修改printf函数,把Demo2_Output也打印出来;
对应的main.c如下所示:
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"#include "demo.h" //包含demo.h头文件才能访问其中的函数和全局变量
#include "demo2.h" //包含demo2.h头文件才能访问其中的函数和全局变量
float Demo_Input; //定义demo.slx模型的输入端口int main(void)
{ Demo_Input = 0; //定义Demo_Input初始值为0delay_init(168); //初始化延时函数uart_init(115200); //串口初始化波特率为115200demo_initialize(); //调用demo.slx模型的初始化函数demo2_initialize(); //调用demo2.slx模型的初始化函数while(1){Demo_Input = Demo_Input + 1; //Demo_Input每个周期加1demo_step(); //调用demo.slx模型生成的step函数demo2_step(); //调用demo2.slx模型生成的step函数printf("Demo_Input = %f;Demo_Output = %f;Demo2_Output = %f;\r\n\r\n",Demo_Input,Demo_Output,Demo2_Output);//串口打印Demo_Input,Demo_Output,Demo2_Outputdelay_ms(1000); //延时1000ms}
}
4.4 软件编译、串口打印
将修改后的工程编译并下载到Stm32中,通过串口打印出如下内容:
通过Demo2_Output返回的数值,可以看出新增的demo2.slx也在while循环中不断地运行。
5 总结与注意点
虽然demo模型和STM32工程都比较简单,但是本文在博主的所有系列博客中,是具有重要意义的。
- 首先,本文把MBD的流程从模型到代码,拓展到了模型–>代码–>可执行文件,并且能够刷到硬件中去验证模型的正确性。
- 其次,通过本文的方法,可以把控制策略和寄存器配置剥离开来,通过全局变量作为底层和应用层沟通的桥梁。
- 最后,本文为MBD实战打下了基础,也就是说以后可以通过建模并生成代码去开发遥控小车或者四旋翼飞行器之类的创客制作,而不用去通过C代码实现复杂的控制算法。
本文中的例子源自于博主的工作经验,是汽车软件开发的高度精简版。因此,对于工作中的经验和教训,在本文中也可以很好地体现出来。总结起来有如下的注意点:
- 注意目标硬件要选择正确,因为这背后是该控制器所规定的不同数据类型的字长。
- 不要手动修改模型生成的代码,即使是策略上的一点小的修改,也应该先改模型,再生成代码,否则模型与代码上的管理会非常混乱,或者在复杂的项目中容易导致代码的重大缺陷。
- 输入输出接口非常重要,这方面经常容易出错。另外也可以不用本文的方式定义Storage Class,接口的数据字典管理也可以放在sldd文件中。
- 由于Simulink本身也只是一个工程软件,自身也有bug,因此生成的代码也有可能与模型的策略不一致。但是这是一个概率极低的事件,如果出现了不符合预期的测试结果,还是应该先去寻找建模过程中的逻辑错误。
以上很多概念在博主的其他博客中也都有更详细的说明。
>>返回个人博客总目录
这篇关于软件集成:Simulink与STM32联合开发的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!