文章目录
- 1 引言
- 2 Simulink中的基本数据类型
- 3 数据类型实例
- 3.1 浮点类型
- 3.2 整数类型
- 3.3 布尔类型
- 3 数据类型使用的注意点
- 3.1 浮点数等于比较
- 3.2 整形数溢出
- 3.3 布尔类型的位域
- 4 关于定点数的说明
- 5 总结
1 引言
正如C语言中为变量区分了不同的数据类型一样,Simulink中也有不同的数据类型,并且与C语言数据类型关系紧密。本文研究Simulink中的基本数据类型的用法和一些相关的配置。
2 Simulink中的基本数据类型
在Simulink中,基本数据类型分为浮点类型、整数类型和布尔类型,具体有以下几种。
数据类型名称 | 含义 | 存储大小 | 范围 |
---|---|---|---|
double | 双精度浮点型 | 4字节 | -1.7E-308~1.7E+308 |
single | 单精度浮点型 | 8字节 | 1.2E-38 ~ 3.4E+38 |
int8 | 有符号 8 位整数 | 1字节 | -128 ~ 127 |
uint8 | 无符号 8 位整数 | 1字节 | 0 ~ 255 |
int16 | 有符号 16 位整数 | 2字节 | -32,768 ~ 32,767 |
uint16 | 无符号 16 位整数 | 2字节 | 0 ~ 65,535 |
int32 | 有符号 32 位整数 | 4字节 | -2,147,483,648 ~ 2,147,483,647 |
uint32 | 无符号 32 位整数 | 4字节 | 0 ~ 4,294,967,295 |
boolean | 布尔类型 | 1字节 | 0~1 |
后文会举例说明不同类型的使用方法及一些注意点。
3 数据类型实例
3.1 浮点类型
浮点类型就是具有浮动小数点的数据类型,用于表示小数。
首先,通过下面的Simulink建模示例,可以清楚地了解浮点类型的用法。
1)新建一个Simulink模型<Data_Type_Basics.slx>,在模型中搭建以下简单模块;
2)分别双击Inport1和Inport2,打开配置界面,将Data type分别配置为single和double;
3)分别双击Gain模块,并对应配置DataType为single和double;
4)在Debug工具栏中点击Base Data Types,可以在信号线上显示数据类型;
完成后Ctrl + D更新模型,就可以看到信号线上的数据类型分别是single和double。
在模型中配置浮点数的方法如上文所示。接着,通过将模型生成代码,可以在C语言的层面上进一步理解Simulink中的浮点类型,以及和C语言中浮点类型的关系。
1)生成代码前,先配置好Embedded Coder,并且Ctrl + B生成代码;
在<Data_Type_Basics.h>头文件中可以看到Inport1和Inport2的类型是real32_T和real_T。
2)接着打开<rtw_types.h>头文件;
在该文件中通过typedef关键字将real32_T和real_T分别定义为C语言的基础类型float和double,也就是单精度和双精度浮点型数。这种做法在C语言中很常见,是为了不直接使用C语言中的基础类型,而是在新的名称中可以直观地看出该类型所占的字节数。
这些被定义好的新的类型名称,也是在Simulink中可以配置的,具体参照下图。
3.2 整数类型
整数类型用于表示整数,和浮点类型在代码生成方面类似,都是通过typedef定义一个新的类型名称,再以此去定义变量名。
类似浮点类型的建模,建模时将Inport中的Data type 选中成整型。在生成代码的形式方面,整数类型和浮点类型除类型定义外,没有差别,这里不再赘述。
3.3 布尔类型
布尔类型用于表示“真”或“假”,常用于条件判断或者逻辑运算中。首先,通过下面的Simulink建模示例,可以清楚地了解布尔类型的用法。
1)将Inport1和Inport2配置成single类型,并且通过一个关系运算符(小于等于)来输出判断结果,如果Inport1 < Inport2,则输出True,否则输出False;
2)配置好显示端口数据类型,然后Ctrl + D更新模型,就可以看到关系运算符输出的信号为boolean,即布尔类型;
同理,逻辑运算符的输入输出都是boolean类型,进行与、或、非等逻辑运算。
3)生成代码前,先配置好Embedded Coder,并且Ctrl + B生成代码;
在生成的<Data_Type_Basics_Bool.h>文件中,Outport的类型为boolean_T。在 rtwtypes.h文件中可以看到通过typedef关键字将unsigned char类型定义为boolean_T,也就是说boolean_T的背后是无符号 8 位整数,和uint8_T是一样的。
3 数据类型使用的注意点
3.1 浮点数等于比较
两个浮点数可以通过比较运算模块,输出true或者false的布尔值。但是在建模的过程中,不可以使用 == 比较运算符来比较两个浮点数相等。即如下情况。
因为计算机在进行浮点数运算的时候会产生精度损失,所以两个浮点类型变量在小数点后的很多位就可能看不一样的,从现实物理意义上来说,基本可以等同一致,但是 == 运算符会判定为不一样,就产生了相反的效果。
在Model Advisor中的Check comparison of floating point types in Simulink项可以检查出模型中进行浮点数等于比较的错误。
建模中判断浮点数相等的正确做法应该是,两个浮点数的差值的绝对值,小于一个定义好的误差量,例如0.00001.
3.2 整形数溢出
由于浮点类型可表示的范围很大,所以一般不需要考虑数值溢出的问题。但是整数类型必须很清楚该类型所对应的范围是否满足信号的需要。例如int8类型可以表示-128~127的整数,但是超出该范围就会溢出,造成了数值的错误。
新建一个模型<Data_Type_Basics_IntOverflow.slx>,如图所示,输入的数值是80,乘以二之后超出了int8类型可以表示的范围。如果将Diagnostics中的Wrap on overflow选配置为error,那么Ctrl + T运行模型仿真时就会检测到溢出而报错。
如果将Wrap on overflow选配置为warning再Ctrl + T运行模型仿真,就会输出-96的错误值。
这里需要注意,仅通过Ctrl + D更新模型是不会报错的,或者输入输出为Inport或者Outport模块时也不会报错。必须首先配置好模型诊断项,以及使用导致溢出的数值输入后,通过Ctrl + T进行仿真后,Simulink才会报错。因此,测试用例需要能够覆盖这种情况。
3.3 布尔类型的位域
布尔类型只需要一个比特就可以存储0和1两种状态,但是C语言最小的整数数据类型是char,也就是一个字节,占据8个比特,这样造成了控制器内存资源的浪费。一种解决方法是把布尔量打包成位域。
通过下面的Simulink示例,可以清楚地了解布尔类型打包成位域的方法。
1)首先,建立一个Matlab脚本<Bitfield_Parameter.m>,输入如下脚本并运行,用于创建Bool_Param_1和Bool_Param_2两个参数;
Bool_Param_1 = Simulink.Parameter;
Bool_Param_1.Value = 0;
Bool_Param_1.CoderInfo.StorageClass = 'Custom';
Bool_Param_1.CoderInfo.Alias = '';
Bool_Param_1.CoderInfo.Alignment = -1;
Bool_Param_1.CoderInfo.CustomStorageClass = 'BitField';
Bool_Param_1.CoderInfo.CustomAttributes.StructName = '';
Bool_Param_1.Description = '';
Bool_Param_1.DataType = 'boolean';
Bool_Param_1.Min = [];
Bool_Param_1.Max = [];
Bool_Param_1.DocUnits = '';Bool_Param_2 = Simulink.Parameter;
Bool_Param_2.Value = 1;
Bool_Param_2.CoderInfo.StorageClass = 'Custom';
Bool_Param_2.CoderInfo.Alias = '';
Bool_Param_2.CoderInfo.Alignment = -1;
Bool_Param_2.CoderInfo.CustomStorageClass = 'BitField';
Bool_Param_2.CoderInfo.CustomAttributes.StructName = '';
Bool_Param_2.Description = '';
Bool_Param_2.DataType = 'boolean';
Bool_Param_2.Min = [];
Bool_Param_2.Max = [];
Bool_Param_2.DocUnits = '';
这里注意,CustomStorageClass配置成了BitField。
2)运行脚本<Bitfield_Parameter.m>,在Matlab工作空间中生成两个Parameter参数;
3)接着在Simulink中建模如下,包含两个Const模块和两个Outport模块,Const模块中的值引用工作空间中的两个参数,直接输出给Outport模块;
4)在Simulink的配置界面中,勾选上Pack Boolean data into bitfields,并将
Bitfield declarator type specifier选择位uchar_T;
这里的选择表示将Boolean类型的数据打包到位域中,并且共享一个uint_T的类型。
5)保存模型后,先配置好Embedded Coder,Ctrl + B生成代码;
在<Data_Type_Basics_Bitfield.h>头文件中,将两个参数定义到一个结构体内,并且后面带了个冒号和数字1。这代表这两个参数共用一个类型uint_T,并且分别只占了这个类型的1个比特。位域就是通过这种共享类型的方式,节省了控制器的内存资源。
4 关于定点数的说明
在早期的模型开发中,常用定点数表示浮点数。定点数的类型配置如下图所示。
定点数就是把小数点固定下来,来表示浮点数。这样做的目的时牺牲一定的小数精度,来换取内存资源空间的节省。
简单举个例子,通过1个字节,也就是8个比特的内存空间表示小数,假如小数点固定在从右往左第三位,如图所示。
整数部分分配到5个比特,也就是整数最大能表示2^5 - 1,即十进制的31。小数部分分配到3个比特,也就是可以把数字1等分成8份,每份是0.125。这样的话小数可以表示的精度就是0.125。也就是说,小数只能是0.0,0.125,0.25,0.375,0.5,0.625,0.75,0.875这几种。0.125和0.25之间的小数值无法表示,因为精度不够。想要表达更精准的小数,就要把小数点往左移,但是这样的话整数的最大值又减小了。因此定点数需要按照需求去一个平衡。
由于集成电路的发展,现在的嵌入式芯片已经有了充足的资源和空间,以及浮点运算单元。不再需要旧时代的定点化的操作,笔者在工作的时候已经没有再见过定点化相关的技术了。
5 总结
本文研究了Simulink中的浮点型(double、single)、整型(int8、uint8等)和布尔型数据类型。建模时需注意浮点数比较的精度、整数溢出问题以及布尔类型的位域优化,以提升模型效率和可靠性。
返回个人博客总目录