默认情况下,嵌入式编码器®软件生成独立的不需要外部实时执行程序或操作系统的可执行程序。一个独立的程序只需要最小的修改就能适应目标硬件。独立的程序架构支持单个或多个采样率模型的执行。金宝app
要生成一个独立的程序:
选择模型配置参数生成一个示例主程序.这将启用目标操作系统菜单。
从目标操作系统菜单中,选择BareBoardExample
.
生成的代码。
根据以下因素,为多速率模型生成不同的代码:
模型是在单任务模式还是多任务模式下执行。
是否生成可重用代码。
这些因素会影响生成代码中使用的调度算法,并且在某些情况下会影响模型入口点函数的API。下面几节将讨论这些变体。
独立程序的核心是主循环。在每次迭代中,主循环执行一个后台任务或空任务,并检查终止条件。
主循环被定时器周期性地中断。这个函数rt_OneStep
被安装为定时器中断服务例程(ISR),或者在每个时钟步由定时器ISR调用。
执行驱动程序,rt_OneStep
的序列调用
功能。操作模型
_steprt_OneStep
根据生成模型是单速率还是多速率而有所不同。在单速率模型中,rt_OneStep
简单调用
函数。在多速率模型中,模型
_steprt_OneStep
根据块运行的速率来优先级和安排执行。
下面的伪代码显示了主程序的执行。
main(){初始化(包括安装rt_OneStep作为实时时钟的中断服务例程)初始化和启动定时器硬件启用中断While(不是错误)和(时间<最终时间)后台任务EndWhile禁用中断(禁止rt_OneStep执行)完成任何后台任务Shutdown}
伪代码是用于驱动模型的控制程序的设计。主程序只部分实现了本设计。你必须根据你的规格进行修改。
本节描述您应该在主程序模块的生产版本中进行的最小修改,以实现您的利用程序。
调用
.模型
_initialize
初始化特定于目标的数据结构和硬件,例如adc或dac。
安装rt_OneStep
作为定时器ISR。
初始化定时器硬件。
启用定时器中断并启动定时器。
请注意
rtModel
是否处于有效状态,直到
已经被呼叫了。定时器中断的服务应该直到模型
_initialize
已经被呼叫了。模型
_initialize
可选地,在主循环中插入后台任务调用。
在主循环终止时(如果适用):
禁用定时器中断。
执行特定于目标的清理,例如将dac归零。
检测和处理错误。请注意,即使您的程序被设计为无限期运行,您也可能需要处理严重的错误条件,例如计时器中断溢出。
您可以使用宏rtmGetErrorStatus
而且rtmSetErrorStatus
检测错误并发出信号。
操作rt_OneStep
取决于
无论您的模型是单费率还是多费率。在单速率模型中,模型中所有块的采样时间和模型的固定步长是相同的。样本时间和步长不满足这些条件的模型称为多速率模型。
模型的求解器模式(SingleTasking
与多任务处理
)
嵌入式实时系统目标文件的允许求解模式总结了单速率和多速率模型的允许求解模式。注意,对于单速率模型,仅SingleTasking
允许使用求解模式。
嵌入式实时系统目标文件的允许求解模式
模式 | 单频 | 多重速率的 |
---|---|---|
|
允许 |
允许 |
|
不允许 |
允许 |
|
允许 (默认为 |
允许 (默认为 |
生成的代码rt_OneStep
(以及相关的定时数据结构和支持函数)是根据模型中的速率数和求解器模式量身定制的。金宝app下面几节讨论每种可能的情况。
对于单速率模型,唯一有效的求解器模式是SingleTasking
.这样的模型运行在“单速率”操作中。
下面的伪代码展示了的设计rt_OneStep
在一个单一费率的项目中。
rt_OneStep(){检查中断溢出或其他错误启用“rt_OneStep”(定时器)中断Model_Step()——时间步长结合输出,日志记录,更新}
对于单速率情况,生成的
函数是模型
_step
无效模型_step(空白)
单频rt_OneStep
被设计来执行
在一个时钟周期内。为了加强这个时间限制,模型
_steprt_OneStep
维护和检查计时器超限标志。在进入时,定时器中断被禁用,直到溢出标志和其他错误条件被检查。如果溢出标志是清除的,rt_OneStep
设置标志,并继续启用定时器中断。
只有从成功返回时才会清除溢出标志
.因此,如果模型
_steprt_OneStep
在完成之前重新中断
,则通过溢出标志检测到再次中断。模型
_step
Reinterruption的rt_OneStep
由定时器是一个错误条件。如果检测到这种情况rt_OneStep
发出错误信号并立即返回。(请注意,如果您想以不同的方式处理条件,可以更改此行为。)
注意设计rt_OneStep
假设中断之前被禁用rt_OneStep
被称为。rt_OneStep
应该是不可中断的,直到中断溢出标志被检查。
在多速率多任务系统中,代码生成使用优先级、抢占式多任务模式来执行模型中的不同采样率。
下面的伪代码展示了的设计rt_OneStep
在多速率多任务程序中。
rt_OneStep(){检查基本速率中断溢出Model_Step0()——运行基本速率时间步骤代码对于N=1:NumTasks-1——遍历子速率任务如果(子速率任务N是计划的)检查子速率中断溢出Model_StepN()——运行子速率时间步长代码EndIf EndFor}
任务标识符。具有不同采样率的块的执行被分解成任务。每个以给定采样率执行的块被分配一个任务标识符(tid
),它将它与以该速率执行的任务相关联。哪里有NumTasks
当系统中存在任务时,任务标识符的范围为0..NumTasks
1。
基本速率和子任务的优先级。任务按速率降序排列。的基础概率Task是系统中运行速度最快的任务(硬件时钟速率)。基本速率任务优先级最高(tid
0).下一个最快的任务(tid
1)具有下一个最高优先级的任务,以此类推,直到最慢、优先级最低的任务(tid
NumTasks
1)。
以基本速率倍数运行的较慢的任务被调用subrate任务。
速率分组和特定速率的model_step函数。在单速率模型中,块输出计算在单个函数中执行,
.对于多速率多任务模型,代码生成器尝试使用不同的策略。这个策略叫做率分组.费率分组生成单独的模型
_step
模型中基本费率任务和每个子任务的函数。这些函数的函数命名约定为模型
_step
模型_stepN
在哪里
任务标识符。例如,对于一个名为N
my_model
有三个速率时,生成以下函数:
Void my_model_step0 (Void);Void my_model_step1;Void my_model_step2;
每一个
函数执行块共享模型
_stepN
tid
;换句话说,在任务中执行的块代码N
是分组成相关的吗N
函数。模型
_stepN
调度model_stepN执行。在每一个时钟滴答声中,rt_OneStep
维护调度计数器和事件标志对于每个子任务。计数器实现为taskCounter
上索引的数组tid
.事件标志被实现为上索引的数组tid
.
子费率的调度计数器和任务标志由rt_OneStep
.调度计数器基本上是时钟速率分配器,计算与每个子速率任务相关的采样周期。交换数据的两个任务以较快的速率维护一个交互标志。任务交互标志表示计划运行快任务和慢任务。
事件标志指示是否计划执行给定的任务。rt_OneStep
根据由模型的主程序模块中的代码维护的任务计数器维护事件标志。当计数器指示任务的采样周期已过时,主代码为该任务设置事件标志。
每次祈祷,rt_OneStep
更新其调度数据结构并执行基本速率任务(rt_OneStep
调用
因为base-rate任务必须在每个时钟步上执行)。然后,模型
_step0rt_OneStep
中的调度标志进行迭代tid
秩序,无条件呼唤
对于任何已设置标志的任务。任务按优先级顺序执行。模型
_stepN
抢占。注意设计rt_OneStep
假设中断之前被禁用rt_OneStep
被称为。rt_OneStep
应该是不可中断的,直到检查了基本速率中断溢出标志(参见上面的伪代码)。
所使用的事件标志数组和循环变量rt_OneStep
存储为本地(堆栈)变量。因此,rt_OneStep
是可重入的。如果rt_OneStep
重新中断时,高优先级任务抢占低优先级任务。从中断中返回后,低优先级的任务将按先前计划的顺序恢复。
被检测。多重速率的rt_OneStep
还维护计时器超限标志的数组。rt_OneStep
根据与单速率相同的逻辑,检测每个任务的计时器溢出rt_OneStep
.
请注意
如果您开发了多速率s函数,或者使用了定制的静态主程序模块,请参见速率分组遵从性和兼容性问题有关如何调整代码以实现费率分组兼容性的信息。这种调整可以让您的多速率多任务模型生成更有效的代码。
在多速率单任务程序中,根据定义,模型中的采样时间必须是模型固定步长的整数倍。
在多速率单任务程序中,块以不同的速率执行,但在相同的任务标识符下。操作rt_OneStep
在这种情况下,是多速率多任务操作的简化版本。不使用速率分组。唯一的任务是基准利率任务。因此,只有一个
函数生成:模型
_step
无效模型_step(空白)
在每一个时钟滴答声中,rt_OneStep
检查溢出标志并调用
.多速率单任务程序的调度功能为模型
_steprate_scheduler
(而不是rate_monotonic_scheduler
).调度器在每个时钟点上维护调度计数器。模型中每个采样率都有一个计数器。对象中的数组(在tid上建立索引)中实现计数器时机
内部结构rtModel
.
计数器是时钟频率分配器,用于计算与每个子任务相关的采样周期。当计数器指示给定速率的采样周期已经过时,rate_scheduler
清理柜台。此条件指示以该速率运行的块应在下一次调用时执行
,负责检查柜台。模型
_step
rt_OneStep
不需要大量修改。惟一需要修改的是在检查了溢出标志和错误条件之后重新启用中断。如果适用,你也应该这样做
保存和恢复您的FPU上下文进入和退出到rt_OneStep
.
在调用之前,设置与基本速率相关的模型输入
.模型
_step0
获取调用后与基本速率关联的模型输出
.模型
_step0
请注意
如果你修改rt_OneStep
要在每个基本速率模型步骤之后从连续输出端口读取值,请参阅下面的相关警告准则。
在多速率多任务模型中,在调用前设置与子对象相关的模型输入
在子回路中。模型
_stepN
在多速率多任务模型中,调用后获得与子对象关联的模型输出
在子回路中。模型
_stepN
注释rt_OneStep
指出要添加代码的位置。
在多重速率的rt_OneStep
,您可以通过展开来提高性能为
而且而
循环。
此外,您可以选择修改溢出行为,以便在错误恢复完成后继续执行。
同时也要注意以下警示准则:
不应修改计数器、事件标志或其他计时数据结构的设置方式rt_OneStep
,或在调用from的函数中rt_OneStep
.的rt_OneStep
定时数据结构(包括rtModel
)和逻辑对于生成程序的操作至关重要。
如果您已经定制了主程序模块,以便在每个基本速率模型步骤之后读取模型输出,请注意选择模型配置参数金宝app支持:连续时间而且单输出/更新功能一起可以导致读取的输出值主要
对于连续输出端口,与模型日志数据中的相应输出值略有不同。这是因为,虽然记录的数据是主要时间步骤的输出快照,但从输出读取主要
在基本速率模型之后,步骤可能反映了中间的小时间步骤。要消除这种差异,可以将生成的输出和更新函数分开(清除单输出/更新功能参数)或在连续输出端口前放置零阶保持块。
如果在每次调用模型步骤函数之前没有设置模型输入,则可能会观察到模拟结果与生成代码的记录MAT文件结果之间的不匹配。在生成的示例主程序中,下面的注释显示了用于设置输入和使用代码对模型进行步进的位置:
/*在这里设置模型输入*/ /*步进模型*/
如果您的模型应用信号重用,并且您正在使用MatFileLogging
若要将模拟结果与生成的代码进行比较,请修改rt_OneStep
按照这些注释的指示在每个时间步中写入模型输入。或者,你可以选择SIL或PIL方法核查。
部署生成代码的策略是使用模型配置参数生成一个示例主程序选项要生成一个示例主程序模块(ert_main.c
或. cpp
).有关更多信息,请参见生成一个独立的程序.
或者,您可以清除生成一个示例主程序参数,并使用生成的静态主程序模块作为开发嵌入式应用程序的示例或模板。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++类
参数设置代码接口打包.
静态主程序不是生成代码的一部分。这些程序是作为开发定制修改和用于仿真的基础而提供的。
一个静态主程序包含:
rt_OneStep
-一个定时器中断服务例程(ISR),调用入口点函数
对模型的一个时钟周期执行处理。模型
_step
骨骼主要
提供功能,主要
仅对模拟有用。你必须修改主要
用于实时中断驱动的执行。
对于单速率模型,操作rt_OneStep
和主要
静态主程序中的函数与中描述的自动生成版本中的函数相同将生成的独立可执行程序部署到目标硬件.对于多速率多任务模型,静态主程序和生成的代码不同,将在下一节中描述。
使用静态主程序开发应用程序:
将程序模块复制到工作文件夹。
重命名文件。例如,重命名rt_main.c
来
.模型
_rt_main.c
自定义程序模块内容。
修改模板生成文件或工具链设置,以便构建过程创建相应的目标文件,例如
(在UNIX®,模型
_rt_main.obj
),放在build文件夹中。模型
_rt_main.o
如果您现有的应用程序依赖于静态ert_main.c
在R2012b之前的版本中开发,rt_main.c
,rt_malloc_main.c
,或rt_cppclass_main.cpp
,您可能需要继续使用静态主程序。
基于ert的系统目标文件可以使用静态主程序模块,并且不允许使用模型配置参数生成一个示例主程序.当对静态主程序进行了特定于目标的更改并且必须保留这些修改时,自定义系统目标文件不会禁止使用该模型配置参数。生成的示例主程序不保留对静态主程序的更改。
对于配置为多任务处理的模型(将每个离散速率视为单独的任务)或并发性(允许任务在目标上并发执行选中后,代码生成器使用速率分组方案,为每个速率生成步骤入口点函数(
,在那里模型
_stepN
标识速率)。代码接口报告列出了主程序可以调用的各个入口点函数。N
若要指定只生成基于速率的步长函数而不生成包装器函数,请使用TLC变量RateBasedStepFcn
.如果目标直接调用速率分组兼容函数,请设置RateBasedStepFcn
来1
.在这种情况下,不会生成包装器函数。
在系统目标文件中%包括“codegenentry.tlc”
语句,设置TLC变量RateBasedStepFcn
来1
.另外,组RateBasedStepFcn
来1
在你的target_settings.tlc
文件。
对主循环和rt_OneStep
函数。看到主程序修改指南而且修改rt_OneStep的指导原则.
取代rt_OneStep
在主循环中使用后台任务调用或空语句调用。
你可能需要做的其他修改有:
如果适用,请按照代码中的注释添加用于读取和写入模型I/O以及保存和恢复FPU上下文的代码。
如果你修改rt_main.c
,rt_malloc_main.c
,或rt_cppclass_main.cpp
若要在每个基本速率模型步骤之后从连续输出端口读取值,请参阅中的相关警告准则修改rt_OneStep的指导原则.
清除模型配置参数时生成一个示例主程序,代码生成器生成rtmodel.h
在主程序模块和生成的模型代码之间提供一个接口。如果您创建自己的静态主程序模块,请包含rtmodel.h
.
或者,您可以抑制生成rtmodel.h
,包括
直接在主程序模块中。来抑制模型
.hrtmodel.h
,在系统目标文件中使用以下语句:
%assign AutoBuildProcedure = 0
如果您已经清除了模型配置参数需要终止函数的生产版本中删除或注释掉以下项目rt_main.c
,rt_malloc_main.c
,或rt_cppclass_main.cpp
:
的#如果TERMFCN……
编译时错误检查
呼唤MODEL_TERMINATE
因为只有rt_main.c
:如果是的话不要结合输出和更新功能,清除模型配置参数单输出/更新功能的生产版本中进行以下更改rt_main.c
:
替换对MODEL_STEP
通过呼叫MODEL_OUTPUT
而且MODEL_UPDATE
.
删除#如果ONESTEPFCN……
错误检查。
静态程序模块rt_main.c
不支持金宝app可重用的功能
代码接口打包。如果你使用可重用的功能
代码接口打包非法,此错误检查将引发编译时错误。
#如果MULTI_INSTANCE_CODE = = 1
如果您正在使用静态主程序模块,并且您的模型配置为可重用的功能
代码接口打包,但模型配置参数对模型初始化使用动态内存分配则必须通过调用主代码静态或动态地分配模型实例数据。必须在顶级实时模型数据结构中设置指向Block IO、DWork和Parameters等各个模型数据结构的指针。
为了支金宝app持主要修改,构建过程会根据模型的数据需求生成这些实时模型(RTM)宏的子集
.模型
.h
RTM宏语法 | 描述 |
---|---|
rtmGetBlockIO (rtm) |
获取块I/O数据结构 |
val rtmSetBlockIO (rtm) |
设置块I/O数据结构 |
rtmGetContStates (rtm) |
获得连续状态数据结构 |
val rtmSetContStates (rtm) |
设置连续状态数据结构 |
rtmGetDefaultParam (rtm) |
获取默认参数数据结构 |
val rtmSetDefaultParam (rtm) |
设置默认参数数据结构 |
rtmGetPrevZCSigState (rtm) |
获得先前过零信号状态数据结构 |
val rtmSetPrevZCSigState (rtm) |
设置前面过零信号状态数据结构 |
rtmGetRootDWork (rtm) |
获取DWork数据结构 |
val rtmSetRootDWork (rtm) |
设置DWork数据结构 |
rtmGetU (rtm) |
获取根输入数据结构(当根输入作为模型数据结构的一部分传递时) |
val rtmSetU (rtm) |
设置根输入数据结构(当根输入作为模型数据结构的一部分传递时) |
rtmGetY (rtm) |
获取根输出数据结构(当根输出作为模型数据结构的一部分传递时) |
val rtmSetY (rtm) |
设置根输出数据结构(当根输出作为模型数据结构的一部分传递时) |
要访问RTM数据结构中的各个模型数据结构,请在静态主程序中使用这些宏。例如,假设示例模型rtwdemo_reusable
配置为可重用的功能
代码接口打包,对模型初始化使用动态内存分配清除,传递根级I/O为设置为个人观点
,优化窗格中选择删除根级I/O零初始化清除。构建模型会生成这些模型数据结构和模型入口点rtwdemo_reusable.h
:
/*系统''的块状态(自动存储)*/ typedef struct {real_T Delay_DSTATE;/* ' /Delay' */} D_Work;/*参数(自动存储)*/ struct参数_ {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_reusable.h
包含RTM宏的定义rtmGetDefaultParam
,rtmsetDefaultParam
,rtmGetRootDWork
,rtmSetRootDWork
.
为参考,生成rtmodel.h
文件包含一个有初始值的参数定义示例(非执行代码):
#if 0 /*初始值的参数数据定义示例*/ static参数rtP ={2.0 /*变量:k1 *引用对象:'/Gain' */};/*可修改的参数
在静态主文件的定义部分中,可以使用以下代码静态分配实时模型数据结构和参数rtwdemo_reusable
模型:
静态RT_MODEL rtM_;RT_MODEL *const rtM = &rtM_;/*实时模型*/静态参数rtP ={2.0 /*变量:k1 *引用对象:'<根>/增益' */};/*可修改参数*/ static D_Work rtDWork;/*可观察状态*/ /* '/In1' */ static real_T rtU_In1;/* ' /In2' */ static real_T rtU_In2;/* ' /Out1' */ static real_T rtY_Out1;
在main函数体中,你可以使用以下RTM宏调用来设置实时模型数据结构中的模型参数和DWork数据:
int_T main(int_T argc, const char *argv[]){…/*将模型数据打包到RTM */ rtmSetDefaultParam(RTM, &rtP);&rtDWork rtmSetRootDWork (rtM);/*初始化模型*/ 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
代码,分别为:
UpdateForTID(block, system, tid)
下面的代码清单展示了不使用速率分组(清单1)和使用速率分组(清单2)的输出计算的生成。注意以下事项:
的tid
参数是任务标识符(0 . . NumTasks-1
).
只有代码由tid
传递给OutputsForTID
是生成的。的如果(% < LibIsSFcnSampleHit (portName) >)
中不使用TestOutputsForTID
.
在生成速率分组代码时,OutputsForTID
和/或UpdateForTID
在代码生成期间调用。当生成非速率分组代码时,输出
和/或更新
被称为。
在速率分组兼容的代码中,顶层输出
和/或更新
函数调用OutputsForTID
和/或UpdateForTID
每个速率的函数(tid
)参与了这个街区。返回的代码OutputsForTID
和/或UpdateForTID
一定要有相应的守护吗tid
警卫:
如果(% < LibIsSFcnSampleHit (portName) >)
如清单2所示。
清单1:输出没有速率分组的代码生成
% % multirate_blk。薄层色谱%implements "multirate_blk" "C" %% Function: mdlOutputs ===================================================== %% Abstract: %% %% Compute the two outputs (input signal decimated by the %% specified parameter). The decimation is handled by sample times. %% The decimation is only performed if the block is enabled. %% Each port has a different 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 /* %Block: % */ %assign enable = LibBlockInputSignal(0, "", "", 0) { int_T *enabled = &% ; %if LibGetSFcnTIDType("InputPortIdx0") == "continuous" %% Only check the enable signal on a major time step. if (% && ... % ) { *enabled = (% > 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。薄层色谱%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位内存块获取输入数据0 x8675309
.假设硬件设备异步地填充地址的后10位。算法必须将地址视为只读(常量
),易失性(挥发性
)数据,并忽略地址的上6位。
生成的代码可以通过定义解引用的宏来访问数据0 x8675309
并掩盖不必要的部分:
#define A2D_INPUT ((*(volatile const uint16_T *)0x8675309)&0x03FF)
配置一个模型以生成定义和使用此宏的代码:
创建示例模型。
创建包以包含数据类和存储类的定义。
使用自定义存储类设计器创建存储类。
定义一个类来存储存储类的属性设置。
编写为存储类发出正确C代码的目标语言编译器(TLC)代码。
为导入数据定义一个类。
将包加载到嵌入式编码器字典中。
配置模型根级导入以使用存储类。
生成并检查代码。
有关显示如何在不编写TLC代码的情况下使用自定义存储类设计器的示例,请参见创建并应用存储类.
作为编写TLC代码的替代方法,您可以使用内存段来生成包含pragmas的代码。根据构建工具链的不同,可以使用pragmas指定存储全局变量的文字内存地址。有关内存部分的详细信息,请参见通过插入Pragmas控制数据和函数在内存中的位置.
宏语法的推导
在本例中,配置生成的代码来定义和使用解引用宏。要确定宏的正确语法,首先要记录目标地址。
0 x8675309
将地址转换为指向16位整数的指针。使用Simuli金宝appnk Coder数据类型名称uint16_T
.
(uint16_T *) 0 x8675309
添加存储类型限定符常量
因为生成的代码不能写入地址。添加挥发性
因为硬件可以在任意时间填充地址。
(volatile const uint16_T *)0x8675309
取消对地址的引用。
*(volatile const uint16_T *)0x8675309
在解引用操作之后,应用掩码来只保留硬件填充的10位。使用显式括号控制操作的顺序。
(*(volatile const uint16_T *)0x8675309)&0x03FF
作为一种安全的编码实践,将整个结构包装在另一层括号中。
(*(volatile const uint16_T *)0x8675309)&0x03FF)
创建示例模型
创建示例模型ex_memmap_simple
.
对于import块,将输出数据类型设置为uint16
.将信号命名为A2D_INPUT
.的轮廓尺寸
块和信号线表示硬件填充的数据。
对于Gain块,将输出数据类型设置为双
.
创建包含数据类和存储类定义的包
在当前文件夹中创建名为+ MemoryMap
.该文件夹定义了一个名为MemoryMap
.
控件的文件夹可以在当前文件夹之外使用该包+ MemoryMap
文件夹到MATLAB路径。
创建存储类
生成定义和读取的代码A2D_INPUT
作为宏,您必须创建一个可以应用于模型根级导入的存储类。稍后,编写补充存储类的TLC代码。
以高级模式打开自定义存储类设计器。若要设计通过自定义TLC代码操作的存储类,必须使用高级模式。
cscdesigner (“MemoryMap”,“先进”);
在“自定义存储类设计器”中,单击新.新的自定义存储类,NewCSC_1
,出现在存储类定义列表中。
将存储类重命名为MemoryMappedAddress
.
为MemoryMappedAddress
,在一般选项卡,设置:
类型来其他
.存储类可以通过稍后编写的自定义TLC代码进行操作。
数据范围来出口
.对于使用此自定义存储类的数据项,Simulink Coder将生成定义(例如金宝app#定义
定义宏的语句)。
数据初始化来没有一个
.金宝appSimulink Coder不生成初始化数据项的代码。使用此设置是因为此自定义存储类表示只读数据。您没有选择宏
因为自定义存储类设计器不允许您使用宏
用于信号数据。
定义文件来指定
(文本框不填写)。对于生成代码中消耗内存的数据项,定义文件指定了.c
分配内存的源文件。这个存储类产生一个宏,它不需要内存。通常,头文件(.h
),而不是.c
文件,定义宏。设置定义文件来指定
而不是具体的实例
防止存储类的用户不必要地指定定义文件。
头文件来具体的实例
.要控制宏定义的文件放置,存储类的用户必须为使用此存储类的每个数据项指定一个头文件。
老板来指定
(文本框不填写)。老板仅适用于消耗内存的数据项。
设置完成后,单击应用而且保存.
现在,当您将存储类应用到数据项时,例如import三机一体
,您可以指定一个头文件来包含生成的宏定义。您还不能为数据项指定内存地址。类关联的自定义属性类,可启用内存地址规范MemoryMappedAddress
存储类。
定义类以存储存储类的属性设置
定义一个MATLAB类来存储使用该存储类的数据项的附加信息。在本例中,附加信息是内存地址。
在MemoryMap
包(+ MemoryMap
文件夹),创建一个名为@MemoryMapAttribs
.
在@MemoryMapAttribs
文件夹,创建一个名为MemoryMapAttribs
.该文件定义了一个从内置类派生的类金宝app仿真软件。CustomStorageClassAttributes
.
classdefMemoryMapAttribs < 金宝appSimulink。CustomStorageClassAttributes属性(PropertyType =“字符”) MemoryAddress =”;结束结束
之后,将此MATLAB类与MemoryMappedAddress
存储类。然后,在将存储类应用于数据项时,可以指定内存地址。
编写能产生正确C代码的TLC代码
编写使用存储类属性的TLC代码,例如HeaderFile
而且MemoryAddress
,为每个数据项生成正确的C代码。
在+ MemoryMap
文件夹,创建一个名为薄层色谱
.
导航到新文件夹。
检查内置模板TLC文件,TEMPLATE_v1.tlc
.
编辑(fullfile (matlabroot,...“工具箱”,“环球套票”,“目标”,“是”,“csc_templates”,“TEMPLATE_v1.tlc”))
保存一份TEMPLATE_v1.tlc
在薄层色谱
文件夹中。重命名副本memory_map_csc.tlc
.
在memory_map_csc.tlc
,找到控制c代码数据声明生成的部分。
%case "declare" %% LibDefaultCustomStorageDeclare是默认的声明函数,用于%%声明一个标识符为数据名称的全局变量。%返回“走读生% < LibDefaultCustomStorageDeclare(记录)>“% % %% ==========================================================================
的声明
例(%的情况
)构造一个返回值(%返回
),代码生成器将其生成到您为每个数据项指定的头文件中。控件中的返回值,可控制声明每个数据项的C代码声明
的情况。
替换现有的%的情况
这个新代码的内容,它指定了一个不同的返回值:
在TLC代码中,“记录”是一个数据项(例如,一条信号线)。'LibGetRecordIdentifier'返回数据项的名称。%assign id = LibGetRecordIdentifier(record) %assign dt = LibGetRecordCompositeDataTypeName(record) %%数据项的'CoderInfo'属性存储%% 'Simulink。金宝app对象,该对象存储代码生成设置%%,例如为项指定%%的存储类或自定义存储类。'ci'变量现在存储'Simulink. object . objectproperties . coderinfo %%金宝appCoderInfo”对象。默认情况下,“Simulink. properties”的“CustomAttributes”属性。金宝appCoderInfo' %%对象存储一个'Simulink. x金宝appml文件。CustomStorageClassAttributes”对象。这个嵌套对象存储专门的代码生成设置,例如您为数据项指定的头文件和定义文件。%% %% 'MemoryMap'包从'Simulink.CustomStorageClassAttributes'派生了一个新类,%% 'MemoryMapA金宝appttribs'。这个新类添加了一个名为“MemoryAddress”的属性。这个TLC代码通过获取'MemoryAddress'属性的值来确定数据项的内存地址。%assign ca = ci.Object.ObjectProperties.CustomAttributes %assign address = ca. object . objectproperties . memoryaddress %assign width = LibGetDataWidth(record) %%这段TLC代码使用正确的C语法,根据TLC变量(如'address'和'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语法构造完整的宏,并将宏存储在变量中宏
.
在同一个文件中,找到控制数据定义生成的部分。
%case "define" %% LibDefaultCustomStorageDefine是默认的定义函数,用于定义%%一个标识符为数据名称的全局变量。如果%%数据是一个参数,定义也被静态初始化为它的标称值%%(在MATLAB中设置)。%返回“% < LibDefaultCustomStorageDefine(记录)>“% % %% ==========================================================================
的定义
实例派生一个返回值,该返回值由代码生成器生成.c
文件,它定义了消耗内存的数据项。
替换现有的%的情况
内容与此新内容:
%例“定义”%返回" % %打破 %% ==========================================================================
MemoryMappedAddress
在生成的代码中生成宏,因此可以使用声明
Case而不是定义
实例来构造和发出宏。为了防止定义
情况下发出重复的宏定义,新的TLC代码返回一个空字符串。
找到控制生成初始化数据的代码的部分。
LibDefaultCustomStorageInitialize是默认的初始化%%函数,将全局变量的标量元素初始化为0。%返回LibDefaultCustomStorageInitialize(记录,idx雷姆)% % %% ==========================================================================
的初始化
例生成初始化数据项的代码(例如模型_initialize
功能)。
替换现有的%的情况
内容与此新内容:
%例“初始化”%返回" % %打破 %% ==========================================================================
MemoryMappedAddress
生成宏,因此生成的代码不能尝试初始化宏的值。新的TLC代码返回一个空字符串。
完成存储类的定义
新的MATLAB类,MemoryMapAttribs
,可以使您的存储类的用户,MemoryMappedAddress
,为每个数据项指定内存地址。要允许此规范,请关联MemoryMapAttribs
与MemoryMappedAddress
.若要根据为每个数据项指定的信息生成正确的C代码,请将自定义TLC文件memory_map_csc.tlc
,MemoryMappedAddress
.
控件所在的文件夹+ MemoryMap
文件夹中。
再次打开自定义存储类设计器。
cscdesigner (“MemoryMap”,“先进”);
为MemoryMappedAddress
,在其他属性选项卡,设置:
TLC文件名来memory_map_csc.tlc
.
CSC属性类来MemoryMap。MemoryMapAttribs
.
点击应用而且保存.
定义信号数据类
要将存储类应用于模型中的数据元素,请在MemoryMap
包,则必须创建派生自的MATLAB类金宝app仿真软件。信号
.当你在模型中配置信号时,你选择这个新的数据类而不是默认的类,金宝app仿真软件。信号
.
在MemoryMap
包,创建一个名为@Signal
.
在@Signal
文件夹,创建一个名为Signal.m
.
classdef信号< Simul金宝appink。信号方法函数setupCoderInfo(this) useLocalCustomStorageClasses(this,“MemoryMap”);返回;结束结束结束
该文件定义了一个名为MemoryMap。信号
.类定义重写setupCoderInfo
方法,该方法金宝app仿真软件。信号
类已实现。对象的对象MemoryMap。信号
类使用自定义存储类MemoryMap
类中的存储类金宝app
包)。属性来配置模型中的信号时MemoryMap。信号
类,您可以选择新的自定义存储类,MemoryMappedAddress
.
加载包到嵌入式编码器字典
导航到包含示例模型的文件夹并打开模型。
打开嵌入式编码器应用程序。
打开嵌入式编码器字典。在C代码选项卡上,选择代码接口>嵌入式编码器词典.
在“嵌入式编码器字典”中,单击管理包.
在“包管理”对话框中,单击刷新.刷新完成后,选择该包MemoryMap
.点击负载.
关闭嵌入式编码器字典。
配置根级导入使用存储类
在嵌入式编码器应用程序,使用代码映射编辑器和属性检查器配置导入三机一体
使用您定义的存储类。
打开代码映射编辑器。在C代码选项卡上,选择代码接口>单个元素代码映射.
在“代码映射编辑器”中港口选项卡,选择导入三机一体
.设置的存储类三机一体
来MemoryMappedAddress
.
在属性检查器中,在代码,设置HeaderFile财产memory_mapped_addresses.h
和MemoryAddress财产0 x8675309
.
保存模型。
生成和检查代码
从模型生成代码。
###开始构建过程:ex_memmap_simple tf =逻辑0成功完成构建过程:ex_memmap_simple构建摘要构建的顶级模型目标:模型动作重建原因================================================================================================= ex_memmap_simple生成和编译的代码生成信息文件不存在。构建1 / 1模型(0个模型已经更新)构建持续时间:0h 0m 32.363秒
检查生成的头文件memory_mapped_addresses.h
.该文件定义宏A2D_INPUT
,对应模型中的信号线。
/*自定义存储类MemoryMappedAddress */ #define A2D_INPUT ((*(volatile const uint16_T*)0x8675309) & 0x03FF)
检查生成的文件ex_memmap_simple.c
.生成的算法代码(对应于Gain块)计算模型输出,而无。着干活
,通过在A2D_INPUT
.
/*模型步长函数*/ void步长(void){/*输出端口:'<根>/Out1'包含:*增益:'<根>/增益' *输入:'<根>/In1' */ rtY.输出端口:'<根>/Out1'Out1 = 42.0 * (real_T)A2D_INPUT;}