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
// Copyright 2021-2022 Jacob Alexander
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
#![no_std]
use embedded_hal::digital::v2::OutputPin;
use kiibohd_hall_effect::{SenseAnalysis, SensorError, Sensors};
/// Handles strobing the Hall Effect sensor matrix
/// ADC reading is handled separately as the current embedded-hal doesn't work
/// well across oneshot, interrupt based and DMA ADC read methods.
/// (usually requires quite a bit of MCU specific setup as well)
///
/// ```rust,ignore
/// const CSIZE: usize = 18; // Number of columns
/// const RSIZE: usize = 6; // Number of rows
/// const MSIZE: usize = RSIZE * CSIZE; // Total matrix size
/// const INVERT_STROBE: bool = true; // P-Channel MOSFETs have an inverted strobe
/// type Matrix = kiibohd_hall_effect_keyscanning::Matrix<PioX<Output<PushPull>>, CSIZE, MSIZE, INVERT_STROBE>; // atsam4-hal
/// let cols = [
/// pins.strobe1.downgrade(),
/// pins.strobe2.downgrade(),
/// pins.strobe3.downgrade(),
/// pins.strobe4.downgrade(),
/// pins.strobe5.downgrade(),
/// pins.strobe6.downgrade(),
/// pins.strobe7.downgrade(),
/// pins.strobe8.downgrade(),
/// pins.strobe9.downgrade(),
/// pins.strobe10.downgrade(),
/// pins.strobe11.downgrade(),
/// pins.strobe12.downgrade(),
/// pins.strobe13.downgrade(),
/// pins.strobe14.downgrade(),
/// pins.strobe15.downgrade(),
/// pins.strobe16.downgrade(),
/// pins.strobe17.downgrade(),
/// pins.strobe18.downgrade(),
/// ];
/// let mut matrix = Matrix::new(cols).unwrap();
/// ```
pub struct Matrix<C: OutputPin, const CSIZE: usize, const MSIZE: usize, const INVERT_STROBE: bool> {
cols: [C; CSIZE],
cur_strobe: usize,
sensors: Sensors<MSIZE>,
}
impl<C: OutputPin, const CSIZE: usize, const MSIZE: usize, const INVERT_STROBE: bool>
Matrix<C, CSIZE, MSIZE, INVERT_STROBE>
{
pub fn new(cols: [C; CSIZE]) -> Result<Self, SensorError> {
let sensors = Sensors::new()?;
let res = Self {
cols,
cur_strobe: CSIZE - 1,
sensors,
};
Ok(res)
}
/// Clears strobes
/// Resets strobe counter to the last element (so next_strobe starts at 0)
pub fn clear<'a, E: 'a>(&'a mut self) -> Result<(), E>
where
C: OutputPin<Error = E>,
{
// Clear strobes
for c in self.cols.iter_mut() {
if INVERT_STROBE {
c.set_high()?;
} else {
c.set_low()?;
}
}
// Reset strobe position
self.cur_strobe = CSIZE - 1;
Ok(())
}
/// Next strobe
pub fn next_strobe<'a, E: 'a>(&'a mut self) -> Result<usize, E>
where
C: OutputPin<Error = E>,
{
// Unset current strobe
if INVERT_STROBE {
self.cols[self.cur_strobe].set_high()?;
} else {
self.cols[self.cur_strobe].set_low()?;
}
// Check for roll-over condition
if self.cur_strobe >= CSIZE - 1 {
self.cur_strobe = 0;
} else {
self.cur_strobe += 1;
}
// Set new strobe
if INVERT_STROBE {
self.cols[self.cur_strobe].set_low()?;
} else {
self.cols[self.cur_strobe].set_high()?;
}
Ok(self.cur_strobe)
}
/// Current strobe
pub fn strobe(&self) -> usize {
self.cur_strobe
}
/// Record ADC Hall Effect reading for the given the current row/sense index
/// The sense index is usually 0-5, though it depends on the typical setup
/// SC: Sample Count - How many samples before computing an analysis for a given index
pub fn record<const SC: usize>(
&mut self,
index: usize,
value: u16,
) -> Result<Option<&SenseAnalysis>, SensorError> {
self.sensors.add::<SC>(index, value)
}
}