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

RP2040基于Rust开发时无法修改DMA等外设寄存器问题

问题根因

你遇到的寄存器写入无效、读回恒为0的问题由两个核心原因导致:

  • 访问DMA外设前没有解除其复位状态。RP2040没有独立的外设时钟门控,所有外设在上电后默认处于复位状态,此时读写寄存器不会得到任何总线响应,写入值会被直接丢弃,读操作固定返回0。你能正常读写看门狗寄存器,是因为初始化时钟时调用的hal::Watchdog::new已经在内部解除了看门狗的复位,而你从未对DMA做过复位释放操作。
  • 寄存器访问位宽、API使用错误。RP2040的DMA通道寄存器全部为32位宽度,你代码中用*mut u16类型指针访问本身就不符合总线访问要求;同时你用的ptr.write()不是volatile语义,开启编译器优化后写入操作可能被直接优化掉。另外你注释的地址对应关系也写错了:0x50000000是DMA通道0的CH0_READ_ADDR寄存器,通道控制寄存器CH0_CTRL_TRIG的偏移地址是0x400,不是0x0。
修复方法
  1. 释放DMA外设复位:在访问任何DMA寄存器前,先通过复位控制器把DMA从复位状态拉出,等待复位完成标志置位:
    // 拉低DMA复位信号
    pac.RESETS.reset().modify(|_, w| w.dma().clear_bit());
    // 等待DMA外设复位完成
    while pac.RESETS.reset_done().read().dma().bit_is_clear() {}
    
  2. 修正寄存器访问逻辑:把指针类型改为*mut u32匹配寄存器宽度,使用volatile语义的读写接口,避免被编译器优化:
    let ptr = 0x50000000 as *mut u32; // CH0_READ_ADDR 32位寄存器
    rprintln!("ptr {:p}", ptr);
    unsafe {
        rprintln!("reg_before {:b}", ptr.read_volatile());
        ptr.write_volatile(0x42);
        rprintln!("reg_after {:b}", ptr.read_volatile());
    }
    
  3. 推荐写法:不要手动硬编码地址裸访寄存器,直接用pac提供的类型安全寄存器访问接口,自动处理位宽、volatile语义和地址偏移,避免手写指针的低级错误:
    let dma_ch0 = pac.DMA.ch0;
    rprintln!("reg_before {:b}", dma_ch0.read_addr().read().bits());
    dma_ch0.read_addr().write(|w| unsafe { w.bits(0x42) });
    rprintln!("reg_after {:b}", dma_ch0.read_addr().read().bits());
    
补充说明

你之前找不到外设时钟配置入口是正常的,RP2040的设计里所有AHB/APB外设的时钟默认和系统总线同步,没有独立的时钟使能控制,复位状态是唯一控制外设是否响应总线访问的开关。后续操作PIO、SPI、UART、PWM等其他外设时如果遇到寄存器读写无响应,第一时间检查是否已经释放对应外设的复位。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 19:18:26