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)
    }
}