转移" 32位浮动"变量

时间:2014-10-09 10:57:51

标签: c floating-point spi

我需要转移许多" 32位浮点数"通过SPI从一个DSP到另一个DSP的变量。

但是,寄存器是16位的,我需要拆分" 32位浮点数"变成两部分。

我使用C类型联盟,如

union mytype {
  float a;
  uint16_t b[2];
};

它运行良好但是我遇到了一个新问题,即在启动时很难知道接收的16位数据是用于b [0]还是b [1]。 (两个DSP可能在不同时间上电,因此第一个数据可能不总是b [0])。

我试图将32位浮点数分成4个字节,当我传输16位时,我在8位数据之前添加一个标记。这也很完美。但是我的表现减慢了一倍。

有没有办法转移" 32位浮点数"通过两个16位数据?我知道我的数字范围在" 0.0000001~1000000"(均为+/-)。还是其他任何建议?我也想到两个DSP之间的SPI握手,但似乎让它变得复杂。我只想捕获启动数据是b [0],然后我可以按顺序接收所有内容。

感谢

4 个答案:

答案 0 :(得分:1)

如果您能够在大数字上获得最小的精度损失,这是一个想法:

丢失浮点的两个最低有效位(位0和1)。然后重新排列浮点的位,以便:

  • b [0]包含0和浮点数
  • 的16..2
  • b [1]包含1和浮点数31..17

然后,当您收到一个16位整数时,检查最高有效位以查看它是b [0]还是b [1],并在位0和1上用2个零重建浮点数。

对于您需要的数字范围,精度损失应该是最小的,并且此转换仅需要逻辑运算,因此它应该很快。

答案 1 :(得分:1)

类似于@Cantfindname的想法,但删除指数中的2位。没有精确损失。

此外,拒绝任何无序对,以防止从不属于一起的一半形成float

我看到@Patricia Shanahan评论说。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void encode(float f, uint16_t *u16) {
  volatile union {
    float f;
    uint32_t u;
  } x;
  x.f = f;
  x.u -= 0x30000000u;
  u16[0] = 0x0000u | ((x.u >>  0) & 0x7FFFu);
  u16[1] = 0x8000u | ((x.u >> 17) & 0x4000u) | ((x.u >> 15) & 0x3FFFu);
}

int decode(const uint16_t *u16, float *f) {
  volatile union {
    float f;
    uint32_t u;
  } x;
  if (((u16[0] & 0x8000u) != 0x0000u) || ((u16[1] & 0x8000u) != 0x8000u)) {
    printf("Fail\n");
    return 1; // fail
  }
  x.u =  (u16[1] & 0x4000ul) << 17;
  x.u |= (u16[1] & 0x3FFFul) << 15;
  x.u |= (u16[0] & 0x7FFFu);
  x.u += 0x30000000u;
  *f = x.f;
  return 0;
}

int test(float f) {
  uint16_t u[2];
  encode(f, u);
  float f2;
  decode(u, &f2);
  if (f != f2) {
    printf("%.8e %.8e\n", f, f2);
  }
  return f != f2;
}

#include <math.h>

int main(void) {
  float f;
  for (f= 0.0000001; f <= 1000000.0; f = nextafterf(f,2*f)) {
    test(f);
    test(-f);
  }
  return 0;
  puts("Done");
}

给定范围&#34; 0.0000001~1000000&#34;,binary32 float的MSBits始终 s011 s100 s 是符号位。通过减去3,范围是 s000 s001 。这允许忽略中间的2位,然后只需要保持32位浮点的30位。发送2个16位消息,每个消息都有一个&#34;阶段&#34;位和30位的15位部分允许完全重建原始float

如果接收端收到异相消息,它应该忽略消息,直到连续的0阶段/ 1阶段组合到来。


通过减去其他值,可以忽略至少1位,允许额外的序列位数,进一步确保正确的重组。毕竟它只显示了4,294,967,296 floats的340,000,000种组合。

答案 2 :(得分:0)

如果有效载荷数据不包含NaN,则定期发送两个全比特 - 一个块。当接收器启动时,它扫描数据直到它看到这样一对,然后将下一个具有至少一个零位的块作为b [0],下一个块作为其b [1]等。

每次它看到一个全位 - 一对,它应该忽略它。

您需要在浪费多少带宽发送不需要的全比特一对,以及在启动之前获得同步和处理实际数据之前接收的数据量之间进行权衡。

答案 3 :(得分:0)

如果该范围是绝对的,则只需要28位来表达它。这让你只想做你想做的事情。我建议将uint16的前两位作为标识符,这样当你收到它时,你可以对它执行一些操作来决定它的整数部分。