文档

将生成的独立可执行程序部署到目标硬件

默认情况下,嵌入式编码器®软件生成独立的不需要外部实时执行程序或操作系统的可执行程序。一个独立的程序只需要最小的修改就能适应目标硬件。独立的程序架构支持单个或多个采样率模型的执行。金宝app

生成一个独立的程序

要生成一个独立的程序:

  1. 自定义模板部份代码生成>模板窗格的“配置参数”对话框中,选择生成一个示例主程序选项(默认开启)。这将启用目标操作系统菜单。

  2. 目标操作系统菜单中,选择BareBoardExample(默认选择)。

  3. 生成代码。

根据以下因素,多速率模型会生成不同的代码:

  • 模型是在单任务模式还是多任务模式下执行。

  • 是否生成可重用代码。

这些因素会影响生成代码中使用的调度算法,并且在某些情况下会影响模型入口点函数的API。下面几节将讨论这些变体。

独立程序组件

独立程序的核心是主循环。在每次迭代中,主循环执行一个后台任务或空任务,并检查终止条件。

主循环被定时器周期性地中断。这个函数rt_OneStep被安装为定时器中断服务例程(ISR),或者在每个时钟步由定时器ISR调用。

执行驱动程序,rt_OneStep的序列调用模型_step功能。操作rt_OneStep根据生成模型是单速率还是多速率而有所不同。在单速率模型中,rt_OneStep简单调用模型_step函数。在多速率模型中,rt_OneStep根据块运行的速率来优先级和安排执行。

主程序

操作概述

下面的伪代码显示了主程序的执行。

main(){初始化(包括安装rt_OneStep作为实时时钟的中断服务例程)初始化和启动定时器硬件启用中断While(不是错误)和(时间<最终时间)后台任务EndWhile禁用中断(禁止rt_OneStep执行)完成任何后台任务Shutdown}

伪代码是用于驱动模型的控制程序的设计。主程序只部分实现了本设计。你必须根据你的规格进行修改。

主程序修改指南

本节描述您应该在主程序模块的生产版本中进行的最小修改,以实现您的利用程序。

  1. 调用模型_initialize

  2. 初始化特定于目标的数据结构和硬件,例如adc或dac。

  3. 安装rt_OneStep作为定时器ISR。

  4. 初始化定时器硬件。

  5. 启用定时器中断并启动定时器。

    请注意

    rtModel是否处于有效状态,直到模型_initialize已经被呼叫了。定时器中断的服务应该直到模型_initialize已经被呼叫了。

  6. 可选地,在主循环中插入后台任务调用。

  7. 在主循环终止时(如果适用):

    • 禁用定时器中断。

    • 执行特定于目标的清理,例如将dac归零。

    • 检测和处理错误。请注意,即使您的程序被设计为无限期运行,您也可能需要处理严重的错误条件,例如计时器中断溢出。

      您可以使用宏rtmGetErrorStatusrtmSetErrorStatus检测错误并发出信号。

rt_OneStep和调度注意事项

操作概述

操作rt_OneStep取决于

  • 无论您的模型是单费率还是多费率。在单速率模型中,模型中所有块的采样时间和模型的固定步长是相同的。样本时间和步长不满足这些条件的模型称为多速率模型。

  • 模型的求解器模式(SingleTasking多任务处理

嵌入式实时系统目标文件的允许求解模式总结了单速率和多速率模型的允许求解模式。注意,对于单速率模型,仅SingleTasking允许使用求解模式。

嵌入式实时系统目标文件的允许求解模式

模式 单频 多重速率的

SingleTasking

允许

允许

多任务处理

不允许

允许

汽车

允许

(默认为SingleTasking

允许

(默认为多任务处理

生成的代码rt_OneStep(以及相关的定时数据结构和支持函数)是根据模型中的速率数和求解器模式量身定制的。金宝app下面几节讨论每种可能的情况。

单速率单任务操作

对于单速率模型,唯一有效的求解器模式是SingleTasking.这样的模型运行在“单速率”操作中。

下面的伪代码展示了的设计rt_OneStep在一个单一费率的项目中。

rt_OneStep(){检查中断溢出或其他错误启用“rt_OneStep”(定时器)中断Model_Step()——时间步长结合输出,日志记录,更新}

对于单速率情况,生成的模型_step函数是

无效模型_step(空白)

单频rt_OneStep被设计来执行模型_step在一个时钟周期内。为了加强这个时间限制,rt_OneStep维护和检查计时器超限标志。在进入时,定时器中断被禁用,直到溢出标志和其他错误条件被检查。如果溢出标志是清除的,rt_OneStep设置标志,并继续启用定时器中断。

只有从成功返回时才会清除溢出标志模型_step.因此,如果rt_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..NumTasks1。

基本速率和子任务的优先级。任务按速率降序排列。的基础概率Task是系统中运行速度最快的任务(硬件时钟速率)。基本速率任务优先级最高(tid0).下一个最快的任务(tid1)具有下一个最高优先级的任务,以此类推,直到最慢、优先级最低的任务(tidNumTasks1)。

以基本速率倍数运行的较慢的任务被调用subrate任务。

速率分组和特定速率的model_step函数。在单速率模型中,块输出计算在单个函数中执行,模型_step.对于多速率多任务模型,代码生成器尝试使用不同的策略。这个策略叫做率分组.费率分组生成单独的模型_step模型中基本费率任务和每个子任务的函数。这些函数的函数命名约定为

模型_stepN

在哪里N任务标识符。例如,对于一个名为my_model有三个速率时,生成以下函数:

Void my_model_step0 (Void);Void my_model_step1;Void my_model_step2;

每一个模型_stepN函数执行块共享tidN;换句话说,在任务中执行的块代码N是分组成相关的吗模型_stepN函数。

调度model_stepN执行。在每一个时钟滴答声中,rt_OneStep维护调度计数器和事件标志对于每个子任务。计数器实现为taskCounter上索引的数组tid.事件标志被实现为上索引的数组tid

子费率的调度计数器和任务标志由rt_OneStep.调度计数器基本上是时钟速率分配器,计算与每个子速率任务相关的采样周期。交换数据的两个任务以较快的速率维护一个交互标志。任务交互标志表示计划运行快任务和慢任务。

事件标志指示是否计划执行给定的任务。rt_OneStep根据由模型的主程序模块中的代码维护的任务计数器维护事件标志。当计数器指示任务的采样周期已过时,主代码为该任务设置事件标志。

每次祈祷,rt_OneStep更新其调度数据结构并执行基本速率任务(rt_OneStep调用模型_step0因为base-rate任务必须在每个时钟步上执行)。然后,rt_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检查溢出标志并调用模型_step.多速率单任务程序的调度功能为rate_scheduler(而不是rate_monotonic_scheduler).调度器在每个时钟点上维护调度计数器。模型中每个采样率都有一个计数器。对象中的数组(在tid上建立索引)中实现计数器时机内部结构rtModel

计数器是时钟频率分配器,用于计算与每个子任务相关的采样周期。当计数器指示给定速率的采样周期已经过时,rate_scheduler清理柜台。此条件指示以该速率运行的块应在下一次调用时执行模型_step,负责检查柜台。

修改rt_OneStep的指导原则

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提供的静态主模块®包括:

  • matlabroot/ rtw / c / src /共同/rt_main.c——金宝app支持那种一次性的功能代码接口打包。

  • matlabroot/ rtw / c / src /共同/rt_malloc_main.c——金宝app支持可重用的功能代码接口打包。模型选项对模型初始化使用动态内存分配必须对和模型参数传递根级I/O为必须设置为部分模型数据结构

  • matlabroot/ rtw / c / src /共同/rt_cppclass_main.cpp——金宝app支持c++类代码接口打包。

静态主模块不是生成代码的一部分;它是为您的自定义修改和在模拟中使用提供的基础。如果您现有的应用程序依赖于静态ert_main.c(在R2012b之前的版本中开发),rt_main.crt_malloc_main.c,或rt_cppclass_main.cpp,您可能需要继续使用静态主程序模块。

在使用静态主模块开发应用程序时,应该将该模块复制到工作文件夹中,并在进行修改之前重命名它。例如,您可以重命名rt_main.c模型_rt_main.c.此外,您必须修改模板makefile或工具链设置,以便构建过程创建相应的目标文件,例如模型_rt_main.obj(在UNIX®模型_rt_main.o),放在build文件夹中。

静态主模块包含

  • rt_OneStep,一个定时器中断服务程序(ISR)。rt_OneStep调用模型_step对模型的一个时钟周期执行处理。

  • 骨骼主要函数。提供,主要仅在模拟中有用。你必须修改主要用于实时中断驱动的执行。

对于单速率模型,操作为rt_OneStep和main函数在静态main模块中本质上是相同的,因为它们在自动生成的版本中描述将生成的独立可执行程序部署到目标硬件.然而,对于多速率多任务模型,静态代码和生成的代码略有不同。下一节将描述这种情况。

速率分组和静态主程序

基于ERT目标的目标有时会使用静态主模块,并且不允许使用生成一个示例主程序选择。这样做是因为特定于目标的修改已经添加到静态主模块中,如果重新生成主程序,这些修改将不会保留。

静态主模块可能使用也可能不使用兼容速率分组模型_stepN功能。如果你的主模块是基于静态的rt_main.crt_malloc_main.c,或rt_cppclass_main.cpp模块,它不使用特定于速率的模型_stepN函数调用。它使用老式的风格模型_step函数,传入一个任务标识符:

无效模型_step (int_T tid);

默认情况下,当生成一个示例主程序选项关闭时,ERT目标生成一个模型_step多速率多任务模型的“包装器”。包装器的目的是连接特定于速率的接口模型_stepN函数转换为旧式调用。包装器代码分派到模型_stepN调用开关语句,如下例所示:

void mymodel_step(int_T tid) /*采样时间:*/ {switch(tid) {case 0: mymodel_step0();打破;案例1:mymodel_step1();打破;案例2:mymodel_step2();打破;默认值:break;}}

下面的伪代码说明了如何操作rt_OneStep调用模型_step来自静态主程序的多速率、多任务模式。

rt_OneStep(){检查基本速率中断溢出启用“rt_OneStep”中断确定哪个速率需要运行这个时间步骤ModelStep(tid=0)——基本速率时间步骤对于N=1:NumTasks-1——遍历子速率任务检查子速率中断溢出如果(子速率任务N是计划的)ModelStep(tid=N)——子速率时间步骤EndIf EndFor}

您可以使用TLC变量RateBasedStepFcn指定只生成基于速率的步骤函数,而不生成包装器函数。如果目标调用速率分组兼容模型_stepN直接函数,设置RateBasedStepFcn1.在这种情况下,不会生成包装器函数。

你应该设置RateBasedStepFcn在此之前%包括“codegenentry.tlc”语句在系统目标文件中。或者,您可以设置RateBasedStepFcn在你的target_settings.tlc文件。

修改静态主程序

和生成的ert_main.c. cpp,您应该对主循环和rt_OneStep.看到主程序修改指南修改rt_OneStep的指导原则

另外,您应该替换rt_OneStep在主循环中使用后台任务调用或空语句调用。

您可能需要做的其他修改是

  • 如果适用,请按照代码中的注释添加用于读取/写入模型I/O和保存/恢复FPU上下文的代码。

    请注意

    如果你修改rt_main.crt_malloc_main.c,或rt_cppclass_main.cpp若要在每个基本速率模型步骤之后从连续输出端口读取值,请参阅中的相关警告准则修改rt_OneStep的指导原则

  • 生成一个示例主程序选项关闭,rtmodel.h在主模块和生成的模型代码之间提供接口。如果您创建自己的静态主程序模块,通常会包含rtmodel.h

    或者,您可以抑制生成rtmodel.h,并包括模型.h直接在主模块中。来抑制rtmodel.h,在系统目标文件中使用以下语句:

    %assign AutoBuildProcedure = 0
  • 如果您已清除需要终止函数选项,在生产版本中删除或注释掉以下内容rt_main.crt_malloc_main.c,或rt_cppclass_main.cpp

    • #如果TERMFCN……编译时错误检查

    • 呼唤MODEL_TERMINATE

  • rt_main.c(不适用于rt_cppclass_main.cpp):如果是的话要结合输出和更新功能,清除单输出/更新功能选项的生产版本中进行以下更改rt_main.c

    • 替换对MODEL_STEP通过呼叫MODEL_OUTPUTMODEL_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宏的定义rtmGetDefaultParamrtmsetDefaultParamrtmGetRootDWork,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模块。看到速率分组和静态主程序获取详细信息。

使您的S-Functions速率分组兼容

内置的仿真软件金宝app®块,以及DSP系统工具箱™块,符合生成速率分组代码的要求。但是,用户编写的多速率内联s函数可能不符合速率分组。不兼容的块生成效率较低的代码,但在其他方面与速率分组兼容。为了充分利用费率分组的效率,必须升级多费率内联s函数以完全兼容费率分组。您应该升级您的TLC s -函数实现,如本节所述。

使用不兼容的多速率块生成速率分组代码会生成死代码。这会导致两个问题:

  • 降低代码效率。

  • 在编译时发出的警告消息。当死代码在初始化之前引用临时变量时,会产生这样的警告。因为死代码不运行,所以这个问题不会影响生成代码的运行时行为。

要使s函数速率分组兼容,可以使用以下TLC函数生成ModelOutputsModelUpdate代码,分别为:

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)

若要配置模型以生成定义和使用此宏的代码,必须创建高级自定义存储类并编写目标语言编译器(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文件,定义宏。设置定义文件指定而不是具体的实例防止自定义存储类的用户不必要地指定定义文件。

  • 头文件具体的实例.要控制宏定义的文件放置,自定义存储类的用户必须为使用此自定义存储类的每个数据项指定头文件。

  • 老板指定(文本框不填写)。老板仅适用于消耗内存的数据项。

设置完成后,单击应用保存

现在,当您将自定义存储类应用于数据项时,例如A2D_INPUT信号线,您可以指定一个头文件来包含生成的宏定义。但是,您还不能为数据项指定内存地址。类关联的自定义属性类,可启用内存地址规范MemoryMappedAddress自定义存储类。

定义类来存储自定义存储类的自定义属性

定义一个MATLAB类来存储使用自定义存储类的数据项的附加信息。在本例中,附加信息是内存地址。

MemoryMap包(+ MemoryMap文件夹),创建一个名为@MemoryMapAttribs

@MemoryMapAttribs文件夹,创建一个名为MemoryMapAttribs.该文件定义了一个从内置类派生的类金宝app仿真软件。CustomStorageClassAttributes

classdefMemoryMapAttribs < 金宝appSimulink。CustomStorageClassAttributes属性(PropertyType =“字符”) MemoryAddress =;结束结束

之后,将此MATLAB类与MemoryMappedAddress自定义存储类。然后,在将自定义存储类应用于数据项时,可以指定内存地址。

编写能产生正确C代码的TLC代码

编写使用自定义存储类属性的TLC代码,例如HeaderFileMemoryAddress,为每个数据项生成正确的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,为每个数据项指定内存地址。要允许此规范,请关联MemoryMapAttribsMemoryMappedAddress.若要根据为每个数据项指定的信息生成正确的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

将自定义存储类应用于信号线

导航到包含示例模型的文件夹并打开模型。

在模型中,选择查看>属性检查器

点击名为A2D_INPUT

在属性检查器中,在代码生成,设置信号对象类MemoryMap。信号.如果你看不见MemoryMap。信号中,选择自定义类列表并使用对话框启用的选择MemoryMap。信号

在属性检查器中,设置存储类MemoryMappedAddress

头文件memory_mapped_addresses.h

MemoryAddress0 x8675309

生成和检查代码

从模型生成代码。

开始模型的构建过程:ex_memmap_simple成功完成模型的构建过程:ex_memmap_simple

检查生成的头文件memory_mapped_addresses.h.该文件定义宏A2D_INPUT,对应模型中的信号线。

/*自定义存储类MemoryMappedAddress */ #define A2D_INPUT ((*(volatile const uint16_T*)0x8675309) & 0x03FF)

检查生成的文件ex_memmap_simple.c.生成的算法代码(对应于Gain块)计算模型输出,而无。着干活,通过在A2D_INPUT

/*模型步函数*/ void ex_memmap_simple_step(void){/*输出端口:'<根>/Out1'合并:*增益:'<根>/增益' *输入:'<根>/In1' */ rtY.输出:'<根>/Out1'Out1 = 42.0 * (real_T)A2D_INPUT;}

相关的话题

这个话题有用吗?