默认情况下,嵌入式编码器®软件生成独立的不需要外部实时执行或操作系统的可执行程序。独立程序需要最小的修改以适应目标硬件。独立程序架构支持使用单个或多个采样率的模型执行。金宝app
要生成独立程序:
选择模型配置参数生成一个示例主程序.这使目标操作系统菜单。
来自目标操作系统菜单,选择赤膊表现
.
生成的代码。
根据这些因素,为多管模型生成不同的代码:
模型是在单任务模式还是多任务模式下执行。
是否正在生成可重用的代码。
这些因素可以影响生成代码中使用的调度算法,并且在某些情况下,在某些情况下会影响模型入口点函数的API。以下部分讨论了这些变体。
独立程序的核心是主循环。在每次迭代时,主循环执行背景或NULL任务并检查终止条件。
主循环由计时器周期性中断。功能RT_ONESTEP.
安装为定时器中断服务例程(ISR),或者从每个时钟步骤中的计时器ISR调用。
执行驱动程序,RT_ONESTEP.
,序列调用
职能。运作模型
_台阶RT_ONESTEP.
这取决于生成模型是单速率还是多速率。在单一费率模型中,RT_ONESTEP.
只需打电话来
函数。在多速率模型中,模型
_台阶RT_ONESTEP.
优先顺序并根据运行的速率执行块的执行。
以下伪代码显示主程序的执行。
main(){初始化(包括安装RT_ONESTEP作为实时时钟的中断服务例程)初始化和启动定时器硬件启用中断(不发生错误)和(时间<最终时间)后台任务重新禁用中断(禁用RT_ONESTEP)执行)完成任何后台任务关机}
伪代码是用于驱动模型的利用程序的设计。主程序只部分实现了这个设计。你必须根据你的规格进行修改。
本节介绍您在主要程序模块的生产版本中所做的最小修改以实现安全性程序。
称呼
.模型
_initialize
初始化特定于目标的数据结构和硬件,如adc或dac。
安装RT_ONESTEP.
作为计时器ISR。
初始化定时器硬件。
启用计时器中断并启动计时器。
Rtmodel.
不是在有效状态之前
被称为。直到才开始提供定时器中断的服务模型
_initialize
被称为。模型
_initialize
可选地,在主循环中插入后台任务调用。
在主循环的终止上(如适用):
禁用计时器中断。
执行特定于目标的清理,例如归零DAC。
检测和处理错误。注意,即使您的程序被设计为无限期地运行,您也可能需要处理严重的错误条件,例如定时器中断溢出。
您可以使用宏rtmGetErrorStatus
和地鼠
检测和信号错误。
运作RT_ONESTEP.
取决于
您的模型是单费率还是多费率。在单费率模型中,模型中所有块的采样次数相同,模型固定步长相同。当样本次数和步长不满足这些条件时,称为多速率模型。
您的模型求解器模式(单一任务
相对多任务
)
嵌入式实时系统目标文件的允许求解器模式总结单速率和多速率模型的允许求解器模式。请注意,仅限单速率模型单一任务
允许求解器模式。
嵌入式实时系统目标文件的允许求解器模式
模式 | 单速率 | 多速率 |
---|---|---|
|
允许 |
允许 |
|
不允许 |
允许 |
|
允许 (默认为 |
允许 (默认为 |
生成的代码RT_ONESTEP.
(以及相关的时序数据结构和支持功能)被定制到模型中的速率和求解器模式。金宝app以下部分讨论了每个可能的案例。
单速率模型的唯一有效解算器模式是单一任务
.这种型号运行在“单费率”操作。
以下伪代码显示了设计RT_ONESTEP.
在单速率计划中。
启用"rt_OneStep" (timer)中断Model_Step()——时间步长结合output,logging,update}
对于单速率案例,生成的
功能是模型
_台阶
空白模型_step(空白)
单速率RT_ONESTEP.
旨在执行
在单个时钟周期内。要强制执行此时间约束,模型
_台阶RT_ONESTEP.
维护并检查计时器溢出标志。在条目上,禁用计时器中断,直到检查过run标志和其他错误条件。如果过度标志很清楚,RT_ONESTEP.
设置标志,并启用计时器中断进行。
只有从成功返回时,才会清除溢出标志
.因此,如果模型
_台阶RT_ONESTEP.
在完成之前是否被再次中断
,通过超支标志检测到reinteruption。模型
_台阶
重新进行RT_ONESTEP.
是一个错误条件。如果检测到这种情况RT_ONESTEP.
引发错误并立即返回。(请注意,如果要以不同方式处理条件,则可以更改此行为。)
注意设计RT_ONESTEP.
假设之前禁用中断RT_ONESTEP.
叫做。RT_ONESTEP.
在检查中断溢出标志之前,应不间断。
在多速率多任务系统中,代码生成使用优先级的抢先的多任务方案来执行模型中的不同样本速率。
以下伪代码显示了设计RT_ONESTEP.
在多轨道多任务程序中。
rt_OneStep() {Check for base-rate interrupt overrun Enable "rt_OneStep" interrupt Determine which rates need to run this time step Model_Step0()——run base-rate time step code for N=1:NumTasks-1——iterate over sub-rate tasks If (sub-rate task N is scheduled)检查子速率中断溢出Model_StepN() - 运行子速率时间步骤代码ENDIF ENDFOR}
任务标识符。将具有不同采样率的块的执行被分解为任务。在给定采样率执行的每个块都被分配了一个任务标识符(t
),它将其与此速率执行的任务相关联。哪里有NumTasks
在系统中的任务,任务标识符的范围为0 ..NumTasks
-1.
基率和次级任务的优先级。任务按速率降序排列优先级。这基率任务是以系统中最快的速率运行的任务(硬件时钟速率)。基息率任务具有最优先级(t
0). 下一个最快的任务(t
1) 具有次高优先级,依此类推到最慢、优先级最低的任务(t
NumTasks
-1)。
调用较慢的任务,以基本速率的倍数运行subrate任务。
速率分组和速率特定的model_step函数。在单速率模型中,块输出计算在单个函数内执行,
.对于多速率,多任务型号,代码生成器尝试使用不同的策略。这个策略被称为率分组. 速率分组生成单独的模型
_台阶
函数用于基础费率任务和模型中的每个子任务。这些函数的函数命名约定是模型
_台阶
模型_台阶N
在哪里
是任务标识符。例如,对于名为N
my_model.
有三种速率,生成以下函数:
void my_model_step0(void);void my_model_step1(void);void my_model_step2(void);
每一个
函数执行块共享模型
_台阶N
t
;换句话说,在任务中执行的块代码N
是分组成关联的N
函数。模型
_台阶N
调度model_stepN执行。每一个时钟滴答作响,RT_ONESTEP.
维护调度计数器事件标志每个子任务。计数器实现为TaskCounter.
数组索引上t
.事件标志实现为索引索引t
.
子速率的调度计数器和任务标志由RT_ONESTEP.
.调度计数器基本上是时钟速率分频器,其计算与每个子速率任务相关的采样周期。交换数据的一对任务以更快的速率维护交互标志。任务交互标志表明,计划运行快速和慢速任务。
事件标志表示是否计划执行给定的任务。RT_ONESTEP.
基于任务计数器维护事件标志,该任务计数器由模型的主程序模块中的代码维护。当计数器指示任务的示例周期已经过时,主代码将为该任务设置事件标志。
每次调用,RT_ONESTEP.
更新其调度数据结构并执行基本速率任务(RT_ONESTEP.
呼叫
因为基本速率任务必须在每个时钟步骤上执行。然后,模型
_step0RT_ONESTEP.
迭代中的调度标志t
秩序,无条件地调用
对于所有标志的任务。任务是按优先级顺序执行的。模型
_台阶N
抢占。注意设计RT_ONESTEP.
假设之前禁用中断RT_ONESTEP.
叫做。RT_ONESTEP.
在检查基本速率中断溢出标志之前,应不间断(参见上面的伪电机)。
使用的事件标志数组和循环变量RT_ONESTEP.
存储为本地(堆栈)变量。因此,RT_ONESTEP.
是可重入的。如果RT_ONESTEP.
重新进行,更高优先级任务抢先较低的优先任务。从中断返回后,较低的优先级任务以先前的预定顺序恢复。
被检测。多速率RT_ONESTEP.
还要维护定时器溢出标志数组。RT_ONESTEP.
通过与单速率相同的逻辑,检测每个任务的计时器超限RT_ONESTEP.
.
如果您已经开发了多速率s函数,或者您使用了定制的静态主程序模块,请参见速率分组合规性和兼容性问题有关如何调整代码以实现速率分组兼容性的信息。这种调整让您的多速率、多任务模型生成更高效的代码。
在多速率单任务程序中,根据定义,模型中的样本时间必须是模型固定步长的整数倍。
在多型单次任务程序中,块以不同的速率执行,但在相同的任务标识符下执行。运作RT_ONESTEP.
在这种情况下,是多轨道多任务操作的简化版本。不使用速率分组。唯一的任务是基本速率任务。因此,只有一个
函数已生成:模型
_台阶
空白模型_step(空白)
每一个时钟滴答作响,RT_ONESTEP.
检查超限标志和呼叫
.多型单任务程序的调度函数是模型
_台阶Rate_scheduler.
(而不是Rate_Monotonic_Scheduler.
)。调度程序在每个时钟刻度上维护调度计数器。模型中的每个采样率有一个计数器。计数器以阵列(在TID上索引)实现定时
内部结构Rtmodel.
.
计数器是时钟速率分隔器,其计算与每个子级任务相关的采样周期。当计数器指示经过给定速率的采样周期时,Rate_scheduler.
清除柜台。此条件表示以此速率运行的块应在下次调用上执行
,负责检查计数器。模型
_台阶
RT_ONESTEP.
不需要大量修改。在检查过度标记和错误条件后,唯一必需的修改是可重新启用中断。如果适用,您也应该
在条目和退出时保存并恢复FPU语境RT_ONESTEP.
.
在调用之前设置与基本速率相关的模型输入
.模型
_step0
在调用后获取与基本速率相关的模型输出
.模型
_step0
如果你修改RT_ONESTEP.
要在每个基本速率模型步骤之后从连续输出端口读取值,请参阅下面的相关警告指南。
在多速率,多任务模型中,在调用之前设置与子元件相关联的模型输入
在子源循环中。模型
_台阶N
在多速率、多任务模型中,在调用后获取与子对象关联的模型输出
在子源循环中。模型
_台阶N
评论in.RT_ONESTEP.
表示添加代码的位置。
在多重速率的RT_ONESTEP.
,您可以通过展开来提高性能为了
和而
循环。
此外,您可以选择修改溢出行为,以便在错误恢复完成后继续执行。
还遵守以下警示指南:
您不应修改计数器,事件标志或其他时序数据结构的方式RT_ONESTEP.
,或从中呼唤的功能RT_ONESTEP.
.这RT_ONESTEP.
时序数据结构(包括Rtmodel.
)和逻辑对生成的程序的操作至关重要。
如果您在每个基本速率模型步骤后定制了主程序模块以读取模型输出,请注意选择模型配置参数金宝app支持:连续时间和单输出/更新功能一起可以导致从输出值读取主要
使连续输出端口与模型记录数据中的相应输出值略有不同。这是因为,虽然记录数据是主要时间步的输出快照,但从主要
在基率模型步骤可能反映中间的次要时间步长之后。要消除差异,请分别为生成的输出和更新功能(清除单输出/更新功能参数)或在连续输出端口之前放置零阶保持块。
如果在每次调用模型步骤功能之前未设置模型输入,则可以从模拟和记录的垫片文件结果之间观察来自模拟和记录的垫子文件之间的不匹配。在生成的示例主程序中,以下注释显示了设置输入和使用代码汇总模型的位置:
/*在这里设置模型输入*/ /*步骤模型*/
如果您的模型应用信号重用并且您正在使用matfilelogging.
为了将模拟结果与生成的代码进行比较,请修改RT_ONESTEP.
按照这些注释的指示,在每个时间步骤中编写模型输入。或者,你可以选择SIL或PIL方法验证。
在大多数情况下,部署生成代码的最简单策略是使用生成示例主程序选项生成ert_main.c.
或.cpp.
模块(见生成一个独立的程序)。
但是,如果你转身生成一个示例主程序选项off,您可以使用静态主模块作为开发嵌入式应用程序的示例或模板。MathWorks提供的静态主模块®包括:
- 金宝app支持matlabroot
/ rtw / c / src /共同/rt_main.c不可用的功能
包装代码接口。
- 金宝app支持matlabroot
/ rtw / c / src /共同/RT_Malloc_main.c.可重复使用的功能
包装代码接口。您必须选择模型配置参数使用动态内存分配进行模型初始化并设置参数将根级I/O传递为到模型数据结构的一部分
.
- 金宝app支持matlabroot
/ rtw / c / src /共同/rt_cppclass_main.cppC ++类
包装代码接口。
静态主模块不是生成代码的一部分;它是作为您自定义修改的基础提供的,并用于模拟。如果您的现有应用程序依赖于静态ert_main.c.
(在R2012B之前发布),rt_main.c
那RT_Malloc_main.c.
, 或者rt_cppclass_main.cpp
,您可能需要继续使用静态主程序模块。
使用静态主模块开发应用程序时,应将模块复制到工作文件夹并在进行修改之前重命名。例如,您可以重命名rt_main.c
到
.此外,您必须修改模板Makefile或Toolchain设置,以使构建过程创建相应的对象文件,例如模型
_rt_main.c
(在Unix上®那模型
_rt_main.obj.
),在构建文件夹中。模型
_rt_main.o
静态主模块包含
RT_ONESTEP.
,定时器中断服务例程(ISR)。RT_ONESTEP.
呼叫
执行模型的一个时钟周期的处理。模型
_台阶
骨骼主要
函数。提供,主要
仅在模拟中有用。你必须修改主要
用于实时中断驱动执行。
对于单速仪,操作RT_ONESTEP.
并且主函数在静态主模块中基本相同,因为它们处于自动生成的版本中将生成的独立可执行程序部署到目标硬件.然而,对于multirate、多任务模型,静态代码和生成代码略有不同。下一节将描述这种情况。
基于ERT目标的目标有时使用静态主模块并禁止使用生成一个示例主程序选项。这样做是因为将目标特定的修改添加到静态主模块中,如果重新生成主程序,则不会保留这些修改。
您的静态主模块可能会或可能不会兼容速率分组
职能。如果您的主模块基于静态模型
_台阶N
rt_main.c
那RT_Malloc_main.c.
, 或者rt_cppclass_main.cpp
模块,它不使用特定的速率
函数调用。它使用旧式模型
_台阶N
函数,传入一个任务标识符:模型
_台阶
空白模型_步骤(国际工贸署);;
默认情况下,当生成一个示例主程序选项关闭时,ERT目标产生
“包装纸”为多速率,多任务型号。包装器的目的是接口特定速率模型
_台阶
函数到旧式呼叫。包装代码会调度模型
_台阶N
打电话给A.模型
_台阶N
开关
声明,如下例所示:
void mymodel_step(int_T tid) /*采样时间:*/ {switch(tid) {case 0: mymodel_step0();打破;案例1:mymodel_step1();打破;案例2:mymodel_step2();打破;默认值:休息;}}
以下伪代码显示了如何RT_ONESTEP.
呼叫
从静态主程序在多速率,多任务模型。模型
_台阶
rt_OneStep(){检查基本速率中断溢出启用“rt_OneStep”中断确定哪些速率需要运行此时间步长ModelStep(tid=0)--N=1的基本速率时间步长:NumTasks-1--迭代子速率任务检查子速率中断溢出如果(子速率任务N已调度)ModelStep(tid=N)--子速率时间步长EndIf EndFor}
您可以使用TLC变量评分率达图斯特普汇款
要指定仅生成基于速率的步函数,而无需包装函数。如果您的目标调用速率分组兼容
直接函数,设置模型
_台阶N
评分率达图斯特普汇款
到1
.在这种情况下,不会生成包装函数。
你应该设置评分率达图斯特普汇款
在...之前%包括“codegenentry.tlc”
语句在系统目标文件中。或者,您可以设置评分率达图斯特普汇款
在你的target_settings.tlc.
文件。
与生成的ert_main.c.
或.cpp.
,您应该对主循环进行一些修改RT_ONESTEP.
.看到主程序修改指南和修改RT_Onestep的指南.
此外,您应该更换RT_ONESTEP.
使用后台任务调用或null语句调用主循环。
您可能需要做的其他修改是
如果适用,请遵循关于添加读/写模型I / O和保存/恢复FPU上下文的代码的代码中的评论。
如果你修改rt_main.c
那RT_Malloc_main.c.
, 或者rt_cppclass_main.cpp
从每个基率模型步骤后从连续输出端口读取值,请参阅相关的警告指南修改RT_Onestep的指南.
当生成一个示例主程序选项已关闭,rtmodel.h
生成以在主模块和生成的模型代码之间提供接口。如果您创建自己的静态主程序模块,通常会包含rtmodel.h
.
或者,您可以抑制生成rtmodel.h
,并包括
直接在您的主模块中。抑制生成模型
.hrtmodel.h
,在你的系统目标文件中使用以下语句:
%分配autobuild procedure = 0
如果你已经通过了需要终止功能选项,在您的产品版本中删除或注释掉以下内容rt_main.c
那RT_Malloc_main.c.
, 或者rt_cppclass_main.cpp
:
这#如果TERMFCN……
编译时错误检查
调用model_terminate.
为了rt_main.c
(不适用于rt_cppclass_main.cpp
):如果你这样做不想要组合输出和更新功能,清除模型配置参数单输出/更新功能并在生产版本中进行以下更改rt_main.c
:
代替打电话model_step.
呼叫呼叫model_output.
和model_update.
.
去除那个#if oneestepfcn ...
错误检查。
静态rt_main.c
模块不支持金宝app可重复使用的功能
代码接口打包。如果可重复使用的功能
代码接口包装被非法使用。
#如果多实例代码==1
如果您正在使用静态主程序模块,并且您的模型被配置为可重复使用的功能
代码接口封装,但模型配置参数使用动态内存分配进行模型初始化清除,模型实例数据必须通过调用主代码静态或动态分配。必须在顶级实时模型数据结构中设置指向各个模型数据结构(例如块IO,dwork和参数)。
为了支金宝app持主要修改,构建过程会根据模型的数据需求,将以下实时模型(RTM)宏的子集生成到
.模型
.h
RTM宏观语法 | 描述 |
---|---|
RTMFTERBLOCKIO(RTM) |
获取块I / O数据结构 |
RTMSetBlockio(RTM,Val) |
设置块I/O数据结构 |
RTMFTCONTSTATE(RTM) |
获取连续状态数据结构 |
RTMsetContstates(RTM,Val) |
设置连续状态数据结构 |
rtmGetDefaultParam(rtm) |
获取默认参数数据结构 |
RTMSetDefaultParam(RTM,Val) |
设置默认参数数据结构 |
rtmGetPrevZCSigState (rtm) |
获取以前的过零信号状态数据结构 |
val rtmSetPrevZCSigState (rtm) |
设置先前过零信号状态数据结构 |
rtmGetRootDWork (rtm) |
获取DWork数据结构 |
RTMSetrootdwork(RTM,Val) |
设置DWork数据结构 |
RTMFTU(RTM) |
获取根输入数据结构(当根部输入作为模型数据结构的一部分传递时) |
Rtmsetu(RTM,Val) |
设置根输入数据结构(当根输入作为模型数据结构的一部分传递) |
RTMFRY(RTM) |
获取根输出数据结构(当根部输出作为模型数据结构的一部分传递时) |
RTMsety(RTM,Val) |
设置根输出数据结构(当根输出作为模型数据结构的一部分传递时) |
在静态主程序中使用这些宏来访问RTM数据结构中的各个模型数据结构。例如,假设示例模型rtwdemo_reusable
配置了可重复使用的功能
包装代码界面,使用动态内存分配进行模型初始化清除,将根级I/O传递为设置个人论据
,优化窗格中选择删除根级I / O零初始化清除。构建模型生成以下模型数据结构和模型入口点rtwdemo_.h
:
/*块状态(自动存储)为系统'<根>' */ typedef struct {real_T Delay_DSTATE;/* '/Delay' */} D_Work;/* struct Parameters_ {real_T k1; /* struct Parameters_ {real_T k1;/*变量:k1 *引用:' /增益' */};/*模型入口点函数*/ extern void rtwdemo_reusable_initialize(RT_MODEL *const rtM, real_T *rtU_In1, real_T *rtU_In2, real_T *rtY_Out1);extern void rtwdemo_reusable_step(RT_MODEL *const rtM, real_T rtU_In1, real_T rtU_In2, real_T *rtY_Out1);
此外,如果模型配置参数生成一个示例主程序未选择模型,rtwdemo_.h
包含RTM宏的定义RTMFETDEFAULTPARAM.
那RTMSetDefaultParam.
那rtmGetRootDWork
,rtmSetRootDWork
.
此外,供参考,生成的rtmodel.h
文件包含一个带有初始值的参数定义示例(非执行代码):
#if 0 / *示例参数数据定义与初始值* /静态参数rtp = {2.0 / *变量:k1 *由以下命令引用:'/ gain' * /};/ *可修改的参数* / #endif
在静态主文件的定义部分,可以使用以下代码静态地分配实时模型数据结构和参数rtwdemo_reusable
模型:
静态RT_MODEL RTM_;静态RT_MODEL * CONST RTM =&RTM_;/ *实时模型* /静态参数rtp = {2.0 / *变量:k1 *由以下命令引用:'/ gain' * /};/ *可修改的参数* /静态d_work RTDWork;/ *可观察状态* / / *' / in1'* /静态real_t rtu_in1;/ *' / in2'* /静态real_t rtu_in2;/ *' / out1'* / static real_t rty_out1;
在主函数的主体中,您可以使用以下RTM宏呼叫在实时模型数据结构中设置模型参数和DWORK数据:
INT_T MAIN(INT_T ARGC,CONST CHAR * ARGV []){... / *将模型数据到RTM * / RTMSETDEFAULTPAULAULPAULTPAULEDPAULAUL(RTM,&RTP);Rtmsetrootdwork(RTM,&RTDORK);/ *初始化模型* / RTWDEMO_REUSABLE_INITIALIZE(RTM,RTU_IN1,&RTU_IN2,&RTY_OUT1);...}
按照类似的方法设置模型数据的多个实例,其中每个实例的实时模型数据结构都有自己的数据。特别是参数结构(rtp.
)对于每个实例,都应该初始化为所需的值,或者作为rtp.
数据定义或运行时。
当生成一个示例主程序选项关闭,代码生成产生略微不同的速率分组代码,用于与较旧的静态兼容ert_main.c.
模块。看到速率分组和静态主程序有关详细信息。
内置的仿真软件金宝app®块以及DSP系统工具箱™ 块,符合生成速率分组代码的要求。但是,用户编写的多速率内联S函数可能不符合速率分组。不兼容的块生成效率较低的代码,但在其他方面与速率分组兼容。要充分利用速率分组的效率,必须将多速率内联S函数升级为完全符合速率分组。您应该升级TLC S函数实现,如本节所述。
使用不兼容的多速率块生成速率分组代码会生成死代码。这可能会导致两个问题:
降低代码效率。
在编译时发出的警告消息。当死代码在初始化之前引用临时变量时,会引起此类警告。因为死代码不会运行,所以这个问题不会影响生成代码的运行时行为。
要使S函数速率分组符合标准,您可以使用以下TLC函数来生成ModelOutputs
和modelupdate.
代码,分别:
OutputsForTID(block, system, tid) UpdateForTID(block, system, tid)
下面的代码清单演示了不进行速率分组(清单1)和进行速率分组(清单2)的输出计算生成。
这t
参数是一个任务标识符(0..NumTasks-1
)。
只有代码守卫t
传递到opportsfortid.
是生成的。这如果(%%
测试未在中使用opportsfortid.
.
生成速率分组代码时,opportsfortid.
和/或UpdateForTID
在代码生成期间被调用。生成非速率分组代码时,输出
和/或更新
叫做。
在符合速率分组的代码中,顶级输出
和/或更新
函数调用opportsfortid.
和/或UpdateForTID
每次速率的功能(t
)涉及块。返回的代码opportsfortid.
和/或UpdateForTID
必须由相应的守护吗t
警卫:
如果(%%)
如清单2所示。
清单1:输出代码生成无需速率分组
%% multirate_blk.tlc%实现“multior_blk”“c”%%功能:mdloutputs ===================================================== %%摘要:%% %%计算两个输出(由%%指定的参数抽取的输入信号)。抽取是由采样时间来处理的。%%只有在块被启用时才执行抽取。%%每个端口都有不同的速率。%% %%注意,应该真正保护启用的使用,以使%%每个任务都有自己的启用状态。在本例中,立即启用%%,这可能是也可能不是预期的行为。%%%函数输出(块,系统)输出/ *%块:% * /%分配enable = libblockinputsignal(0,“,”,0){int_t *已启用=&% ;%如果libgetsfcntidtype(“inputportidx0”)==“连续”%%只有在一个主要时间步骤中检查使能信号。if(% && ...% ){*启用=(% > 0.0); } %else if (% ) { *enabled = (% > 0.0); } %endif if (*enabled) { %assign signal = LibBlockInputSignal(1, "", "", 0) if (% ) { %assign y = LibBlockOutputSignal(0, "", "", 0) % = % ; } if (% ) { %assign y = LibBlockOutputSignal(1, "", "", 0) % = % ; } } } %endfunction %% [EOF] sfun_multirate.tlc
清单2:输出速率分组的代码生成
% % example_multirateblk。TLC.%implements "example_multirateblk" "C" %% Function: mdlOutputs ===================================================== %% Abstract: %% %% Compute the two outputs (the input signal decimated by the %% specified parameter). The decimation is handled by sample times. %% The decimation is only performed if the block is enabled. %% All ports have different sample rate. %% %% Note: the usage of the enable should really be protected such that %% each task has its own enable state. In this example, the enable %% occurs immediately which may or may not be the expected behavior. %% %function Outputs(block, system) Output %assign portIdxName = ["InputPortIdx0","OutputPortIdx0","OutputPortIdx1"] %assign portTID = [%, ... % , ... % ] %foreach i = 3 %assign portName = portIdxName[i] %assign tid = portTID[i] if (% ) { % } %endforeach %endfunction %function OutputsForTID(block, system, tid) Output /* % Block: % */ %assign enable = LibBlockInputSignal(0, "", "", 0) %assign enabled = LibBlockIWork(0, "", "", 0) %assign signal = LibBlockInputSignal(1, "", "", 0) %switch(tid) %case LibGetGlobalTIDFromLocalSFcnTID("InputPortIdx0") %if LibGetSFcnTIDType("InputPortIdx0") == "continuous" %% Only check the enable signal on a major time step. if (% ) { % = (% > 0.0); } %else % = (% > 0.0); %endif %break %case LibGetGlobalTIDFromLocalSFcnTID("OutputPortIdx0") if (% ) { %assign y = LibBlockOutputSignal(0, "", "", 0) % = % ; } %break %case LibGetGlobalTIDFromLocalSFcnTID("OutputPortIdx1") if (% ) { %assign y = LibBlockOutputSignal(1, "", "", 0) % = % ; } %break %default %% error it out %endswitch %endfunction %% [EOF] sfun_multirate.tlc
此示例显示如何通过解除指定的内存地址来生成读取信号值的代码。通过这种技术,您可以生成一个控制算法,该控制算法与硬件群体(例如,存储在微控制器中的模数转换器输出的存储器)的内存交互。
在这个例子中,您生成了一个算法,从地址处的16位内存块中获取输入数据0x8675309
.假设硬件设备异步地只填充地址的低10位。算法必须将地址视为只读(常数
)、挥发性(不稳定的
)数据,并忽略地址的上部6位。
生成的代码可以通过定义取消引信的宏来访问数据0x8675309
并掩盖不必要的比特:
#define a2d_input((*(volatile const16_t *)0x8675309)和0x03ff)
要配置模型以生成定义和使用此宏的代码,必须创建高级自定义存储类和编写目标语言编译器(TLC)代码。有关显示如何使用自定义存储类设计器而不编写TLC代码的示例,请参阅创建并应用存储类.
作为编写TLC代码的替代方法,您可以使用内存段来生成包含实用程序的代码。根据构建工具链的不同,您可以使用pragmas来指定用于存储全局变量的文字内存地址。有关内存段的更多信息,请参见通过插入语控制数据和函数在内存中的位置.
宏语法的推导
在此示例中,您将生成的代码配置为定义和使用解密宏。要确定宏的正确语法,首先录制目标地址。
0x8675309
将地址转换为指向16位整数的指针。使用Simuli金宝appnk Coder数据类型名称uint16_t.
.
(UINT16_T *)0x8675309
添加存储类型限定符常数
因为生成的代码不能写入地址。添加不稳定的
因为硬件可以在任意时间填充地址。
(易失性常数uint16_T*)0x8675309
取消引用地址。
*(Volatile Const16_t *)0x8675309
解引用操作后,应用掩码仅保留硬件填充的10位。使用显式括号控制操作顺序。
(*(volatile const uint16_T *)0x8675309)&0x03FF
作为一种安全编码实践,将整个构造包裹在另一层括号中。
((*(volatile const16_t *)0x8675309)和0x03ff)
创建示例模型
创建示例模型ex_memmap_simple.
.
对于import块,将输出数据类型设置为uint16
.将信号命名为A2D_INPUT.
.输入块和信号线表示硬件填充的数据。
对于增益块,将输出数据类型设置为双重的
.
创建包以包含数据类和自定义存储类的定义
在当前文件夹中,创建一个名为+ MemoryMap.
.该文件夹定义了名为的包MemoryMap.
.
要使包可用用于当前文件夹外部,可以添加包含的文件夹+ MemoryMap.
文件夹到MATLAB路径。
创建自定义存储类
生成定义和读取的代码A2D_INPUT.
作为宏,您必须创建一个可以应用于模型中的信号线的自定义存储类。稍后,您编写补充自定义存储类的TLC代码。
在高级模式下打开自定义存储类设计器。要设计通过自定义TLC代码操作的自定义存储类,您必须使用高级模式。
cscdesigner(“MemoryMap”那“先进”);
在自定义存储类设计师中,单击新的.一个新的自定义存储类,新CSC_1
,出现在自定义存储类定义列表中。
重命名新的自定义存储类MemoryMappeAddress.
.
为了MemoryMappeAddress.
,在这方面一般的选项卡,设置:
类型到其他
.自定义存储类可以通过您稍后编写的自定义TLC代码进行操作。
数据范围到出口
.对于使用此自定义存储类的数据项,Simulink编码器生成定义(例如,金宝app#定义
定义宏的声明)。
数据初始化到没有一个
.金宝appSimulink编码器不会生成初始化数据项的代码。使用此设置,因为此自定义存储类表示只读数据。你没有选择宏
因为自定义存储类设计器不允许您使用宏
用于信号数据。
定义文件到具体说明
(将文本框留空)。对于在生成的代码中消耗内存的数据项,定义文件指定.c
源文件分配内存。但是,此自定义存储类产生了一种宏,它不需要内存。通常,标题文件(.h
), 不是.c
文件,定义宏。环境定义文件到具体说明
代替具体的实例
防止用户从不必要地指定定义文件的用户。
头文件到具体的实例
.要控制宏定义的文件放置,自定义存储类的用户必须为使用此自定义存储类的每个数据项指定头文件。
老板到具体说明
(将文本框留空)。老板仅适用于消耗内存的数据项。
选择完成后,单击申请和保存.
现在,当您将自定义存储类应用于数据项时,例如A2D_INPUT.
信号线,您可以指定标题文件以包含生成的宏定义。但是,您尚无法为数据项指定内存地址。要启用内存地址的规范,请创建可以与之关联的自定义属性类MemoryMappeAddress.
自定义存储类。
定义类为自定义存储类存储自定义属性
定义一个MATLAB类来存储使用自定义存储类的数据项的附加信息。在这种情况下,附加信息是内存地址。
在里面MemoryMap.
包装(套餐)+ MemoryMap.
文件夹),创建一个名为的文件夹@memorymapattrib
.
在里面@memorymapattrib
文件夹,创建名为MemoryMapattrib
.该文件定义了从内置类中派生的类金宝app仿真软件。CustomStorageClassAttributes
.
Classdef.MemoryMapattribs属性(PropertyType =.“字符”) MemoryAddress ='';结尾结尾
稍后,您将此Matlab类与此课程联系起来MemoryMappeAddress.
自定义存储类。然后,将自定义存储类应用于数据项时,可以指定内存地址。
编写TLC代码,发出正确的C代码
编写使用自定义存储类的属性的TLC代码,例如HeaderFile
和MemoryAddress
,为每个数据项生成正确的C代码。
在里面+ MemoryMap.
文件夹,创建一个名为的文件夹TLC.
.
导航到新文件夹。
检查内置模板TLC文件,template_v1.tlc.
.
编辑(fullfile (matlabroot,......'工具箱'那'RTW'那'目标'那'ecoder'那“csc_templates”那“TEMPLATE_v1.tlc”)))
保存副本template_v1.tlc.
在TLC.
文件夹中。重命名复制memory_map_csc.tlc.
.
在memory_map_csc.tlc.
,找到控制C代码数据声明的生成的部分。
%案例“声明”%% libdefaultCustomStoragedEclare是默认声明函数到%%声明一个全局变量,其标识符是数据的名称。%返回“extern%”%% Break %% ==========================================================================
这宣布
例(%案例
)构造一个返回值(%返回
),代码生成器将其发送到为每个数据项指定的头文件中。要控制声明每个数据项的C代码,请调整宣布
的情况。
替换现有%案例
内容使用此新代码,其中指定了不同的返回值:
在TLC代码中,“record”是一个数据项(例如信号线)。%% 'LibGetRecordIdentifier'返回数据项的名称。%assign id = LibGetRecordIdentifier(record) %assign dt = LibGetRecordCompositeDataTypeName(record) %%金宝app对象,该对象存储代码生成设置%%,例如为该项指定的存储类或自定义存储类。%assign ci = record.Object.ObjectProperties.CoderInfo %% 'ci'变量现在存储'Simulink. txt '。金宝appCoderInfo”对象。%%默认情况下,Simulink的CustomAttributes属性。金宝appCoderInfo' %%对象存储'Simulink。金宝appCustomStorageClassAttributes”对象。此嵌套对象存储专门的代码生成设置,例如您为数据项指定的头文件和定义文件。%% %%“MemoryMap”包从“Simulink.CustomStorageClassAttributes”派生了一个新类,%%“MemoryMapAtt金宝appribs”。新类添加了一个名为“MemoryAddress”的属性。这个TLC代码通过获取“MemoryAddress”属性的值来确定数据项的内存地址。%分配ca = ci.Object.ObjectProperties.CustomAttributes %分配地址= ca.Object.ObjectProperties.MemoryAddress %分配宽度= LibGetDataWidth(记录)% %这TLC代码构造完整的宏,用正确的C语法,% %基于TLC变量的值,例如“地址”和“dt”。 %% This TLC code also asserts that the data item must be a scalar. %if width == 1 %assign macro = ... "#define %((*(volatile const % *)%) & 0x03FF)" %else %error( "Non scalars are not supported yet." ) %endif %return "% " %%break %% ==========================================================================
新的TLC代码使用内置的、有文档记录的TLC函数,例如LibGetRecordIdentifier
以及其他TLC命令和操作以访问有关数据项的信息。临时变量,如DT.
和地址
存储该信息。TLC代码通过扩展变量来构造完整的宏,使用正确的C语法,并将宏存储在变量中宏
.
在同一个文件中,找到控制数据定义生成的部分。
LibDefaultCustomStorageDefine是默认的define函数,用于定义一个全局变量,该变量的标识符是数据的名称。如果%%数据是一个参数,定义也会静态初始化为其标称值%%(如MATLAB中设置的)。%返回“% < LibDefaultCustomStorageDefine(记录)>“% % %% ==========================================================================
这定义
Case派生一个返回值,该返回值由代码生成器生成到.c
文件,它定义消耗内存的数据项。
替换现有%案例
包含此新内容的内容:
%例“定义”%返回" % %打破 %% ==========================================================================
MemoryMappeAddress.
在生成的代码中产生宏,因此您使用了宣布
Case而不是定义
用实例构造和发出宏。为了防止定义
如果发出重复的宏定义,新的TLC代码将返回空字符串。
找到控制初始化数据的代码生成的部分。
LibDefaultCustomStorageInitialize是默认的初始化%%函数,它将全局变量的标量元素初始化为0。%返回LibDefaultCustomStorageInitialize(记录,idx雷姆)% % %% ==========================================================================
这初始化
Case生成初始化数据项的代码(例如模型_initialize
功能)。
替换现有%案例
包含此新内容的内容:
%案例“初始化”%返回“%% Break %% ==========================================================================.
MemoryMappeAddress.
产生宏,因此生成的代码不得尝试初始化宏的值。新的TLC代码返回一个空字符串。
完成自定义存储类的定义
你的新matlab类,MemoryMapattrib
,可以启用新的自定义存储类的用户,MemoryMappeAddress.
,为每个数据项指定一个内存地址。要允许此规范,请关联MemoryMapattrib
和MemoryMappeAddress.
.要根据为每个数据项指定的信息生成正确的C代码,请关联定制的TLC文件,memory_map_csc.tlc.
, 和MemoryMappeAddress.
.
导航到包含文件的文件夹+ MemoryMap.
文件夹中。
再次打开自定义存储类设计器。
cscdesigner(“MemoryMap”那“先进”);
为了MemoryMappeAddress.
,在这方面其他属性选项卡,设置:
TLC文件名到memory_map_csc.tlc.
.
CSC属性类到MemoryMap。MemoryMap.Attribs
.
点击申请和保存.
定义信号数据类
将自定义存储类应用于模型中的信号,在其中MemoryMap.
包,您必须创建一个来自的Matlab类金宝appsimulink.signal.
.当您在模型中配置信号时,您选择这个新的数据类而不是默认的类,金宝appsimulink.signal.
.
在里面MemoryMap.
包,创建一个名为的文件夹@信号
.
在里面@信号
文件夹,创建名为signal.m.
.
Classdef.信号<仿真软件。金宝app信号方法功能setupcoderinfo(此)USELOCALCUSTOMSTORAGEClasses(这,“MemoryMap”);返回;结尾结尾结尾
该文件定义了一个名为的类MemoryMap.Signal.
.类定义覆盖了setupcoderinfo.
方法,哪个金宝appsimulink.signal.
类已经实施。新实现指定了该对象MemoryMap.Signal.
类使用自定义存储类MemoryMap.
包(而不是自定义存储类金宝app
包裹)。通过选择模型中的信号在模型中配置信号时MemoryMap.Signal.
类,您可以选择新的自定义存储类,MemoryMappeAddress.
.
将自定义存储类应用于信号线
导航到包含示例模型的文件夹并打开模型。
在模型中,选择视图>属性检查器.
单击名为的信号A2D_INPUT.
.
在属性检查器中,在代码生成, 放信号对象类到MemoryMap.Signal.
. 如果你没有看到MemoryMap.Signal.
, 选择定制类列表
并使用该对话框启用以下选项的选择:MemoryMap.Signal.
.
在属性检查器中,设置存储类到MemoryMappeAddress.
.
放头文件到memory_mapped_addresses.h
.
放MemoryAddress到0x8675309
.
生成和检查代码
从模型生成代码。
###启动模型的构建过程:ex_memmap_simple
检查生成的头文件memory_mapped_addresses.h
.该文件定义了宏A2D_INPUT.
,对应于模型中的信号线。
/ *与自定义存储类存储器的数据声明MemoryMappeAddress * / #define A2D_Input((*(volatile const uint16_t *)0x8675309)和0x03ff)
检查生成的文件ex_memmap_simple.c
.生成的算法代码(对应于增益块)计算模型输出,RTY.OUT1
通过操作A2D_INPUT.
.
/*模型阶跃函数*/void ex_memmap_simple_step(void){/*输出端口:'/Out1'包含:*增益:' /Gain'*In1'*/rtY.Out1=42.0*(实输出)二维输入;}