嵌入式 开发 软件
csdh11 2024-12-09 11:25 24 浏览
提示:文章撰写完成后,可以自动生成目录,如何生成请参考右侧帮助文档。
目录
前言
在嵌入式开发中,利用Simulink的相关功能模块搭建好逻辑流程之后,很重要的一个步骤就是利用代码生成功能将我们搭建好的函数转换成代码,并与嵌入式平台的芯片软件工程进行融合,使得我们开发出来的函数能够在单片机上运行,??从而实现产品的功能需求。
1. 项目创建
本次用Simulink搭建的项目目标产品是车载智能保险丝盒,前两篇文章中的CAN模块、雨刮器模块都是该项目的函数,这些函数通过一个完整的Simulink项目进行组装和管理,函数之间以文件夹的形式,将相关的函数文件统一归类,我的理解是这样建立的工程和编辑代码时在编译器中建立的工程类似。
和在KEIL或者IAR中编译运行一样,simulink工程也可以进行模型测试、依赖检查、运行检查等。版本管理也集成了SVN或者git,简单设置即可实现。我使用SVN进行版本管理,不过一般都是直接复制文件夹进行更新、提交即可。
2. 代码生成 1. 构建模块
各个模块的操作都一样,设置参数和操作。本文以近光模块为例进行说明,主要是因为近光模块功能比较简单,信号量小,逻辑简单,主要信号如下:
输入信号:
1. 点火开关IGN信号
2. 大灯电源信号
3. 近光灯开关信号
4.短路判断信号
5.开路判断信号
6. 消息刷新标志
输出信号:
1.近光继电器控制信号
2. CAN 报文信号
3. 消息发送标志
消息刷新标志和消息发送标志是系统内部判断信号,不作为对外输出,相当于写代码时设置的标志位是局部变量。
功能逻辑结构如下:
总体结构
2. 内部结构
三状态流
函数搭建完成后点击RUN图标,若无报错则证明语法结构正确,满足生成代码的前提条件。至于控制逻辑是否正确,可以通过搭建仪表盘上的指示灯进行仿真,仿真方法请参考上一篇文章《Simulink模型实现汽车雨刮器基本功能并仿真》。
2.设置参数
点击工具栏模型设置的齿轮图标,进入参数设置界面
以本项目中嵌入代码的生成为例,主要需要调整的参数设置如下:
在求解器下,选择求解器选择,类型选择固定步长,求解器选择离散
细节选项fixed-step size设置为0.01,采样步长为0.01s。这个参数和嵌入式工程中函数循环调用的频率有关,以本工程为例,设置为0.01s表示每10ms调用一次该函数。如果不按照设置值进行定时中断,将会影响模块中的定时操作。
代码生成选项中主要有两个参数需要注意,一是System target file选择为ert.tlc,二是Language选择为C语言。
在Report选项中选择Generate Code Report,一般前两项分别是Generate Report和Automatically Open,第三项Web View是可选的。
根据使用的MCU型号,我们需要在Hardware Implementation中选择对应的MCU厂商和内核类型。本工程采用的是NXP,cortexM4核心的S32k148芯片,选择对应的选项即可。这里的选择不像Keil等编译器那么详细,主要就是确定MCU的一些数据类型定义的位数,是否支持长整型等,点击detail可以查看详细信息。
3.自动生成
完成以上步骤后,就可以生成代码了,首先在APP选项中选择内嵌的coder功能卡,如下图:
选择进入嵌入式编码器APP
单击“Build”按钮生成嵌入式 C 代码
代码生成之后会自动出现在右侧,方便我们查看。
因为我们勾选了生成报告选项,所以会出现一个报告对话框,在报告中可以查看此模块生成的代码的相关内容,包括代码预览,模型信息,内存使用情况等信息。
在生成的代码中我们可以看到很多文件,但是主要用到的是LowBeam.c和LowBeam.h这两个文件,除了ert_main之外的其他文件大家可以忽略,直接复制到我们自己的代码工程目录下即可。我们不需要ert_main,使用我们自己的main函数,但是大家可以参考ert_main中函数函数的调用方法。
3. 代码集成
按照上面的方法自动生成代码之后,还差最后一步就能成功使用了,就是把这些文件添加到工程中,并与驱动代码进行匹配。针对这个芯片,其他同事已经进行了平台开发,并提供了相关接口,包括ADC、GPIO、CAN等资源都已经封装好了,我们需要做的就是接口对应。以近光功能模块为例,LowBeam.c文件几乎可以忽略不计,它是Matlab软件根据我们搭建的Simulink逻辑自动生成的代码,可读性不是很强,但是功能肯定是实现了的,我们会对应好接口,只关注LowBeam.h文件就行。
在.h文件中找到以下代码:
/* External inputs (root inport signals with default storage) */
typedef struct {
IGN IG; /* '/IGN' */
uint8_T Front_Power_Supply; /* '/Front_Power_Supply' */
uint8_T Work_Cmd; /* '/Work_Cmd' */
uint8_T IS_Opencircuit; /* '/DILAMP_Open' */
uint8_T IS_Shortcircuit; /* '/DILAMP_Short' */
uint8_T New_Can_Flag; /* '/New_Can_Flag' */
} ExtU_LowBeam_T;
/* External outputs (root outports fed by signals with default storage) */
typedef struct {
uint8_T Low_Beam_On; /* '/Low_Beam_On' */
uint8_T Can_Send_Flag; /* '/Can_Send_Flag' */
CAN_MESSAGE_BUS CAN_Msg_HighBeam; /* '/CAN_Msg_HighBeam' */
} ExtY_LowBeam_T;
ExtU_LowBeam_T表示输入信号的结构体,ExtY_LowBeam_T表示输出信号的结构体,均以全局变量的形式赋值。
我们自己写一个赋值函数:
/*近光灯模块*/
LowBeam_U.New_Can_Flag = (uint8_T)can_status_store.bit.store_Newflag_LowBeam;
LowBeam_U.Front_Power_Supply = (FrontLampTogether_Y.Left_Together_ON & FrontLampTogether_Y.Right_Together_ON);
LowBeam_U.Work_Cmd = CAN_Receive_Model_Y.LowBeam_WorkCmd;
LowBeam_U.IG = CAN_Receive_Model_Y.IGN_Sig;
LowBeam_U.IS_Opencircuit = (Output_Current_U.DILamp_FL_DRV < LOWBEAM_CURRENT_MIN) ||
(Output_Current_U.DILamp_FR_DRV < LOWBEAM_CURRENT_MIN) ? 1: 0;
LowBeam_U.IS_Shortcircuit = (Output_Current_U.DILamp_FL_DRV > LOWBEAM_CURRENT_MAX) ||
(Output_Current_U.DILamp_FR_DRV > LOWBEAM_CURRENT_MAX) ? 1: 0;
涉及到一些其他模块的信号,但这些都是独立的全局变量,可以直接用于逻辑判断。
输出信号:
if(LowBeam_Y.Low_Beam_On == 1)
{
HSD_SET_LEFT_LOWBEAM_ON; //打开左近光灯
HSD_SET_RIGHT_LOWBEAM_ON; //打开又近光灯
}
else
{
HSD_SET_LEFT_LOWBEAM_OFF; //关闭左近光灯
HSD_SET_RIGHT_LOWBEAM_OFF; //关闭右近光灯
}
输出函数已经封装好了,这里直接调用就可以。
对于总线信号,编写一个赋值函数:
void set_can_LowBeam_output(void)
{
if(LowBeam_Y.Can_Send_Flag)
{
if(can_send_output_set(CAN_INSTANCE_SET, &LowBeam_Y.CAN_Msg_HighBeam) == CAN_EOK) //发送报文
{
LowBeam_Y.Can_Send_Flag = 0;
}
}
}
这里我们直接使用发送消息的接口函数can_send_output_set()。
最后在main函数中调用该函数,我们可以在生成的LowBeam.h文件中看到三个函数声明:
/* Model entry point functions */
extern void LowBeam_initialize(void);
extern void LowBeam_step(void);
extern void LowBeam_terminate(void);
实际上,我们唯一需要的函数是 LowBeam_step(void),它可以在主函数的主循环中每 10ms 调用一次:
if(Get1MsTickInterval(modeldely) >= 10)
{
modeldely = Get1MsTickVal();
LowBeam_step(); //近光灯
}
其它功能模块可以按照同样的方法在近光灯函数下面添加_step函数。
总结
以上就是我们今天要讲的内容,本文简单介绍了如何通过Simulink搭建的逻辑框图生成嵌入式代码,并将生成的代码添加到现有的MCU项目中,实现近光灯模块的功能。
当然,自动代码生成需要在项目调用时多一层封装,占用一定的资源。而且对于一些精通业务的工程师来说,一些简单的手写逻辑,用很简洁的C代码量就可以实现同样的功能。但是我们使用自动代码生成技术也有相应的优势,比如当功能逻辑比较复杂时,我们可以更清晰地理清思路,使用simulink可以很方便地进行功能仿真。验证功能步骤可以放在代码生成之前,而手写代码必须在编译通过后,使用硬件设备进行功能测试后,才能烧录到sample中。这样,使用simulink进行功能仿真和自动代码生成,就省去了重复烧录的过程。
当然,Simulink 的代码生成功能远不止本文所提到的这些,作者会在项目中进行探索,并在后续的文章中与大家进一步探讨。
相关推荐
- 探索Java项目中日志系统最佳实践:从入门到精通
-
探索Java项目中日志系统最佳实践:从入门到精通在现代软件开发中,日志系统如同一位默默无闻却至关重要的管家,它记录了程序运行中的各种事件,为我们排查问题、监控性能和优化系统提供了宝贵的依据。在Java...
- 用了这么多年的java日志框架,你真的弄懂了吗?
-
在项目开发过程中,有一个必不可少的环节就是记录日志,相信只要是个程序员都用过,可是咱们自问下,用了这么多年的日志框架,你确定自己真弄懂了日志框架的来龙去脉嘛?下面笔者就详细聊聊java中常用日志框架的...
- 物理老师教你学Java语言(中篇)(物理专业学编程)
-
第四章物质的基本结构——类与对象...
- 一文搞定!Spring Boot3 定时任务操作全攻略
-
各位互联网大厂的后端开发小伙伴们,在使用SpringBoot3开发项目时,你是否遇到过定时任务实现的难题呢?比如任务调度时间不准确,代码报错却找不到方向,是不是特别头疼?如今,随着互联网业务规模...
- 你还不懂java的日志系统吗 ?(java的日志类)
-
一、背景在java的开发中,使用最多也绕不过去的一个话题就是日志,在程序中除了业务代码外,使用最多的就是打印日志。经常听到的这样一句话就是“打个日志调试下”,没错在日常的开发、调试过程中打印日志是常干...
- 谈谈枚举的新用法--java(java枚举的作用与好处)
-
问题的由来前段时间改游戏buff功能,干了一件愚蠢的事情,那就是把枚举和运算集合在一起,然后运行一段时间后buff就出现各种问题,我当时懵逼了!事情是这样的,做过游戏的都知道,buff,需要分类型,且...
- 你还不懂java的日志系统吗(javaw 日志)
-
一、背景在java的开发中,使用最多也绕不过去的一个话题就是日志,在程序中除了业务代码外,使用最多的就是打印日志。经常听到的这样一句话就是“打个日志调试下”,没错在日常的开发、调试过程中打印日志是常干...
- Java 8之后的那些新特性(三):Java System Logger
-
去年12月份log4j日志框架的一个漏洞,给Java整个行业造成了非常大的影响。这个事情也顺带把log4j这个日志框架推到了争议的最前线。在Java领域,log4j可能相对比较流行。而在log4j之外...
- Java开发中的日志管理:让程序“开口说话”
-
Java开发中的日志管理:让程序“开口说话”日志是程序员的朋友,也是程序的“嘴巴”。它能让程序在运行过程中“开口说话”,告诉我们它的状态、行为以及遇到的问题。在Java开发中,良好的日志管理不仅能帮助...
- OS X 效率启动器 Alfred 详解与使用技巧
-
问:为什么要在Mac上使用效率启动器类应用?答:在非特殊专业用户的环境下,(每天)用户一般可以在系统中进行上百次操作,可以是点击,也可以是拖拽,但这些只是过程,而我们的真正目的是想获得结果,也就是...
- Java中 高级的异常处理(java中异常处理的两种方式)
-
介绍异常处理是软件开发的一个关键方面,尤其是在Java中,这种语言以其稳健性和平台独立性而闻名。正确的异常处理不仅可以防止应用程序崩溃,还有助于调试并向用户提供有意义的反馈。...
- 【性能调优】全方位教你定位慢SQL,方法介绍下!
-
1.使用数据库自带工具...
- 全面了解mysql锁机制(InnoDB)与问题排查
-
MySQL/InnoDB的加锁,一直是一个常见的话题。例如,数据库如果有高并发请求,如何保证数据完整性?产生死锁问题如何排查并解决?下面是不同锁等级的区别表级锁:开销小,加锁快;不会出现死锁;锁定粒度...
- 看懂这篇文章,你就懂了数据库死锁产生的场景和解决方法
-
一、什么是死锁加锁(Locking)是数据库在并发访问时保证数据一致性和完整性的主要机制。任何事务都需要获得相应对象上的锁才能访问数据,读取数据的事务通常只需要获得读锁(共享锁),修改数据的事务需要获...
- 一周热门
- 最近发表
- 标签列表
-
- mydisktest_v298 (34)
- document.appendchild (35)
- 头像打包下载 (61)
- acmecadconverter_8.52绿色版 (39)
- word文档批量处理大师破解版 (36)
- server2016安装密钥 (33)
- mysql 昨天的日期 (37)
- parsevideo (33)
- 个人网站源码 (37)
- centos7.4下载 (33)
- mysql 查询今天的数据 (34)
- intouch2014r2sp1永久授权 (36)
- 先锋影音源资2019 (35)
- jdk1.8.0_191下载 (33)
- axure9注册码 (33)
- pts/1 (33)
- spire.pdf 破解版 (35)
- shiro jwt (35)
- sklearn中文手册pdf (35)
- itextsharp使用手册 (33)
- 凯立德2012夏季版懒人包 (34)
- 反恐24小时电话铃声 (33)
- 冒险岛代码查询器 (34)
- 128*128png图片 (34)
- jdk1.8.0_131下载 (34)