基于Rust与QEMU实现Atmega328P SPI通信遇阻求助
Atmega328P Rust SPI示例在QEMU中无输出的排查方案
背景
- 正在为Atmega328P开发Rust自定义HAL,USART功能已验证通过:代码可回传接收数据,通过TCP传输至电脑。
- 验证USART使用的QEMU命令:
运行后通过qemu-system-avr -nographic -machine uno -bios target/avr-atmega328p/debug/examples/usart.elf -serial tcp::5678,server=on,wait=ontelnet可正常收发数据。
SPI测试问题
参考现有HAL代码编写SPI测试示例,代码如下:
#![no_std] #![no_main] use arduino_hal::prelude::*; use arduino_hal::spi; use panic_halt as _; #[arduino_hal::entry] fn main() -> ! { let dp = arduino_hal::Peripherals::take().unwrap(); let pins = arduino_hal::pins!(dp); // 初始化串口用于文本输出 let mut serial = arduino_hal::default_serial!(dp, pins, 57600); // 创建SPI接口 let (mut spi, _) = arduino_hal::Spi::new( dp.SPI, pins.d13.into_output(), pins.d11.into_output(), pins.d12.into_pull_up_input(), pins.d10.into_output(), spi::Settings::default(), ); loop { // 发送字节 nb::block!(spi.send(0b00001111)).unwrap_infallible(); // 假设MISO与MOSI短接,读取回传数据 let data = nb::block!(spi.read()).unwrap_infallible(); ufmt::uwriteln!(&mut serial, "data: {}\r", data).unwrap_infallible(); arduino_hal::delay_ms(1000); } }
使用相同QEMU命令运行后,telnet连接无任何输出,无法确定是SPI代码、QEMU命令还是对SPI与QEMU的理解存在偏差,也找不到将虚拟机MISO引脚映射到Linux Shell输出的方法。
排查与解决方案
1. 先验证串口输出有效性
在SPI初始化前添加测试输出,确认串口通道正常:
// 在SPI初始化代码前添加 ufmt::uwriteln!(&mut serial, "SPI test starting...\r").unwrap_infallible();
如果telnet能收到这段文本,说明串口配置正常,问题出在SPI逻辑上;否则需要检查串口初始化或QEMU的-serial参数配置。
2. QEMU AVR SPI模拟的局限性
QEMU对AVR的SPI外设模拟并不完整,默认不会模拟MISO与MOSI短接的硬件连接。代码中依赖回读MISO数据的逻辑会读到无效值,而unwrap_infallible()在实际出错时会触发panic halt,导致程序终止,后续串口输出无法执行。
3. 修改SPI测试逻辑,避免隐性panic
调整代码,显式处理SPI读写的错误,同时输出调试信息:
loop { ufmt::uwriteln!(&mut serial, "Attempting SPI send...\r").unwrap_infallible(); // 捕获发送结果 match nb::block!(spi.send(0b00001111)) { Ok(_) => ufmt::uwriteln!(&mut serial, "Send success\r").unwrap_infallible(), Err(e) => ufmt::uwriteln!(&mut serial, "Send error: {:?}\r", e).unwrap_infallible(), } ufmt::uwriteln!(&mut serial, "Attempting SPI read...\r").unwrap_infallible(); // 捕获读取结果 match nb::block!(spi.read()) { Ok(data) => ufmt::uwriteln!(&mut serial, "Read data: {}\r", data).unwrap_infallible(), Err(e) => ufmt::uwriteln!(&mut serial, "Read error: {:?}\r", e).unwrap_infallible(), } arduino_hal::delay_ms(1000); }
这样可以明确看到SPI环节的具体问题,避免程序直接halt。
4. 替代测试方案
由于QEMU对SPI的模拟能力有限,可通过以下方式验证代码正确性:
- 使用真实Arduino Uno硬件测试,确认SPI逻辑本身无问题
- 手动读取SPI寄存器状态并输出到串口,验证外设是否正确初始化,比如输出SPCR、SPDR寄存器的值
- 更换AVR专用硬件模拟工具(如AVR Studio)进行SPI交互测试
5. 检查SPI初始化参数
确认spi::Settings::default()的配置(时钟频率、数据顺序、SPI模式)是否符合Atmega328P的要求,默认参数可能与硬件不匹配,导致SPI无法正常工作。
内容的提问来源于stack exchange,提问作者Mat
相关产品推荐
相关产品推荐

