use crate::pins::{
HexpansionDetectPins, InputRegisters,
aw9523b::{GpioDirection, PinMode, set_io_direction, set_io_state, set_pin_mode},
pin::PinExt,
};
use defmt::{Format, debug};
use embassy_time::Instant;
use embedded_hal::digital::PinState;
use getset::Getters;
use heapless::Vec;
const HEXPANSION_SLOT_COUNT: usize = 6;
pub struct HexpansionSlotControl<I2C> {
i2c: I2C,
pins: HexpansionDetectPins,
state: [InnerState; HEXPANSION_SLOT_COUNT],
}
impl<I2C, E> HexpansionSlotControl<I2C>
where
I2C: embedded_hal_async::i2c::I2c<Error = E>,
{
pub async fn try_new(mut i2c: I2C, pins: HexpansionDetectPins) -> Result<Self, E> {
macro_rules! setup_pin {
($bus:expr, $pin:expr) => {
set_pin_mode($bus, &$pin, PinMode::Gpio).await?;
set_io_direction($bus, &$pin, GpioDirection::Output).await?;
set_io_state($bus, &$pin, PinState::High).await?;
};
}
setup_pin!(&mut i2c, pins.a);
setup_pin!(&mut i2c, pins.b);
setup_pin!(&mut i2c, pins.c);
setup_pin!(&mut i2c, pins.d);
setup_pin!(&mut i2c, pins.e);
setup_pin!(&mut i2c, pins.f);
let state = [InnerState {
state: Some(HexpansionState::Disabled),
notified: false,
}; 6];
Ok(Self { i2c, pins, state })
}
pub async fn set_enabled(&mut self, slot: HexpansionSlot, enabled: bool) -> Result<(), E> {
let pin: &dyn PinExt = match slot {
HexpansionSlot::A => &self.pins.a,
HexpansionSlot::B => &self.pins.b,
HexpansionSlot::C => &self.pins.c,
HexpansionSlot::D => &self.pins.d,
HexpansionSlot::E => &self.pins.e,
HexpansionSlot::F => &self.pins.f,
};
let state = if enabled {
set_io_direction(&mut self.i2c, pin, GpioDirection::Input).await?;
None
} else {
set_io_direction(&mut self.i2c, pin, GpioDirection::Output).await?;
set_io_state(&mut self.i2c, pin, PinState::High).await?;
Some(HexpansionState::Disabled)
};
self.state[slot as usize] = InnerState {
state,
notified: false,
};
Ok(())
}
pub fn update(
&mut self,
regs: &InputRegisters,
) -> Vec<HexpansionSlotEvent, HEXPANSION_SLOT_COUNT> {
let now = Instant::now();
let states_now = states_from_registers(&self.pins, regs);
let mut events = Vec::new();
for (idx, old) in self.state.iter_mut().enumerate() {
let new = &states_now[idx];
if old.state == Some(HexpansionState::Disabled) {
if old.notified {
continue;
} else {
events
.push(HexpansionSlotEvent {
slot: HexpansionSlot::from_index(idx),
time: now,
state: HexpansionState::Disabled,
})
.unwrap();
old.notified = true;
}
} else if old.state != Some(*new) {
events
.push(HexpansionSlotEvent {
slot: HexpansionSlot::from_index(idx),
time: now,
state: *new,
})
.unwrap();
old.state = Some(*new);
old.notified = true;
}
}
debug!("Hexpansion events: {}", events);
events
}
}
#[derive(Clone, Copy)]
struct InnerState {
state: Option<HexpansionState>,
notified: bool,
}
#[derive(Debug, Format, PartialEq, Eq, Clone, Copy)]
#[repr(usize)]
pub enum HexpansionSlot {
A,
B,
C,
D,
E,
F,
}
impl HexpansionSlot {
fn from_index(index: usize) -> Self {
match index {
0 => Self::A,
1 => Self::B,
2 => Self::C,
3 => Self::D,
4 => Self::E,
5 => Self::F,
_ => unreachable!(),
}
}
}
#[derive(Debug, Format, PartialEq, Eq, Clone, Copy, Getters)]
pub struct HexpansionSlotEvent {
#[getset(get = "pub")]
slot: HexpansionSlot,
#[getset(get = "pub")]
time: Instant,
#[getset(get = "pub")]
state: HexpansionState,
}
#[derive(Debug, Format, PartialEq, Eq, Clone, Copy)]
pub enum HexpansionState {
Disabled,
Empty,
Occupied,
}
fn states_from_registers(
pins: &HexpansionDetectPins,
regs: &InputRegisters,
) -> [HexpansionState; HEXPANSION_SLOT_COUNT] {
[
regs.pin_state(&pins.a),
regs.pin_state(&pins.b),
regs.pin_state(&pins.c),
regs.pin_state(&pins.d),
regs.pin_state(&pins.e),
regs.pin_state(&pins.f),
]
.map(|high| {
if high {
HexpansionState::Empty
} else {
HexpansionState::Occupied
}
})
}