Code cleanup
This commit is contained in:
parent
308b6d708c
commit
967d544bbc
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user