我正在进行一些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)可以正常
答案 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位。一些想法:
放弃精确度。将12位值简单地除以16,将这8位发送并在接收端乘以16。
通过将12位值切换为2个6位半,将第一个A / D采样分为2个部分。使用MSB来区分是发送了上部还是下部。接收端需要2个样本才能将事物重新组合在一起。传输端的第二个A / D样本被丢弃。
与#2类似,但每3个样本中发送2个。 3个8位消息中的24位数据。
在本回答的开头回答。有时会发送课程值,其他时间是delta。
如下面评论@Clifford,始终发送带符号的8位增量。可能需要进行微调以确保接收金额中的任何偏差最终都能解决。
取几千个样本,并将其作为打包的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 )