AVR uint8_t 没有得到正确的值

AVR uint8_t doesn't get correct value

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

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);
}

温度是 uint8_t,path_table 是 uint64_t,current_bit 是 uint8_t。我试着让它们都变成 uint64_t 但没有任何改变。我也尝试过使用 unsigned long long int 代替。没有了。

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

注意 - 如果有人知道更多 efficient/compact 从变量中提取单个位的方法,我将不胜感激,如果你能分享 ^^

1 是一个整数常量,类型为 int。表达式 1 << current_bit 也具有类型 int,但对于 16 位 int,当 current_bit 大于 14 时,该表达式的结果未定义。行为未定义那么,您的情况可能是您的调试器显示的整体表达式结果似乎与观察到的行为不一致。如果您改用 unsigned int 常量, 1u,那么只要 current_bit 大于15,因为左移的结果为零。

通过在足够宽的类型中执行计算来保存结果来解决这个问题。这是更正代码以执行此操作的紧凑、正确且非常清晰的方法:

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

或者,如果 path_table 有一个无符号类型,那么我更喜欢这个,尽管它与你的原始类型有很大的不同:

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

这里的认识#1 是 AVR 是 1980-1990 年代的技术核心。它不是咀嚼 64 位数字作为早餐的 x64 PC,而是效率极低的 8 位 MCU。因此:

  • 喜欢8位算术。
  • 它将通过 16 位索引寄存器、双累加器或它喜欢做的任何 8 位核心技巧来处理 16 位算术。
  • 通过调用内联软件库,执行 32 位算法确实需要很长时间。
  • 如果尝试 64 位运算,它可能会熔穿地板。

在你做任何其他事情之前,你需要摆脱所有 64 位算法并从根本上减少 32 位算法的使用。时期。你的代码中不应该有 uint64_t 的单个变量,否则你做的非常非常错误。


所有 8 位 MCU 总是有一个 int 类型,即 16 位。

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

为避免这种情况,切勿对有符号数使用任何形式的按位运算符。将 1 等整数常量与按位运算符混合使用时,将它们更改为 1u 以避免出现上述问题。


If anyone knows a more efficient/compact way to extract a single bit from a variable i would really appreciate if you could share

C 中最有效的方法是:uint8_t variable; ... if(variable & (1u << bits))。这应该转化为相关的 "branch if bit set" 指令。


我的一般建议是找到您的工具链的反汇编器并查看 C 代码实际生成的机器代码。您不必成为汇编大师也能阅读它,浏览 instruction set 就足够了。