AVR uint8_t没有得到正确的值

问题描述 投票:0回答:2

我有一个uint8_t应该包含按位计算的结果。调试器表示变量设置正确,但是当我检查内存时,var总是为0.无论调试器告诉我什么,代码都会像var一样继续进行。这是代码:

temp = (path_table & (1 << current_bit)) >> current_bit;
//temp is always 0, debugger shows correct value
if (temp > 0) {
    DS18B20_send_bit(pin, 0x01);
} else {
    DS18B20_send_bit(pin, 0x00);
}

Temp是uint8_t,path_table是uint64_t,current_bit是uint8_t。我试图让它们全部uint64_t但没有任何改变。我也尝试过使用unsigned long long int。没有了。

代码总是进入else子句。 Chip的Atmega4809,在代码的其他部分使用uint64_t没有问题。

注意 - 如果有人知道从变量中提取单个位的更有效/紧凑的方法,我真的很感激,如果你可以分享^^

c embedded avr avr-gcc
2个回答
4
投票

1int类型的整数常量。表达式1 << current_bit也有类型int,但是对于16位int,当current_bit大于14时,该表达式的结果是未定义的。在你的情况下,行为是不确定的,那么,调试器为整体提供结果是合理的。表达似乎与观察到的行为不一致。如果你使用了unsigned int常数,即1u,那么每当current_bit大于15时,temp的结果值将被很好地定义为0,因为左移的结果将为零。

通过以足够宽的类型执行计算来保存结果来解决此问题。这是一个紧凑,正确,非常清晰的方法来纠正您的代码:

DS18B20_send_bit(pin, (path_table & (((uint64_t) 1) << current_bit)) != 0);

或者如果path_table有一个无符号类型,那么我更喜欢这个,虽然它更偏离你原来的:

DS18B20_send_bit(pin, (path_table >> current_bit) & 1);

3
投票

这里实现#1是AVR是1980-1990的技术核心。早餐时不是用64位数字来咀嚼x64 PC,而是效率极低的8位MCU。因此:

  • 它喜欢8位算术。
  • 通过使用16位索引寄存器,双累加器或任何它更喜欢的8位核心技巧,它将难以实现16位算术。
  • 通过内联调用软件库,执行32位算法将花费很长时间。
  • 如果尝试64位算术,它可能会在地板上融化。

在你做任何其他事情之前,你需要摆脱所有64位算术并从根本上最小化32位算术的使用。期。你的代码中应该没有uint64_t的单个变量,或者你做得非常错误。


有了这个启示,所有8位MCU总是有一个16位的int类型。

在代码1<<current_bit中,整数常量1的类型为int。这意味着如果current_bit为15或更大,您将把位移到这个临时int的符号位。这总是一个错误。严格来说,这是未定义的行为。在实践中,您可能最终会随机更改数字符号。

为避免这种情况,请勿在带符号数字上使用任何形式的按位运算符。当将1等整数常量与按位运算符混合时,将它们更改为1u以避免像上面提到的错误一样。


如果有人知道从变量中提取单个位的更有效/紧凑的方法,我真的很感激,如果你可以分享

C中最有效的方式是:uint8_t variable; ... if(variable & (1u << bits))。这应转换为相关的“分支(如果位设置”)指令。


我的一般建议是找到你的工具链的反汇编程序,看看C代码实际生成的机器代码。你不必是一个汇编大师来阅读它,偷看instruction set就足够了。

© www.soinside.com 2019 - 2024. All rights reserved.