You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何省略unsigned int中间变量会导致UART通信程序失效?

为什么直接用宏给UBRR寄存器赋值会失效,改用中间变量就正常?

我想要通过UART接口发送消息,最初编写了如下程序:

#ifndef F_CPU
#error "F_CPU has to be defined"
#endif

#ifndef CONSOLE_BAUD
#pragma message "CONSOLE_BAUD was not defined, using 9600"
#define CONSOLE_BAUD 9600
#endif
#define UBRR_VALUE F_CPU/16/CONSOLE_BAUD-1

// init
UBRRH = (unsigned char)(UBRR_VALUE >> 8);
UBRRL = (unsigned char)UBRR_VALUE;
UCSRB = (1 << TXEN);
UCSRC = (1 << URSEL) | (1 << USBS) | (3 << UCSZ0);

// send
while (!(UCSRA & (1 << UDRE)));
UDR = 'A';

该程序无法正常工作,发送的十六进制数据为00。

随后我将程序修改为如下形式:

#ifndef F_CPU
#error "F_CPU has to be defined"
#endif

#ifndef CONSOLE_BAUD
#pragma message "CONSOLE_BAUD was not defined, using 9600"
#define CONSOLE_BAUD 9600
#endif
#define UBRR_VALUE F_CPU/16/CONSOLE_BAUD-1

// init
unsigned int ubrr = UBRR_VALUE; // changed
UBRRH = (unsigned char)(ubrr >> 8); // changed
UBRRL = (unsigned char)ubrr; // changed
UCSRB = (1 << TXEN);
UCSRC = (1 << URSEL) | (1 << USBS) | (3 << UCSZ0);

// send
while (!(UCSRA & (1 << UDRE)));
UDR = 'A';

修改后程序恢复正常。请问这是为什么?直接将预处理器宏赋值给UBRR寄存器与使用中间变量有何区别?

编辑补充:我的问题被标记为重复问题,但我一开始并未发现括号缺失的问题,根本找不到关联的重复问题,我认为该标记并不合理。


问题原因分析

核心问题出在预处理器宏的文本替换特性和运算符优先级上:

  1. 宏是纯文本替换,不会像变量那样先计算完整表达式结果。你的宏UBRR_VALUE展开后,当你写(UBRR_VALUE >> 8)时,实际会变成:

    (F_CPU/16/CONSOLE_BAUD - 1 >> 8)
    

    由于减法-的优先级高于右移>>,编译器会先计算1 >> 8(结果为0),再用除法结果减去0,最终得到的是F_CPU/16/CONSOLE_BAUD右移8位,而不是正确的(F_CPU/16/CONSOLE_BAUD - 1)右移8位。这导致UBRRH和UBRRL的赋值完全错误,UART波特率配置失效,自然发不出正确数据。

  2. 改用中间变量unsigned int ubrr = UBRR_VALUE;时,编译器会先完整计算UBRR_VALUE的结果(先执行除法再减1),得到正确的16位波特率寄存器值,再分别提取高8位和低8位赋值给UBRRH、UBRRL,这就保证了寄存器配置的正确性。

宏与中间变量的核心区别

  • 宏:预处理器阶段直接做文本替换,完全遵循代码的运算符优先级,不会自动给表达式加括号保证计算顺序。
  • 中间变量:编译器先计算表达式的完整结果,再将结果拆解使用,相当于给整个表达式套了隐形括号,确保计算逻辑符合预期。

解决方案

除了用中间变量,最直接的修复是给宏表达式加上足够的括号,避免优先级问题:

#define UBRR_VALUE (((F_CPU) / (16 * CONSOLE_BAUD)) - 1)

内容的提问来源于stack exchange,提问作者mnj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 18:35:16