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 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> {
rows: &'a [&'a dyn InputPin<Error = Infallible>; R],
cols: &'a mut [&'a mut dyn OutputPin<Error = Infallible>; C],
delay: &'a mut Delay,
state: [bool; N],
state_raw: [bool; N],
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 [&'a dyn InputPin<Error = Infallible>; R],
cols: &'a mut [&'a mut dyn OutputPin<Error = Infallible>; C],
delay: &'a mut Delay,
debounce: u8,
) -> Self {
Self {
rows,
cols,
delay,
state: [false; N],
state_raw: [false; N],
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();
}
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() {
self.cols[col_index].set_low().unwrap();
self.delay.delay_us(10);
delay.delay_us(10);
self.process_column(col_index);
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() {
let button_index: usize = col_index + (row_index * C);
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;
continue;
}
self.debounce_counter[button_index] += 1;
if self.debounce_counter[button_index] >= DEBOUNCE_COUNT {
self.state[button_index] = current_state;
if self.debounce_counter[button_index] >= self.debounce {
self.pressed[button_index] = current_state;
}
}
}
// pub fn scan_matrix(&mut self) {
// for (col_index, col) in self.cols.iter_mut().enumerate() {
// col.set_low().unwrap();
// 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
/// Returns an array of booleans indicating whether each button is pressed.
pub fn buttons_pressed(&mut self) -> [bool; N] {
self.pressed
}
}

View File

@ -12,6 +12,7 @@ mod status_led;
use crate::status_led::{StatusMode, Ws2812StatusLed};
use button_matrix::ButtonMatrix;
use core::convert::Infallible;
use cortex_m::delay::Delay;
use embedded_hal::digital::v2::*;
use embedded_hal::timer::CountDown;
use fugit::ExtU32;
@ -37,33 +38,26 @@ use waveshare_rp2040_zero::{
Pins, XOSC_CRYSTAL_FREQ,
};
// Public constants
pub const KEY_ROWS: usize = 4;
pub const KEY_COLS: usize = 12;
pub const NUMBER_OF_KEYS: usize = KEY_ROWS * KEY_COLS;
#[derive(Copy, Clone)]
// Public types
#[derive(Copy, Clone, Default)]
pub struct KeyboardButton {
pub current_state: bool,
pub previous_state: bool,
pub pressed: bool,
pub previous_pressed: bool,
pub fn_mode: u8,
}
impl KeyboardButton {
pub fn default() -> Self {
Self {
current_state: false,
previous_state: false,
fn_mode: 0,
}
}
}
#[entry]
fn main() -> ! {
let mut pac = pac::Peripherals::take().unwrap();
let core = pac::CorePeripherals::take().unwrap();
let mut watchdog = Watchdog::new(pac.WATCHDOG);
// Configure clocks and PLLs
let clocks = init_clocks_and_plls(
XOSC_CRYSTAL_FREQ,
pac.XOSC,
@ -76,6 +70,7 @@ fn main() -> ! {
.ok()
.unwrap();
// Configure GPIOs
let sio = Sio::new(pac.SIO);
let pins = Pins::new(
pac.IO_BANK0,
@ -84,38 +79,7 @@ fn main() -> ! {
&mut pac.RESETS,
);
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("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(),
);
// Create button matrix
let button_matrix_row_pins: &[&dyn InputPin<Error = Infallible>; KEY_ROWS] = &[
&pins.gp0.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> =
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 report_count_down = timer.count_down();
report_count_down.start(10.millis());
// Create timers/delays
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();
tick_count_down.start(1.millis());
let mut usb_hid_report_count_down = timer.count_down();
usb_hid_report_count_down.start(10.millis());
let mut indicator_count_down = timer.count_down();
indicator_count_down.start(250.millis());
let mut usb_tick_count_down = timer.count_down();
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 fn_mode: u8 = 0;
// Set all column pins to output and high
// Initialize button matrix
button_matrix.init_pins();
// Check if esc pressed while power on. If yes then enter bootloader
button_matrix.scan_matrix();
if button_matrix.get_button_state_raw()[0] == true {
// Scan matrix to get initial state
for _ in 0..10 {
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);
let gpio_activity_pin_mask = 0;
let disable_interface_mask = 0;
@ -168,47 +172,43 @@ fn main() -> ! {
}
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);
}
if report_count_down.wait().is_ok() {
// Scan keyboard matrix
let pressed_keys = button_matrix.get_button_state();
if usb_hid_report_count_down.wait().is_ok() {
let pressed_keys = button_matrix.buttons_pressed();
// Get current function layer
fn_mode = get_fn_mode(pressed_keys);
if caps_lock_active == false {
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() {
buttons[index].current_state = *key;
buttons[index].pressed = *key;
}
// Generate keyboard report
let keyboard_report = get_keyboard_report(&mut buttons, layout::MAP, fn_mode);
let keyboard_report = get_keyboard_report(&mut buttons, fn_mode);
match keyboard.device().write_report(keyboard_report) {
Err(UsbHidError::WouldBlock) => {}
Err(UsbHidError::Duplicate) => {}
Ok(_) => {}
Err(e) => {
status_led.update(StatusMode::ERROR);
core::panic!("Failed to write keyboard report: {:?}", e)
}
};
}
//Tick once per ms
if tick_count_down.wait().is_ok() {
button_matrix.scan_matrix();
if usb_tick_count_down.wait().is_ok() {
button_matrix.scan_matrix(&mut delay);
match keyboard.tick() {
Err(UsbHidError::WouldBlock) => {}
Ok(_) => {}
Err(e) => {
status_led.update(StatusMode::ERROR);
core::panic!("Failed to process keyboard tick: {:?}", e)
}
};
@ -216,26 +216,33 @@ fn main() -> ! {
if usb_dev.poll(&mut [&mut keyboard]) {
match keyboard.device().read_report() {
Err(UsbError::WouldBlock) => {
//do nothing
}
Err(UsbError::WouldBlock) => {}
Err(e) => {
status_led.update(StatusMode::ERROR);
core::panic!("Failed to read keyboard report: {:?}", e)
}
Ok(leds) => {
if leds.caps_lock == true {
caps_lock_active = true;
} else {
caps_lock_active = false;
}
caps_lock_active = leds.caps_lock;
}
}
}
}
}
// Set status LED colour based on function layer and capslock
// 0 = green, 1 = blue, 2 = orange, capslock active = flashing red.
/// Update status LED colour based on function layer and capslock
///
/// # 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>(
status_led: &mut Ws2812StatusLed<P, SM, I>,
fn_mode: u8,
@ -246,7 +253,7 @@ fn update_status_led<P, SM, I>(
Function<P>: ValidPinMode<I>,
SM: StateMachineIndex,
{
if caps_lock_active == true {
if caps_lock_active {
status_led.update(StatusMode::WARNING);
} else {
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 {
// Check Fn mode
let mut fn_mode: u8 = 0;
for button_id in crate::layout::FN_BUTTONS.iter() {
if pressed_keys[*button_id as usize] == true {
fn_mode += 1;
}
// Check how many Fn keys are pressed
let mut active_fn_keys = layout::FN_BUTTONS
.iter()
.filter(|button_id| pressed_keys[**button_id as usize])
.count() as u8;
// Limit Fn mode to 2
if active_fn_keys > 2 {
active_fn_keys = 2;
}
if fn_mode > 2 {
fn_mode = 2;
}
fn_mode
active_fn_keys
}
// 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(
matrix_keys: &mut [KeyboardButton; NUMBER_OF_KEYS],
layout_map: [[Keyboard; NUMBER_OF_KEYS]; 3],
fn_mode: u8,
) -> [Keyboard; NUMBER_OF_KEYS] {
// Create default report
let mut keyboard_report: [Keyboard; NUMBER_OF_KEYS] =
[Keyboard::NoEventIndicated; NUMBER_OF_KEYS];
// Filter report based on Fn mode and pressed keys
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.previous_state = key.current_state;
if key.current_state == true {
keyboard_report[index] = layout_map[key.fn_mode as usize][index];
key.previous_pressed = key.pressed;
if key.pressed {
keyboard_report[index] = layout::MAP[key.fn_mode as usize][index];
}
}
// Return report
keyboard_report
}

View File

@ -5,6 +5,7 @@ use rp2040_hal::{
use smart_leds::{SmartLedsWrite, RGB8};
use ws2812_pio::Ws2812Direct;
/// Status LED modes
#[derive(PartialEq, Eq, Copy, Clone)]
pub enum StatusMode {
OFF = 0,
@ -35,6 +36,13 @@ where
SM: StateMachineIndex,
{
/// 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(
pin: Pin<I, Function<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) {
let colors: [RGB8; 7] = [
(0, 0, 0).into(), // Off