#![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] = &[ &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] = &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], 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], cols: &mut [&mut dyn OutputPin], 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 }