Simulink仿真入门到精通(十八) TLC语言
TLC(Target Language Compiler)是一种为转换为目标语言而存在的额解释性语言,其目的就是将模型中编译出来的rtw文件转换为目标代码(C/C++等)。与M语言类似,既可以写成脚本文件,也能够作为函数存在,都是解释性语言,更相似的是它们都提供具有强大功能的内建函数库。
18.1 TLC的作用
- 支持模型针对通用或特定目标硬件的代码生成功能;
- 为S函数模块提供代码生成功能,可以让用户自己增加支持代码生成的模块;
- 在代码生成过程中,生成不依赖S函数模块的自定义过程代码。
Simulink中提供了很多既有TLC文件,如果擅自修改可能导致Simulink Coder功能性错误,故Mathworks提倡用户尽量不要修改TLC。但是如果能熟练掌握TLC的运行机制和编写方法,不仅不会伤害SImulink的功能,还可以巧妙利用TLC语言实现更多的自动化代码生成功能。
如matlab目录下Constant.tlc文件内容如下:
%%
%%
%%
%%
%% Copyright - 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[] == "" && idNum[] ==
%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 基本语法
- [text|%<expression>]*
text表示字符串,将原原本本地展开到输出流中。在%<>之中的是TLC变量,通过%<>作用将变量的执行结果显示到输出流中。
- %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搜索路径依照如下顺序:
- 当前路径;
- 所有的%addincludepath添加的路径,对于多个%addincludepath,依照从下到上的搜索顺序;
- 命令行中通过-I命令添加的路径。详见18.2.6TLC命令行。
%addincludepath "C:\\folder1\\folder2" %%添加一个绝对路径
%addincludepath "\\folder2" %%添加一个相对路径
输出格式控制
使用%realformat命令控制输出实变量所显示的格式。
如使用16位精度的指数显示。
%realformat "EXPONENTIAL"
或者无精度损失和最小字符数格式(为S函数提供生成代码功能的模块级TLC文件就使用这种格式)
%realformat "CONCISE"
例:
%%text.tlc
%createrecord NEW_RECORD {foo .;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 =[[, "good"]; [3, 4.5F]]
%assign cast = CAST("Real32", %<data[][]>)
%warning %<cast>
>> tlc text.tlc
Warning: 1.0E+0F
变量的存在性
通常为了避免语法错误,在访问记录的某个参数前需要确定它是否真正存在,这是就需要使用EXISTS函数,另外,对于文件的存在性,需要使用FILE_EXISTS(expr)函数。
EXISTS(Var)
%%text.tlc
%createrecord record {foo subrec{foo }}
%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 subrec{foo }}
%if ISFIELD(record, "foo")
%warning record has a parameter foo which value is %<GETFIELD(record, "foo")>.
%<SETFIELD(record, "foo", )>
%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 subrec{foo }}
%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,)>
%warning %<ISEQUAL(,.0F)>
%warning %<ISEQUAL("Str","St")>
>> tlc text.tlc
Warning: 1
Warning: 1
Warning: 0
判断变量类型
使用TYPE函数可以返回一个变量的类型,格式为:
TYPE(expr)
%%text.tlc
%warning %<TYPE("Str")>
%warning %<TYPE([,;3,5])>
%warning %<TYPE(.3F+.71Fi)>
>> tlc text.tlc
Warning: String
Warning: Matrix
Warning: Complex32
判断空格
判断空格与判断空值不同,当一个变量内仅仅包含空格,如\n、\t、\r等时返回1,否则返回零。
WHITE_SPACE函数就是判断参数是否为全空格的函数。
WHITE_SPACE(expr)
%%text.tlc
%warning %<WHITE_SPACE("\t\n\r\r")>
>> 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", [:])
%matlab disp(data)
%warning The data type of variable "data" is %<TYPE(data)>.
%warning And the element data type is %<TYPE(data[])>.
>> 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
%assign at = "gsd"
%endif
>> tlc text.tlc -dn
Source: C:\Users\lenovo\Desktop\text.tlc
: %%text.tlc
: %if
: %assign at = "gsd"
: %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 ,
: %% Abstract: Embedded real-time system target file.
: %%
: %selectfile NULL_FILE
:
: %assign CodeFormat = "Embedded-C"
:
使用next命令(或简易命令n)可进行单步执行,step则是step in执行。使用continue或cont可以全速执行,只有遇到断点和错误时停下。断点设置通过break或b实现,并制定断点所在的文件及行数,文件名及行数之间使用冒号":"分隔,如break ert.tlc:14。如果break命令后面的数值对应的行没有代码(全部为空或全部为注释 ),那么TLC调试器将会自动把断点转移到其后最近的有效代码行,并给出warning提醒。
TLC-DEBUG> break ert.tlc:
Warning: File: Debugger Command Line Line: Column:
Breakpoint number was set on the next valid line ()
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(, " ", lcv, sigIdx)
%assign y = LibBlockOutputSignal(, " ", lcv, sigIdx)
%assign x = LibBlockDWork(dwork, "", lcv, sigIdx)
/* Calculate the filter result */
%<y> = (%<u> - %<x>) * %<t_coef> + %<x>;
%<x> = %<y>;
%endroll %endfunction
Simulink仿真入门到精通(十八) TLC语言的更多相关文章
- Simulink仿真入门到精通(八) M语言对Simulink模型的自动化操作及配置
8.1 M语言控制模型的仿真 M语言与Simulink结合的方式: 在Simulink模型或模块中使用回调函数 在M语言中调用与模型相关的命令,控制模型的建立,设置模块的属性,增删信号线,以及运行模型 ...
- Simulink仿真入门到精通(十九) 总结回顾&自我练习
从2019年12月27到2020年2月12日,学习了Simulink仿真及代码生成技术入门到精通,历时17天. 学习的比较粗糙,有一些地方还没理解透彻,全书梳理总结: Simulink的基础模块已基本 ...
- Simulink仿真入门到精通(十七) Simulink代码生成技术详解
17.1 基于模型的设计 基于模型设计是一种流程,较之传统软件开发流程而言,使开发者能够更快捷.更高效地进行开发.适用范围包括汽车电子信号处理.控制系统.通信行业和半导体行业. V字模型开发流程整体描 ...
- Simulink仿真入门到精通(五) Simulink模型的仿真
5.1 模型的配置仿真 由各种模块所构建的可视化逻辑连接,只是模型的外在表现,模型仿真的核心驱动器是被称作解算器(Solver)的组件,相当于Simulink仿真过程的心脏,驱动着模型仿真,它在每一个 ...
- Simulink仿真入门到精通(三) Simulink信号
3.1 Simulink信号概述 所谓信号,表示一种随着时间而变化的量,在时间轴上的采样时刻都对应有数值. 信号在Simulink中是相当重要的组成部分,有线(line)表示,在模型中穿针引线地将各模 ...
- Simulink仿真入门到精通(十) S函数
10.1 S函数概述 S函数也称为Simulink中的系统函数,是用来描述模块的Simulink宏函数,支持M.C等多种语言.当Simulink默认的模块不能满足用户的需求时,用户可以通过S函数自己打 ...
- Simulink仿真入门到精通(十六) Simulink基于模型设计的工业应用概述
16.1 Simulink用途概述 在基于模型设计广泛应用于汽车电子嵌入式开发的今天,MBD(Model Besed Design)技术也逐步推广到各种嵌入式控制方面.与传统的嵌入式开发相比,BMD以 ...
- Simulink仿真入门到精通(十五) Simulink在流程工业中的仿真应用
15.1 工业乙醇生产与计算机仿真 乙醇作为可再生清洁能源不仅可以代替四乙基铅作为汽油的防爆剂,还可以制造汽油醇.这一巨大的潜在需求促使人们去寻找提高乙醇工业生产率的途径,使人们着手于发酵工程的研究. ...
- Simulink仿真入门到精通(十四) Simulink自定义环境
14.1 Simulink环境自定义功能 sl_sustomization.m函数是Simulink提供给用户使用MATLAB语言自定义Simulink标准人机界面的函数机制.若sl_sustomiz ...
随机推荐
- 关于AI行业创业的6个问题
第一个问题:互联网 vs 人工智能 首先如果今天大家选择创业,我建议更应该关注人工智能,而非互联网.为什么这么讲? 1. 互联网的流量红利已经消失: 以PC来说,全球PC出货量连续5年下滑.大家知道国 ...
- Java 9 新特性 – 内部类的方块操作符
方块操作符 ( <> ) 在 Java 7 中就引入了,目的是为了使代码更可读. 但是呢,这个操作符一直不能在匿名内部类中使用 Java 9 修正了这个问题,就是可以在匿名内部类中使用方块 ...
- 化学键|甘氨酸|谷氨酸|半胱胺酸|motif|domain|疏水相互作用|序列相似性|clustering analysis|Chou and Fasman|GOR|PHD|穿线法|first-principle ab initio folding|
化学键|甘氨酸|谷氨酸|半胱胺酸|motif|domain|疏水相互作用|序列相似性|clustering analysis|Chou and Fasman|GOR|PHD|穿线法|first-pri ...
- fiddler问题汇总
fiddler教程:https://kb.cnblogs.com/page/130367/ fiddler下载安装:https://www.cnblogs.com/mini-monkey/p/1128 ...
- Nginx笔记总结十:Nginx日志切割
1.Nginx日志切割 logrotate日志文件管理工具,通过cron程序定期执行,默认在cron默认程序的dayli目录下 [root@joker logrotate.d]# cat /etc/c ...
- idea(or maven) 未结束字符串字面值 非法的表达式开始
[ERROR] *.java:[38,27] 未结束的字符串字面值 [ERROR] *.java:[38,53] 需要 ';' [ERROR] *.java:[41,19] 需要 ')' [ERROR ...
- java 7 try with resources理解
参考文档: 官方文档:http://docs.oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html#su ...
- 前进中的人工智能——聚焦Faculty Summit 2015人工智能主题研讨会
Summit 2015人工智能主题研讨会" title="前进中的人工智能--聚焦Faculty Summit 2015人工智能主题研讨会"> 在近几年上映的科幻大 ...
- java的23种设计模式之建造者模式
场景和本质 场景 本质 案例 原理 应用场景 场景和本质 场景 我们要建造一个复杂的产品.比如:神州飞船,Iphone.这个复杂的产品的创建.有这样一个问题需要处理:装配这些子组件是不是有个步骤问题? ...
- yum配置与使用
yum的配置一般有两种方式,一种是直接配置/etc目录下的yum.conf文件,另外一种是在/etc/yum.repos.d目录下增加.repo文件. 一.yum的配置文件 [main] cached ...