本帖最后由 xinyuanliu 于 2020-9-25 13:09 编辑
本文首先通过一个简单的Simulink模型生成代码,然后将代码copy到一个STM32工程中去编译软件。最后将软件下载到STM32F407中,通过串口输出Simulink计算的结果。
阅读本文需要有一定的STM32开发调试经验和Simulink代码生成经验。关于Simulink代码生成可以参考我的专栏《Simulink代码生成》。
文章目录
1 问题引入
博主在《Simulink代码生成》专栏的博客中,通过将模型生成代码,然后研究C代码的逻辑和结构。但是,在汽车ECU软件开发的过程中,代码并不是最终产物,还需要将代码编译成可执行文件,刷入控制器中。过程如下示意图:
如果自己在家学习MBD,想要验证一下自己生成的代码到底能不能在硬件中跑起来,首先就需要一个目标硬件。这个硬件当然不能是公司项目用的TC27X以及刷新工具,因为价格太贵了,只为了学习MBD犯不着花这么大价钱。所以博主选择了创客们比较喜欢的STM32开发板来验证模型开发的结果,只需要一个STM32开发板和ST-LINK下载工具。
最后通过串口验证了Simulink生成的代码能在STM32中正确的跑起来,Simulink与STM32联合开发就算是成功了。
2 集成方案
后文会通过一个例程,将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初始值为0
delay_init(168); //初始化延时函数
uart_init(115200); //串口初始化波特率为115200
demo_initialize(); //调用demo.slx模型的初始化函数
while(1)
{
Demo_Input = Demo_Input + 1; //Demo_Input每个周期加1
demo_step(); //调用demo.slx模型生成的step函数
printf("Demo_Input = %f;Demo_Output = %f;\r\n\r\n",Demo_Input,Demo_Output);//串口打印Demo_Input,Demo_Output
delay_ms(1000); //延时1000ms
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
这里对代码做一些简单的解释:
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 总结
虽然demo模型和STM32工程都比较简单,但是本文在我的所有系列博客中,是具有重要意义的。
首先,本文把MBD的流程从模型到代码,拓展到了模型到代码到可执行文件,并且能够刷到硬件中去验证模型的正确性。
其次,通过本文的方法,可以把控制策略和寄存器配置剥离开来,通过全局变量作为底层和应用层沟通的桥梁。
最后,本文为MBD实战打下了基础,也就是说以后可以通过MBD的方法去开发遥控小车或者四旋翼飞行器之类的创客制作,而不用去通过C代码实现复杂的控制算法。