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

基于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=on
    
    运行后通过telnet可正常收发数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 19:53:23