This commit is contained in:
Christoffer Martinsson 2023-06-29 17:28:38 +02:00
parent 7967ceb623
commit 308b6d708c
5 changed files with 300 additions and 143 deletions

View File

@ -14,11 +14,12 @@ embedded-hal ="0.2.5"
fugit = "0.3.5"
nb = "1.0.0"
smart-leds = "0.3.0"
smart-leds-trait = "0.2.1"
ws2812-pio = "0.6.0"
usbd-human-interface-device = "0.4.2"
usb-device = "0.2"
packed_struct = { version = "0.10", default-features = false }
keypad = "0.2.2"
pio = "0.2.0"
[features]
# This is the set of features we enable by default

View File

@ -0,0 +1,98 @@
use core::convert::Infallible;
use cortex_m::delay::Delay;
use embedded_hal::digital::v2::*;
pub const DEBOUNCE_COUNT: u8 = 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],
debounce_counter: [u8; N],
}
impl<'a, const R: usize, const C: usize, const N: usize> ButtonMatrix<'a, R, C, N> {
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,
) -> Self {
Self {
rows,
cols,
delay,
state: [false; N],
state_raw: [false; N],
debounce_counter: [0; N],
}
}
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) {
for col_index in 0..self.cols.len() {
self.cols[col_index].set_low().unwrap();
self.delay.delay_us(10);
self.process_column(col_index);
self.cols[col_index].set_high().unwrap();
self.delay.delay_us(10);
}
}
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();
self.state_raw[button_index] = current_state;
if current_state == self.state[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;
}
}
}
// 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
}
}

View File

@ -3,8 +3,8 @@
// | 0 | 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 |------------------
// ------------------- -------------------
// ------------------| 39 | 40 | 41 | | 42 | 43 | 44 |------------------
// 36 37 38 ------------------- ------------------- 45 46 47
//
// Swedish keymap conversion table:
// US Swedish
@ -26,13 +26,15 @@ use crate::NUMBER_OF_KEYS;
use usbd_human_interface_device::page::Keyboard;
// Function (Fn) buttons index (need two buttons)
pub const FN_BUTTONS: [u8; 3] = [37, 40, 41];
// Button 41 are used both as Fn and RightAlt (AltGr) for better ergonomics.
// This means that RightAlt is only available on layer 1 keys.
pub const FN_BUTTONS: [u8; 3] = [40, 43, 44];
// Button map to HID key (three Function layers)
pub const MAP: [[Keyboard; NUMBER_OF_KEYS]; 3] = [
[
// Function layer 0
// HID Key // Button Index
// -----------------------------------------
Keyboard::Tab, // 0
Keyboard::Q, // 1
Keyboard::W, // 2
@ -44,7 +46,7 @@ pub const MAP: [[Keyboard; NUMBER_OF_KEYS]; 3] = [
Keyboard::I, // 8
Keyboard::O, // 9
Keyboard::P, // 10
Keyboard::LeftBrace, // 11 // Å
Keyboard::LeftBrace, // 11 å
Keyboard::LeftControl, // 12
Keyboard::A, // 13
Keyboard::S, // 14
@ -55,8 +57,8 @@ pub const MAP: [[Keyboard; NUMBER_OF_KEYS]; 3] = [
Keyboard::J, // 19
Keyboard::K, // 20
Keyboard::L, // 21
Keyboard::Semicolon, // 22 // Ö
Keyboard::Apostrophe, // 23 // Ä
Keyboard::Semicolon, // 22 ö
Keyboard::Apostrophe, // 23 ä
Keyboard::LeftShift, // 24
Keyboard::Z, // 25
Keyboard::X, // 26
@ -67,18 +69,25 @@ pub const MAP: [[Keyboard; NUMBER_OF_KEYS]; 3] = [
Keyboard::M, // 31
Keyboard::Comma, // 32
Keyboard::Dot, // 33
Keyboard::ForwardSlash, // 34 // -
Keyboard::ForwardSlash, // 34 -
Keyboard::RightShift, // 35
Keyboard::LeftAlt, // 36
Keyboard::NoEventIndicated, // 37 // Fn
Keyboard::Space, // 38
Keyboard::ReturnEnter, // 39
Keyboard::NoEventIndicated, // 40 // Fn
Keyboard::RightAlt, // 41
Keyboard::NoEventIndicated, // 36 no button connected
Keyboard::NoEventIndicated, // 37 no button connected
Keyboard::NoEventIndicated, // 38 no button connected
Keyboard::LeftAlt, // 39
Keyboard::NoEventIndicated, // 40 Fn (= will never trigg this layer)
Keyboard::Space, // 41
Keyboard::ReturnEnter, // 42
Keyboard::NoEventIndicated, // 43 Fn (= will never trigg this layer)
Keyboard::NoEventIndicated, // 44 Fn (= will never trigg this layer)
Keyboard::NoEventIndicated, // 45 no button connected
Keyboard::NoEventIndicated, // 46 no button connected
Keyboard::NoEventIndicated, // 47 no button connected
],
[
// Function layer 1
// HID Key // Button Index
// -----------------------------------------
Keyboard::Escape, // 0
Keyboard::F1, // 1
Keyboard::F2, // 2
@ -109,29 +118,36 @@ pub const MAP: [[Keyboard; NUMBER_OF_KEYS]; 3] = [
Keyboard::Keyboard8, // 27
Keyboard::Keyboard9, // 28
Keyboard::Keyboard0, // 29
Keyboard::NonUSBackslash, // 30 // <
Keyboard::Equal, // 31 // ´
Keyboard::Backslash, // 32 // '
Keyboard::RightBrace, // 33 // ^
Keyboard::Minus, // 34 // +
Keyboard::NonUSBackslash, // 30 <
Keyboard::Equal, // 31 ´
Keyboard::Backslash, // 32 '
Keyboard::RightBrace, // 33 ^
Keyboard::Minus, // 34 +
Keyboard::RightShift, // 35
Keyboard::LeftAlt, // 36
Keyboard::NoEventIndicated, // 37 // Fn
Keyboard::DeleteBackspace, // 38
Keyboard::ReturnEnter, // 39
Keyboard::NoEventIndicated, // 40// Fn
Keyboard::RightAlt, // 41
Keyboard::NoEventIndicated, // 36 no button connected
Keyboard::NoEventIndicated, // 37 no button connected
Keyboard::NoEventIndicated, // 38 no button connected
Keyboard::LeftAlt, // 39
Keyboard::NoEventIndicated, // 40 Fn
Keyboard::DeleteBackspace, // 41
Keyboard::ReturnEnter, // 42
Keyboard::NoEventIndicated, // 43 Fn
Keyboard::RightAlt, // 44 Fn
Keyboard::NoEventIndicated, // 45 no button connected
Keyboard::NoEventIndicated, // 46 no button connected
Keyboard::NoEventIndicated, // 47 no button connected
],
[
// Function layer 2
// HID Key // Button Index
// -----------------------------------------
Keyboard::F11, // 0
Keyboard::F12, // 1
Keyboard::F13, // 2
Keyboard::F14, // 3
Keyboard::F15, // 4
Keyboard::F16, // 5
Keyboard::Grave, // 6 // §
Keyboard::Grave, // 6 §
Keyboard::NoEventIndicated, // 7
Keyboard::LeftGUI, // 8
Keyboard::NoEventIndicated, // 9
@ -161,11 +177,17 @@ pub const MAP: [[Keyboard; NUMBER_OF_KEYS]; 3] = [
Keyboard::End, // 33
Keyboard::Insert, // 34
Keyboard::RightShift, // 35
Keyboard::LeftAlt, // 36
Keyboard::NoEventIndicated, // 37 // Fn
Keyboard::LeftGUI, // 38
Keyboard::ReturnEnter, // 39
Keyboard::NoEventIndicated, // 40 // Fn
Keyboard::RightAlt, // 41
Keyboard::NoEventIndicated, // 36 no button connected
Keyboard::NoEventIndicated, // 37 no button connected
Keyboard::NoEventIndicated, // 38 no button connected
Keyboard::LeftAlt, // 39
Keyboard::NoEventIndicated, // 40 Fn
Keyboard::LeftGUI, // 41
Keyboard::ReturnEnter, // 42
Keyboard::NoEventIndicated, // 43 Fn
Keyboard::NoEventIndicated, // 44 Fn
Keyboard::NoEventIndicated, // 45 no button connected
Keyboard::NoEventIndicated, // 46 no button connected
Keyboard::NoEventIndicated, // 47 no button connected
],
];

View File

@ -5,13 +5,21 @@
#![no_std]
#![no_main]
mod button_matrix;
mod layout;
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;
use panic_halt as _;
use smart_leds::{SmartLedsWrite, RGB8};
use rp2040_hal::{
gpio::{Function, FunctionConfig, PinId, ValidPinMode},
pio::StateMachineIndex,
};
use usb_device::class_prelude::*;
use usb_device::prelude::*;
use usbd_human_interface_device::page::Keyboard;
@ -28,23 +36,20 @@ use waveshare_rp2040_zero::{
},
Pins, XOSC_CRYSTAL_FREQ,
};
use ws2812_pio::Ws2812;
mod layout;
pub const KEY_ROWS: usize = 4;
pub const KEY_COLS: usize = 12;
pub const NUMBER_OF_KEYS: usize = 42;
pub const NUMBER_OF_KEYS: usize = KEY_ROWS * KEY_COLS;
#[derive(Copy, Clone)]
struct ButtonMatrix {
current_state: bool,
previous_state: bool,
fn_mode: u8,
pub struct KeyboardButton {
pub current_state: bool,
pub previous_state: bool,
pub fn_mode: u8,
}
impl ButtonMatrix {
fn default() -> Self {
impl KeyboardButton {
pub fn default() -> Self {
Self {
current_state: false,
previous_state: false,
@ -104,17 +109,13 @@ fn main() -> ! {
// Configure the status LED
let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
let mut status_led = Ws2812::new(
let mut status_led = Ws2812StatusLed::new(
pins.neopixel.into_mode(),
&mut pio,
sm0,
clocks.peripheral_clock.freq(),
timer.count_down(),
);
let mut button_matrix: [ButtonMatrix; NUMBER_OF_KEYS] =
[ButtonMatrix::default(); NUMBER_OF_KEYS];
let button_matrix_row_pins: &[&dyn InputPin<Error = Infallible>; KEY_ROWS] = &[
&pins.gp0.into_pull_up_input(),
&pins.gp1.into_pull_up_input(),
@ -137,6 +138,11 @@ fn main() -> ! {
&mut pins.gp11.into_push_pull_output(),
];
let mut button_matrix: ButtonMatrix<KEY_ROWS, KEY_COLS, NUMBER_OF_KEYS> =
ButtonMatrix::new(button_matrix_row_pins, button_matrix_col_pins, &mut delay);
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());
@ -146,79 +152,44 @@ fn main() -> ! {
let mut indicator_count_down = timer.count_down();
indicator_count_down.start(250.millis());
let mut status_led_onoff: bool = false;
let mut caps_lock_active: bool = false;
let mut fn_mode: u8 = 0;
let status_led_colors: [RGB8; 6] = [
(0, 0, 0).into(), // Off
(10, 7, 0).into(), // Green
(10, 4, 10).into(), // Blue
(5, 10, 0).into(), // Orange
(2, 20, 0).into(), // Red
(0, 10, 10).into(), // Purple
];
// Set all column pins to output and high
init_button_matrix_pins(button_matrix_col_pins, &mut delay);
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 {
status_led.update(StatusMode::BOOTLOADER);
let gpio_activity_pin_mask = 0;
let disable_interface_mask = 0;
rp2040_hal::rom_data::reset_to_usb_boot(gpio_activity_pin_mask, disable_interface_mask);
}
loop {
if indicator_count_down.wait().is_ok() {
// Set status LED colour based on function layer and capslock
// 0 = green, 1 = blue, 2 = orange, capslock active = flashing.
if caps_lock_active == true && status_led_onoff == true {
status_led
.write([status_led_colors[0]].iter().copied())
.unwrap();
status_led_onoff = false;
} else {
status_led
.write([status_led_colors[usize::from(fn_mode) + 1]].iter().copied())
.unwrap();
status_led_onoff = true;
}
update_status_led(&mut status_led, fn_mode, caps_lock_active);
}
if report_count_down.wait().is_ok() {
// Scan keyboard matrix
let pressed_keys =
get_pressed_buttons(button_matrix_row_pins, button_matrix_col_pins, &mut delay);
let pressed_keys = button_matrix.get_button_state();
// Check if all four corners are pressed, if so, reset to USB boot mode
if pressed_keys[0] == true
&& pressed_keys[11] == true
&& pressed_keys[24] == true
&& pressed_keys[35] == true
{
status_led
.write([status_led_colors[5]].iter().copied())
.unwrap();
delay.delay_us(100);
let gpio_activity_pin_mask = 0;
let disable_interface_mask = 0;
rp2040_hal::rom_data::reset_to_usb_boot(
gpio_activity_pin_mask,
disable_interface_mask,
);
}
// Get current function layer
fn_mode = get_fn_mode(pressed_keys);
// Set status LED colour based on function layer and capslock
// 0 = green, 1 = blue, 2 = orange, capslock active = flashing.
if caps_lock_active == false {
status_led
.write([status_led_colors[usize::from(fn_mode) + 1]].iter().copied())
.unwrap();
status_led_onoff = true;
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() {
button_matrix[index].current_state = *key;
buttons[index].current_state = *key;
}
// Generate keyboard report
let keyboard_report = get_keyboard_report(&mut button_matrix, layout::MAP, fn_mode);
let keyboard_report = get_keyboard_report(&mut buttons, layout::MAP, fn_mode);
match keyboard.device().write_report(keyboard_report) {
Err(UsbHidError::WouldBlock) => {}
@ -232,6 +203,8 @@ fn main() -> ! {
//Tick once per ms
if tick_count_down.wait().is_ok() {
button_matrix.scan_matrix();
match keyboard.tick() {
Err(UsbHidError::WouldBlock) => {}
Ok(_) => {}
@ -261,54 +234,36 @@ fn main() -> ! {
}
}
// Initialise keyboard matrix pins
fn init_button_matrix_pins(cols: &mut [&mut dyn OutputPin<Error = Infallible>], delay: &mut Delay) {
for col in cols.iter_mut() {
col.set_high().unwrap();
}
delay.delay_us(10);
}
// Scan keyboard matrix for pressed keys and return a bool array
// representing the state of each key (true = pressed)
fn get_pressed_buttons(
rows: &[&dyn InputPin<Error = Infallible>],
cols: &mut [&mut dyn OutputPin<Error = Infallible>],
delay: &mut Delay,
) -> [bool; NUMBER_OF_KEYS] {
// Scan keyboard matrix for pressed keys
let mut pressed_keys: [bool; NUMBER_OF_KEYS] = [false; NUMBER_OF_KEYS];
for (col_index, col) in cols.iter_mut().enumerate() {
// Activate column
col.set_low().unwrap();
delay.delay_us(10);
// Read rows
for (row_index, row) in rows.iter().enumerate() {
// Do not check unconnected keys in the matrix
if row_index == 3 && (col_index < 3 || col_index > 8) {
continue;
}
if row_index < 3 && row.is_low().unwrap() {
pressed_keys[col_index + (row_index * KEY_COLS)] = true;
} else if row.is_low().unwrap() {
// Correct index for unconnected keys
pressed_keys[col_index + (row_index * KEY_COLS) - 3] = true;
}
// Set status LED colour based on function layer and capslock
// 0 = green, 1 = blue, 2 = orange, capslock active = flashing red.
fn update_status_led<P, SM, I>(
status_led: &mut Ws2812StatusLed<P, SM, I>,
fn_mode: u8,
caps_lock_active: bool,
) where
P: PIOExt + FunctionConfig,
I: PinId,
Function<P>: ValidPinMode<I>,
SM: StateMachineIndex,
{
if caps_lock_active == true {
status_led.update(StatusMode::WARNING);
} else {
match fn_mode {
0 => status_led.update(StatusMode::OK),
1 => status_led.update(StatusMode::ACTIVE1),
2 => status_led.update(StatusMode::ACTIVE2),
_ => status_led.update(StatusMode::ERROR),
}
col.set_high().unwrap();
delay.delay_us(10);
}
// Return scan result
pressed_keys
}
// Get current 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 layout::FN_BUTTONS.iter() {
if pressed_keys[usize::from(*button_id)] == true {
for button_id in crate::layout::FN_BUTTONS.iter() {
if pressed_keys[*button_id as usize] == true {
fn_mode += 1;
}
}
@ -320,7 +275,7 @@ fn get_fn_mode(pressed_keys: [bool; NUMBER_OF_KEYS]) -> u8 {
// Generate keyboard report based on pressed keys and Fn mode (0, 1 or 2)
fn get_keyboard_report(
matrix_keys: &mut [ButtonMatrix; NUMBER_OF_KEYS],
matrix_keys: &mut [KeyboardButton; NUMBER_OF_KEYS],
layout_map: [[Keyboard; NUMBER_OF_KEYS]; 3],
fn_mode: u8,
) -> [Keyboard; NUMBER_OF_KEYS] {
@ -335,7 +290,7 @@ fn get_keyboard_report(
key.previous_state = key.current_state;
if key.current_state == true {
keyboard_report[index] = layout_map[usize::from(key.fn_mode)][index];
keyboard_report[index] = layout_map[key.fn_mode as usize][index];
}
}
// Return report

81
rp2040/src/status_led.rs Normal file
View File

@ -0,0 +1,81 @@
use rp2040_hal::{
gpio::{Function, FunctionConfig, Pin, PinId, ValidPinMode},
pio::{PIOExt, StateMachineIndex, UninitStateMachine, PIO},
};
use smart_leds::{SmartLedsWrite, RGB8};
use ws2812_pio::Ws2812Direct;
#[derive(PartialEq, Eq, Copy, Clone)]
pub enum StatusMode {
OFF = 0,
OK = 1,
ACTIVE1 = 2,
ACTIVE2 = 3,
WARNING = 4,
ERROR = 5,
BOOTLOADER = 6,
}
pub struct Ws2812StatusLed<P, SM, I>
where
I: PinId,
P: PIOExt + FunctionConfig,
Function<P>: ValidPinMode<I>,
SM: StateMachineIndex,
{
ws2812_direct: Ws2812Direct<P, SM, I>,
state: bool,
}
impl<P, SM, I> Ws2812StatusLed<P, SM, I>
where
I: PinId,
P: PIOExt + FunctionConfig,
Function<P>: ValidPinMode<I>,
SM: StateMachineIndex,
{
/// Creates a new instance of this driver.
pub fn new(
pin: Pin<I, Function<P>>,
pio: &mut PIO<P>,
sm: UninitStateMachine<(P, SM)>,
clock_freq: fugit::HertzU32,
) -> Self {
// prepare the PIO program
let ws2812_direct = Ws2812Direct::new(pin, pio, sm, clock_freq);
let state = false;
Self {
ws2812_direct,
state,
}
}
pub fn update(&mut self, mode: StatusMode) {
let colors: [RGB8; 7] = [
(0, 0, 0).into(), // Off
(10, 7, 0).into(), // Green
(10, 4, 10).into(), // Blue
(5, 10, 0).into(), // Orange
(2, 20, 0).into(), // Red
(2, 20, 0).into(), // Red
(0, 10, 10).into(), // Purple
];
if mode == StatusMode::WARNING && self.state == false {
self.ws2812_direct
.write([colors[mode as usize]].iter().copied())
.unwrap();
self.state = true;
} else if mode == StatusMode::WARNING || mode == StatusMode::OFF {
self.ws2812_direct
.write([colors[0]].iter().copied())
.unwrap();
self.state = false;
} else {
self.ws2812_direct
.write([colors[mode as usize]].iter().copied())
.unwrap();
self.state = true;
}
}
}