1

遥控板

首先来看一下遥控板,目前我手里的遥控板是JoyStlck Shield,大概长这样:
摇杆控制板

关于这块板子的资料网上有很多,就不做细说,主要是看一下这块板子有几个信号输入,每个按钮是一个信号,摇杆是两个信号,分为X和Y。我目前打算主要用到的按钮就是很明显的带颜色的按钮和摇杆按钮信号,上下黄色的按钮信号为A、C,左右蓝色信号按钮为B、D。罗列如下就是:

  • V 接电源 VCC
  • G 接电源负极 GND
  • A
  • B
  • C
  • D
  • X 模拟信号
  • Y 模拟信号

接线

既然找到了这几个信号,然后看开发板上,我使用黄色的这几个插针,电路板上的图已经标注了每个信号对应的引脚。如图所示:
引脚信号示意图
接线后大概如下,排线顺序按照V/G/A/B/C/D/X/Y顺序,容易记忆,方便接 ESP32 的时候不接错。
摇杆接线后

然后就是继续查看 ESP32 的引脚图 引脚图,然后根据引脚图,把 V 接到 3V3,然后把 G 接到 GND,然后再挑选 4个普通GPIO,以及 2个带ADC的引脚,把几根信号线接上。

我挑的4根普通GPIO引脚及其对应遥控板上的输入按钮编号分别对应为:

  • gpio19 A
  • gpio18 B
  • gpio5 C
  • gpio17 D

我挑的2根ADC引脚及其对应遥控板上摇杆的输入信号编号对应位:

  • gpio32 X 信号
  • gpio33 Y 信号

ESP32端接线图

由于我这个插针在背面,看的时候不太方便,得把原本得 GPIO 做个镜像反转,主要的是找到引脚编号,编号正确就行。

另外就是,我真正的接线的时候,我发现这个遥控板上的信号标记的X和Y轴应该是标记反了,这个地方具体在查看信号输出的时候才发现的,我把这两根引脚西安调换一下位置,至于后续程序,还是按照我列出的引脚对应关系来编写。

按钮输入高低电压判断

首先,很不一样的就是,对于输出,可以通过一个LED直接 真·上手 就可以观察到灯亮不亮就知道有输出,但是输入很不一样,虽然来说,把 GPIO 引脚配置作为了输入模式,但是必须最终得转换为程序判断是否有输入,而这个硬件数字电路转为程序之后,就是判断输入电平高低。

那么应该是判断这个输入信号是高是低呢?可以采用两种方法,我觉得我采用了最简单的一种,就是用 LED 做判断,首先我把 LED正极 用排线连接到遥控板 按钮A 的信号输入线上,然后 LED负极ESP32GND 进行连接,然后,我发现这个LED是亮着的,然后试着按下 按钮A,然后发现灯灭了,我就得到了默认输入是高电平。这里就有一个问题,假设就算按按钮也不亮的话,我肯定会换着来,原本LED正极接到ESP32的正极,原本接ESP32负极的接到遥控板上,如果插入去亮,按下按钮不亮,那么肯定输入也是高电平,其实也就是说:判断输入是否高电平,看按钮接上去是不是亮的,如果是亮且按下按钮不亮的话,那么就是高电平,如果不亮且按下按钮亮则是低电平

另外一种就是直接用程序写程序呗,获取 button 按钮后,然后通过 button.is_low().unwrap()button.is_high().unwrap() 来判断,总会是其中一种。

关于 ADC,以及为什么摇杆要以 ADC 输入

这个摇杆其实就是个电位器,也就是说,这就是分为 X 轴和 Y 轴相交叉的两条滑动电阻,掰动这个摇杆的时候,会改变这两个滑动电阻的阻值,所以,最终我们需要通过滑动电阻的线性的电压或者说电流改变,最终得到一个模拟后的数值,然后通过数值(或者说数据)才能判定这个摇杆的位置。

如果把整个 X 轴和 Y 轴的总电阻看作 100,那么摇杆居中的时候,这个电阻为 50,然后在 X 轴或者 Y 轴方向的最顶端和最低端就是0100。在 ESP32 中,ADC 是 12 位(12 bit),也就是说,能识别到的模拟值范围为:0-4095,也就是在 X 轴或 Y 轴上,能识别到4096级的位置变化,把4096 / 2 - 1=2047 作为摇杆居中位置,然后就能得到四个方向,可能不同摇杆安装的方式不同,最终结果也不同,作为参考,大概如下:

  • X 轴值 <= 2047:方向往右
  • X 轴值 >= 2048:方向往左
  • Y 轴值 <= 2047:方向往上
  • Y 轴值 >= 2048:方向往下

实际上整个摇杆就那么一点,但是能分为 4096 个级别,相当灵敏了,那么具体在后续实际得使用得时候,必须得做虚位处理,就像方向盘一样,直线行驶的时候,要是没有虚位,跑高速估计很容易就翻车。

具体关于 ESP32 的 ADC 相关资料,这篇文章写得不错:ESP32 ADC

ESP32 有两个 ADC,每个 ADC对应有不同的GPIO,我在实际程序中使用得是 ADC1,一方面我发现使用 ADC2 获取的值不准确,另外一方面我发现就是如果要使用 Wi-Fi 的话,就不能使用 ADC2,这个 ADC 是给 Wi-Fi使用的,考虑到这块 ESP32 加上这块摇杆板子做成无线遥控器后,后续我需要用到 Wi-Fi 用于与另外一块作为控制板的 ESP32 进行通信,最恰当的就是用 ADC1了。

搞程序

参考之前写的电灯的,先创建程序 joystick.rs

然后在 esp32-hal/examples/adc.rs 程序中,复制一下,然后依葫芦画瓢操作就得到以下程序:

// esp32-hal/examples/joystick.rs
#![no_std]
#![no_main]

use esp_backtrace as _;
use esp_println::println;
use esp32_hal::{
    adc::{
        {ADC, ADC1, AdcConfig, Attenuation}
    },
    prelude::*,
    clock::ClockControl,
    gpio::{
        Input,
        PullDown,
        IO,
        AnyPin,
    },
    peripherals::{
        Peripherals
    },
    Delay,
};

#[entry]
fn main() -> ! {
    // 获取外围设备
    let peripherals = Peripherals::take();
    // 从外围设备得到传感器设备
    let analog = peripherals.SENS.split();
    let system = peripherals.SYSTEM.split();

    // 获取时钟控制器
    let clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    // 以 GPIO 多路复用创建 IO(输入输出)
    let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);

    let button_a = io.pins.gpio19.into_pull_down_input().degrade();
    let button_b = io.pins.gpio18.into_pull_down_input().degrade();
    let button_c = io.pins.gpio5.into_pull_down_input().degrade();
    let button_d = io.pins.gpio17.into_pull_down_input().degrade();

    // 获取 adc1 配置
    let mut adc1_config = AdcConfig::new();
    // X 轴输入
    let mut button_x =
        adc1_config.enable_pin(io.pins.gpio32.into_analog(), Attenuation::Attenuation11dB);
    // Y 轴输入
    let mut button_y =
        adc1_config.enable_pin(io.pins.gpio33.into_analog(), Attenuation::Attenuation11dB);

    let mut adc1 = ADC::<ADC1>::adc(analog.adc1, adc1_config).unwrap();

    let mut delay = Delay::new(&clocks);

    loop {
        show_button(&button_a, "a");
        show_button(&button_b, "b");
        show_button(&button_c, "c");
        show_button(&button_d, "d");

        let x_value: u16 = nb::block!(adc1.read(&mut button_x)).unwrap();
        println!("X_Value ADC reading = {}", x_value);

        let y_value: u16 = nb::block!(adc1.read(&mut button_y)).unwrap();
        println!("Y_Value ADC reading = {}", y_value);

        delay.delay_ms(500u32);
    }
}

fn show_button(button: &AnyPin<Input<PullDown>>, button_type: &str) {
    if button.is_low().unwrap() {
        println!("pull button : {}", button_type)
    }
}

最后执行程序后输出结果大概如下:

X_Value ADC reading = 2248
Y_Value ADC reading = 2229
pull button : a
X_Value ADC reading = 2247
Y_Value ADC reading = 2227
X_Value ADC reading = 2252
Y_Value ADC reading = 2227
pull button : b
X_Value ADC reading = 2249
Y_Value ADC reading = 2228
X_Value ADC reading = 2245
Y_Value ADC reading = 2227
pull button : c
X_Value ADC reading = 2244
Y_Value ADC reading = 2224
X_Value ADC reading = 2243
Y_Value ADC reading = 2224
pull button : d
X_Value ADC reading = 2248
Y_Value ADC reading = 2154
X_Value ADC reading = 2245
Y_Value ADC reading = 2228

在程序中,我为了观察数据,通过 delay_ms() 暂停 500毫秒 获取一次按钮以及摇杆数据进行输出。

研究这个控制板数据,昨天晚上花了半晚上,今天晚上花了半晚上,踩了挺多坑,把收集到的相关资料大概都整理到了,放在文中做记录,也方便后续自己遇到相关问题可以查阅或按照这个依葫芦画瓢。

接下来得研究这个遥控板取到的数据该怎么做处理,首先这里先把我所想到的问题先提一下,防止后续处理的时候忘记:

loop 这个循环是定义的程序最终常驻进程执行的主逻辑,那么我这上面的程序大概就是每隔 500ms 获取一次数据,如果在500ms内我本来按下按钮但是又抬起来了,肯定没法监测到我按下了按钮,那么如果换成 10ms 或者 1ms,基本上凭着锻炼多年的手速也能监测到按下了按钮,但是如果说有的功能需要检测单击一次就执行某个功能,长按执行某个功能,但是又产生一个问题就是怎么检查单击功能,长按功能等问题。


kumfo
6.7k 声望4.1k 粉丝

程序生存法则: