我正在使用ATMEGA328PB AVR微控制器,并且通过UART发送某些浮点数时一直遇到问题。
我在编译器(avr-gcc)中启用了浮点,并且大多数情况下都可以使用。
例如,此代码:
floatVal = ((double)tempResult / (double)oversample) * 0.000457777;
char outbuffer[255];
sprintf(outbuffer, "%f\n", floatVal);
usart1TXString(outbuffer);
在UART上提供正确的输出:
3.866311
我也想打印出32位int。这也有效:
char outbuffer[255];
sprintf(outbuffer, "%ld\n", transform(combVal));
usart1TXString(outbuffer);
并在UART上提供正确的输出:
117864
(这些输出都是我期望的输出)
当我尝试将这两段代码合并为单个(甚至连续)传输时,会发生问题:
floatVal = ((double)tempResult / (double)oversample) * 0.000457777;
char outbuffer[255];
sprintf(outbuffer, "%f,%ld\n", floatVal, transform(combVal));
usart1TXString(outbuffer);
产生此输出:
584991570000.000000,117864
大数字不会改变,除非我的floatVal值相差很大。
我没有运气就试图将其分解为两种不同的传输方式:
floatVal = ((double)tempResult / (double)oversample) * 0.000457777;
char outbuffer[255];
sprintf(outbuffer, "%f\n", floatVal);
usart1TXString(outbuffer);
char outbuffertwo[255];
sprintf(outbuffertwo, ",%ld\n", transform(combVal));
usart1TXString(outbuffertwo);
产生相同的错误结果。
584991570000.000000,117419
我也尝试使用dtostrf()而不是sprintf():
floatVal = ((double)tempResult / (double)oversample) * 0.000457777;
char outbuffer[255];
dtostrf(floatVal, 6, 4, outbuffer);
usart1TXString(outbuffer);
Works。但是,一旦我尝试添加其他值,它就会再次中断:
floatVal = ((double)tempResult / (double)oversample) * 0.000457777;
char outbuffer[255];
dtostrf(floatVal, 6, 4, outbuffer);
usart1TXString(outbuffer);
char outbuffertwo[255];
sprintf(outbuffertwo, ",%ld\n", transform(combVal));
usart1TXString(outbuffertwo);
返回到来自UART的相同中断值。
我尝试增加缓冲区的大小(即使它应该已经足够大了)。
我不认为这很重要,但以防万一:
'tempResult'是一个无符号的64位int,它是从ADC接收的16位值的累加。
'oversample'是一个16位int,它是指定的过采样量。当前50。
'floatVal'是双精度型。
'combVal'是一个32位int,其中包含24位ADC值,以两位的方式表示赞赏。
transform()是处理两者的补码的函数,如下所示:
int32_t transform(int32_t value)
{
if (value > MAX_VALUE)
{
value -= MODULO;
}
return value;
}
关于损坏%f的%ld是什么?
编辑:即使我仅用包含正确数字的变量替换了transform(combVal)
,它仍然会破坏其他值。
我发现了问题,这是我说的我认为不重要的其中一件事。具体来说:
'tempResult' is an unsigned 64 bit int that is an accumulation of 16 bit values received from an ADC.
从avr-libc常见问题解答:
float and double are 32 bits (this is the only supported floating point format)
我将问题缩小到这一行:
floatVal = ((double)tempResult / (double)oversample) * 0.000457777;
因为如果我用这个替换它:
floatVal = 3.123456
一切正常。
将uint64_t tempResult;
更改为uint32_t tempResult;
会修复所有问题。我只需要避免过度采样就可以了,一切都会好起来的。
长话短说:滥用8位AVR上的浮点会导致UB,这是UB喜欢做的事情(有时会工作,而会引起很多其他混乱)。