Skip to main content

AnalogHallMatrix

Struct AnalogHallMatrix 

Source
pub struct AnalogHallMatrix<'peripherals, ADC, D, IRQ, IM, const ROW: usize, const COL: usize>
where ADC: Instance<Regs = Adc> + BasicInstance, D: RxDma<ADC>, IRQ: Binding<D::Interrupt, InterruptHandler<D>> + Copy + 'peripherals, IM: MasterMode, <<ADC as BasicInstance>::Regs as BasicAdcRegs>::SampleTime: Clone,
{ adc_part: AdcPart<'peripherals, ADC, D, ROW>, auto_calib: [[AutoCalib; COL]; ROW], calib: [[KeyCalib; COL]; ROW], cfg: HallCfg, cols: Hc164Cols<'peripherals>, eeprom: Ft24c64<'peripherals, IM>, irq: IRQ, state: [[KeyState; COL]; ROW], }
Expand description

Hall-effect analog matrix scanner with EEPROM-backed per-key calibration and continuous auto-calibration.

On first boot (or after EEPROM corruption) the firmware performs a guided two-phase calibration:

  1. Zero-travel pass - all keys fully released; the firmware averages HallCfg::calib_passes reads per key. Backlight signals amber.
  2. Full-travel pass - user presses every key to the bottom within HallCfg::full_calib_duration; each key must be held for crate::matrix::analog_matrix::types::CALIB_HOLD_DURATION_MS before it is accepted and its LED turns green.

After all keys are accepted a crate::matrix::analog_matrix::types::CALIB_SETTLE_AFTER_ALL_DONE window continues sampling to capture the true bottom-out ADC. Validated entries are written to the FT24C64 EEPROM and verified by read-back. On all subsequent boots only full-travel data is loaded from EEPROM; zero-travel is re-measured fresh to compensate for temperature drift.

During normal operation the auto-calibrator silently refines both zero and full-travel values on every press/release cycle, keeping the scanner accurate as the sensor drifts over time without requiring user interaction.

Fields§

§adc_part: AdcPart<'peripherals, ADC, D, ROW>

ADC peripherals and channels grouped for split-borrow compatibility.

§auto_calib: [[AutoCalib; COL]; ROW]

Per-key auto-calibration state used to refine self::AnalogHallMatrix::calib during normal operation.

§calib: [[KeyCalib; COL]; ROW]

Per-key calibration data applied during the scan loop.

§cfg: HallCfg

Sensing and scanning configuration.

§cols: Hc164Cols<'peripherals>

Column driver used to select the active column via the HC164.

§eeprom: Ft24c64<'peripherals, IM>

EEPROM driver for loading and persisting calibration data.

§irq: IRQ

DMA interrupt binding reused for every ADC sequence read.

§state: [[KeyState; COL]; ROW]

Dynamic per-key runtime state for the scan loop.

Implementations§

Source§

impl<'peripherals, ADC, D, IRQ, IM, const ROW: usize, const COL: usize> AnalogHallMatrix<'peripherals, ADC, D, IRQ, IM, ROW, COL>
where ADC: Instance<Regs = Adc> + BasicInstance, D: RxDma<ADC>, IRQ: Binding<D::Interrupt, InterruptHandler<D>> + Copy + 'peripherals, IM: MasterMode, <<ADC as BasicInstance>::Regs as BasicAdcRegs>::SampleTime: Clone,

Source

pub(super) const fn default_entries() -> [[CalibEntry; COL]; ROW]

Build the default calibration entries used when EEPROM is blank or invalid, giving a reasonable approximation of travel until real calibration runs.

Source

pub(super) async fn calibrate_zero_raw(&mut self) -> [[u16; COL]; ROW]

Average cfg.calib_passes full-matrix scans to establish per-key zero-travel (resting) ADC values.

All keys must be fully released during this pass. Returns a ROW × COL array of raw ADC averages, each reduced by ZERO_TRAVEL_DEAD_ZONE so that the resting position sits cleanly below the measured average, preventing ADC noise from producing spurious non-zero travel readings. Does not modify self.calib.

Source

pub(super) async fn sample_full_raw( &mut self, duration: Duration, zero_raw: &[[u16; COL]; ROW], ) -> [[u16; COL]; ROW]

Sample the matrix for duration (or until all real keys are accepted), recording the minimum ADC reading seen per key.

Lower ADC = more magnet travel, so the minimum reading over the window is the deepest press seen. A key is accepted only after it has stayed continuously below CALIB_PRESS_THRESHOLD for CALIB_HOLD_DURATION_MS; releasing and re-pressing resets the timer. The LED turns green only at acceptance, not at first crossing.

After all keys are accepted a CALIB_SETTLE_AFTER_ALL_DONE continuation window keeps updating min_raw so the stored value reflects the true bottom-out ADC, not merely the acceptance instant.

Source

pub(super) async fn run_first_boot_calib( &mut self, eeprom_buf: &mut [u8; 259], entries: &mut [[CalibEntry; COL]; ROW], )

Run the guided first-boot two-phase calibration, persist the result to EEPROM, and apply it to self.calib.

Backlight signals during the process:

  • Amber - zero-travel pass, all keys must be released.
  • Blue → green per key - full-travel press window.
  • Green blink ×3 - all keys accepted; keys may be released.
  • Green for 2 s - calibration stored successfully.
  • Amber - EEPROM write-back verification failed; keyboard will re-calibrate on the next boot.

Keys not pressed during the full-travel window fall back to zero − DEFAULT_FULL_RANGE so the keyboard remains functional.

Source§

impl<'peripherals, ADC, D, IRQ, IM, const ROW: usize, const COL: usize> AnalogHallMatrix<'peripherals, ADC, D, IRQ, IM, ROW, COL>
where ADC: Instance<Regs = Adc> + BasicInstance, D: RxDma<ADC>, IRQ: Binding<D::Interrupt, InterruptHandler<D>> + Copy + 'peripherals, IM: MasterMode, <<ADC as BasicInstance>::Regs as BasicAdcRegs>::SampleTime: Clone,

Source

pub(super) async fn scan_for_next_change( cols: &mut Hc164Cols<'peripherals>, state: &mut [[KeyState; COL]; ROW], calib: &mut [[KeyCalib; COL]; ROW], auto_calib: &mut [[AutoCalib; COL]; ROW], seq: &mut ConfiguredSequence<'_, Adc>, buf: &mut [u16; ROW], cfg: HallCfg, ) -> Option<KeyboardEvent>

Scan the matrix once, returning the first key state change found.

The [ConfiguredSequence] is programmed once per invocation and reused across all columns. Both the column settle delay and the DMA transfer are fully async. For each reading that passes the noise gate the auto-calibrator is updated before the travel and rapid-trigger logic runs, so any calibration refinement takes effect within the same scan pass.

Source§

impl<'peripherals, ADC, D, IRQ, IM, const ROW: usize, const COL: usize> AnalogHallMatrix<'peripherals, ADC, D, IRQ, IM, ROW, COL>
where ADC: Instance<Regs = Adc> + BasicInstance, D: RxDma<ADC>, IRQ: Binding<D::Interrupt, InterruptHandler<D>> + Copy + 'peripherals, IM: MasterMode, <<ADC as BasicInstance>::Regs as BasicAdcRegs>::SampleTime: Clone,

Source

pub fn new( adc_part: AdcPart<'peripherals, ADC, D, ROW>, irq: IRQ, cols: Hc164Cols<'peripherals>, cfg: HallCfg, eeprom: Ft24c64<'peripherals, IM>, ) -> Self

Create a new matrix scanner.

Calibration is deferred to [Runnable::run], which loads from EEPROM on subsequent boots or runs a full first-boot calibration pass.

Source

fn apply_calib( &mut self, entries: &[[CalibEntry; COL]; ROW], zero_raw: &[[u16; COL]; ROW], )

Apply a row-major array of CalibEntry values to self.calib, pairing each stored full-travel reading with the corresponding live zero-travel reading from zero_raw.

Trait Implementations§

Source§

impl<'peripherals, ADC, D, IRQ, IM, const ROW: usize, const COL: usize> InputDevice for AnalogHallMatrix<'peripherals, ADC, D, IRQ, IM, ROW, COL>
where ADC: Instance<Regs = Adc> + BasicInstance, D: RxDma<ADC>, IRQ: Binding<D::Interrupt, InterruptHandler<D>> + Copy + 'peripherals, IM: MasterMode, <<ADC as BasicInstance>::Regs as BasicAdcRegs>::SampleTime: Clone,

Source§

async fn read_event(&mut self) -> Self::Event

Not used - events are published directly via [publish_event_async] in [Runnable::run]. Returns [pending] to satisfy the trait bound without competing with the scan loop.

Source§

type Event = KeyboardEvent

The event type produced by this input device
Source§

impl<'peripherals, ADC, D, IRQ, IM, const ROW: usize, const COL: usize> Runnable for AnalogHallMatrix<'peripherals, ADC, D, IRQ, IM, ROW, COL>
where ADC: Instance<Regs = Adc> + BasicInstance, D: RxDma<ADC>, IRQ: Binding<D::Interrupt, InterruptHandler<D>> + Copy + 'peripherals, IM: MasterMode, <<ADC as BasicInstance>::Regs as BasicAdcRegs>::SampleTime: Clone,

Source§

async fn run(&mut self) -> !

Auto Trait Implementations§

§

impl<'peripherals, ADC, D, IRQ, IM, const ROW: usize, const COL: usize> Freeze for AnalogHallMatrix<'peripherals, ADC, D, IRQ, IM, ROW, COL>
where SampleTime: Sized, IRQ: Freeze, D: Freeze, ADC: Freeze,

§

impl<'peripherals, ADC, D, IRQ, IM, const ROW: usize, const COL: usize> !RefUnwindSafe for AnalogHallMatrix<'peripherals, ADC, D, IRQ, IM, ROW, COL>

§

impl<'peripherals, ADC, D, IRQ, IM, const ROW: usize, const COL: usize> Send for AnalogHallMatrix<'peripherals, ADC, D, IRQ, IM, ROW, COL>
where SampleTime: Sized, IRQ: Send, D: Send, ADC: Send, IM: Send,

§

impl<'peripherals, ADC, D, IRQ, IM, const ROW: usize, const COL: usize> Sync for AnalogHallMatrix<'peripherals, ADC, D, IRQ, IM, ROW, COL>
where SampleTime: Sized, IRQ: Sync, D: Sync, ADC: Sync, IM: Sync,

§

impl<'peripherals, ADC, D, IRQ, IM, const ROW: usize, const COL: usize> Unpin for AnalogHallMatrix<'peripherals, ADC, D, IRQ, IM, ROW, COL>
where SampleTime: Sized, IRQ: Unpin, D: Unpin, ADC: Unpin, IM: Unpin,

§

impl<'peripherals, ADC, D, IRQ, IM, const ROW: usize, const COL: usize> UnsafeUnpin for AnalogHallMatrix<'peripherals, ADC, D, IRQ, IM, ROW, COL>
where SampleTime: Sized, IRQ: UnsafeUnpin, D: UnsafeUnpin, ADC: UnsafeUnpin,

§

impl<'peripherals, ADC, D, IRQ, IM, const ROW: usize, const COL: usize> !UnwindSafe for AnalogHallMatrix<'peripherals, ADC, D, IRQ, IM, ROW, COL>

Blanket Implementations§

§

impl<T> Any for T
where T: 'static + ?Sized,

§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<T> Borrow<T> for T
where T: ?Sized,

§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
§

impl<T> BorrowMut<T> for T
where T: ?Sized,

§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> From<T> for T

§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T, U> Into<U> for T
where U: From<T>,

§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of [From]<T> for U chooses to do.

§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.