Simulink仿真入门到精通(十八) TLC语言

摘要:
Simulink提供了许多现有的TLC文件。未经授权的修改可能会导致SimulinkCoder中的功能错误,因此Mathworks鼓励用户尽量不要修改TLC。但如果你能熟练掌握TLC的运行机制和编写方法,你不仅可以不损害SImulink的功能,还可以熟练地使用TLC语言来实现更多的自动代码生成功能。%<>为TLC变量。变量的执行结果通过%.%显示在输出流中。%关键字[argument1,argument2,…]%关键字表示TLC语言中的命令,而[argument 1,argument 2,…]表示此命令操作的参数。切换分支%switchexpression%caseexpression%break%default%break%endswitch每个%case分支必须后跟%break才能进行真正的选择。否则,将执行下一个%case语句。

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命令可以将其后的变量或字符串的内容输出。

Simulink仿真入门到精通(十八) TLC语言第1张

>> tlc text.tlc
Warning:  Simulink User

Simulink仿真入门到精通(十八) TLC语言第2张

>> 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。

Simulink仿真入门到精通(十八) TLC语言第3张

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

Simulink仿真入门到精通(十八) TLC语言第4张

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。

Simulink仿真入门到精通(十八) TLC语言第5张

当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)s3.14159浮点数
Real32(F)s3.14159F32位浮点数
Complex(C)1.0+2.0i64位双精度浮点数
Complex32(C32)10.F+2.0Fi32位单精度浮点数
Gaussion(G)1+2i32位整型复数
Unsigned(U)100U32位无符号整数
Unsigned Gaussion(UG)1U+2Ui32位无符号整型复数
String"I Love MATLAB"包含在双引号中的字符串
Identifierabc此类型仅出现在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
Scopesystem {...}范围类型,如rtw中的CompiledModel、block记录的范围
Subsystem<sub>子系统标示符,作为语句扩展的内容
SpecialFILE_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,才能够在生成模型的代码时进入到调试模式。

Simulink仿真入门到精通(十八) TLC语言第6张

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

Simulink仿真入门到精通(十八) TLC语言第7张

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

Simulink仿真入门到精通(十八) TLC语言第8张

使用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文件的覆盖度

Simulink仿真入门到精通(十八) TLC语言第9张

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

Simulink仿真入门到精通(十八) TLC语言第10张

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

 Simulink仿真入门到精通(十八) TLC语言第11张

根据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代码的元素的执行时间。

Simulink仿真入门到精通(十八) TLC语言第12张

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

18.3 为S函数编写TLC文件

18.3.1 支持代码生成的S函数

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

Simulink仿真入门到精通(十八) TLC语言第13张

带有参数的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章)。

Simulink仿真入门到精通(十八) TLC语言第14张

%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

免责声明:文章转载自《Simulink仿真入门到精通(十八) TLC语言》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇juniper 命令frp 开机自启动下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Scala从入门到精通之四-映射和元组

在Scala中映射之键值对的集合,元组是n个对象的聚集,但是对象的类型不一定相同 本节内容要点   Scala中映射的创建,遍历和查询   如何从可变和不可变映射中做出选择   Scala映射和Java映射见的互操作   Scala中元组的使用 一.构造映射 我们可以这样来构造一个映射: val scores = Map("ysl"->100,"...

修改Android中strings.xml文件, 动态改变数据

有些朋友可能会动态的修改Android中strings.xml文件中的值,在这里给大家推荐一种简单的方法。strings.xml中节点是支持占位符的,如下所示: <string name="data">整数型:%1$d,浮点型:%2$.2f,字符串:%3$s</string> 其中%后面是占位符的位置,从1开始,          ...

Golang中使用lua进行扩展

前言 最近在项目中需要使用lua进行扩展,发现github上有一个用golang编写的lua虚拟机,名字叫做gopher-lua.使用后发现还不错,借此分享给大家. 数据类型 lua中的数据类型与golang中的数据类型对应关系作者已经在文档中说明,值得注意的是类型是以L开头的,类型的名称是以LT开头的.golang中的数据转换为lua中的数据就必须转...

vue 点击图片获取x,y坐标值

点击图片拿到x,y值 template中 <image x="0"y="0"width="100%"height="100%"xlink:href="../../assets/images/background/bg_nav.png"@click="tapMap($event)" /> js中 tapMap(e) { //cons...

Android游戏开发实践(1)之NDK与JNI开发01

Android游戏开发实践(1)之NDK与JNI开发01 NDK是Native Developement Kit的缩写,顾名思义,NDK是Google提供的一套原生Java代码与本地C/C++代码“交互”的开发工具集。而Android是运行在Dalvik虚拟机之上,支持通过JNI的方式调用本地C/C++动态链接库。C/C++有着较高的性能和移植性,通过这种...

Java接口自动化测试实战001----get、post方法实现与封装

一、接口测试 1、接口测试流程 根据接口文档编写测试用例 准备测试数据 准备工具(测试工具or接口测试代码) 填写接口信息(接口地址、请求方式等) 准备请求头数据(如果有必要,比如:cookies,Content-Type等) 发起请求,获取接口的相应信息(状态码、响应报文、或者某些特殊的响应头数据) 根据报文判断实际与预期结果是否一致 2、HTTP请...