1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
//! # Joystick Module
//!
//! Provides an interface to a 2-axis joystick with an integrated select button.
//! This module is configured to use specific pins for the X and Y axes and
//! assumes that the select button uses a digital input pin.
//!
//! ### ATTENTION: THIS MODULE IS SUPPOSED TO BE USED ONLY WITH X-AXIS AND Y-AXIS CONNECTED TO DEFAULT PINS!!!
use embedded_hal::{adc::OneShot, digital::v2::InputPin};
use esp_hal::{
analog::adc::{AdcPin, ADC},
gpio::{Analog, GpioPin},
prelude::*,
};
#[cfg(not(feature = "esp32"))]
type XPin = AdcPin<GpioPin<Analog, 1>, esp_hal::peripherals::ADC1>;
#[cfg(feature = "esp32")]
type XPin = AdcPin<GpioPin<Analog, 32>, esp_hal::peripherals::ADC1>;
#[cfg(not(feature = "esp32"))]
type YPin = AdcPin<GpioPin<Analog, 3>, esp_hal::peripherals::ADC1>;
#[cfg(feature = "esp32")]
type YPin = AdcPin<GpioPin<Analog, 35>, esp_hal::peripherals::ADC1>;
#[cfg(not(feature = "esp32"))]
#[macro_export]
macro_rules! get_x_adc_pin {
($pins:expr) => {
$pins.gpio1.into_analog()
};
}
#[cfg(feature = "esp32")]
#[macro_export]
macro_rules! get_x_adc_pin {
($pins:expr) => {
$pins.gpio32.into_analog()
};
}
#[cfg(not(feature = "esp32"))]
#[macro_export]
macro_rules! get_y_adc_pin {
($pins:expr) => {
$pins.gpio3.into_analog()
};
}
#[cfg(feature = "esp32")]
#[macro_export]
macro_rules! get_y_adc_pin {
($pins:expr) => {
$pins.gpio35.into_analog()
};
}
/// Represents a joystick with two axes and a select button.
pub struct Joystick<SELECT: InputPin> {
/// The select button of the joystick, wrapped in a `Button` struct for
/// debouncing.
pub select: crate::peripherals::button::Button<SELECT>,
/// The analog input pin for the X-axis.
pub x_axis: XPin,
/// The analog input pin for the Y-axis.
pub y_axis: YPin,
}
/// A threshold value to interpret the joystick's value in direction.
pub const ROUGH_THRESHOLD: u16 = 2048;
/// Macro for creating a `Joystick` instance.
///
/// Unlike a function, this macro can take ownership of parts of the
/// `Peripherals` without consuming the whole `Peripherals` struct. This allows
/// setting up the ADC configuration for the joystick's analog pins and passing
/// it along for ADC initialization with `peripherals.ADC1` without running into
/// Rust's ownership errors.
///
/// Functions in Rust take ownership or borrow the entire value they are given,
/// which would not allow us to partially consume `Peripherals`. This macro,
/// however, performs the setup inline where it's invoked and thus avoids the
/// mentioned ownership issue.
///
/// # Arguments
/// * `$peripherals` - The `esp-hal` `Peripherals` instance.
/// * `$pins` - The `esp-hal` GPIO pins split from `Peripherals`.
/// * `$pin_select` - The GPIO pin used for the joystick's select button.
///
/// # Usage
/// This macro's name still holds "naming convention" of
/// "create_<device_type>", if sensor/peripheral does not work on top of
/// `I2C/SPI` buses. This macro should be used where you have access to
/// the `Peripherals` and the split pins, and it will return a tuple containing
/// the `Joystick` instance and the initialized ADC.
///
/// ```no_run
/// /// let peripherals = take_periph!();
/// let system = take_system!(peripherals);
/// let (clocks, pins) = init_chip!(peripherals, system);
/// let (joystick, adc1) = create_joystick!(peripherals, pins, pin_select);
/// ```
#[macro_export]
macro_rules! create_joystick {
($peripherals: expr, $pins: expr, $pin_select: expr ) => {{
let mut adc1_config = esp_hal::analog::adc::AdcConfig::<esp_hal::peripherals::ADC1>::new();
let mut select = esp_ward::peripherals::button::Button::create_on_pins($pin_select);
let x_axis_pin = esp_ward::get_x_adc_pin!($pins);
let y_axis_pin = esp_ward::get_y_adc_pin!($pins);
let mut x_axis = adc1_config.enable_pin(
x_axis_pin,
esp_hal::analog::adc::Attenuation::Attenuation11dB,
);
let mut y_axis = adc1_config.enable_pin(
y_axis_pin,
esp_hal::analog::adc::Attenuation::Attenuation11dB,
);
let mut adc1 = esp_hal::analog::adc::ADC::<esp_hal::peripherals::ADC1>::new(
$peripherals.ADC1,
adc1_config,
);
(
Joystick {
select: select,
x_axis: x_axis,
y_axis: y_axis,
},
adc1,
)
}};
}
pub use create_joystick;
impl<SELECT: InputPin<Error = core::convert::Infallible>> Joystick<SELECT> {
/// Retrieves the current positions of both axes.
///
/// # Arguments
/// * `adc` - The ADC instance to read the values from the analog pins.
///
/// # Returns
/// Returns a tuple `(u16, u16)` where the first element is the X-axis value
/// and the second is the Y-axis value.
pub fn get_axes(&mut self, adc: &mut ADC<'_, esp_hal::peripherals::ADC1>) -> (u16, u16) {
(
nb::block!(adc.read(&mut self.x_axis)).unwrap(),
nb::block!(adc.read(&mut self.y_axis)).unwrap(),
)
}
/// Retrieves the current position of the X-axis.
///
/// # Arguments
/// * `adc` - The ADC instance to read the value from the analog pin.
///
/// # Returns
/// Returns a `u16` representing the X-axis value.
pub fn get_x(&mut self, adc: &mut ADC<'_, esp_hal::peripherals::ADC1>) -> u16 {
let (x, _) = self.get_axes(adc);
x
}
/// Retrieves the current position of the Y-axis.
///
/// # Arguments
/// * `adc` - The ADC instance to read the value from the analog pin.
///
/// # Returns
/// Returns a `u16` representing the Y-axis value.
pub fn get_y(&mut self, adc: &mut ADC<'_, esp_hal::peripherals::ADC1>) -> u16 {
let (_, y) = self.get_axes(adc);
y
}
/// Checks if the select button is currently pressed.
///
/// # Arguments
/// * `delay` - A delay provider for debouncing the button press.
///
/// # Returns
/// Returns `true` if the select button is pressed; otherwise `false`.
pub fn select_pressed(&mut self, mut delay: esp_hal::delay::Delay) -> bool {
if let crate::peripherals::button::Event::Pressed = self.select.poll(&mut delay) {
return true;
} else {
return false;
}
}
}
// `UnifiedData` trait can not be implemented due to peculiarities of ADC
// peripheral in esp-hal driver.