Feb 20, 2026
2 min read
ESP32,
Rust,
C,
Embedded Systems,

ESP32-S3中使用Rust进行ADC模数转换:读取XY轴摇杆传感器教程

本文介绍了如何在ESP32-S3上使用Rust进行ADC模数转换,以读取XY轴摇杆传感器的数据。文章详细讲解了ESP32-S3的ADC功能、引脚配置、代码实现(包括X/Y轴模拟信号读取和Z轴按键检测),以及如何使用esp-hal库进行硬件编程。

和普通按键只需要通过电平来识别触发不同,我们需要对xy轴摇杆传感器进行测量它的信号值,因此需要用到模数转换器 (ADC) 。

对了,xy轴摇杆传感器长这样,就是小孩子玩具车配的遥控器所用的那个摇杆。

xy

它有5个引脚,分别为:GND、VCC、VRx、VRy、SW。

其中 VRx、VRy 是两个模拟输入引脚,用于测量摇杆的X、Y轴信号。 SW 是一个按键引脚,用于检测摇杆的按键。

ESP32-S3 集成了两个 12 位 SAR ADC, 支持生成ADC 单次转换结果和连续转换结果。然而,并不是所有的GPIO引脚都支持ADC转换。通过查询技术参考手册得知分布如下:

image

依赖如下:

[dependencies]
esp-hal = { version = "~1.0", features = [ "esp32s3","unstable","defmt"] }
anyhow      = {version = "=1.0.102", default-features = false}
esp-bootloader-esp-idf = { version = "0.4.0", features = ["esp32s3"] }
critical-section = "1.2.0"
esp-alloc        = "0.9.0"
rtt-target       = "0.6.2"
defmt = "1.0.1"
defmt-rtt = "1.1.0"
nb = "1.1.0"

xy轴的读数

esp_hal 的 adc 功能放在 analog模块里。

先初始化 gpio 引脚:

    let cpu_clock = hal::clock::CpuClock::max();
    let config = hal::Config::default().with_cpu_clock(cpu_clock);
    let peripherals = hal::init(config);

我这里把 x和y轴的引脚都放在了gpio1和gpio2里。通过上图可知,gpio1和gpio2 归属于 adc1 模块。

    let mut adc1_config = AdcConfig::new();
    let mut x_adc = adc1_config.enable_pin(peripherals.GPIO1, Attenuation::_11dB);
    let mut y_adc = adc1_config.enable_pin(peripherals.GPIO2, Attenuation::_11dB);
    let mut adc1 = Adc::new(peripherals.ADC1, adc1_config);

设置为 Attenuation::_11dB 的来源于官方技术手册:https://documentation.espressif.com/esp32-s3_technical_reference_manual_cn.pdf

第39章有讲到,衰减可配置为 0 dB、2.5 dB、6 dB 和 12 dB。而 rust 的实现上,其实是0 dB、2.5 dB、6 dB 和 11 dB。

然后在循环里读取数据:

loop {
        // 读取 X 轴 ADC 值
        let x_value: u16 = nb::block!(adc1.read_oneshot(&mut x_adc)).unwrap();
        // 读取 Y 轴 ADC 值
        let y_value: u16 = nb::block!(adc1.read_oneshot(&mut y_adc)).unwrap();
        
        // ADC 值范围: 0-4095 (12位 ADC)
        // 中点值约为 2048(摇杆居中)
        println!("X: {}, Y: {}", x_value, y_value);
        
        delay.delay_millis(100);
    }

刷入固件,我们会在控制台里看到x和y的值。通过拨动摇杆,我们可以看到x和y的值不断变化。

z 轴的检测

z 轴本质就是一个安装在摇杆上的按键。就是普通按键的处理流程:通过GPIO中断来判断按键是否被按下。

先定义一个静态的变量,用于保存按键状态:】

static Z_BUTTON: Mutex<RefCell<Option<Input>>> = Mutex::new(RefCell::new(None));

设置中断处理函数:

    let mut io = Io::new(peripherals.IO_MUX);
    io.set_interrupt_handler(button_handler);
   

SW 接在35号引脚上,我们还需要设置监听下降沿触发的中断:

  let z_pin = peripherals.GPIO35;
    let config = InputConfig::default().with_pull(Pull::Up);
    let mut z_button = Input::new(z_pin, config);
    
    critical_section::with(|cs| {
        z_button.listen(esp_hal::gpio::Event::FallingEdge);
        Z_BUTTON.borrow_ref_mut(cs).replace(z_button);
    });

针对回调 button_handler 实现如下:

#[handler]
#[ram]
fn button_handler() {
    critical_section::with(|cs| {
        let mut z_btn = Z_BUTTON.borrow_ref_mut(cs);
        if let Some(ref mut btn) = *z_btn {
            if btn.is_interrupt_set() {
                println!("Button pressed!");
                btn.clear_interrupt();
            }
        }
    });
}