OFTIDOHYR5GXVY3ON52BR3WJ4WWQY3SV3NBEWOVQJ3QM523EZKZQC
stm32f3xx-hal = { version = "^0.9", features = ["stm32f303x8"] }
embassy = { git = "https://github.com/embassy-rs/embassy.git", features = ["defmt", "nightly"] }
embassy-stm32 = { git = "https://github.com/embassy-rs/embassy.git", features = ["time-driver-any", "stm32f303cb", "unstable-pac", "memory-x"] }
tinyvec = { version = "^1.6", features = ["alloc"] }
# https://github.com/embassy-rs/embassy/pull/709
usb-device = "^0.2"
usbd-hid = "^0.6"
[toolchain]
channel = "nightly"
#![no_main]
#![no_std]
#![feature(type_alias_impl_trait)]
use ashtoret as _; // global logger + panicking-behavior + memory layout
use defmt::*;
use embassy::executor::Spawner;
use embassy::time::{Duration, Timer};
use embassy_stm32::{
gpio::{Level, Output, Speed},
Peripherals,
};
use ashtoret::keyboards::{moonlander::Moonlander, Keyboard};
#[embassy::main]
async fn main(_spawner: Spawner, p: Peripherals) {
let keyboard = Moonlander::init(p);
keyboard.run()
}
pub struct Hid {}
use embassy::time::{Duration, Timer};
use embassy_stm32::gpio::{AnyPin, Input, Output};
type MatrixArray<const COLS: usize, const ROWS: usize> = [[bool; COLS]; ROWS];
pub enum ScanKind<const COLS: usize, const ROWS: usize, T> {
Direct([[Input<'static, AnyPin>; COLS]; ROWS]),
Row2Col {
row_pins: [Output<'static, AnyPin>; ROWS],
col_pins: [Input<'static, AnyPin>; COLS],
},
Custom(T),
}
#[derive(Copy, Clone, Default, Debug, defmt::Format)]
pub enum KeyEvent {
Pressed(u16),
Released(u16),
#[default]
Nop,
}
// TODO: Bitstuffing
pub struct Matrix<const COLS: usize, const ROWS: usize, T> {
state: MatrixArray<COLS, ROWS>,
prev_state: MatrixArray<COLS, ROWS>,
pins: ScanKind<COLS, ROWS, T>,
debounce: Option<Duration>,
scan_fn: Option<fn(&ScanKind<COLS, ROWS, T>) -> MatrixArray<COLS, ROWS>>,
}
impl<const COLS: usize, const ROWS: usize, T> Matrix<{ COLS }, { ROWS }, T> {
pub fn new(pins: ScanKind<COLS, ROWS, T>) -> Self {
Self {
state: [[false; COLS]; ROWS],
prev_state: [[false; COLS]; ROWS],
pins,
debounce: None,
scan_fn: None,
}
}
pub fn with_debounce(mut self, time: Duration) -> Self {
self.debounce = Some(time);
self
}
pub fn with_custom_scan(
mut self,
scan_fn: fn(&ScanKind<COLS, ROWS, T>) -> MatrixArray<COLS, ROWS>,
) -> Self {
self.scan_fn = Some(scan_fn);
self
}
fn raw_update(&mut self) -> tinyvec::TinyVec<[KeyEvent; 6]> {
core::mem::swap(&mut self.prev_state, &mut self.state);
self.state = if let Some(custom_scan) = self.scan_fn {
custom_scan(&self.pins)
} else {
// Note: Not needed for the Moonlander, as it implements a custom scanning routine
todo!()
};
let mut vec = tinyvec::TinyVec::new();
for (idx, (key, prev_key)) in self
.state
.iter()
.flatten()
.zip(self.prev_state.iter().flatten())
.enumerate()
{
if let Some(event) = if *key && !prev_key {
Some(KeyEvent::Pressed(idx as u16))
} else if !key && *prev_key {
Some(KeyEvent::Released(idx as u16))
} else {
None
} {
vec.push(event)
}
}
vec
}
pub async fn update(&mut self) -> Option<tinyvec::TinyVec<[KeyEvent; 6]>> {
let changes = self.raw_update();
if let Some(debounce) = self.debounce
&& !changes.is_empty() {
Timer::after(debounce).await;
if self.raw_update().is_empty() {
Some(changes)
} else {
None
}
} else {
Some(changes)
}
}
}
use crate::drivers::SharedBus;
use embassy_stm32::i2c::{self, I2c};
pub struct Mcp23018<T: i2c::Instance> {
i2c: SharedBus<I2c<'static, T>>,
}
impl<T: i2c::Instance> Mcp23018<T> {
pub fn new() -> Self {
todo!()
}
}
use embassy::blocking_mutex::NoopMutex;
pub mod hid;
pub mod matrix;
pub mod mcp23018;
pub type SharedBus<T> = &'static NoopMutex<T>;
use crate::drivers::mcp23018::Mcp23018;
use embassy_stm32::gpio::{AnyPin, Input, Output};
use embassy_stm32::peripherals::I2C1;
pub struct MatrixScanner {
pub row_pins: [Output<'static, AnyPin>; 6],
pub col_pins: [Input<'static, AnyPin>; 7],
pub right_side: Mcp23018<I2C1>,
}
pub fn scan_matrix(pins: &MatrixScanner) -> [[bool; super::MATRIX_COLS]; super::MATRIX_ROWS] {
todo!()
}
mod matrix;
use crate::drivers::matrix::{Matrix, ScanKind};
use crate::drivers::mcp23018::Mcp23018;
use crate::keyboards::moonlander::matrix::MatrixScanner;
use crate::keyboards::Keyboard;
use embassy_stm32::Peripherals;
pub const MATRIX_COLS: usize = 7;
pub const MATRIX_ROWS: usize = 12;
pub struct Moonlander {
matrix: Matrix<MATRIX_COLS, MATRIX_ROWS, MatrixScanner>,
}
impl Keyboard for Moonlander {
fn init(p: Peripherals) -> Self {
let matrix = Matrix::new(ScanKind::Custom(MatrixScanner {
row_pins: outputs![p.PB10, p.PB11, p.PB12, p.PB13, p.PB14, p.PB15],
col_pins: inputs![p.PA0, p.PA1, p.PA2, p.PA3, p.PA6, p.PA7, p.PB0,],
right_side: Mcp23018::new(),
}))
.with_custom_scan(|scanner| {
if let ScanKind::Custom(scanner) = scanner {
matrix::scan_matrix(scanner)
} else {
unreachable!()
}
});
Self { matrix }
}
fn run(self) -> ! {
cortex_m::asm::udf()
}
}
use embassy_stm32::Peripherals;
pub trait Keyboard {
fn init(p: Peripherals) -> Self;
fn run(self) -> !;
}
pub mod moonlander;
#![feature(let_chains)]
#![feature(alloc_error_handler)]
#[macro_export]
macro_rules! outputs {
($($pin:expr),* $(,)?) => {{
use embassy_stm32::gpio::{Level, Output, Speed, Pin};
[$(
Output::new($pin.degrade(), Level::Low, Speed::High),
)*]
}
}}
#[macro_export]
macro_rules! inputs {
($($pin:expr),* $(,)?) => {{
use embassy_stm32::gpio::{Input, Pull, Pin};