C2000 DSP 踩坑记录:float与字节转换

问题来源

由于在项目中需要用到E2PROM来保存一个浮点型数据,E2PROM读写的驱动都是按照字节来读写的,所以需要将float数据类型转换为字节序列,再写入E2。同理读取float数据时也要先读出字节序列,然后转换为float类型数据。

原程序

开始编写时图代码简单,直接使用指针强制类型转换的方式,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>

// float 类型转换为字符数组
void
float_2_byte(float f, unsigned char *s)
{
unsigned char *byte;
int i = 0;

byte = (unsigned char*)&f;
for (i = 0; i < 4; i++) {
*(s+i) = *(byte+i);
}
}

// 字符数组转换为float类型
float
byte_2_float(unsigned char *s)
{
float *fp;

fp = (float*)s;

return *fp;
}

// 测试程序
int main(void)
{
float f = 3.141582657;
float f2 = 0.0;
unsigned char s[4];

float_2_byte(f, s);
printf("%x\t%x\t%x\t%x\n", s[0], s[1], s[2], s[3]);

f2 = byte_2_float(s);
printf("float = %f\n", f2);

return 0;
}

将上述代码再PC上测试了一下,输出结果如下:

PC Result

说明字节序和float类型之间的相互转换没有问题。

问题现象与原因

将上述程序移植到28335平台时,通过E2读写数据时,发现读出的数据与写入的不同。经过调试发现,实际写入的数据并没有按照预想的那样写入float型数据的各个字节。

原因分析:dsp28335的最小数据类型单位是16位的,28335平台上的unsigned char类型实际上是16位!当采用如上代码运行在28335平台上,将一个float类型数据0x12345678转换为unsigned char类型数组时,实际上得到的unsigned char类型数组为:

s[0] = 0x5678, s[1] = 0x1234, s[2] = 0x????, s[3] = 0x????。也就是说,直接采用unsigned char类型指针运算的方式,指针加1并不是移动8位,而是16位,因此采用指针去取字节出现了错误。

修改程序

采用指针进行暴力类型转换的方法在28335上行不通了。还可以采用位运算或者联合体的方式。这里直接采用位运算和类型转换的方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

void
float_2_byte(float f, unsigned char *s)
{
uint32_t longdata = 0;

longdata = *(uint32_t*)&f;

s[0] = (longdata & 0xFF000000) >> 24;
s[1] = (longdata & 0x00FF0000) >> 16;
s[2] = (longdata & 0x0000FF00) >> 8;
s[3] = (longdata & 0x000000FF);
}

float
byte_2_float(unsigned char *s)
{
uint32_t longdata = 0;
float f = 0.0;
int i = 0;

for (i = 0; i < 3; i++) {
longdata += s[i];
longdata <<= 8;
}
longdata += s[3];

f = *(float*)&longdata;
return f;
}

int main(void)
{
float f = 3.1415;
float f2 = 0.0;
unsigned char s[4];

float_2_byte(f, s);
printf("%x\t%x\t%x\t%x\n", s[0], s[1], s[2], s[3]);

f2 = byte_2_float(s);
printf("float = %f\n", f2);

return 0;
}

上述代码在移植到DSP28335平台之后,能够正确的实现float类型数据和字节序列之间的转换。

总结

之所以源程序在PC上没问题,而在DSP28335平台上出现bug的原因在于对于unsigned char类型的指针操作。在DSP28335上对于8位数据的指针操作与预想的是不同的,因为DSP28335中实际上是以16位的形式存储char和unsigned char类型数据。在对8位数据指针类型进行加减操作时,指针移动也是16位而不是8位。