Code cleanup

This commit is contained in:
Christoffer Martinsson 2023-07-01 16:28:56 +02:00
parent 308b6d708c
commit 967d544bbc
3 changed files with 179 additions and 146 deletions

View File

@ -2,47 +2,63 @@ use core::convert::Infallible;
use cortex_m::delay::Delay; use cortex_m::delay::Delay;
use embedded_hal::digital::v2::*; use embedded_hal::digital::v2::*;
pub const DEBOUNCE_COUNT: u8 = 5; /// 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> { pub struct ButtonMatrix<'a, const R: usize, const C: usize, const N: usize> {
rows: &'a [&'a dyn InputPin<Error = Infallible>; R], rows: &'a [&'a dyn InputPin<Error = Infallible>; R],
cols: &'a mut [&'a mut dyn OutputPin<Error = Infallible>; C], cols: &'a mut [&'a mut dyn OutputPin<Error = Infallible>; C],
delay: &'a mut Delay, pressed: [bool; N],
state: [bool; N], debounce: u8,
state_raw: [bool; N],
debounce_counter: [u8; N], debounce_counter: [u8; N],
} }
impl<'a, const R: usize, const C: usize, const N: usize> ButtonMatrix<'a, R, C, 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( pub fn new(
rows: &'a [&'a dyn InputPin<Error = Infallible>; R], rows: &'a [&'a dyn InputPin<Error = Infallible>; R],
cols: &'a mut [&'a mut dyn OutputPin<Error = Infallible>; C], cols: &'a mut [&'a mut dyn OutputPin<Error = Infallible>; C],
delay: &'a mut Delay, debounce: u8,
) -> Self { ) -> Self {
Self { Self {
rows, rows,
cols, cols,
delay, pressed: [false; N],
state: [false; N], debounce,
state_raw: [false; N],
debounce_counter: [0; N], debounce_counter: [0; N],
} }
} }
/// Initializes the button matrix.
/// This should be called once before scanning the matrix.
pub fn init_pins(&mut self) { pub fn init_pins(&mut self) {
for col in self.cols.iter_mut() { for col in self.cols.iter_mut() {
col.set_high().unwrap(); col.set_high().unwrap();
} }
self.delay.delay_us(10);
} }
pub fn scan_matrix(&mut self) { /// 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() { for col_index in 0..self.cols.len() {
self.cols[col_index].set_low().unwrap(); self.cols[col_index].set_low().unwrap();
self.delay.delay_us(10); delay.delay_us(10);
self.process_column(col_index); self.process_column(col_index);
self.cols[col_index].set_high().unwrap(); self.cols[col_index].set_high().unwrap();
self.delay.delay_us(10); delay.delay_us(10);
} }
} }
@ -50,49 +66,21 @@ impl<'a, const R: usize, const C: usize, const N: usize> ButtonMatrix<'a, R, C,
for row_index in 0..self.rows.len() { for row_index in 0..self.rows.len() {
let button_index: usize = col_index + (row_index * C); let button_index: usize = col_index + (row_index * C);
let current_state = self.rows[row_index].is_low().unwrap(); let current_state = self.rows[row_index].is_low().unwrap();
self.state_raw[button_index] = current_state;
if current_state == self.state[button_index] { if current_state == self.pressed[button_index] {
self.debounce_counter[button_index] = 0; self.debounce_counter[button_index] = 0;
continue; continue;
} }
self.debounce_counter[button_index] += 1; self.debounce_counter[button_index] += 1;
if self.debounce_counter[button_index] >= DEBOUNCE_COUNT { if self.debounce_counter[button_index] >= self.debounce {
self.state[button_index] = current_state; self.pressed[button_index] = current_state;
} }
} }
} }
// pub fn scan_matrix(&mut self) { /// Returns an array of booleans indicating whether each button is pressed.
// for (col_index, col) in self.cols.iter_mut().enumerate() { pub fn buttons_pressed(&mut self) -> [bool; N] {
// col.set_low().unwrap(); self.pressed
// self.delay.delay_us(10);
//
// for (row_index, row) in self.rows.iter().enumerate() {
// let button_index: usize = col_index + (row_index * C);
// let current_state = row.is_low().unwrap();
// self.state_raw[button_index] = current_state;
//
// if current_state == self.state[button_index] {
// self.debounce_counter[button_index] = 0;
// } else {
// self.debounce_counter[button_index] += 1;
// if self.debounce_counter[button_index] >= DEBOUNCE_COUNT {
// self.state[button_index] = current_state;
// }
// }
// }
// col.set_high().unwrap();
// self.delay.delay_us(10);
// }
// }
pub fn get_button_state(&mut self) -> [bool; N] {
self.state
}
pub fn get_button_state_raw(&mut self) -> [bool; N] {
self.state_raw
} }
} }

View File

@ -12,6 +12,7 @@ mod status_led;
use crate::status_led::{StatusMode, Ws2812StatusLed}; use crate::status_led::{StatusMode, Ws2812StatusLed};
use button_matrix::ButtonMatrix; use button_matrix::ButtonMatrix;
use core::convert::Infallible; use core::convert::Infallible;
use cortex_m::delay::Delay;
use embedded_hal::digital::v2::*; use embedded_hal::digital::v2::*;
use embedded_hal::timer::CountDown; use embedded_hal::timer::CountDown;
use fugit::ExtU32; use fugit::ExtU32;
@ -37,33 +38,26 @@ use waveshare_rp2040_zero::{
Pins, XOSC_CRYSTAL_FREQ, Pins, XOSC_CRYSTAL_FREQ,
}; };
// Public constants
pub const KEY_ROWS: usize = 4; pub const KEY_ROWS: usize = 4;
pub const KEY_COLS: usize = 12; pub const KEY_COLS: usize = 12;
pub const NUMBER_OF_KEYS: usize = KEY_ROWS * KEY_COLS; pub const NUMBER_OF_KEYS: usize = KEY_ROWS * KEY_COLS;
#[derive(Copy, Clone)] // Public types
#[derive(Copy, Clone, Default)]
pub struct KeyboardButton { pub struct KeyboardButton {
pub current_state: bool, pub pressed: bool,
pub previous_state: bool, pub previous_pressed: bool,
pub fn_mode: u8, pub fn_mode: u8,
} }
impl KeyboardButton {
pub fn default() -> Self {
Self {
current_state: false,
previous_state: false,
fn_mode: 0,
}
}
}
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
let mut pac = pac::Peripherals::take().unwrap(); let mut pac = pac::Peripherals::take().unwrap();
let core = pac::CorePeripherals::take().unwrap(); let core = pac::CorePeripherals::take().unwrap();
let mut watchdog = Watchdog::new(pac.WATCHDOG); let mut watchdog = Watchdog::new(pac.WATCHDOG);
// Configure clocks and PLLs
let clocks = init_clocks_and_plls( let clocks = init_clocks_and_plls(
XOSC_CRYSTAL_FREQ, XOSC_CRYSTAL_FREQ,
pac.XOSC, pac.XOSC,
@ -76,6 +70,7 @@ fn main() -> ! {
.ok() .ok()
.unwrap(); .unwrap();
// Configure GPIOs
let sio = Sio::new(pac.SIO); let sio = Sio::new(pac.SIO);
let pins = Pins::new( let pins = Pins::new(
pac.IO_BANK0, pac.IO_BANK0,
@ -84,38 +79,7 @@ fn main() -> ! {
&mut pac.RESETS, &mut pac.RESETS,
); );
let usb_bus = UsbBusAllocator::new(waveshare_rp2040_zero::hal::usb::UsbBus::new( // Create button matrix
pac.USBCTRL_REGS,
pac.USBCTRL_DPRAM,
clocks.usb_clock,
true,
&mut pac.RESETS,
));
let mut keyboard = UsbHidClassBuilder::new()
.add_device(
usbd_human_interface_device::device::keyboard::NKROBootKeyboardConfig::default(),
)
.build(&usb_bus);
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x1209, 0x0001))
.manufacturer("usbd-human-interface-device")
.product("CMDR keyboard")
.serial_number("0001")
.build();
let timer = Timer::new(pac.TIMER, &mut pac.RESETS);
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
// Configure the status LED
let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
let mut status_led = Ws2812StatusLed::new(
pins.neopixel.into_mode(),
&mut pio,
sm0,
clocks.peripheral_clock.freq(),
);
let button_matrix_row_pins: &[&dyn InputPin<Error = Infallible>; KEY_ROWS] = &[ let button_matrix_row_pins: &[&dyn InputPin<Error = Infallible>; KEY_ROWS] = &[
&pins.gp0.into_pull_up_input(), &pins.gp0.into_pull_up_input(),
&pins.gp1.into_pull_up_input(), &pins.gp1.into_pull_up_input(),
@ -139,28 +103,68 @@ fn main() -> ! {
]; ];
let mut button_matrix: ButtonMatrix<KEY_ROWS, KEY_COLS, NUMBER_OF_KEYS> = let mut button_matrix: ButtonMatrix<KEY_ROWS, KEY_COLS, NUMBER_OF_KEYS> =
ButtonMatrix::new(button_matrix_row_pins, button_matrix_col_pins, &mut delay); ButtonMatrix::new(button_matrix_row_pins, button_matrix_col_pins, 5);
// Configure USB
let usb_bus = UsbBusAllocator::new(waveshare_rp2040_zero::hal::usb::UsbBus::new(
pac.USBCTRL_REGS,
pac.USBCTRL_DPRAM,
clocks.usb_clock,
true,
&mut pac.RESETS,
));
let mut keyboard = UsbHidClassBuilder::new()
.add_device(
usbd_human_interface_device::device::keyboard::NKROBootKeyboardConfig::default(),
)
.build(&usb_bus);
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x1209, 0x0001))
.manufacturer("CMtec")
.product("CMDR keyboard")
.serial_number("0001")
.build();
// Create status LED
let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
let mut status_led = Ws2812StatusLed::new(
pins.neopixel.into_mode(),
&mut pio,
sm0,
clocks.peripheral_clock.freq(),
);
// Create keyboard button array
let mut buttons: [KeyboardButton; NUMBER_OF_KEYS] = [KeyboardButton::default(); NUMBER_OF_KEYS]; let mut buttons: [KeyboardButton; NUMBER_OF_KEYS] = [KeyboardButton::default(); NUMBER_OF_KEYS];
let mut report_count_down = timer.count_down(); // Create timers/delays
report_count_down.start(10.millis()); let timer = Timer::new(pac.TIMER, &mut pac.RESETS);
let mut delay = Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
let mut tick_count_down = timer.count_down(); let mut usb_hid_report_count_down = timer.count_down();
tick_count_down.start(1.millis()); usb_hid_report_count_down.start(10.millis());
let mut indicator_count_down = timer.count_down(); let mut usb_tick_count_down = timer.count_down();
indicator_count_down.start(250.millis()); usb_tick_count_down.start(1.millis());
let mut status_led_count_down = timer.count_down();
status_led_count_down.start(250.millis());
// Create variables to track caps lock and fn mode
let mut caps_lock_active: bool = false; let mut caps_lock_active: bool = false;
let mut fn_mode: u8 = 0; let mut fn_mode: u8 = 0;
// Set all column pins to output and high // Initialize button matrix
button_matrix.init_pins(); button_matrix.init_pins();
// Check if esc pressed while power on. If yes then enter bootloader // Scan matrix to get initial state
button_matrix.scan_matrix(); for _ in 0..10 {
if button_matrix.get_button_state_raw()[0] == true { button_matrix.scan_matrix(&mut delay);
}
// Check if esc key is pressed while power on. If yes then enter bootloader
if button_matrix.buttons_pressed()[0] {
status_led.update(StatusMode::BOOTLOADER); status_led.update(StatusMode::BOOTLOADER);
let gpio_activity_pin_mask = 0; let gpio_activity_pin_mask = 0;
let disable_interface_mask = 0; let disable_interface_mask = 0;
@ -168,47 +172,43 @@ fn main() -> ! {
} }
loop { loop {
if indicator_count_down.wait().is_ok() { if status_led_count_down.wait().is_ok() {
update_status_led(&mut status_led, fn_mode, caps_lock_active); update_status_led(&mut status_led, fn_mode, caps_lock_active);
} }
if report_count_down.wait().is_ok() { if usb_hid_report_count_down.wait().is_ok() {
// Scan keyboard matrix let pressed_keys = button_matrix.buttons_pressed();
let pressed_keys = button_matrix.get_button_state();
// Get current function layer
fn_mode = get_fn_mode(pressed_keys); fn_mode = get_fn_mode(pressed_keys);
if caps_lock_active == false { if caps_lock_active == false {
update_status_led(&mut status_led, fn_mode, caps_lock_active); update_status_led(&mut status_led, fn_mode, caps_lock_active);
} }
// Copy result from scanned keys to matrix struct
for (index, key) in pressed_keys.iter().enumerate() { for (index, key) in pressed_keys.iter().enumerate() {
buttons[index].current_state = *key; buttons[index].pressed = *key;
} }
// Generate keyboard report let keyboard_report = get_keyboard_report(&mut buttons, fn_mode);
let keyboard_report = get_keyboard_report(&mut buttons, layout::MAP, fn_mode);
match keyboard.device().write_report(keyboard_report) { match keyboard.device().write_report(keyboard_report) {
Err(UsbHidError::WouldBlock) => {} Err(UsbHidError::WouldBlock) => {}
Err(UsbHidError::Duplicate) => {} Err(UsbHidError::Duplicate) => {}
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
status_led.update(StatusMode::ERROR);
core::panic!("Failed to write keyboard report: {:?}", e) core::panic!("Failed to write keyboard report: {:?}", e)
} }
}; };
} }
//Tick once per ms if usb_tick_count_down.wait().is_ok() {
if tick_count_down.wait().is_ok() { button_matrix.scan_matrix(&mut delay);
button_matrix.scan_matrix();
match keyboard.tick() { match keyboard.tick() {
Err(UsbHidError::WouldBlock) => {} Err(UsbHidError::WouldBlock) => {}
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
status_led.update(StatusMode::ERROR);
core::panic!("Failed to process keyboard tick: {:?}", e) core::panic!("Failed to process keyboard tick: {:?}", e)
} }
}; };
@ -216,26 +216,33 @@ fn main() -> ! {
if usb_dev.poll(&mut [&mut keyboard]) { if usb_dev.poll(&mut [&mut keyboard]) {
match keyboard.device().read_report() { match keyboard.device().read_report() {
Err(UsbError::WouldBlock) => { Err(UsbError::WouldBlock) => {}
//do nothing
}
Err(e) => { Err(e) => {
status_led.update(StatusMode::ERROR);
core::panic!("Failed to read keyboard report: {:?}", e) core::panic!("Failed to read keyboard report: {:?}", e)
} }
Ok(leds) => { Ok(leds) => {
if leds.caps_lock == true { caps_lock_active = leds.caps_lock;
caps_lock_active = true;
} else {
caps_lock_active = false;
}
} }
} }
} }
} }
} }
// Set status LED colour based on function layer and capslock /// Update status LED colour based on function layer and capslock
// 0 = green, 1 = blue, 2 = orange, capslock active = flashing red. ///
/// # Arguments
/// * `status_led` - Reference to status LED
/// * `fn_mode` - Current function layer
/// * `caps_lock_active` - Is capslock active
///
/// # Results
///
/// Fn0 = green (OK)
/// Fn1 = blue (ACTIVE1)
/// Fn2 = orange (ACTIVE2)
/// Capslock active = flashing red (WARNING)
/// Error = steady red (ERROR)
fn update_status_led<P, SM, I>( fn update_status_led<P, SM, I>(
status_led: &mut Ws2812StatusLed<P, SM, I>, status_led: &mut Ws2812StatusLed<P, SM, I>,
fn_mode: u8, fn_mode: u8,
@ -246,7 +253,7 @@ fn update_status_led<P, SM, I>(
Function<P>: ValidPinMode<I>, Function<P>: ValidPinMode<I>,
SM: StateMachineIndex, SM: StateMachineIndex,
{ {
if caps_lock_active == true { if caps_lock_active {
status_led.update(StatusMode::WARNING); status_led.update(StatusMode::WARNING);
} else { } else {
match fn_mode { match fn_mode {
@ -258,41 +265,59 @@ fn update_status_led<P, SM, I>(
} }
} }
// Get current Fn mode (0, 1 or 2) /// Get current Fn mode (0, 1 or 2)
/// layout::FN_BUTTONS contains the keycodes for each Fn key
///
/// # Arguments
///
/// * `pressed_keys` - Array of pressed keys
///
/// # Results
///
/// * Fn mode (0, 1 or 2)
fn get_fn_mode(pressed_keys: [bool; NUMBER_OF_KEYS]) -> u8 { fn get_fn_mode(pressed_keys: [bool; NUMBER_OF_KEYS]) -> u8 {
// Check Fn mode // Check how many Fn keys are pressed
let mut fn_mode: u8 = 0; let mut active_fn_keys = layout::FN_BUTTONS
for button_id in crate::layout::FN_BUTTONS.iter() { .iter()
if pressed_keys[*button_id as usize] == true { .filter(|button_id| pressed_keys[**button_id as usize])
fn_mode += 1; .count() as u8;
}
// Limit Fn mode to 2
if active_fn_keys > 2 {
active_fn_keys = 2;
} }
if fn_mode > 2 { active_fn_keys
fn_mode = 2;
}
fn_mode
} }
// Generate keyboard report based on pressed keys and Fn mode (0, 1 or 2) /// Generate keyboard report based on pressed keys and Fn mode (0, 1 or 2)
/// layout::MAP contains the keycodes for each key in each Fn mode
///
/// # Arguments
///
/// * `matrix_keys` - Array of pressed keys
/// * `fn_mode` - Current function layer
///
/// # Results
///
/// * Keyboard report
fn get_keyboard_report( fn get_keyboard_report(
matrix_keys: &mut [KeyboardButton; NUMBER_OF_KEYS], matrix_keys: &mut [KeyboardButton; NUMBER_OF_KEYS],
layout_map: [[Keyboard; NUMBER_OF_KEYS]; 3],
fn_mode: u8, fn_mode: u8,
) -> [Keyboard; NUMBER_OF_KEYS] { ) -> [Keyboard; NUMBER_OF_KEYS] {
// Create default report
let mut keyboard_report: [Keyboard; NUMBER_OF_KEYS] = let mut keyboard_report: [Keyboard; NUMBER_OF_KEYS] =
[Keyboard::NoEventIndicated; NUMBER_OF_KEYS]; [Keyboard::NoEventIndicated; NUMBER_OF_KEYS];
// Filter report based on Fn mode and pressed keys // Filter report based on Fn mode and pressed keys
for (index, key) in matrix_keys.iter_mut().enumerate() { for (index, key) in matrix_keys.iter_mut().enumerate() {
if key.current_state != key.previous_state && key.current_state == true { if key.pressed != key.previous_pressed && key.pressed {
key.fn_mode = fn_mode; key.fn_mode = fn_mode;
} }
key.previous_state = key.current_state;
if key.current_state == true { key.previous_pressed = key.pressed;
keyboard_report[index] = layout_map[key.fn_mode as usize][index];
if key.pressed {
keyboard_report[index] = layout::MAP[key.fn_mode as usize][index];
} }
} }
// Return report
keyboard_report keyboard_report
} }

View File

@ -5,6 +5,7 @@ use rp2040_hal::{
use smart_leds::{SmartLedsWrite, RGB8}; use smart_leds::{SmartLedsWrite, RGB8};
use ws2812_pio::Ws2812Direct; use ws2812_pio::Ws2812Direct;
/// Status LED modes
#[derive(PartialEq, Eq, Copy, Clone)] #[derive(PartialEq, Eq, Copy, Clone)]
pub enum StatusMode { pub enum StatusMode {
OFF = 0, OFF = 0,
@ -35,6 +36,13 @@ where
SM: StateMachineIndex, SM: StateMachineIndex,
{ {
/// Creates a new instance of this driver. /// Creates a new instance of this driver.
///
/// # Arguments
///
/// * `pin` - PIO pin
/// * `pio` - PIO instance
/// * `sm` - PIO state machine
/// * `clock_freq` - PIO clock frequency
pub fn new( pub fn new(
pin: Pin<I, Function<P>>, pin: Pin<I, Function<P>>,
pio: &mut PIO<P>, pio: &mut PIO<P>,
@ -50,6 +58,18 @@ where
} }
} }
/// Update status LED
/// Depending on the mode, the LED will be set to a different colour
///
/// * OFF = off
/// * OK = green
/// * ACTIVE1 = blue
/// * ACTIVE2 = orange
/// * WARNING = red (flashing)
/// * ERROR = red
/// * BOOTLOADER = purple
///
/// Make sure to call this function regularly to keep the LED flashing
pub fn update(&mut self, mode: StatusMode) { pub fn update(&mut self, mode: StatusMode) {
let colors: [RGB8; 7] = [ let colors: [RGB8; 7] = [
(0, 0, 0).into(), // Off (0, 0, 0).into(), // Off