AVR uint8_t无法获得正确的值

时间:2019-02-26 13:49:59

标签: c embedded avr avr-gcc

我有一个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,路径表是uint64_t,而电流位是uint8_t。我试图使它们全部为uint64_t,但没有任何改变。我也尝试使用unsigned long long int代替。没事了。

代码始终输入else子句。 Chip的Atmega4809,并在代码的其他部分使用uint64_t没有问题。

注意-如果有人知道一种更有效/更紧凑的方法来从变量中提取单个位,如果您可以共享^^

,我将不胜感激。

2 个答案:

答案 0 :(得分:4)

1是一个整数常量,类型为int。表达式1 << current_bit的类型也为int,但是对于16位int,当current_bit大于14时,该表达式的结果是不确定的。那么,在您的情况下,调试器可能会给出总体表达式的结果,该结果似乎与观察到的行为不一致。如果改用unsigned int常量 ie 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);

答案 1 :(得分:3)

实现#1是AVR是1980-1990年代的技术核心。它不是x64早餐机可以咀嚼64位数字,而是效率极低的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以避免出现上述错误。


  

如果有人知道一种更有效/更紧凑的方法来从变量中提取单个位,我将非常感谢您能共享

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


我的一般建议是找到您的工具链的反汇编程序,并查看C代码实际生成的机器代码。您不必是汇编专家也可以阅读它,窥视instruction set就足够了。