458 lines
14 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#![no_std]
#![no_main]
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 usb_device::class_prelude::*;
use usb_device::prelude::*;
use usbd_human_interface_device::page::Keyboard;
use usbd_human_interface_device::prelude::*;
use waveshare_rp2040_zero::entry;
use waveshare_rp2040_zero::{
hal::{
clocks::{init_clocks_and_plls, Clock},
pac,
pio::PIOExt,
timer::Timer,
watchdog::Watchdog,
Sio,
},
Pins, XOSC_CRYSTAL_FREQ,
};
use ws2812_pio::Ws2812;
pub const KEY_ROWS: usize = 4;
pub const KEY_COLS: usize = 12;
pub const NUMBER_OF_KEYS: usize = 42;
#[derive(Copy, Clone)]
struct MatrixKey {
current_state: bool,
previous_state: bool,
fn_mode: u8,
}
impl MatrixKey {
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);
let clocks = init_clocks_and_plls(
XOSC_CRYSTAL_FREQ,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog,
)
.ok()
.unwrap();
let sio = Sio::new(pac.SIO);
let pins = Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&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 = Ws2812::new(
pins.neopixel.into_mode(),
&mut pio,
sm0,
clocks.peripheral_clock.freq(),
timer.count_down(),
);
let mut matrix_keys: [MatrixKey; NUMBER_OF_KEYS] = [MatrixKey::default(); NUMBER_OF_KEYS];
let matrix_rows: &[&dyn InputPin<Error = Infallible>] = &[
&pins.gp0.into_pull_up_input(),
&pins.gp1.into_pull_up_input(),
&pins.gp29.into_pull_up_input(),
&pins.gp28.into_pull_up_input(),
];
let matrix_cols: &mut [&mut dyn OutputPin<Error = Infallible>] = &mut [
&mut pins.gp12.into_push_pull_output(),
&mut pins.gp13.into_push_pull_output(),
&mut pins.gp14.into_push_pull_output(),
&mut pins.gp15.into_push_pull_output(),
&mut pins.gp26.into_push_pull_output(),
&mut pins.gp27.into_push_pull_output(),
&mut pins.gp7.into_push_pull_output(),
&mut pins.gp8.into_push_pull_output(),
&mut pins.gp6.into_push_pull_output(),
&mut pins.gp9.into_push_pull_output(),
&mut pins.gp10.into_push_pull_output(),
&mut pins.gp11.into_push_pull_output(),
];
// ------------------------------------- -------------------------------------
// | 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 |------------------
// ------------------- -------------------
// GRAVE = §
// SEMICOLON = ö
// APOSTROPHE = ä
// LEFT_BRACE = å
// FORWARDSLASH = -
// NON_US_BACKSLASH = <
// EQUAL = ´
// BACKSLASH = '
// RIGHT_BRACE = ^
// MINUS = +
// LEFT_ALT = Alt
// RIGHT_ALT = AltGr
let layout: [[Keyboard; NUMBER_OF_KEYS]; 3] = [
[
// Function layer 0
Keyboard::Tab,
Keyboard::Q,
Keyboard::W,
Keyboard::E,
Keyboard::R,
Keyboard::T,
Keyboard::Y,
Keyboard::U,
Keyboard::I,
Keyboard::O,
Keyboard::P,
Keyboard::LeftBrace,
Keyboard::LeftControl,
Keyboard::A,
Keyboard::S,
Keyboard::D,
Keyboard::F,
Keyboard::G,
Keyboard::H,
Keyboard::J,
Keyboard::K,
Keyboard::L,
Keyboard::Semicolon,
Keyboard::Apostrophe,
Keyboard::LeftShift,
Keyboard::Z,
Keyboard::X,
Keyboard::C,
Keyboard::V,
Keyboard::B,
Keyboard::N,
Keyboard::M,
Keyboard::Comma,
Keyboard::Dot,
Keyboard::ForwardSlash,
Keyboard::RightShift,
Keyboard::LeftAlt,
Keyboard::NoEventIndicated,
Keyboard::Space,
Keyboard::Space,
Keyboard::NoEventIndicated,
Keyboard::RightAlt,
],
[
// Function layer 1
Keyboard::Escape,
Keyboard::F1,
Keyboard::F2,
Keyboard::F3,
Keyboard::F4,
Keyboard::F5,
Keyboard::F6,
Keyboard::F7,
Keyboard::F8,
Keyboard::F9,
Keyboard::F10,
Keyboard::DeleteBackspace,
Keyboard::LeftControl,
Keyboard::Keyboard1,
Keyboard::Keyboard2,
Keyboard::Keyboard3,
Keyboard::Keyboard4,
Keyboard::Keyboard5,
Keyboard::Keyboard6,
Keyboard::Keyboard7,
Keyboard::Keyboard8,
Keyboard::Keyboard9,
Keyboard::Keyboard0,
Keyboard::ReturnEnter,
Keyboard::LeftShift,
Keyboard::Keyboard6,
Keyboard::Keyboard7,
Keyboard::Keyboard8,
Keyboard::Keyboard9,
Keyboard::Keyboard0,
Keyboard::NonUSBackslash,
Keyboard::Equal,
Keyboard::Backslash,
Keyboard::RightBrace,
Keyboard::Minus,
Keyboard::RightShift,
Keyboard::LeftAlt,
Keyboard::NoEventIndicated,
Keyboard::DeleteBackspace,
Keyboard::DeleteBackspace,
Keyboard::NoEventIndicated,
Keyboard::RightAlt,
],
[
// Function layer 2
Keyboard::F11,
Keyboard::F12,
Keyboard::F13,
Keyboard::F14,
Keyboard::F15,
Keyboard::F16,
Keyboard::Grave,
Keyboard::NoEventIndicated,
Keyboard::LeftGUI,
Keyboard::NoEventIndicated,
Keyboard::CapsLock,
Keyboard::DeleteBackspace,
Keyboard::LeftControl,
Keyboard::NoEventIndicated,
Keyboard::NoEventIndicated,
Keyboard::F17,
Keyboard::F18,
Keyboard::F19,
Keyboard::LeftArrow,
Keyboard::DownArrow,
Keyboard::UpArrow,
Keyboard::RightArrow,
Keyboard::DeleteForward,
Keyboard::ReturnEnter,
Keyboard::LeftShift,
Keyboard::F20,
Keyboard::F21,
Keyboard::F22,
Keyboard::F23,
Keyboard::F24,
Keyboard::Home,
Keyboard::PageDown,
Keyboard::PageUp,
Keyboard::End,
Keyboard::Insert,
Keyboard::RightShift,
Keyboard::LeftAlt,
Keyboard::NoEventIndicated,
Keyboard::LeftGUI,
Keyboard::DeleteBackspace,
Keyboard::NoEventIndicated,
Keyboard::RightAlt,
],
];
let mut input_count_down = timer.count_down();
input_count_down.start(10.millis());
let mut tick_count_down = timer.count_down();
tick_count_down.start(1.millis());
let status_led_color: [RGB8; 5] = [
(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
];
let mut caps_lock_active = false;
init_keyboard_matrix_pins(matrix_cols, &mut delay);
// Infinite colour wheel loop
loop {
if input_count_down.wait().is_ok() {
// Scan keyboard matrix
let pressed_keys = get_pressed_keys(matrix_rows, matrix_cols, &mut delay);
// Get current function layer
let 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 = red.
if caps_lock_active == true {
status_led
.write([status_led_color[4]].iter().copied())
.unwrap();
} else {
status_led
.write([status_led_color[usize::from(fn_mode) + 1]].iter().copied())
.unwrap();
}
// Copy result from scanned keys to matrix struct
for (index, key) in pressed_keys.iter().enumerate() {
matrix_keys[index].current_state = *key;
}
// Generate keyboard report
let keyboard_report = get_keyboard_report(matrix_keys, layout, fn_mode);
match keyboard.device().write_report(keyboard_report) {
Err(UsbHidError::WouldBlock) => {}
Err(UsbHidError::Duplicate) => {}
Ok(_) => {}
Err(e) => {
core::panic!("Failed to write keyboard report: {:?}", e)
}
};
}
//Tick once per ms
if tick_count_down.wait().is_ok() {
match keyboard.tick() {
Err(UsbHidError::WouldBlock) => {}
Ok(_) => {}
Err(e) => {
core::panic!("Failed to process keyboard tick: {:?}", e)
}
};
}
if usb_dev.poll(&mut [&mut keyboard]) {
match keyboard.device().read_report() {
Err(UsbError::WouldBlock) => {
//do nothing
}
Err(e) => {
core::panic!("Failed to read keyboard report: {:?}", e)
}
Ok(leds) => {
if leds.caps_lock == true {
caps_lock_active = true;
} else {
caps_lock_active = false;
}
}
}
}
}
}
// Initialise keyboard matrix pins
fn init_keyboard_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)
// TODO: This is a bit of a mess, needs refactoring
fn get_pressed_keys(
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;
}
}
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;
if pressed_keys[37] == true && pressed_keys[40] == true {
fn_mode = 2;
} else if pressed_keys[37] == true || pressed_keys[40] == true {
fn_mode = 1;
}
fn_mode
}
// Generate keyboard report based on pressed keys and Fn mode (0, 1 or 2)
fn get_keyboard_report(
mut matrix_keys: [MatrixKey; NUMBER_OF_KEYS],
layout: [[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 {
key.previous_state = key.current_state;
key.fn_mode = fn_mode;
}
if key.current_state == true {
keyboard_report[index] = layout[usize::from(key.fn_mode)][index];
}
}
// Return report
keyboard_report
}