implement basic keyboard firmware for my 40% ortho prototype, along with debug display info
This commit is contained in:
295
src/main.rs
295
src/main.rs
@@ -1,222 +1,141 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
mod matrix;
|
||||
mod keymap;
|
||||
mod display;
|
||||
mod usb;
|
||||
|
||||
use defmt::{info, warn};
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
use embassy_rp::{bind_interrupts, gpio::{Input, Level, Output, Pull}, peripherals::USB, usb::{Driver, InterruptHandler}};
|
||||
use embassy_rp::{
|
||||
bind_interrupts,
|
||||
i2c::{Config as I2cConfig, I2c, InterruptHandler as I2cInterruptHandler},
|
||||
peripherals::USB,
|
||||
usb::{Driver, InterruptHandler as UsbInterruptHandler},
|
||||
};
|
||||
use embassy_time::{Duration, Timer};
|
||||
use embassy_usb::{Builder, Config, Handler, class::hid::{HidReaderWriter, ReportId, RequestHandler, State}, control::OutResponse};
|
||||
use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
|
||||
use usbd_hid::descriptor::KeyboardReport;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
pub struct Debouncer<'a> {
|
||||
input: Input<'a>,
|
||||
debounce: Duration,
|
||||
}
|
||||
|
||||
impl<'a> Debouncer<'a> {
|
||||
pub fn new(input: Input<'a>, debounce: Duration) -> Self {
|
||||
Self { input, debounce }
|
||||
}
|
||||
|
||||
pub async fn debounce_low(&mut self) -> Level {
|
||||
loop {
|
||||
let l1 = self.input.get_level();
|
||||
|
||||
self.input.wait_for_low().await;
|
||||
|
||||
Timer::after(self.debounce).await;
|
||||
|
||||
let l2 = self.input.get_level();
|
||||
if l1 != l2 {
|
||||
break l2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn debounce_high(&mut self) -> Level {
|
||||
loop {
|
||||
let l1 = self.input.get_level();
|
||||
|
||||
self.input.wait_for_high().await;
|
||||
|
||||
Timer::after(self.debounce).await;
|
||||
|
||||
let l2 = self.input.get_level();
|
||||
if l1 != l2 {
|
||||
break l2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
use keymap::get_keycode;
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
USBCTRL_IRQ => InterruptHandler<USB>;
|
||||
I2C0_IRQ => I2cInterruptHandler<embassy_rp::peripherals::I2C0>;
|
||||
USBCTRL_IRQ => UsbInterruptHandler<USB>;
|
||||
});
|
||||
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) -> () {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
let driver = Driver::new(p.USB, Irqs);
|
||||
info!("Firmware starting");
|
||||
|
||||
let mut config = Config::new(0x1234, 0xabcd);
|
||||
config.manufacturer = Some("Lukrecja");
|
||||
config.product = Some("kb-prototype");
|
||||
config.serial_number = Some("00000000");
|
||||
config.composite_with_iads = false;
|
||||
config.device_class = 0x00;
|
||||
config.device_sub_class = 0x00;
|
||||
config.device_protocol = 0x00;
|
||||
// Setup USB HID
|
||||
let usb_driver = Driver::new(p.USB, Irqs);
|
||||
let mut usb_keyboard = usb::setup_usb(usb_driver);
|
||||
let usb_fut = usb_keyboard.usb.run();
|
||||
info!("USB configured");
|
||||
|
||||
let mut config_descriptor = [0; 256];
|
||||
let mut bos_descriptor = [0; 256];
|
||||
let mut msos_descriptor = [0; 256];
|
||||
let mut control_buf = [0; 64];
|
||||
let mut request_handler = KbRequestHandler {};
|
||||
let mut device_handler = KbDeviceHandler::new();
|
||||
// Setup I2C display
|
||||
info!("Initializing display...");
|
||||
let i2c_config = I2cConfig::default();
|
||||
let bus = I2c::new_async(p.I2C0, p.PIN_17, p.PIN_16, Irqs, i2c_config);
|
||||
let mut display = display::init_display(bus);
|
||||
match display {
|
||||
Some(_) => info!("Display initialized"),
|
||||
None => warn!("Can't initialize display"),
|
||||
}
|
||||
|
||||
let mut state = State::new();
|
||||
|
||||
let mut builder = Builder::new(
|
||||
driver,
|
||||
config,
|
||||
&mut config_descriptor,
|
||||
&mut bos_descriptor,
|
||||
&mut msos_descriptor,
|
||||
&mut control_buf,
|
||||
// Setup matrix
|
||||
info!("Initializing matrix...");
|
||||
let mut matrix = matrix_4x12!(p,
|
||||
rows: [PIN_0, PIN_1, PIN_2, PIN_3],
|
||||
cols: [PIN_4, PIN_5, PIN_6, PIN_7, PIN_8, PIN_9, PIN_10, PIN_11, PIN_12, PIN_13, PIN_14, PIN_15]
|
||||
);
|
||||
info!("Matrix initialized");
|
||||
|
||||
builder.handler(&mut device_handler);
|
||||
let mut writer = usb_keyboard.writer;
|
||||
let reader = usb_keyboard.reader;
|
||||
let request_handler = usb_keyboard.request_handler;
|
||||
info!("Starting main loop...");
|
||||
|
||||
let config = embassy_usb::class::hid::Config {
|
||||
report_descriptor: KeyboardReport::desc(),
|
||||
request_handler: None,
|
||||
poll_ms: 60,
|
||||
max_packet_size: 64,
|
||||
};
|
||||
|
||||
let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config);
|
||||
|
||||
let mut usb = builder.build();
|
||||
|
||||
let usb_fut = usb.run();
|
||||
|
||||
let mut led = Output::new(p.PIN_25, Level::Low);
|
||||
let mut switch_input = Debouncer::new(Input::new(p.PIN_0, Pull::Up), Duration::from_millis(20));
|
||||
switch_input.input.set_schmitt(true);
|
||||
|
||||
let (reader, mut writer) = hid.split();
|
||||
display::clear_display(&mut display);
|
||||
|
||||
// Keyboard scanning task
|
||||
let in_fut = async {
|
||||
let mut previous_keycodes: [u8; 6] = [0; 6];
|
||||
let mut previous_modifier: u8 = 0;
|
||||
|
||||
loop {
|
||||
defmt::info!("waiting for low");
|
||||
led.set_low();
|
||||
|
||||
switch_input.debounce_low().await;
|
||||
|
||||
info!("got low");
|
||||
led.set_high();
|
||||
|
||||
let report = KeyboardReport {
|
||||
keycodes: [4, 0, 0, 0, 0, 0],
|
||||
leds: 0,
|
||||
modifier: 0,
|
||||
reserved: 0,
|
||||
};
|
||||
|
||||
match writer.write_serialize(&report).await {
|
||||
Ok(()) => info!("report sent"),
|
||||
Err(e) => warn!("failed to send: {:?}", e),
|
||||
};
|
||||
|
||||
switch_input.debounce_high().await;
|
||||
info!("got high");
|
||||
|
||||
let report = KeyboardReport {
|
||||
keycodes: [0, 0, 0, 0, 0, 0],
|
||||
leds: 0,
|
||||
modifier: 0,
|
||||
reserved: 0,
|
||||
};
|
||||
|
||||
match writer.write_serialize(&report).await {
|
||||
Ok(()) => info!("report sent"),
|
||||
Err(e) => warn!("failed to send: {:?}", e),
|
||||
};
|
||||
let scan_result = matrix.scan().await;
|
||||
|
||||
// Collect all pressed keys and their scancodes
|
||||
let mut pressed_keys: heapless::Vec<(usize, usize, keymap::KeyCode), 48> = heapless::Vec::new();
|
||||
|
||||
for (row_idx, row) in scan_result.iter().enumerate() {
|
||||
for (col_idx, &is_pressed) in row.iter().enumerate() {
|
||||
if is_pressed {
|
||||
if let Some(keycode) = get_keycode(row_idx, col_idx) {
|
||||
let _ = pressed_keys.push((row_idx, col_idx, keycode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build modifier byte and keycodes array
|
||||
let mut modifier: u8 = 0;
|
||||
let mut keycodes = [0u8; 6];
|
||||
let mut keycode_index = 0;
|
||||
|
||||
for (_, _, keycode) in pressed_keys.iter() {
|
||||
if keycode.is_modifier() {
|
||||
// Add to modifier byte
|
||||
modifier |= keycode.modifier_bit();
|
||||
} else if keycode_index < 6 {
|
||||
// Add to keycodes array (max 6 regular keys)
|
||||
keycodes[keycode_index] = keycode.as_u8();
|
||||
keycode_index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Update display
|
||||
display::update_display(&mut display, &pressed_keys);
|
||||
|
||||
// Send HID report if keycodes or modifiers changed
|
||||
if keycodes != previous_keycodes || modifier != previous_modifier {
|
||||
if keycodes != [0; 6] || modifier != 0 {
|
||||
info!("Keys: {:?}, Mods: 0x{:02X}", keycodes, modifier);
|
||||
} else {
|
||||
info!("All keys released");
|
||||
}
|
||||
|
||||
let report = KeyboardReport {
|
||||
keycodes,
|
||||
leds: 0,
|
||||
modifier,
|
||||
reserved: 0,
|
||||
};
|
||||
|
||||
match writer.write_serialize(&report).await {
|
||||
Ok(()) => {},
|
||||
Err(e) => warn!("Failed to send report: {:?}", e),
|
||||
};
|
||||
|
||||
previous_keycodes = keycodes;
|
||||
previous_modifier = modifier;
|
||||
}
|
||||
|
||||
Timer::after(Duration::from_millis(10)).await;
|
||||
}
|
||||
};
|
||||
|
||||
// USB reader task
|
||||
let out_fut = async {
|
||||
reader.run(false, &mut request_handler).await;
|
||||
reader.run(false, request_handler).await;
|
||||
};
|
||||
|
||||
join(usb_fut, join(in_fut, out_fut)).await;
|
||||
}
|
||||
|
||||
struct KbRequestHandler {}
|
||||
|
||||
impl RequestHandler for KbRequestHandler {
|
||||
fn get_report(&mut self, _id: ReportId, _buf: &mut [u8]) -> Option<usize> {
|
||||
info!("get report");
|
||||
None
|
||||
}
|
||||
|
||||
fn set_report(&mut self, _id: ReportId, data: &[u8]) -> OutResponse {
|
||||
info!("set report: {=[u8]}", data);
|
||||
OutResponse::Accepted
|
||||
}
|
||||
|
||||
fn set_idle_ms(&mut self, _id: Option<ReportId>, dur: u32) {
|
||||
info!("set idle rate to {}", dur);
|
||||
}
|
||||
|
||||
fn get_idle_ms(&mut self, _id: Option<ReportId>) -> Option<u32> {
|
||||
info!("get idle rate");
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
struct KbDeviceHandler {
|
||||
configured: AtomicBool,
|
||||
}
|
||||
|
||||
impl KbDeviceHandler {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
configured: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler for KbDeviceHandler {
|
||||
fn enabled(&mut self, enabled: bool) {
|
||||
self.configured.store(false, Ordering::Relaxed);
|
||||
if enabled {
|
||||
info!("device enabled");
|
||||
} else {
|
||||
info!("device disabled");
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
self.configured.store(false, Ordering::Relaxed);
|
||||
info!("bus reset, Vbus current limit is 100mA");
|
||||
}
|
||||
|
||||
fn addressed(&mut self, addr: u8) {
|
||||
self.configured.store(false, Ordering::Relaxed);
|
||||
info!("USB address set to: {}", addr);
|
||||
}
|
||||
|
||||
fn configured(&mut self, configured: bool) {
|
||||
self.configured.store(configured, Ordering::Relaxed);
|
||||
if configured {
|
||||
info!("device configured")
|
||||
} else {
|
||||
info!("device no longer configured");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user