//! Project: CMtec CMDR joystick 24 //! Date: 2025-03-09 //! Author: Christoffer Martinsson //! Email: cm@cmtec.se //! License: Please refer to LICENSE in root directory use core::convert::Infallible; use cortex_m::delay::Delay; use embedded_hal::digital::{InputPin, OutputPin}; /// Button matrix driver /// /// # Example /// ``` /// let button_matrix: ButtonMatrix<4, 6, 48> = ButtonMatrix::new(row_pins, col_pins, 5); /// ``` pub struct ButtonMatrix<'a, const R: usize, const C: usize, const N: usize> { rows: &'a mut [&'a mut dyn InputPin; R], cols: &'a mut [&'a mut dyn OutputPin; C], pressed: [bool; N], debounce: u8, debounce_counter: [u8; N], } impl<'a, const R: usize, const C: usize, const N: usize> ButtonMatrix<'a, R, C, N> { /// Creates a new button matrix. /// /// # Arguments /// /// * `rows` - An array of references to the row pins. /// * `cols` - An array of references to the column pins. /// * `debounce` - The debounce time in number of scans. pub fn new( rows: &'a mut [&'a mut dyn InputPin; R], cols: &'a mut [&'a mut dyn OutputPin; C], debounce: u8, ) -> Self { Self { rows, cols, pressed: [false; N], debounce, debounce_counter: [0; N], } } /// Initializes the button matrix. /// This should be called once before scanning the matrix. pub fn init_pins(&mut self) { for col in self.cols.iter_mut() { col.set_high().unwrap(); } } /// Scans the button matrix and updates the pressed state of each button. /// This should be called at regular intervals. /// Allow at least 5 times the delay compared to the needed button latency. /// /// # Arguments /// /// * `delay` - A mutable reference to a delay object. pub fn scan_matrix(&mut self, delay: &mut Delay) { for col_index in 0..self.cols.len() { self.cols[col_index].set_low().unwrap(); delay.delay_us(1); self.process_column(col_index); self.cols[col_index].set_high().unwrap(); delay.delay_us(1); } } /// Processes a column of the button matrix. /// /// # Arguments /// /// * `col_index` - The index of the column to process. fn process_column(&mut self, col_index: usize) { for row_index in 0..self.rows.len() { let button_index: usize = col_index + (row_index * C); let current_state = self.rows[row_index].is_low().unwrap(); if current_state == self.pressed[button_index] { self.debounce_counter[button_index] = 0; continue; } self.debounce_counter[button_index] += 1; if self.debounce_counter[button_index] >= self.debounce { self.pressed[button_index] = current_state; } } } /// Returns an array of booleans indicating whether each button is pressed. pub fn buttons_pressed(&mut self) -> [bool; N] { self.pressed } }