我可以将2字节大小的变量作为1字节大小的变量发送吗?

时间:2015-12-18 22:15:12

标签: python c byte

我正在进行一些ADC(模数转换)值转换,需要通过UART发送这些值,但由于速度和其他大小限制,我想将它们转换为一个字节大小

ADC为12位,范围为0.0到5.0。

目前我正在将2字节变量拆分为两个单独的字节,并将它们连接回接收器部分

发送(C)

// i am trying to send 0xFF8 (4088) which is read from that register 
send[0] = (VADC_G5RES4.B.RESULT) >> 8 ;
send[1] = (VADC_G5RES4.B.RESULT) & 0x0FF;
 .
send[30] = .....

接收(Python)

adc1 = data[3]
adc1 = adc1 << 8 | data[4]
adc1 = (float(adc1 * 5) / 4096)
# gives me 4.990234 as the value 

有什么方法可以只用一个字节发送值(也许是通过在发送端执行计算)

小数点后只有2位数(4.99)可以正常

4 个答案:

答案 0 :(得分:7)

如果有一些神奇的方法可以将双字节值填充到一个字节的值中,我们会对所有内容进行处理,然后我们会再次执行此操作,并且我们将继续执行此操作并将所有内容的大小减半。我们把所有世界的信息推到一个字节。这种魔力是不可能的。

如果你丢弃一半的数据,你可以减少事物的大小,但你说你想要小数点后至少2位数。一个字节只能编码256个不同的值,编码从0.00到9.99的所有值都需要1000个不同的值,所以你仍然无法做到。

UPDATE :附加信息表明这些只是12位值,而不是16位,最大值是5.0,我们可以做得更好,但仍然不太8位中的3位有效数字。

首先,我们可以将2个值打包成3个字节,而不是每个值浪费4位。这看起来像

def pack(val1, val2):
    byte1 = val1 >> 4
    byte2 = ((val1 & 0xf) << 4) | (val2 >> 8)
    byte3 = val2 & 0xff
    return byte1, byte2, byte3

您可能需要处理一个尾随的半字节。

如果每个值1.5个字节仍然太多,则存在有损编码选项。例如,我们可以丢弃每个值的4个最低有效位:

encoded = val >> 4

并正确解码:

decoded = encoded * 5.0 / 256

这保持精度为5/256,或约0.02。其他编码可能会表现得更好,具体取决于您对信号的期望。例如,只要值变化不太快,chux's solution就会对值进行编码,但是当值快速变化时,只有大约0.04的精度。

答案 1 :(得分:3)

使用时间压缩。

准确交易时间。当ADC快速变化时,发送7位近似值。当变化缓慢时,发送增量。

以下是粗略的实施理念。

假设2字节ADC值在0-3FFF范围内。

 sample = ADC();
 encode_value = sample >> 7;
 send(encode_value);
 diff = sample - (encode_value << 7);

 loop:
   previous_sample = sample
   sample = ADC();
   diff = previous_sample - sample;
   if (diff >= -64 && diff <= 63)  {
     send(diff | 0x80);
     diff = 0;
   } else {
     encode_value = sample >> 7;
     send(encode_value);
     diff = sample - (encode_value << 7);
   }

//接收

valid = 0;
loop:
  sample = receive()
  if (sample & 0x80) {
    delta = sample & 0x7F
    if (delta & 0x40) delta -= 128;
    ADC += delta;
  } else {
    ADC = sample << 9;
    valid = 1;
  } 

在发送增量时,可能应该保留另一位来检测丢失的序列。其他考虑因素适用,但这里是给OP另一种观点。

优化包括让delta为2位指数放弃2位mantissa

[编辑]

12位随机数据​​不适合8位。一些想法:

  1. 放弃精确度。将12位值简单地除以16,将这8位发送并在接收端乘以16。

  2. 通过将12位值切换为2个6位半,将第一个A / D采样分为2个部分。使用MSB来区分是发送了上部还是下部。接收端需要2个样本才能将事物重新组合在一起。传输端的第二个A / D样本被丢弃。

  3. 与#2类似,但每3个样本中发送2个。 3个8位消息中的24位数据。

  4. 在本回答的开头回答。有时会发送课程值,其他时间是delta。

  5. 如下面评论@Clifford,始终发送带符号的8位增量。可能需要进行微调以确保接收金额中的任何偏差最终都能解决。

  6. 取几千个样本,并将其作为打包的12位数据写入文件。压缩(压缩)文件。找到压缩的每一种比例,都是我们可以推导出的最佳压缩方案的指标。如果它不是至少33%,那么你最好接受数据太动态,不能完全按照给定的要求传输。

答案 2 :(得分:2)

如果不丢失比特并因此精确,则无法完成。基本上你需要把你的ADC视为8位。这可能没有听起来那么糟糕,例如,如果你的ADC是12位(在你的问题中不清楚),你只会松掉4位。

uint8_t send = (adc_high_byte << 6) | (adc_low_byte >> 2) ;

然后在接收器中:

adc1 = (data * 5.0) / 256 ;

在您的示例中,4088以4088/16 = 255发送,并在接收器处进行转换:

(255 * 5.0) / 256 = 4.98

但请注意

(254 * 5.0) / 256= 4.96, 

因此最终得到的精度约为0.02(准确地说,如果255表示的最大值为5.0,则精度为5.0 / 256 = 0.01953125)。这与原始精度5/4096 = 0.00122

相比较

使用companding可以进一步细化。在某些应用中,例如音频,保留细节信号电平比高电平更重要,因此您可以使用非线性编码将12位数据转换为8,这样0和1之间的差异要小得多这取决于应用程序的适用性和成功率 - 它适用于音频。在其他应用中,不同的非线性编码可能是合适的,例如对于电池监测,您可能在放电曲线的拐点处使用高分辨率,其中电压降速率快速增加(在一些电池化学中)。

最后,正如@chux建议的那样,你可以用更少的比特传输delta(级别的变化)并保持完整的分辨率(有一些绝对精度的损失),但这是他的建议所以我不会详细说明,除了说这项技术可以用于压缩扩展。

答案 3 :(得分:0)

您可以转换发送方并将其乘以2并加入&#39;。&#39;在第二个位置..这个方法有效(但不是很准确)

(4088*5)/4096 = 4.990234 

使用前3位数并删除小数点

499 

将值除以2,以便可以使用一个带符号的字节(0-255)

发送
499/2 = 249

使用常规数据传输方法发送值

send[0] = 249 

在接收部分将值乘以2并添加小数点

adc1 = 249*2
newvalue= adc1[:1]+'.'+adc1[1:]
# you get newvalue = 4.98 ( But you loose the accuracy of the converted adc values )