Get a dummy keyboard working
This commit is contained in:
31
src/board.rs
Normal file
31
src/board.rs
Normal file
@ -0,0 +1,31 @@
|
||||
use embassy_rp::peripherals::*;
|
||||
|
||||
/// Pinouts for the ItsyBitsy
|
||||
#[allow(dead_code)]
|
||||
pub struct Board {
|
||||
pub a0: PIN_26,
|
||||
pub a1: PIN_27,
|
||||
pub a2: PIN_28,
|
||||
pub a3: PIN_29,
|
||||
pub d24: PIN_24,
|
||||
pub d25: PIN_25,
|
||||
pub sck: PIN_18,
|
||||
pub mosi: PIN_19,
|
||||
pub miso: PIN_20,
|
||||
pub d2: PIN_12,
|
||||
pub d3: PIN_5,
|
||||
pub d4: PIN_4,
|
||||
pub rx: PIN_1,
|
||||
pub tx: PIN_0,
|
||||
pub sda: PIN_2,
|
||||
pub scl: PIN_3,
|
||||
pub d5: PIN_14,
|
||||
pub d7: PIN_6,
|
||||
pub d9: PIN_7,
|
||||
pub d10: PIN_8,
|
||||
pub d11: PIN_9,
|
||||
pub d12: PIN_10,
|
||||
pub d13: PIN_11,
|
||||
pub neopixel: PIN_17,
|
||||
pub neopixel_power: PIN_16,
|
||||
}
|
||||
189
src/keyboard.rs
Normal file
189
src/keyboard.rs
Normal file
@ -0,0 +1,189 @@
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use embassy_time::{Duration, Timer};
|
||||
|
||||
pub const ROWS: usize = 3;
|
||||
pub const COLS: usize = 5;
|
||||
const NEW_SWITCH: Switch = Switch::new();
|
||||
const NEW_ROW: [Switch; COLS] = [NEW_SWITCH; COLS];
|
||||
pub static MATRIX: [[Switch; COLS]; ROWS] = [NEW_ROW; ROWS];
|
||||
|
||||
pub static TEST_KEYMAP: [[Button; COLS]; ROWS] = [
|
||||
[
|
||||
Button::KEY_A,
|
||||
Button::KEY_B,
|
||||
Button::KEY_C,
|
||||
Button::KEY_D,
|
||||
Button::KEY_E,
|
||||
],
|
||||
[
|
||||
Button::KEY_T,
|
||||
Button::KEY_R,
|
||||
Button::KEY_H,
|
||||
Button::KEY_I,
|
||||
Button::KEY_SPACE,
|
||||
],
|
||||
[
|
||||
Button::KEY_O,
|
||||
Button::KEY_L,
|
||||
Button::KEY_LSHIFT,
|
||||
Button::KEY_LCTRL,
|
||||
Button::KEY_LALT,
|
||||
],
|
||||
];
|
||||
|
||||
pub struct Switch {
|
||||
state: AtomicBool,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Switch {
|
||||
pub const fn new() -> Self {
|
||||
Switch {
|
||||
state: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn press(&self) {
|
||||
self.state.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn release(&self) {
|
||||
self.state.store(false, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn is_pressed(&self) -> bool {
|
||||
self.state.load(Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Button {
|
||||
Key { keycode: u8 },
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Button {
|
||||
pub const fn key(keycode: u8) -> Self {
|
||||
Button::Key { keycode }
|
||||
}
|
||||
|
||||
// https://usb.org/sites/default/files/hut1_3_0.pdf
|
||||
pub const KEY_A: Button = Button::key(0x04);
|
||||
pub const KEY_B: Button = Button::key(0x05);
|
||||
pub const KEY_C: Button = Button::key(0x06);
|
||||
pub const KEY_D: Button = Button::key(0x07);
|
||||
pub const KEY_E: Button = Button::key(0x08);
|
||||
pub const KEY_F: Button = Button::key(0x09);
|
||||
pub const KEY_G: Button = Button::key(0x0A);
|
||||
pub const KEY_H: Button = Button::key(0x0B);
|
||||
pub const KEY_I: Button = Button::key(0x0C);
|
||||
pub const KEY_J: Button = Button::key(0x0D);
|
||||
pub const KEY_K: Button = Button::key(0x0E);
|
||||
pub const KEY_L: Button = Button::key(0x0F);
|
||||
pub const KEY_M: Button = Button::key(0x10);
|
||||
pub const KEY_N: Button = Button::key(0x11);
|
||||
pub const KEY_O: Button = Button::key(0x12);
|
||||
pub const KEY_P: Button = Button::key(0x13);
|
||||
pub const KEY_Q: Button = Button::key(0x14);
|
||||
pub const KEY_R: Button = Button::key(0x15);
|
||||
pub const KEY_S: Button = Button::key(0x16);
|
||||
pub const KEY_T: Button = Button::key(0x17);
|
||||
pub const KEY_U: Button = Button::key(0x18);
|
||||
pub const KEY_V: Button = Button::key(0x19);
|
||||
pub const KEY_W: Button = Button::key(0x1A);
|
||||
pub const KEY_X: Button = Button::key(0x1B);
|
||||
pub const KEY_Y: Button = Button::key(0x1C);
|
||||
pub const KEY_Z: Button = Button::key(0x1D);
|
||||
|
||||
pub const KEY_1: Button = Button::key(0x1E);
|
||||
pub const KEY_2: Button = Button::key(0x1F);
|
||||
pub const KEY_3: Button = Button::key(0x20);
|
||||
pub const KEY_4: Button = Button::key(0x21);
|
||||
pub const KEY_5: Button = Button::key(0x22);
|
||||
pub const KEY_6: Button = Button::key(0x23);
|
||||
pub const KEY_7: Button = Button::key(0x24);
|
||||
pub const KEY_8: Button = Button::key(0x25);
|
||||
pub const KEY_9: Button = Button::key(0x26);
|
||||
pub const KEY_0: Button = Button::key(0x27);
|
||||
|
||||
pub const KEY_SPACE: Button = Button::key(0x2C);
|
||||
pub const KEY_RETURN: Button = Button::key(0x28);
|
||||
|
||||
pub const KEY_LCTRL: Button = Button::key(0xE0);
|
||||
pub const KEY_RCTRL: Button = Button::key(0xE4);
|
||||
|
||||
pub const KEY_LSHIFT: Button = Button::key(0xE1);
|
||||
pub const KEY_RSHIFT: Button = Button::key(0xE5);
|
||||
|
||||
pub const KEY_LALT: Button = Button::key(0xE2);
|
||||
pub const KEY_RALT: Button = Button::key(0xE6);
|
||||
}
|
||||
|
||||
pub fn letter_to_key(c: char) -> Option<Button> {
|
||||
if !c.is_ascii() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let c = c.to_ascii_uppercase();
|
||||
|
||||
let key = match c {
|
||||
'A' => Button::KEY_A,
|
||||
'B' => Button::KEY_B,
|
||||
'C' => Button::KEY_C,
|
||||
'D' => Button::KEY_D,
|
||||
'E' => Button::KEY_E,
|
||||
'F' => Button::KEY_F,
|
||||
'G' => Button::KEY_G,
|
||||
'H' => Button::KEY_H,
|
||||
'I' => Button::KEY_I,
|
||||
'J' => Button::KEY_J,
|
||||
'K' => Button::KEY_K,
|
||||
'L' => Button::KEY_L,
|
||||
'M' => Button::KEY_M,
|
||||
'N' => Button::KEY_N,
|
||||
'O' => Button::KEY_O,
|
||||
'P' => Button::KEY_P,
|
||||
'Q' => Button::KEY_Q,
|
||||
'R' => Button::KEY_R,
|
||||
'S' => Button::KEY_S,
|
||||
'T' => Button::KEY_T,
|
||||
'U' => Button::KEY_U,
|
||||
'V' => Button::KEY_V,
|
||||
'W' => Button::KEY_W,
|
||||
'X' => Button::KEY_X,
|
||||
'Y' => Button::KEY_Y,
|
||||
'Z' => Button::KEY_Z,
|
||||
' ' => Button::KEY_SPACE,
|
||||
'\n' => Button::KEY_RETURN,
|
||||
_ => {
|
||||
log::info!("char {c:?} -> None");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
log::info!("char {c:?} -> {key:?}");
|
||||
|
||||
Some(key)
|
||||
}
|
||||
|
||||
pub async fn test_type(s: &str) {
|
||||
log::info!("typing {s:?}");
|
||||
|
||||
for c in s.chars() {
|
||||
let Some(key) = letter_to_key(c) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
for col in 0..COLS {
|
||||
for row in 0..ROWS {
|
||||
if TEST_KEYMAP[row][col] == key {
|
||||
MATRIX[row][col].press();
|
||||
Timer::after(Duration::from_millis(20)).await;
|
||||
MATRIX[row][col].release();
|
||||
Timer::after(Duration::from_millis(5)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
src/led.rs
Normal file
25
src/led.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use adafruit_itsy_bitsy_rp2040::hal::gpio::{Output, Pin, PinId, PushPull};
|
||||
use cortex_m::delay::Delay;
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
|
||||
pub struct Led<I: PinId> {
|
||||
pub pin: Pin<I, Output<PushPull>>,
|
||||
}
|
||||
|
||||
pub struct Blink {
|
||||
ms: u32,
|
||||
}
|
||||
|
||||
pub const SHORT: Blink = Blink { ms: 200 };
|
||||
pub const LONG: Blink = Blink { ms: 600 };
|
||||
|
||||
impl<I: PinId> Led<I> {
|
||||
pub fn dance(&mut self, delay: &mut Delay, moves: &[Blink]) {
|
||||
for m in moves {
|
||||
self.pin.set_high().unwrap();
|
||||
delay.delay_ms(m.ms);
|
||||
self.pin.set_low().unwrap();
|
||||
delay.delay_ms(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
126
src/main.rs
126
src/main.rs
@ -8,90 +8,76 @@
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
// The macro for our start-up function
|
||||
use adafruit_itsy_bitsy_rp2040::{
|
||||
entry,
|
||||
hal::{clocks::ClocksManager, usb},
|
||||
};
|
||||
extern crate cortex_m_rt;
|
||||
extern crate panic_halt;
|
||||
|
||||
// Ensure we halt the program on panic (if we don't mention this crate it won't
|
||||
// be linked)
|
||||
use panic_halt as _;
|
||||
mod board;
|
||||
mod keyboard;
|
||||
mod usb;
|
||||
|
||||
// Some traits we need
|
||||
use embedded_hal::digital::v2::OutputPin;
|
||||
use board::Board;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio::{Level, Output};
|
||||
use embassy_time::{Duration, Timer};
|
||||
|
||||
use adafruit_itsy_bitsy_rp2040::{
|
||||
hal::{
|
||||
clocks::{init_clocks_and_plls, Clock},
|
||||
pac,
|
||||
sio::Sio,
|
||||
watchdog::Watchdog,
|
||||
},
|
||||
Pins, XOSC_CRYSTAL_FREQ,
|
||||
};
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
use cortex_m::delay::Delay;
|
||||
let board = Board {
|
||||
a0: p.PIN_26,
|
||||
a1: p.PIN_27,
|
||||
a2: p.PIN_28,
|
||||
a3: p.PIN_29,
|
||||
d24: p.PIN_24,
|
||||
d25: p.PIN_25,
|
||||
sck: p.PIN_18,
|
||||
mosi: p.PIN_19,
|
||||
miso: p.PIN_20,
|
||||
d2: p.PIN_12,
|
||||
d3: p.PIN_5,
|
||||
d4: p.PIN_4,
|
||||
rx: p.PIN_1,
|
||||
tx: p.PIN_0,
|
||||
sda: p.PIN_2,
|
||||
scl: p.PIN_3,
|
||||
d5: p.PIN_14,
|
||||
d7: p.PIN_6,
|
||||
d9: p.PIN_7,
|
||||
d10: p.PIN_8,
|
||||
d11: p.PIN_9,
|
||||
d12: p.PIN_10,
|
||||
d13: p.PIN_11,
|
||||
neopixel: p.PIN_17,
|
||||
neopixel_power: p.PIN_16,
|
||||
};
|
||||
|
||||
/// Entry point to our bare-metal application.
|
||||
///
|
||||
/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
|
||||
/// as soon as all global variables are initialised.
|
||||
///
|
||||
/// The function configures the RP2040 peripherals, then toggles a GPIO pin in
|
||||
/// an infinite loop. If there is an LED connected to that pin, it will blink.
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
// Grab our singleton objects
|
||||
let mut pac = pac::Peripherals::take().unwrap();
|
||||
let core = pac::CorePeripherals::take().unwrap();
|
||||
let mut led = Output::new(board.d13, Level::High);
|
||||
|
||||
// Set up the watchdog driver - needed by the clock setup code
|
||||
let mut watchdog = Watchdog::new(pac.WATCHDOG);
|
||||
let mut builder = usb::builder(p.USB);
|
||||
|
||||
// Configure the clocks
|
||||
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();
|
||||
usb::logger::setup(&mut builder).await;
|
||||
|
||||
let usb = usb::UsbBus::new(
|
||||
pac.USBCTRL_REGS,
|
||||
pac.USBCTRL_DPRAM,
|
||||
clocks.usb_clock,
|
||||
false,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
log::error!("log_level: error");
|
||||
log::warn!("log_level: warn");
|
||||
log::info!("log_level: info");
|
||||
log::debug!("log_level: debug");
|
||||
log::trace!("log_level: trace");
|
||||
|
||||
// TODO: how to keyboard
|
||||
usb::keyboard::setup(&mut builder).await;
|
||||
|
||||
let mut delay = Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
|
||||
let usb = builder.build();
|
||||
|
||||
// The single-cycle I/O block controls our GPIO pins
|
||||
let sio = Sio::new(pac.SIO);
|
||||
spawner.must_spawn(usb::run(usb));
|
||||
|
||||
let pins = Pins::new(
|
||||
pac.IO_BANK0,
|
||||
pac.PADS_BANK0,
|
||||
sio.gpio_bank0,
|
||||
&mut pac.RESETS,
|
||||
);
|
||||
let mut led_pin = pins.d13.into_push_pull_output();
|
||||
Timer::after(Duration::from_millis(1000)).await;
|
||||
|
||||
crate::keyboard::test_type("Hello there!\n").await;
|
||||
|
||||
loop {
|
||||
led_pin.set_high().unwrap();
|
||||
delay.delay_ms(500);
|
||||
led_pin.set_low().unwrap();
|
||||
delay.delay_ms(500);
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
led.toggle();
|
||||
}
|
||||
}
|
||||
|
||||
// End of file
|
||||
|
||||
57
src/usb.rs
Normal file
57
src/usb.rs
Normal file
@ -0,0 +1,57 @@
|
||||
use embassy_rp::{interrupt, peripherals::USB, usb::Driver};
|
||||
use embassy_usb::{Builder, Config, UsbDevice};
|
||||
use static_cell::StaticCell;
|
||||
|
||||
pub mod keyboard;
|
||||
pub mod logger;
|
||||
|
||||
pub const MAX_PACKET_SIZE: u8 = 64;
|
||||
|
||||
struct State {
|
||||
device_descriptor: [u8; 256],
|
||||
config_descriptor: [u8; 256],
|
||||
bos_descriptor: [u8; 256],
|
||||
control_buf: [u8; 64],
|
||||
}
|
||||
|
||||
static STATE: StaticCell<State> = StaticCell::new();
|
||||
|
||||
pub fn builder(usb: USB) -> Builder<'static, Driver<'static, USB>> {
|
||||
let state = STATE.init(State {
|
||||
device_descriptor: [0; 256],
|
||||
config_descriptor: [0; 256],
|
||||
bos_descriptor: [0; 256],
|
||||
control_buf: [0; 64],
|
||||
});
|
||||
|
||||
// Create embassy-usb Config
|
||||
let mut config = Config::new(0xc0de, 0xcafe);
|
||||
config.manufacturer = Some("Tux");
|
||||
config.product = Some("Tangentbord1");
|
||||
config.serial_number = Some("42069");
|
||||
config.max_power = 100;
|
||||
config.max_packet_size_0 = MAX_PACKET_SIZE;
|
||||
|
||||
// Required for windows compatiblity.
|
||||
// https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
|
||||
config.device_class = 0xEF;
|
||||
config.device_sub_class = 0x02;
|
||||
config.device_protocol = 0x01;
|
||||
config.composite_with_iads = true;
|
||||
|
||||
let driver = Driver::new(usb, interrupt::take!(USBCTRL_IRQ));
|
||||
|
||||
Builder::new(
|
||||
driver,
|
||||
config,
|
||||
&mut state.device_descriptor,
|
||||
&mut state.config_descriptor,
|
||||
&mut state.bos_descriptor,
|
||||
&mut state.control_buf,
|
||||
)
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn run(mut device: UsbDevice<'static, Driver<'static, USB>>) {
|
||||
device.run().await
|
||||
}
|
||||
185
src/usb/keyboard.rs
Normal file
185
src/usb/keyboard.rs
Normal file
@ -0,0 +1,185 @@
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::{peripherals::USB, usb::Driver};
|
||||
use embassy_time::{Duration, Timer};
|
||||
use embassy_usb::{
|
||||
class::hid::{self, HidReaderWriter, ReadError, ReportId, RequestHandler},
|
||||
control::OutResponse,
|
||||
Builder,
|
||||
};
|
||||
use embassy_usb_driver::EndpointError;
|
||||
use static_cell::StaticCell;
|
||||
use usbd_hid::descriptor::{KeyboardReport, MouseReport, SerializedDescriptor};
|
||||
|
||||
use crate::keyboard::{Button, COLS, MATRIX, ROWS, TEST_KEYMAP};
|
||||
|
||||
use super::MAX_PACKET_SIZE;
|
||||
|
||||
struct Handler;
|
||||
|
||||
static CONTEXT: StaticCell<Context> = StaticCell::new();
|
||||
|
||||
struct Context {
|
||||
handler: Handler,
|
||||
state: hid::State<'static>,
|
||||
}
|
||||
|
||||
pub async fn setup(builder: &mut Builder<'static, Driver<'static, USB>>) {
|
||||
log::info!("setting up usb hid");
|
||||
|
||||
let context = CONTEXT.init(Context {
|
||||
handler: Handler,
|
||||
state: hid::State::new(),
|
||||
});
|
||||
|
||||
let config = hid::Config {
|
||||
//report_descriptor: MouseReport::desc(),
|
||||
report_descriptor: KeyboardReport::desc(),
|
||||
request_handler: Some(&context.handler),
|
||||
poll_ms: 2,
|
||||
max_packet_size: MAX_PACKET_SIZE as u16,
|
||||
};
|
||||
|
||||
let stream = HidStream::new(builder, &mut context.state, config);
|
||||
|
||||
let spawner = Spawner::for_current_executor().await;
|
||||
|
||||
spawner.must_spawn(task(stream, &context.handler));
|
||||
|
||||
log::info!("done setting up usb keyboard");
|
||||
}
|
||||
|
||||
impl RequestHandler for Handler {
|
||||
fn get_report(&self, id: ReportId, buf: &mut [u8]) -> Option<usize> {
|
||||
log::info!("get_report({id:?}, {buf:?})");
|
||||
let _ = (id, buf);
|
||||
None
|
||||
}
|
||||
|
||||
fn set_report(&self, id: ReportId, data: &[u8]) -> embassy_usb::control::OutResponse {
|
||||
log::info!("set_report({id:?}, {data:?})");
|
||||
let _ = (id, data);
|
||||
OutResponse::Rejected
|
||||
}
|
||||
|
||||
fn get_idle_ms(&self, id: Option<ReportId>) -> Option<u32> {
|
||||
log::info!("get_idle_ms({id:?})");
|
||||
let _ = id;
|
||||
None
|
||||
}
|
||||
|
||||
fn set_idle_ms(&self, id: Option<ReportId>, duration_ms: u32) {
|
||||
log::info!("set_idle_ms({id:?}, {duration_ms})");
|
||||
let _ = (id, duration_ms);
|
||||
}
|
||||
}
|
||||
type HidStream = HidReaderWriter<'static, Driver<'static, USB>, 256, 256>;
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn task(stream: HidStream, handler: &'static Handler) {
|
||||
if let Err(e) = keyboard_test(stream, handler).await {
|
||||
log::error!("keyboard error: {e:?}");
|
||||
}
|
||||
//if let Err(e) = mouse_wiggler(stream).await {
|
||||
// log::error!("mouse wiggler: {e:?}");
|
||||
//}
|
||||
}
|
||||
|
||||
async fn keyboard_test(mut stream: HidStream, handler: &'static Handler) -> Result<(), Error> {
|
||||
stream.ready().await;
|
||||
loop {
|
||||
Timer::after(Duration::from_millis(2)).await;
|
||||
|
||||
let keymap = &TEST_KEYMAP;
|
||||
|
||||
let mut keycodes = [0u8; 6];
|
||||
let mut i = 0;
|
||||
|
||||
'keyscan: for col in 0..COLS {
|
||||
for row in 0..ROWS {
|
||||
if !MATRIX[row][col].is_pressed() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Button::Key { keycode } = &keymap[row][col];
|
||||
// else { continue; };
|
||||
|
||||
keycodes[i] = *keycode;
|
||||
i += 1;
|
||||
if i >= keycodes.len() {
|
||||
break 'keyscan;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if keycodes.iter().any(|&b| b != 0) {
|
||||
log::info!("keycodes: {keycodes:?}");
|
||||
}
|
||||
stream
|
||||
.write_serialize(&KeyboardReport {
|
||||
modifier: 0,
|
||||
reserved: 0,
|
||||
leds: 0,
|
||||
keycodes,
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
async fn mouse_wiggler(mut stream: HidStream) -> Result<(), Error> {
|
||||
stream.ready().await;
|
||||
|
||||
let (_r, mut w) = stream.split();
|
||||
|
||||
let write_fut = async move {
|
||||
let mut x = 1;
|
||||
loop {
|
||||
for _ in 0..100 {
|
||||
Timer::after(Duration::from_millis(10)).await;
|
||||
log::info!("sending mouse report");
|
||||
//w.ready().await;
|
||||
w.write_serialize(&MouseReport {
|
||||
x,
|
||||
y: 0,
|
||||
buttons: 0,
|
||||
wheel: 0,
|
||||
pan: 0,
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
x = -x;
|
||||
}
|
||||
};
|
||||
|
||||
//let read_fut = async move {
|
||||
// let mut buf = [0u8; MAX_PACKET_SIZE as usize];
|
||||
// loop {
|
||||
// Timer::after(Duration::from_millis(30)).await;
|
||||
// let n = r.read(&mut buf).await?;
|
||||
// log::info!("got packet: {:?}", &buf[..n]);
|
||||
// }
|
||||
//};
|
||||
|
||||
//let r: Result<((), ()), Error> = try_join(write_fut, read_fut).await;
|
||||
let r: Result<(), Error> = write_fut.await;
|
||||
r?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
Read(ReadError),
|
||||
Endpoint(EndpointError),
|
||||
}
|
||||
|
||||
impl From<ReadError> for Error {
|
||||
fn from(value: ReadError) -> Self {
|
||||
Error::Read(value)
|
||||
}
|
||||
}
|
||||
impl From<EndpointError> for Error {
|
||||
fn from(value: EndpointError) -> Self {
|
||||
Error::Endpoint(value)
|
||||
}
|
||||
}
|
||||
79
src/usb/logger.rs
Normal file
79
src/usb/logger.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use super::MAX_PACKET_SIZE;
|
||||
use core::fmt::Write as WriteFmt;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::{peripherals::USB, usb::Driver};
|
||||
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe};
|
||||
use embassy_time::Instant;
|
||||
use embassy_usb::{
|
||||
class::cdc_acm::{self, CdcAcmClass},
|
||||
Builder,
|
||||
};
|
||||
use log::{Metadata, Record};
|
||||
use static_cell::StaticCell;
|
||||
|
||||
pub const BUFFER_SIZE: usize = 16 * 1024;
|
||||
static BUFFER: Pipe<CriticalSectionRawMutex, BUFFER_SIZE> = Pipe::new();
|
||||
static STATE: StaticCell<cdc_acm::State<'static>> = StaticCell::new();
|
||||
|
||||
struct UsbLogger;
|
||||
|
||||
pub async fn setup(usb_builder: &mut Builder<'static, Driver<'static, USB>>) {
|
||||
unsafe {
|
||||
static LOGGER: UsbLogger = UsbLogger;
|
||||
log::set_logger_racy(&LOGGER).unwrap();
|
||||
log::set_max_level(log::LevelFilter::Debug);
|
||||
}
|
||||
|
||||
let spawner = Spawner::for_current_executor().await;
|
||||
|
||||
let state = STATE.init(cdc_acm::State::new());
|
||||
|
||||
let class = CdcAcmClass::new(usb_builder, state, MAX_PACKET_SIZE as u16);
|
||||
|
||||
spawner.must_spawn(log_task(class));
|
||||
}
|
||||
|
||||
//pub async fn print(s: &str) {
|
||||
// BUFFER.writer().write_all(s.as_bytes()).await.ok(/* infallible */);
|
||||
//}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn log_task(mut class: CdcAcmClass<'static, Driver<'static, USB>>) {
|
||||
let mut buf = [0u8; MAX_PACKET_SIZE as usize];
|
||||
|
||||
class.wait_connection().await;
|
||||
loop {
|
||||
let n = BUFFER.read(&mut buf).await;
|
||||
|
||||
// not much we can do if this fails, just ignore the error
|
||||
let _ = class.write_packet(&buf[..n]).await;
|
||||
}
|
||||
}
|
||||
|
||||
impl log::Log for UsbLogger {
|
||||
fn enabled(&self, _metadata: &Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn log(&self, record: &Record) {
|
||||
if self.enabled(record.metadata()) {
|
||||
let mut w = Writer;
|
||||
let now = Instant::now();
|
||||
let s = now.as_secs();
|
||||
let ms = now.as_millis() % 1000;
|
||||
let level = record.metadata().level();
|
||||
let _ = write!(w, "[{s}.{ms:04}] ({level}) {}\n", record.args());
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
struct Writer;
|
||||
|
||||
impl core::fmt::Write for Writer {
|
||||
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
|
||||
let _ = BUFFER.try_write(s.as_bytes());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user