zoukankan      html  css  js  c++  java
  • Simulink仿真入门到精通(十八) TLC语言

    TLC(Target Language Compiler)是一种为转换为目标语言而存在的额解释性语言,其目的就是将模型中编译出来的rtw文件转换为目标代码(C/C++等)。与M语言类似,既可以写成脚本文件,也能够作为函数存在,都是解释性语言,更相似的是它们都提供具有强大功能的内建函数库。

    18.1 TLC的作用

    1. 支持模型针对通用或特定目标硬件的代码生成功能;
    2. 为S函数模块提供代码生成功能,可以让用户自己增加支持代码生成的模块;
    3. 在代码生成过程中,生成不依赖S函数模块的自定义过程代码。

    Simulink中提供了很多既有TLC文件,如果擅自修改可能导致Simulink Coder功能性错误,故Mathworks提倡用户尽量不要修改TLC。但是如果能熟练掌握TLC的运行机制和编写方法,不仅不会伤害SImulink的功能,还可以巧妙利用TLC语言实现更多的自动化代码生成功能。

    如matlab目录下Constant.tlc文件内容如下:

    %% 
    %% 
    %% 
    %%
    %% Copyright 1994-2015 The MathWorks, Inc.
    %%
    %% Abstract: Constant block target file
    
    %implements Constant "C"
    
    
    %% Function: BlockInstanceSetup ================================================
    %% Abstract:
    %%      Set expression folding compliant
    %%
    %function BlockInstanceSetup(block,system) void
      %<LibBlockSetIsExpressionCompliant(block)>
    %endfunction
    
    
    %% Function: BlockOutputSignal =================================================
    %% Abstract:
    %%      Return the appropriate reference to the parameter.  This function *may*
    %%      be used by Simulink when optimizing the Block IO data structure.
    %%
    %function BlockOutputSignal(block,system,portIdx,ucv,lcv,idx,retType) void
      %switch retType
        %case "Signal"
          %return LibBlockParameter(Value,ucv,lcv,idx)
        %case "SignalAddr"
          %assign idNum = SLibGetReimAndIdx(idx) 
          %if ucv == "" && lcv == "" && idNum[0] == "" && idNum[1] == 0
            %return SLibBlockParameterBaseAddrAsOutputExpr(Value)
          %else
            %return SLibBlockParameterAddrAsOutputExpr(Value, ucv, lcv, idx)
          %endif
          %%START_ASSERT
        %default
          %assign errTxt = "Unsupported return type: %<retType>"
          %<LibBlockReportError(block,errTxt)>
          %%END_ASSERT
      %endswitch
    %endfunction
    
    %% [EOF] constant.tlc

    18.2 TLC的语法

    TLC是一种以单个%打头的关键字为命令,空格之后跟参数的脚本语言,自身包含了流控制语法、内建函数、关键字和常用命令。

    18.2.1 基本语法

    1. [text|%<expression>]*

      text表示字符串,将原原本本地展开到输出流中。在%<>之中的是TLC变量,通过%<>作用将变量的执行结果显示到输出流中。

    2. %keyword[arguement1,arguement2,...]

      %keyword表示TLC语言中的命令符,[arguement1,arguement2,...]则表示这个命令符所操作的参数。

    如:

    % assign Str = "Hello World"

    %warning、%error、%trace命令可以将其后的变量或字符串的内容输出。

    >> tlc text.tlc
    Warning:  Simulink User
    

    >> tlc text.tlc
    Error:  Simulink User
    Main program:
    ==> [00] text.tlc:<NONE>(1)
    
    错误使用 tlc_new
    Error: Errors occurred - aborting
    
    
    出错 tlc (line 88)
      tlc_new(varargin{:});

    若用%trace命令代替%warning命令显示信息,只有在执行TLC文件时在最后增加-v或者-v1才能将%trace后的信息显示出来。

    TLC语言有两个内建宏TLC_TRUE=1和TLC_FALSE=0,在TLC语言的编写中会经常用到。

    18.2.2 常用指令

    注释

    单行注释:双百分号%%

    多行注释:/% comment %/

    变量内容扩展

    即通过%<>操作符将其内容扩展到输出流中。

    %%text.tlc
    %assign input1 = 3
    %assign input2 = 5
    %warning %<input1> + %<input2> = %<input1 + input2>
    >> tlc text.tlc
    Warning:  3 + 5 = 8

    注意:%<>不能嵌套使用。

    条件分支

    %if expression

    %elseif expression

    %else

    %endif

    例:

    %%text.tlc
    %assign var = 2
    %if ISEQUAL(var,1)
        %warning evering is OK.
    %else
        %warning var should be 1 but now there is something wrong.
    %endif
    >> tlc text.tlc
    Warning:  var should be 1 but now there is something wrong.

    在内建函数ISEQUAL中不需要使用%<>。

    开关分支

    %switch expression

    %case expression

    %break

    %default

    %break

    %endswitch

    每个%case分支后必须跟%break才能起到真正的选择作用,否则将执行下一个%case语句。

    %%text.tlc
    %assign data = [1,2,3,4,5]
    %switch TYPE(data)
    %case "Number"
    %warning Type is Number.
    %break
    %case "String"
    %warning Type is String.
    %break
    %case "Vector"
    %warning Type is Vector.
    %break
    %case "Matrix"
    %warning Type is Matrix.
    %break
    %endswitch
    >> tlc text.tlc
    Warning:  Type is Vector.

    循环

    含%foreach、%roll、%for 3种常用方式。

    (1)%foreach

    %foreach loopIdx = iterNum

    xxxxx

    %endforeach

    上述语句将loopIdx作为循环体句柄变量控制循环进行,从0开始,每次增加1,一直循环到iterMum-1为止。每个%foreach需要使用%endforeach来终止。

    在循环体中可以使用%continue终止当前循环进入下一个循环,或者使用%break直接跳出循环。

    %%text.tlc
    %assign data = [1,2,3,4,5]
    %foreach idx = 5
        %if ISEQUAL(idx,1)
            %continue
        %elseif ISEQUAL(idx,4)
            %break
        %endif
        %warning data[%<idx>] = %<data[idx]>
    %endforeach
    >> tlc text.tlc
    Warning:  data[0] = 1
    Warning:  data[2] = 3
    Warning:  data[3] = 4
    

    (2)%roll

    TLC提供%roll这种循环方式主要是为Simulink模块端口信号相关代码生成定义时使用的。当模块的输入/输出信号是多维时,需要通过%roll对信号的每一维进行循环,使生成的代码同在Simulink环境中进行仿真时具有相同的维数。

    %roll

    %endroll

    例:y=2×u

    /% roll normally used in block tlc files to roll the 
    inport/outport/parameter to access each dimension of them. %/
    /* %<Type> Block: %<Name> */
    %assign rollVars = ["U", "Y"]
    %roll sigIdx = RollRegions, lcv = RollThreshold, block,...
    "Roller", rollVars
    %assign y = LibBlockOutputSignal(0, "", lcv, sigIdx)
    %assign u = LibBlockInputSignal(0, "", lcv, sigIdx)
    %<y> = %<u> * 2;
    %endroll

    %assign rollVars = ["U", "Y", "P"]定义rollVars变量存储循环体所涉及的模块要素,U表示输入端口,Y表示输出端口,在带有参数的模块中,P代表模块的参数。rollVars将被传递给Roller,设置模块输入/输出或参数中每一维roll的结构体。

    %roll sigIdx = RollRegions定义了循环体的循环变量sigIdx,RollRegions是自动计算出来的模块输入/输出或参数的维数向量,如20维输入信号的RollRegions为[0:19],sigIdx按照这个向量进行逐一循环。

    lcv = RollThreshold中lcv是循环控制变量(loop control variable),从RollThreshold获取值,表示当信号维数小于此数值时不生成for循环语句,而逐条生成语句,只有信号或参数的维数大于等于此数值时才生成for循环。TLC全局变量RollThreshold的值可以在Configuration Parameter→Code Generation→Advanced parameters中的Loop unrolling threshold设定,默认值为5。

    LibBlockOutputSignal(portIdx, ucv, lcv, sigIdx)函数包含4个参数,portIdx表示模块输入端口的索引号(对于使能或触发端口,可以使用字符串enable和trigger获取),ucv通常为空,lcv和sigIdx同前面定义。该函数使用TLC库函数获取模块的输入/输出的参数中第sigIdx维所对应的变量,再通过%<y> = %<u> * %<k>;进行代码生成的变量展开。

    在循环体的结尾,使用%endroll作为终止符号。

    (3)for

    %for的使用方法与%foreach基本相同,并且加入了对于是否执行body以外部分进行roll的判断。

    %for ident1 = const-exp1, const-exp2, ident2 = const-exp3

        Code section1

        %body

        Code section2

        %endbody

        Code section3

    %endfor

    当const-exp2为非零值时,则Code section1/2/3所有代码就会执行一次,并且ident2会接收const-exp3的值;当const-exp2为0时则仅执行Code section2段,其他段不执行,并且其以ident1为循环变量进行循环,ident2为空值。

    %%text.tlc
    %for idx = 3, 0, str = "yes"
       %warning OK?
        %body
             %warning Answer is %<str> .
        %endbody
       %warning Over
    
    %endfor
    >> tlc text.tlc
    Warning:  Answer is  .
    Warning:  Answer is  .
    Warning:  Answer is  .
    
    %%text.tlc
    %for idx = 3, 1, str = "yes"
       %warning OK?
        %body
             %warning Answer is %<str> .
        %endbody
       %warning Over
    
    %endfor
    >> tlc text.tlc
    Warning:  OK?
    Warning:  Answer is yes .
    Warning:  Over

    文件流

    TLC语言使用%openfile创建文件流缓存或打开一个文件,%selectfile选中或激活一个存在的文件流缓存或文件,%closefile关闭一个文件流缓存或文件。

    %openfile streamId = "filename.txt" mode {open for writing}

    %selectfile streanId {select an open file}

    %closefile streamId {close an open file}

    %openfile在打开文件时,第2个参数mode可以是a或者w,表示以“追加”或者“重写”的方式创建文件或缓存块,默认重写。当所填的文件名不存在时,则使用StreamId为名创建一个缓存块buffer进行后续操作。在%openfile和%closefile之间的内容将被写入到缓存块buffer中作为变量保存起来,并可以使用%<buffer>将其展开到生成代码中去,这个buffer就称为“流”。

    %%text.tlc
    %openfile buffer = "my_flow_control.txt"
    This is the first time flow control is used. 
    %closefile buffer
    >> tlc text.tlc
    

    StreamID所表示的参数有2个内建流变量:NULL_FILE和STDOUT,分别表示无输出和使用终端输出文件流内容。%selectfile同STDOUT联合使用时,也可以将字符串输出到MATLAB的Command Window中。

    %%text.tlc
    %selectfile STDOUT
    text to be placed in the 'buffer' variable.
    >> tlc text.tlc
    text to be placed in the 'buffer' variable.

    记录

    TLC记录相当于M语言或C语言中的结构体类型,但形式不同,是构成rtw文件的基本元素,格式为record_item{Name Value}。Name是字符串格式,按照字母顺序进行排列;Value既可以是字符串也可以是数据类型,这个数据可以是scalar、向量或矩阵类型。

    (1)创建一个新纪录

    使用%createrecord命令创建一个新纪录。

    %createrecord NEW_RECORD {foo 1;SUB_RECORD {foo 2}}

    NEW_RECORD为新创建的纪录名,{ }内是其所属的子纪录。用一层次的子纪录使用";"隔开,子纪录再创建嵌套子纪录时使用record_item {Name Value}方式。

    可以通过最上层记录名访问其子纪录内容。

    %assign var1 = NEW_RECORD.foo

    %assign var2 = NEW_RECORD.SUB_RECORD.foo

    也可以创建具有全局访问权限的纪录(::表示全局标量标识符)

    %createrecord::NEW_RECORD {foo 1;SUB_RECORD {foo 2}}

    (2)追加纪录

    使用%addtorecord命令向已经存在的额记录中追加新的子纪录。

    %addtorecord OLD_RECORD NEW_FIELD_NAME NEW_FIELD_VALUE

    3个参数,第一个参数为追加纪录的对象,第2个和第3个为子纪录的名字和值。

    %%text.tlc
    %createrecord NEW_RECORD {foo 1;SUB_RECORD {foo 2}}
    %addtorecord NEW_RECORD str "I love Simulink"
    %warning %<NEW_RECORD>
    >> tlc text.tlc
    Warning:  { SUB_RECORD { foo 2 }; foo 1; str "I love Simulink" }
    

    (3)合并纪录

    使用%mergerecord命令合并既存的两个纪录。

    %mergerecord OLD_RECORD NEW_RECORD

    合并后的内容保存在第一个参数中,第二个参数的值不变。

    当新旧两个记录中同样层次下存在相同名的纪录时,则保留这项纪录各自的值不合并。

    %%text.tlc
    %createrecord NEW_RECORD {foo 1;SUB_RECORD {foo 2}}
    %createrecord NEW_RECORD2 {str "I love Simulink"} %mergerecord NEW_RECORD NEW_RECORD2 %warning NEW_RECORD = %<NEW_RECORD> NEW_RECORD2 = %<NEW_RECORD2> %% another demo
    %createrecord NEW_RECORD {foo 1;SUB_RECORD {foo 2}}
    %createrecord NEW_RECORD2 {foo 12} %mergerecord NEW_RECORD NEW_RECORD2 %warning NEW_RECORD = %<NEW_RECORD> NEW_RECORD2 = %<NEW_RECORD2>
    Warning:  { SUB_RECORD { foo 2 }; foo 1; str "I love Simulink" }
    >> tlc text.tlc
    Warning:  NEW_RECORD = { SUB_RECORD { foo 2 }; foo 1; str "I love Simulink" } NEW_RECORD2 = { str "I love Simulink" }
    Warning:  NEW_RECORD = { SUB_RECORD { foo 2 }; foo 1 } NEW_RECORD2 = { foo 12 }
    

    (4)拷贝纪录

    使用%copyrecord命令进行纪录拷贝。

    %copyrecord NEW_RECORD OLD_RECORD

    OLD_RECORD是一个既存的纪录,NEW_RECORD则是OLD_RECORD的一份拷贝。

    %%text.tlc
    %createrecord NEW_RECORD {foo 1;SUB_RECORD {foo 2}}
    %copyrecord NEW_RECORD2 NEW_RECORD
    %warning NEW_RECORD = %<NEW_RECORD> NEW_RECORD2 = %<NEW_RECORD2>
    >> tlc text.tlc
    Warning:  NEW_RECORD = { SUB_RECORD { foo 2 }; foo 1 } NEW_RECORD2 = { SUB_RECORD { foo 2 }; foo 1 }
    

    (5)删除纪录

    使用%undef命令删除记录中的域或者整个纪录。

    %undef var

    var表示一个TLC变量、一个记录名或一个纪录中的域的名字。

    要删除一个域成员,可以借助%with进行范围指定。

    %%text.tlc
    %createrecord NEW_RECORD {foo 1;SUB_RECORD {foo 2}}
    %with NEW_RECORD
    %undef foo
    %endwith
    %warning NEW_RECORD = %<NEW_RECORD> 
    >> tlc text.tlc
    Warning:  NEW_RECORD = { SUB_RECORD { foo 2 } } 

    变量清除

    使用%under命令可以删除TLC变量。

    %assign data = [1,2,3,4,5]
    %undef data
    %foreach idx = 5
        %warning data[%<idx>] = %<data[idx]>
    %endforeach
    
    %% error will occur because data is deleted.
    >> tlc text.tlc
    错误使用 tlc_new
    Error: File: text.tlc Line: 5 Column: 31
    Undefined identifier data
    出错 tlc (line 88)
      tlc_new(varargin{:});

    语句换行连接

    换行符有两种,C语言的“”和M语言的“...”。

    %%text.tlc
    %createrecord NEW_RECORD1...
        {foo 1;SUB_RECORD {foo 2}}
    %warning NEW_RECORD1 = %<NEW_RECORD1> 
    %createrecord NEW_RECORD2
        {foo 1;SUB_RECORD {foo 2}}
    %warning NEW_RECORD2 = %<NEW_RECORD2> 
    >> tlc text.tlc
    Warning:  NEW_RECORD1 = { SUB_RECORD { foo 2 }; foo 1 } 
    Warning:  NEW_RECORD2 = { SUB_RECORD { foo 2 }; foo 1 }

    访问范围

    使用%assign str = "I Love Simulink"建立的string型变量str是局部变量,如果定义在TLC脚本中,只有在此脚本内的语句可以访问;如果是定义在函数里,仅此函数内能访问该变量;特别地,如果变量定义在for等循环语句块内部,只有在这个语句块内部能够访问。

    %%text.tlc
    %assign idx_i = "original string"
    %assign data = [[1, "good"];[3, 4.5F]]
    %foreach idx_i = 2
        %foreach idx_j = 2
            %warning The element of data[%<idx_i>][%<idx_j>] is ...
    %<data[idx_i][idx_j]>
        %endforeach
    %endforeach
    %warning The original is %<idx_i>
    >> tlc text.tlc
    Warning:  The element of data[0][0] is 1
    Warning:  The element of data[0][1] is good
    Warning:  The element of data[1][0] is 3
    Warning:  The element of data[1][1] is 4.5E+0F
    Warning:  The original is original string
    

    "::"为全局变量标识符。

    %assign::str = "I Love Simulink"

    输入文件控制

    输入文件控制包括两种方式:%include string和%addincludepath string

    %include string将在搜索路径下寻找名为string的文件,并在%include语句出现的地方将此文件的内容内联展开,类似于C语言中的#include。

    %addincludepath string中string是一个绝对路径或相对路径,%addincludepath将这个路径添加到TLC的搜索路径中,以便出现%include语句时搜索器包含的文件,类似于MATLAB中的addpath命令。

    TLC搜索路径依照如下顺序:

    1. 当前路径;
    2. 所有的%addincludepath添加的路径,对于多个%addincludepath,依照从下到上的搜索顺序;
    3. 命令行中通过-I命令添加的路径。详见18.2.6TLC命令行。
    %addincludepath "C:\folder1\folder2"  %%添加一个绝对路径
    %addincludepath "\folder2"  %%添加一个相对路径

    输出格式控制

    使用%realformat命令控制输出实变量所显示的格式。

    如使用16位精度的指数显示。

    %realformat "EXPONENTIAL"

    或者无精度损失和最小字符数格式(为S函数提供生成代码功能的模块级TLC文件就使用这种格式)

    %realformat "CONCISE"

    例:

    %%text.tlc
    %createrecord NEW_RECORD {foo 100.0;SUB_RECORD {foo 2}}
    %realformat "EXPONENTIAL"
    %warning %<NEW_RECORD.foo>
    %realformat "CONCISE"
    %warning %<NEW_RECORD.foo>
    >> tlc text.tlc
    Warning:  1.0E+2
    Warning:  100.0

    指定模块生成代码的语言类型

    使用%language命令指定该模块生成代码的语种。如果自定义支持嵌入式代码生成的模块及其TLC文件,那么在TLC文件中就可以使用%language C来指定语言类型为C语言。

    对于Simulink模块的TLC文件,其中必须包含%implements指令。

    %implements "Block-Type" "Language"

    Block-Type是指模块的S函数名,TLC文件也是此名字。Language表示生成的目标代码的语言类型。

    对于自定义的为了生成嵌入式C语言的MCU芯片驱动中断控制器模块,其TLC文件开头必须包含这样一个命令:

    %implements Interrupt "C"

    紧接此句的是%function的函数定义,实现S函数代码生成的具体功能。

    另外,%generatefile提供了一个匹配关系,将Simulink模块和TLC文件联系起来。

    %generatefile "Type" "blockwise.tlc"

    Type为rtw文件中模块的记录中Type参数的值,也即模块的blocktype属性,如:%generatefile "Sin" "sin_wave.tlc"。

    断言

    使用%assert命令为TLC断言。

    %assert expression

    %%text.tlc
    %assert TLC_FALSE
    >> tlc text.tlc -da
    错误使用 tlc_new
    Error: File: text.tlc Line: 2 Column: 1
    Assertion failed
    

    -da表示使TLC执行%assert命令。

    当使用rtwbuild()命令或Ctrl+B启动模型代码生成时,为了开启TLC的断言功能,必须在Configuration Parameter中勾选Enable TLC assertion。

    当expression结果为TLC_FALSE时,TLC将进行堆栈追踪。

    函数

    使用%function为开头定义函数,以%endfunction为终止符结束函数体。

    %function name(optional-arguments) void

    %return

    %endfunction

    %function GoodBad(Message) void
    %warning Good or Bad %<Message>?!
    %endfunction
    %%text.tlc
    %warning GoodBad(OK)
    >> tlc text.tlc
    Warning:  GoodBad(OK)

    18.2.3 变量类型

    TLC语言使用的变量类型和MATLAB变量所使用的内建类型有所不同。在TLC语言中不仅是数据的类型,甚至数据的组织方式都被作为一个单独的类型,如Matrix、Vector。Range等。

    数据类型名(简写) 表现形式举例 说明
    Boolean(B) 0==0 返回值为TLC_TRUE或TLC_FALSE,由逻辑操作返回
    Number(N) 100 整型数
    Real(D)s 3.14159 浮点数
    Real32(F)s 3.14159F 32位浮点数
    Complex(C) 1.0+2.0i 64位双精度浮点数
    Complex32(C32) 10.F+2.0Fi 32位单精度浮点数
    Gaussion(G) 1+2i 32位整型复数
    Unsigned(U) 100U 32位无符号整数
    Unsigned Gaussion(UG) 1U+2Ui 32位无符号整型复数
    String "I Love MATLAB" 包含在双引号中的字符串
    Identifier abc 此类型仅出现在rtw记录中,不可直接在TLC表达式中使用。如果希望比较其内容,可直接与String类型比较,Identifer将自动转换为String类型
    File

    %openfile out="xx.c"

    %openfile buffer

    使用%openfile打开的字符串缓冲或文件
    Function %function my_func ... TLC的函数类型,需要使用%endfunction结束
    Range [1:10] 表示一组整数序列,如RollRegions使用在Roll循环体中表示循环变量遍历的值
    Vector [1,2.0F,"good"] 向量类型,每个元素可以是TLC的内建类型,但不能是Vector或Matrix
    Matrix %assign a=[[1,"good"];[3,4.5F]] 矩阵类型,矩阵中每个元素类型可以不同,但是不能为Vector或Matrix
    Scope system {...} 范围类型,如rtw中的CompiledModel、block记录的范围
    Subsystem <sub> 子系统标示符,作为语句扩展的内容
    Special FILE_EXISTS 特殊内建类型如FILE_EXISTS

    18.2.4 操作符和表达式

    操作符和表达式 作用说明
    ::variable 全局变量,当出现在函数中时,告诉函数该变量的访问权限是全局的,不适用函数的局部访问权限
    expr[indx] 数组或矩阵的下标索引访问方式,indx必须是0~(N-1),N为数组长度或矩阵某一维的长度
    func([expr[,expr]...]) 函数调用,func为函数名,expr为函数的参数列表
    expr.expr 域访问符,第一个expr为纪录类型,第2个expr为其内部的一个参数名
    (expr) 括号,括号内的表达式优先度高
    !expr 逻辑取反,expr必须是Bollean或数值类型,返回值为TLC_TURE或TLC_FALSE
    -expr 一元操作符,取负,expr必须为数值类型
    +expr 一元操作符,取正,expr必须为数值类型,一般表达式默认为,不必写出
    ~expr 按位取反,expr必须为整数
    expr*expr 乘法运算符,expr必须为数值类型
    expr/expr 除法运算符,expr必须为数值类型
    expr+expr

    +运算符,可以用于scalar、vector、matrix和record等多种数据类型,且作用不同

    用于scalar数值时表示两个数相加,此时expr必须为数值类型

    用于string时,将两个字符串拼接起来返回

    当首个expr是vector,第二个expr是数值类型时,将第二个数追加到vector中

    当首个expr是matrix,第二个expr是vector时,如果vector与matrix的列数相同,则追加到matrix中作为新的一行元素

    如果首个expr是记录类型,则第二个expr作为一个参数追加到这个记录类型中去,其值为第二个expr的当前值

    expr-expr 减法运算符,expr必须是数值类型
    expr%expr 求余运算符,expr必须是整数类型
    expr<<expr2 左移操作符,将expr按照二进制位向左移动expr2位
    expr>>expr2 右移操作符,将expr按照二进制位向右移动expr2位。>>不能直接在%<>中被识别,需要使用进行转义,如%<expr>>expr2>
    expr<expr2 比较expr是否小于expr2,expr和expr2都必须是数值类型
    expr>expr2 比较expr是否大于expr2,expr和expr2都必须是数值类型,在%<>中时需要进行转义,如%<expr>expr2>
    expr<=expr2 比较expr是否小于等于expr2,expr和expr2都必须是数值类型
    expr>=expr2 比较expr是否大于等于expr2,expr和expr2都必须是数值类型,在%<>中时需要进行转义,如%<expr>=expr2>
    expr==expr2 比较expr是否等于expr2
    expr!=expr2 比较expr是否不等于expr2
    expr&expr2 将两个操作数进行二进制按位与操作,expr和expr2必须是整数类型
    expr|expr2 将两个操作数进行二进制按位或操作,expr和expr2必须是整数类型
    expr^expr2 将两个操作数进行二进制按位异或操作,expr和expr2必须是整数类型
    expr&&expr2 将两个操作数进行按逻辑与操作,返回值为TLC_TRUE或TLC_FALSE,expr和expr2可以是数值类型或Boolean类型
    expr||expr2 将两个操作数进行按逻辑或操作,返回值为TLC_TRUE或TLC_FALSE,expr和expr2可以是数值类型或Boolean类型
    expr?expr1:expr2 当expr值为TLC_TRUE时返回expr1,否则返回expr2
    expr1,expr2 逗号分隔符,返回后面的变量expr2,多个逗号分开时返回最后一个变量

    注意:没有幂运算符。

    18.2.5 TLC内建函数

    TLC提供的内建函数全部使用大写字母书写。

    数据类型转换

    CAST函数是TLC语言中负责数据类型转换的重要函数。

    CAST("DataType", variablename)

    第1个参数表示第2个参数转换的目标类型名,如"Number"、"Real32"、"String"等,第2个操作数则是进行转换的目标操作数。

    %%text.tlc
    %assign data =[[1, "good"]; [3, 4.5F]]
    %assign cast = CAST("Real32", %<data[0][0]>)
    %warning %<cast>
    >> tlc text.tlc
    Warning:  1.0E+0F

    变量的存在性

    通常为了避免语法错误,在访问记录的某个参数前需要确定它是否真正存在,这是就需要使用EXISTS函数,另外,对于文件的存在性,需要使用FILE_EXISTS(expr)函数。

    EXISTS(Var)

    %%text.tlc
    %createrecord record {foo 1 subrec{foo 2}} %if EXISTS(record.foo) %warning record has a parameter which value is %<record.foo>. %endif %if FILE_EXISTS("text.tlc") %warning text.tlc is on the path. %endif
    >> tlc text.tlc
    Warning:  record has a parameter which value is 1.
    Warning:  text.tlc is on the path.

    记录的域操作

    ISFIELD:判断某个字符串表示的参数名是否是记录的域;

    GETFIELD:获取记录中参数的值;

    SETFIELD:设置记录中参数的值;

    REMOVEFIELD;删除记录中某参数。

    %%text.tlc
    %createrecord record {foo 1 subrec{foo 2}}
    %if ISFIELD(record, "foo")
    %warning record has a parameter foo which value is %<GETFIELD(record, "foo")>.
    %<SETFIELD(record, "foo", 12)>
    %warning parameter foo value is %<GETFIELD(record, "foo")> now.
    %<REMOVEFIELD(record, "foo")>
    %warning parameter foo of record now is existed? Ans: %<ISFIELD(record, "foo")>
    %endif
    >> tlc text.tlc
    Warning:  record has a parameter foo which value is 1.
    Warning:  parameter foo value is 12 now.
    Warning:  parameter foo of record now is existed? Ans: 0

    除了上述4个函数之外,还有FIELDNAMES函数可以查询记录中包含的内一层的纪录名。

    %%text.tlc
    %createrecord record {foo 1 subrec{foo 2}}
    %warning %<FIELDNAMES(record)>
    >> tlc text.tlc
    Warning:  [foo, subrec]

    相等判断

    使用ISEQUAL函数可以判断2个变量是否相等。

    ISEQUAL(expr1, expr2)

    当expr1、expr2都为数值类型时,即使不是同一个数据类型,只要表达式运算的值大小相等,都会返回TLC_TRUE,否则才返回TLC_FALSE。如果expr1和expr2不是数据类型,二者的变量类型和内容必须完全一样时才会返回TLC_TRUE。

    %%text.tlc
    %warning %<ISEQUAL(TLC_TRUE,1)>
    %warning %<ISEQUAL(3,3.0F)>
    %warning %<ISEQUAL("Str","St")> 
    >> tlc text.tlc
    Warning:  1
    Warning:  1
    Warning:  0 

    判断变量类型

    使用TYPE函数可以返回一个变量的类型,格式为:

    TYPE(expr)

    %%text.tlc
    %warning %<TYPE("Str")>
    %warning %<TYPE([1,2;3,5])>
    %warning %<TYPE(10.3F+6.71Fi)> 
    >> tlc text.tlc
    Warning:  String
    Warning:  Matrix
    Warning:  Complex32 

    判断空格

    判断空格与判断空值不同,当一个变量内仅仅包含空格,如 、 、 等时返回1,否则返回零。

    WHITE_SPACE函数就是判断参数是否为全空格的函数。

    WHITE_SPACE(expr)

    %%text.tlc
    %warning %<WHITE_SPACE("	
    
    
    ")>
    >> tlc text.tlc
    Warning:  1调用

    matlab函数

    TLC本身虽然没有MATLAB那么强大的函数库,却可以方便地调用MATLAB的内建函数。调用没有返回值的MATLAB函数时使用%matlab命令,调用有返回值的函数时则使用FEVAL命令,对于有多个返回值的函数,TLC只能接受首个返回值。

    %matlab fun(agr)

    %%text.tlc
    %matlab disp("TLC calls MATLAB function.")
    >> tlc text.tlc
    TLC calls MATLAB function.
    

    %assign result = FEVAL("MATLAB-function-name", rhs1, rhs2, ..., rhs3, ...)

    FEVAL函数的首个参数为MATLAB函数名,用双引号括起来,其后参数为这个MATLAB函数的参数列表,返回值只能接收MATLAB函数的首个返回值,且其数据类型自动转换为TLC的内建数据类型。

    %%text.tlc
    %assign data = FEVAL("sin", [0:9])
    %matlab disp(data)
    %warning The data type of variable "data" is %<TYPE(data)>. 
    %warning And the element data type is %<TYPE(data[0])>.
    >> tlc text.tlc
             0    0.8415    0.9093    0.1411   -0.7568   -0.9589   -0.2794    0.6570    0.9894    0.4121
    
    Warning:  The data type of variable "data" is Vector. 
    Warning:  And the element data type is Real.

    又:

    %%text.tlc
    %realformat "CONCISE"
    %assign pos = FEVAL("regexp", "I love Simulink", "Simu")
    %assign pos = CAST("Number", pos)
    %warning The index of Simu is %<pos>.
    >> tlc text.tlc
    Warning:  The index of Simu is 8.
    

    18.2.6 TLC命令行

    tlc[switch1 expr1 switch2 expr2 ...] filename.tlc

    switchx exprx这样的开关命令可以有多个,顺序随意,switchx表示开关符号,exprx表示开关的参数。如果同一个开关符号出现多次,那么最后一个是有效的。

    开关 意义
    -r filename 读取数据文件载入到TLC中,如rtw文件
    -v[number] 设置输出信息的详细级别为number,不设置时默认级别为1
    -Ipath 增加特定文件夹路径到TLC搜索路径中
    -Opath 指定输出文件的路径,输出文件包括%openfile和%closefile生成的流文件或者日志文件
    -m[number] 指定最大的操作数为number,不设置时,默认报出前5个错误,如果只写-m而不写number,则认为number为1
    -x0 仅解析TLC文件而不执行
    -lint 进行一些简单的性能检查
    -p[number] 设置TLC每执行number个操作输出一个
    -d[a|c|f|n|o]

    启动TLC的debug模式

    -da使TLC执行%assert命令

    -dc启动TLC命令行调试器

    -df filename将启动TLC调试器并运行名为filename的tlc调试脚本文件。所谓调试脚本文件,就是包含调试命令的文本文件。TLC仅在当前路径下搜索有效脚本文件

    -dn将为所执行的tlc文件产生行覆盖度日志,告知哪一行被执行了,哪一行没有执行

    -do则停止调试行为

    -dr 检查是否存在环形文件,这种文件彼此相互引用,会造成内存泄漏
    -a[ident]=expr 为一个变量ident设置一个初始值expr,与%assign命令功能相同
    -shadow[0|1]

    设置是否开启遮蔽警告功能,当记录中的参数覆盖了一个局部变量时:

    -shadow0关闭警告

    -shadow1开启警告

    %%text.tlc
    %if 0
    %assign at = "gsd"
    %endif
    >> tlc text.tlc -dn
    
    Source: C:UserslenovoDesktop	ext.tlc
         0: %%text.tlc
         1: %if 0
         0: %assign at = "gsd"
         0: %endif

    又:untitled.rtw

    CompiledModel {
      Name              "untitled"
      OrigName          "untitled"
      Version          "8.5 (R2013b) 08-Aug-2013"
      SimulinkVersion      "8.2"
      ModelVersion          "1.0"
    %%text.tlc
    %warning CompiledModel.name = %<GETFIELD(CompiledModel, "Name")>
    >> tlc text.tlc -r untitled.rtw -v
    Warning: File: text.tlc Line: 2 Column: 9
    %warning directive:  CompiledModel.name = untitled
    

    18.2.7 TLC调试方法

    在Configuration Parameter中开启Start TLC debugger when generating code,才能够在生成模型的代码时进入到调试模式。

    在按下Ctrl+B后在Command Window上动态显示编译信息,便进入了TLC调试模式。

    使用whos命令查看当前访问范围内存在的变量及变量的TLC数据类型。

    使用print命令查看某个变量的值,在print命令后也可以使用TLC内建函数。

    TLC-DEBUG> print BlockCommentType
    BlockPathComment
    
    TLC-DEBUG> print TYPE(CombineOutputUpdateFcns)
    Number

    list命令后面可以跟两个整数,如list 10,20,意义是显示当前TLC文件的第10~20行代码。

    TLC-DEBUG> list 10,15
    00010: %% Abstract: Embedded real-time system target file.
    00011: %%
    00012: %selectfile NULL_FILE
    00013: 
    00014: %assign CodeFormat = "Embedded-C"
    00015: 

    使用next命令(或简易命令n)可进行单步执行,step则是step in执行。使用continue或cont可以全速执行,只有遇到断点和错误时停下。断点设置通过break或b实现,并制定断点所在的文件及行数,文件名及行数之间使用冒号":"分隔,如break ert.tlc:14。如果break命令后面的数值对应的行没有代码(全部为空或全部为注释 ),那么TLC调试器将会自动把断点转移到其后最近的有效代码行,并给出warning提醒。

    TLC-DEBUG> break ert.tlc:13
    Warning: File: Debugger Command Line Line: 1 Column: 1
    Breakpoint number 1 was set on the next valid line (14)

    TLC-DEBUG> cont

    Breakpoint 1
    00014: %assign CodeFormat = "Embedded-C"

    每次断点设置后,运行到断点处会停下来,断点序号就被显示出来,这个序号作为断点的标示符,可以用clear命令清除,如clear 1,clear all可以清除已经设置的全部断点,或者编译模型生成代码全过程执行完毕之后,断点也会自动清除。

    使用Disable可以关闭断点,使用Enable再次打开。

    在调试模式下,可以省略%直接使用assign赋值,其他TLC命令不能在调试模式下使用。

    TLC-DEBUG> assign str = "Let me try TLC"
    
    TLC-DEBUG> print str
    Let me try TLC

    调试结束后,可以输入cont运行代码生成流程,或者quit命令退出调试模式。

    其他的调试命令还有:Condition、up、down、finish、ignore、loadstate、savestate、stop、thread、threads和where等,使用helpcommand可以查询命令的帮助。

    TLC-DEBUG> help finish
      finish - Break after completing the current function
        Continues execution from where it is stopped, and re-enters the debugger after
        the current function has exited, or some other reason to enter the debugger
        (e.g., a breakpoint or error) is encountered, whichever comes first.

    18.2.8 TLC文件的覆盖度

    勾选该项后,代码生成过程中,TLC编译器会为每个被执行的TLC文件生成一个log文件,存放在model_ert_rtw文件夹中。

    这些log文件对TLC的每行语句在代码生成过程中的执行次数做统计,0表示没有被执行,1则表示执行一次。

     

    根据log文件,开发者能够发现那些分支语句没有被执行过,并根据这个分支语句的判断条件进行新的测试事件的设置,重新执行并分析覆盖度,不断改进,使tlc文件编写得更加可靠高效。

    注意:TLC编译器认为下列命令是不执行的语句,其执行次数都是0,也不产生时间消耗。

    %filescope %else %endif %endforeach %endfor %endroll %endwith %body %endbody %endfunction %endswitch %default

    Comment: %% or /% text %/

    18.2.9 TLC Profiler

    TLC代码的执行时间取决于TLC脚本、宏、函数和内建函数这些构成TLC代码的元素的执行时间。

    勾选该选项,TLC Profiler可以再执行过程中收集TLC代码各个元素的执行时间,并汇总到HTML的报告中,让开发者更容易分析找到代码生成过程中最话费时间的代码。

    18.3 为S函数编写TLC文件

    18.3.1 支持代码生成的S函数

    只有C MEX S函数和Level2 M S函数才支持代码生成功能,并且这个功能要求S函数有配套的TLC文件。

    带有参数的S函数首先从GUI上获取用户的输入作为参数,通过C语言的宏将GUI控件上的值读入S函数,再通过S函数的子方法mdlRTW将参数值写入到模型的rtw文件中,使得TLC 文件能够获取这些参数的值,最终展开到生成代码中合适的位置中去。

    C MEX S函数获取GUI参数的宏

    (1)获取Edit中的数值

    #define PARAM(S)(mxGetScalar(ssGetSFcnParam(S,PARAM_INDEX)))

    PARAM_INDEX为Edit等控件的参数在GUI控件中的索引号,首先通过ssGetSFcnParam宏函数获取指向Edit控件中参数的数值,再使用mxGetScalar宏函数获取指针指向地址的数值。

    (2)获取Edit中的数组

    #define PARAM(S) mxGetNumberOfElements(ssGetSFcnParam(S,PARAM_INDEX))

    (3)在Edit中获取字符串

    #define PARAM(S)(ssGetSFcnParam(S,PARAM_INDEX))

    (4)获取Popup/radiobutton所选项目的字符串

    #define PARAM(S)(mxArrayToString(ssGetSFcnParam(S,PARAM_INDEX)))

    (5)获取Popup/radiobutton所选项目的索引号

    (6)获取Check-box的值

    C MEX S函数的mdlRTW函数

    mdlRTW函数是专为支持代码生成的S函数设计的,不支持仅用于仿真的C MEX S函数,其功能为传递S函数的参数数据到rtw文件(这些参数必须被设置为not tunable)。它必须被包含在以下 这个预处理语句中。

    #if defined(MATLAB_MEX_FILE)

    #endif

    则函数定义体呈现为:

    #ifdefined(MATLAB_MEX_FILE)

    #define MDL_RTW

    static viod mdlRTW(SimStruct *S)

    {

    }

    18.3.2 模块TLC文件的构成

    子函数 输出 功能说明
    BlockInstanceSetup(block,  system) 不产生输出 同种类的模块存在多个时,每一模块都会执行一次此函数,可以将模块共同的操作或特例的操作写入此函数中
    BlockTypeSetup(block, system) 不产生输出 同类模块即使存在多个也只执行此函数一次,可以将同一类模块共同地且执行一次的操作写入此函数中,也可以不实现此函数
    Enable(block, system) 产生输出 为模块中非虚拟子系统创建Enable函数,并将使能某功能的代码生成在该函数中
    Disable(block, system) 产生输出 为模块中非虚拟子系统创建Disable函数,并将禁止某功能的代码生成在该函数中
    Start(block, system) 产生输出 为模块中仅执行一次的函数,内部代码会生成到model_initialize()函数中,通常将模型各变量、状态或硬件外设初始化的代码写在此函数中,因为他们不需要重复执行
    InitializeCondition(block, system) 产生输出 此函数里的代码通常也用于初始化某个子系统的状态变量,但是它不一定仅执行一次,而是在当前模块所在的子系统每次被使能时都会执行
    Outputs(block, system) 产生输出 用于编写模块计算输出的代码,并将其生成到model_step()函数中
    Update(block, system) 产生输出 用于编写每个步长更新模块状态变量的代码,其内容生成到model_update()中
    Derivatives(block, system) 产生输出 用于计算模块连续变量的函数,其内容生成到model_Derivatives()函数中
    Terminate(block, system) 产生输出 此函数用于自定义代码用,可以存储数据,释放内存,复位硬件寄存器等操作,此函数内的代码将生成到MdlTerminate()中

    18.3.3 模块TLC函数实例

    以自定义滤波器为例(第10章)。

    %implements sfun_c_filter "C"
    
    %% Function: blockTypeSetup ==========================================================
    %%
    %% Purpose:
    %%      Add some macro defines .
    %%
    %function BlockTypeSetup(block, system) void
    
    %endfunction
    
    
    %% Function: Start ==========================================================
    %%
    %% Purpose:
    %%   
    %%     these code will appear at model.c initialization function
    %%
    %function Start(block, system) Output
        /* If need user can add custom initialize code here */
    %endfunction
    
    
    %% Function: Outputs ==========================================================
    %%
    %% Purpose:
    %%   
    %%     these code will appear at model.c step function
    %%
    %function Outputs(block, system) Output
    
      %assign t_coef = SFcnParamSettings.r_coef
        %assign rollVars = ["U", "Y","DWork"]
        %roll sigIdx = RollRegions, lcv = RollThreshold, block, "Roller", rollVars
            %assign u = LibBlockInputSignal(0, " ", lcv, sigIdx)
            %assign y = LibBlockOutputSignal(0, " ", lcv, sigIdx)
            %assign x = LibBlockDWork(dwork, "", lcv, sigIdx)
                /* Calculate the filter result */
                %<y> = (%<u> - %<x>) * %<t_coef> + %<x>;
                %<x> = %<y>;
        %endroll
    
    %endfunction
  • 相关阅读:
    文件类型的判断
    VS 2003 源码配置管理(subversion+apache)
    DataView
    sql server 挂起的文件操作
    关于权限设计的轻量级实现
    各种类型文件在SQL Server中存储的解决方案
    免费或开源的项目管理工具
    UML中的用例(Use Case)概念分析及实例
    大对象的存储
    用js实现同一页面多个运动效果
  • 原文地址:https://www.cnblogs.com/dingdangsunny/p/12276167.html
Copyright © 2011-2022 走看看