Rust开发Arduino Nano时不可变函数参数传入后异常变更
问题分析与解决方案
核心问题
你的问题本质是栈帧损坏导致变量值被意外覆盖:在interrupt::free闭包内,调用ufmt::uwriteln后,栈上的unit变量被修改,导致第二次读取UNIT_PATTERNS[unit]时拿到了错误的值(从0xd4变成0x54,对应UNIT_PATTERNS[0])。
为什么会发生?
Arduino Nano(atmega328p)的栈空间极小(默认通常仅512字节),而interrupt::free闭包内的操作(尤其是uwriteln的格式化输出会占用大量栈空间)可能引发栈溢出,覆盖了栈上的unit变量。第一次打印UNIT_PATTERNS[unit]时unit仍为正确值2,但uwriteln执行过程中栈帧被破坏,unit被改为0,因此传入set_digit时读取到的是UNIT_PATTERNS[0](0x54)。
解决方案
将unit对应的模式值提前取出并存储在interrupt::free闭包外部,避免闭包内栈操作破坏变量:
fn display_inductance(l: u64, serial: &mut impl ufmt::uWrite) { let (mut l, unit) = match l { 0..=9999 => (l, 0), 10000..=9999999 => (l/1000, 1), 10000000..=9999999999 => (l/1000000, 2), _ => (l/1000000000, 3) }; // 提前取出单位模式,存在闭包外部栈帧 let unit_pattern = UNIT_PATTERNS[unit]; interrupt::free(|cs| { let mut state = DISPLAY_STATE.borrow(cs).borrow_mut(); let state = state.as_mut().unwrap(); for i in (0..4).rev() { let digit = (l%10) as usize; set_digit(&mut state.buffer, i, DIGIT_PATTERNS[digit], serial); l /= 10; } ufmt::uwriteln!(serial, "Unit pattern = 0x{:x}", unit_pattern); set_digit(&mut state.buffer, 4, unit_pattern, serial); }); }
验证方法
可以在闭包内的uwriteln后额外打印unit的值,确认是否被篡改:
ufmt::uwriteln!(serial, "Unit pattern = 0x{:x}, unit = {}", UNIT_PATTERNS[unit], unit); ufmt::uwriteln!(serial, "Unit pattern again = 0x{:x}", UNIT_PATTERNS[unit]);
如果输出中unit从2变为0,即可确认是栈覆盖导致的问题。
额外优化建议
- 减少
uwriteln在闭包内的使用:调试输出尽量放在闭包外,避免占用闭包内栈空间。 - 增大栈空间:通过修改链接脚本或编译参数调整atmega328p的栈大小,但需注意不要超过RAM总容量(2KB)。
内容的提问来源于stack exchange,提问作者GutturalWasp
相关产品推荐
相关产品推荐

