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

基于Rust no_std ESP32 HAL实现192kHz采样率的ADC DMA非阻塞模式

在Rust no_std环境下基于ESP32 HAL实现192kHz采样率的ADC DMA非阻塞转换

核心实现逻辑

ESP32的ADC要实现高采样率非阻塞转换,必须结合**PDMA(外设DMA)**与ADC连续采样模式,核心流程如下:

  • 配置ADC为连续采样模式,匹配192kHz采样率的时钟分频参数
  • 初始化PDMA并分配内存缓冲区,绑定ADC采样输出通道与PDMA通道
  • 设置PDMA为循环传输模式,启用传输完成中断,用于触发数据处理逻辑
  • 启动ADC连续采样与PDMA传输后,CPU可完全释放,专注于以太网传输、DSP运算等任务

完整代码示例

#![no_std]
#![no_main]
#![allow(non_snake_case)]

use esp_backtrace as _;
use esp_println::println;
use esp32_hal::{
    prelude::*,
    peripherals::Peripherals,
    clock::{ClockControl, CpuClock},
    interrupt,
    Rtc,
    timer::TimerGroup,
    adc::{self, ADC1, AdcConfig, ADC, Attenuation, AdcCalibration, AdcSampling},
    IO,
    pdma::{Dma, DmaChannel, Target, Transfer, TransferMode},
};

// 全局静态变量存储DMA传输实例(供中断处理使用)
static mut DMA_TRANSFER: Option<Transfer<DmaChannel, Target::ADC1, &'static mut [u16]>> = None;
static mut SAMPLE_BUFFER: [u16; 256] = [0; 256];

#[entry]
fn main() -> ! {
    let peripherals = Peripherals::take();
    let mut system = peripherals.DPORT.split();
    let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock240MHz).freeze();
    let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);

    // 禁用看门狗
    let rtc_cntl = Rtc::new(peripherals.RTC_CNTL);
    let mut rwdt = rtc_cntl.rwdt;
    let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
    let mut wdt0 = timer_group0.wdt;
    let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks);
    let mut wdt1 = timer_group1.wdt;
    rwdt.disable();
    wdt0.disable();
    wdt1.disable();

    // 初始化PDMA
    let mut dma = Dma::new(peripherals.DMA, &mut system.peripheral_clock_control);
    let dma_channel = dma.channel0;

    // 初始化ADC并配置采样参数
    let analog = peripherals.SENS.split();
    let mut adc_config: AdcConfig<ADC1> = adc::AdcConfig::new();
    let mut adc_pin = adc_config.enable_pin(
        io.pins.gpio34.into_analog(),
        Attenuation::Attenuation11dB,
    );
    // 计算分频系数:192kHz采样率需要ADC时钟≈2.4MHz,APB时钟80MHz,分频系数=80/2.4 -1≈32
    let mut adc1 = ADC::<ADC1>::adc(analog.adc1, adc_config)
        .unwrap()
        .set_sampling(AdcSampling::new().set_clk_divider(32))
        .calibrate(AdcCalibration::new());

    // 配置DMA循环传输:将ADC采样数据持续写入缓冲区
    unsafe {
        let transfer = dma_channel
            .transfer_to_memory(Target::ADC1, &mut SAMPLE_BUFFER)
            .with_mode(TransferMode::Circular)
            .enable_interrupt();
        DMA_TRANSFER = Some(transfer);
    }

    // 启用DMA通道0中断
    interrupt::enable(peripherals::Interrupt::DMA_CH0, interrupt::Priority::Priority1).unwrap();

    // 启动ADC连续采样
    adc1.start_continuous_sampling(&mut adc_pin).unwrap();

    // CPU释放,处理其他任务(示例仅作演示,实际替换为以太网/DSP逻辑)
    loop {
        println!("CPU Idle: Processing other tasks...");
        cortex_m::asm::wfi(); // 进入低功耗模式,等待中断唤醒
    }
}

// DMA通道0中断处理函数:采样完成后触发数据处理
#[interrupt]
fn DMA_CH0() {
    unsafe {
        if let Some(transfer) = DMA_TRANSFER.as_mut() {
            transfer.clear_interrupt();
            // 处理采样数据:示例打印前10个采样值,实际替换为以太网传输/DSP运算
            let buffer = transfer.buffer();
            for i in 0..10 {
                println!("DMA Sample {}: {}", i, buffer[i]);
            }
        }
    }
}

关键注意事项

  • 采样率校准:代码中分频系数为理论计算值,实际需根据硬件时钟精度微调,确保采样率准确
  • 缓冲区管理:循环模式下DMA会持续覆盖缓冲区,需在中断中及时处理数据,避免丢失
  • 中断优先级:DMA中断优先级需高于非实时任务,保证采样数据及时处理
  • 硬件限制:ESP32 ADC最高采样率约1MHz,192kHz在硬件支持范围内,连续采样需注意功耗与发热

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 10:29:56