关于使用CCSV5利用断点导入外部数据在Graph中显示波形的实践
- 关于**.dat**文件的介绍
- 关于DSP中定点浮点数的表示与转换
- 关于补码的计算
.dat文件不是一种标准的文件格式,许多软件都在使用这个扩展名,但是各个软件对.dat文件的定义格式不一定相同
现在讲CCS,DSP可以识别的.dat文件的格式,
图示为用记事本打开的(有些.dat文件记事本打开是乱码原因可能是生成这个.dat文件的软件有自己的加密方式)利用matlab按照自己的格式生成的.dat文件,这种格式的.dat文件是CCS支持的格式:第一行为五个数据,第一个1651,固定数;第二个为.dat文件存储的下面数据的格式“1”代表十六进制整型、“2”代表十进制整型、“3“代表十进制长整型、”4“代表十进制浮点型;第三个数代表DSP的存储地址,说明从文件读取的数据要存放的位置;第四个数代表页类型,可以是程序也可以是数据;第五个数表示的是数据的长度。其中后面三项可以全部为0。
以上是对CCSDSP可以识别的.dat文件的格式的介绍,下面介绍关于DSP当中定点数与浮点数的介绍
暂时理解为当存储位数一定时,DSP是如何存储小数的,例如用十六位来存储小数,要介绍定标法
其中红 {MOD}的位标识的是符号位,利用的是补码表示,从介绍中可以看到(就以Q定标为例吧)当无定标的时候除符号位以外其它位按照二进制位数从高到低表示数的大小,当定标以后(以Q14为例)无定标也可以理解为(Q0)意思就是小数点在第0位的前面就是在第一位的后面,前十五位(除去符号位)都是表示的整数部分,自然Q15表示的就是小数点在第十五位的前面,后面的十五位都是表示小数部分因此Q15和Q0所表示的数的取值范围不同,Q15中第十五位上的”1“实际上是处于小数部分第一位,他的”1“表示的是0.5,因此第十四位上的”1“表示的是0.25,以此类推。具体我们在定标时采用Q多少来定标就是说小数点向左移动几位,这个东西是由我们要表示的数的最大值以及小数位数的精度来决定的,就是说,用N位来存储数据,出去符号位后小数点向左移动,要保证左边留出的位数够大右面的位数够小数进度,现在我们知道了DSP是如何定标的了。那么我们如何在DSP中存储0.5这个小数呢,我们发现0.5在16位存储时是可以利用Q15来定标的,那么我们如何把这个浮点数表示出来呢
我们让这个浮点数乘以2的Q多少次方就可以将这个浮点数转化成一个定点数,而这个定点数用二进制表示出来后如果用定标的约定去解读这个二进制数的话求出来就正好是这个浮点数的大小,其实前面让这个浮点数去乘以2的多少次方其实就是小数点的左移造成的,然而实际当中DSP存储的还是那个定点数的二进制,DSP本身不知道定标这件事情,这个事情只不过是程序员自己的约定罢了,我们要存储0.5,(16位存储,定标Q15)的情况下,我们先求出0.5*2^15这个数,然后在DSP中存储这个定点数,当我们从内存中读取出这个数之后再利用定点数与浮点数的转换求出我们实际上存储的浮点数。
这样就实现了在DSP中存储浮点数。实际当中我们的float,double等都是这样的规则,只不过他们的存储位数以及定标数都是确定的所以这些转化就是系统自动转化的,当我们要自己编写内存数据文件然而恰巧又有浮点数时我们就要这样存储了。
以上是关于DSP中浮点数的实际存储问题的介绍,下面讲数据存储过程中补码的问题
数据在计算机中存储的是其补码,对于有符号数,用N位来存储数据,最高位为符号位,正数的补码为其二进制码本身,符号位为0;负数的符号位为1,负数的补码是:符号位不变,数值位在原来二进制码的基础上按位取反然后加上一,得到的二进制码为其补码,下面介绍一些概念
模和补数
假如现在是6点,想知道3个小时前是几点,我们可以直接把时针逆时针旋转3个小时,也可以顺时针旋转9个小时,得到的时间是一样的。用数学语言我们可以这样表示:
6 - 3 = 6 +(12 - 3)
还有,我们可以发现把物体左转60度,和把物体右转300度的结果也是一样的。
数字 87,减去 25,和加上 75,在不考虑百位数的条件下,效果也是相同的。
上述几组数字,有这样的关系:
3+9=12
60 + 300 = 360
25 + 75 = 100
式中的 12、360 和 100,就是“模”。
式中的 3 和 3、60 和 300,以及 25 和 75,就是一对对“互补”的数字。
知道了“模”,求某个数字的“补数”,就是轻而易举的了:
如果模为 365,数字 120 的补数为:365 - 120 = 245。
用补数代替原数,可把减法转变为加法。出现的进位就是模,此时的进位,就应该忽略不计。
我们可以把减法理解成加一个负数。于是我们在计算减法时只要加上模减去这个负数的绝对值就可以得到正确的结果。
这样就给我们在计算机中做减法提供了一个良好的思路。
二进制的模
十进制下,两位数的模是100,三位数的模是1000,n位数的模式10n。在二进制下,我们可以发现两位数的模是100(十进制的4)。模表示数字的容量和状态,两位二进制能表示4个数。所以可以推出,n位二进制的数的模式2n,也就是1后面有n个0。
如果是 3 位二进制数参加运算,模就是 1000(2^3),即 1 的后面加上 3 个 0;
那么当 8 位二进制数参加运算,模就是 1 0000 0000(2^8),即 1 的后面加上 8 个 0。
16 位二进制数参加运算,模可就大了,是 1 的后面加上 16 个 0。
二进制的补码
在计算机里,数字、字符串等等会被称为“码”,还有机器码。那么补数也就变个称呼,显得专业一点,我们称之为补码。
前面我们说了补码的提出是为了解决减法计算的问题。所以在计算时,加法运算我们仍然按照原来的机器码(原码)进行计算。
我们一般是在8位二进制的情况下讨论补码。
例如-3的原码是10000011,其绝对值的原码是00000011。那么-3的补码就是100000000-00000011=11111101。
总结
首先“补”和“模”的概念适用于像 {MOD}相环、钟表这种有个环的事物,模就相当于那个环。十一点加一个小时就是零点,在8位二进制的数字中,11111111加1就是00000000。当你忘了补码的样子,就想一想钟表吧。正是有了模,才有所谓的补。
其次在二进制中,n位数的模就是2^n,也就是1后面有n个0。
负值在内存中以补码形式存贮
负数X的补码 = |X|按位求反+1
= X按拉求反(除符号位外)+1
= 模 - |X| (强调了补码运算本质上是一种模运算,这就是称其为“补码”的缘由)
-128的补码是10000000,等于128的原码。
上面介绍了如何用模来求负数的补码,下面我们来介绍是如何同matlab产生DSP所需的内存.dat文件的
fs = 50;
t = 1/fs:1/fs:20;
k = 2;
A = 2
sin(2pi
kt);
//产生数据【-2,2】采用十六位存储的话用Q14定标
B = (4*(A<0)+A); //求出补码(浮点数,还没有最后转化为相对应的定点数)其中4是十六位存储时Q14定标下的模(二进制100,4 = 2^(16-14)原本是16位存储现在小数点左移了14位相当于只留下了2位)
C = (4*(A<0)+A)*2^14;//产生相应的定点数,浮点数与定点数的转化
fid = fopen(‘test.dat’,‘wt’);
fprintf(fid,‘1651 1 0 0 0
’);
fprintf(fid,‘0x%x
’,C);
fclose(fid)
//上面代码为写DSP所可以识别的.dat文件,由于格式第二个数据为”1“所以DSP会以十六进制整型数存储所读信息32位,然而matlab中默认的fprintf是输出16位,所以DSP读取到信息后会在前面16位全部置零,这样我们的补码的符号位信息就失效了
matlab写出来的数据是这样的,从图中可以看出来十六进制的第四位数表示的二进制是第13~16位(第十六位是符号位),前12个数的第四位数都是小于8(1000)的所以可见这些数是正数,后面几个是大于8的所以第十六位肯定是1所以他们是负数
但是如果对matlab的写入程序不加修改的话DSP读取之后存储得到的数据是这样的
这样的话DSP就都认为是正数了因为这些数的最高位第32位都是0。为了避免这个误会,我们在做matlab的输出时做了一些调整
就补码而言,正数的补码前面加多少0都不影响这个数真正表示的数例如0x1fd5表示的是8149,0x00001fd5也表示8149,唯一不同的就是第一种是十六位存储的8149而第二种是32位存储的8149,不影响所表示的数的值;对于负数而言正好相反,因为正数的补码是其本身,而负数要数值位取反因此负数是其补码前面不管加多少1都是不影响其实际存数的数的值的,例如0xeff5表示-4107,0xffffeff5表示的也是-4107,这个叫做补码的符号拓展,实际上符号拓展不影响存储数据的值,只是存储数据的位数变化了。由于DSP要用32为存数所读数据,因此我们要保证数据正确的前提下扩展到32位就用到了下面的语句:
D = ((A<0)*4294901760) +C;
4294901760用十六进制表示是0xffff0000这样的话再加上原来的十六位数就是相当于32位表示了,所以DSP读取的数据就是
根据前面的介绍,对于补码来说正数做0符号位扩展负数做1符号位扩展不影响所表示数据的大小,只是说数据存数的位数变了,
利用CCSv3里面的volume实例利用程序断点,在断点处从.dat文件里面读取数据,然后在Tools的Graph里面演示波形得到: