stuff
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1 @@
|
|||||||
/target
|
target
|
||||||
|
|||||||
1798
left/Cargo.lock
generated
Normal file
1798
left/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
left/Cargo.toml
Normal file
29
left/Cargo.toml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
[package]
|
||||||
|
name = "tangentbord1-left"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Joakim Hulthe <joakim@hulthe.net>"]
|
||||||
|
description = "Keyboard firmware"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies.tangentbord1]
|
||||||
|
path = "../lib"
|
||||||
|
package = "tangentbord1-lib"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tgnt = { git = "https://git.nubo.sh/hulthe/tgnt.git", default-features = false }
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
embassy-rp = { git = "https://github.com/embassy-rs/embassy.git", features = ["log", "nightly", "unstable-traits", "unstable-pac", "time-driver", "critical-section-impl"] }
|
||||||
|
embassy-executor = { git = "https://github.com/embassy-rs/embassy.git", features = ["arch-cortex-m", "log", "executor-thread", "nightly", "integrated-timers" ] }
|
||||||
|
embassy-sync = { git = "https://github.com/embassy-rs/embassy.git", features = ["log", "nightly"] }
|
||||||
|
embassy-time = { git = "https://github.com/embassy-rs/embassy.git", features = ["log"] }
|
||||||
|
embassy-futures = { git = "https://github.com/embassy-rs/embassy.git", features = ["log"] }
|
||||||
|
log = "0.4.17"
|
||||||
|
postcard = { version = "1.0.4", features = ["alloc"] }
|
||||||
|
|
||||||
|
[patch."https://git.nubo.sh/hulthe/tgnt.git"]
|
||||||
|
tgnt = { path = "../../tgnt" }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tgnt = { git = "https://git.nubo.sh/hulthe/tgnt.git", default-features = false }
|
||||||
|
ron = "0.8.0"
|
||||||
|
postcard = { version = "1", features = ["use-std"] }
|
||||||
@ -17,8 +17,7 @@ use tgnt::layer::Layer;
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
memory();
|
memory();
|
||||||
serialize_layout("./layers-left.ron", "./src/bin/layers-left.pc");
|
serialize_layout("./layers.ron", "./src/layers.pc");
|
||||||
serialize_layout("./layers-right.ron", "./src/bin/layers-right.pc");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn memory() {
|
fn memory() {
|
||||||
@ -27,7 +26,7 @@ fn memory() {
|
|||||||
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||||
File::create(out.join("memory.x"))
|
File::create(out.join("memory.x"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.write_all(include_bytes!("memory.x"))
|
.write_all(include_bytes!("../memory.x"))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
println!("cargo:rustc-link-search={}", out.display());
|
println!("cargo:rustc-link-search={}", out.display());
|
||||||
|
|
||||||
@ -35,7 +34,7 @@ fn memory() {
|
|||||||
// any file in the project changes. By specifying `memory.x`
|
// any file in the project changes. By specifying `memory.x`
|
||||||
// here, we ensure the build script is only re-run when
|
// here, we ensure the build script is only re-run when
|
||||||
// `memory.x` is changed.
|
// `memory.x` is changed.
|
||||||
println!("cargo:rerun-if-changed=memory.x");
|
println!("cargo:rerun-if-changed=../memory.x");
|
||||||
|
|
||||||
// --nmagic turns off page alignment of sections (which saves flash space)
|
// --nmagic turns off page alignment of sections (which saves flash space)
|
||||||
println!("cargo:rustc-link-arg-bins=--nmagic");
|
println!("cargo:rustc-link-arg-bins=--nmagic");
|
||||||
@ -25,7 +25,7 @@
|
|||||||
// Thumbpad
|
// Thumbpad
|
||||||
Key(Backspace),
|
Key(Backspace),
|
||||||
Key(Space),
|
Key(Space),
|
||||||
NextLayer,
|
HoldLayer(1),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Layer(
|
Layer(
|
||||||
@ -54,7 +54,7 @@
|
|||||||
// Thumbpad
|
// Thumbpad
|
||||||
Key(Backspace),
|
Key(Backspace),
|
||||||
Key(Space),
|
Key(Space),
|
||||||
NextLayer,
|
HoldLayer(1),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Layer(
|
Layer(
|
||||||
@ -83,7 +83,7 @@
|
|||||||
// Thumbpad
|
// Thumbpad
|
||||||
Key(Backspace),
|
Key(Backspace),
|
||||||
Key(Space),
|
Key(Space),
|
||||||
NextLayer,
|
HoldLayer(1),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
BIN
left/src/layers.pc
Normal file
BIN
left/src/layers.pc
Normal file
Binary file not shown.
@ -3,6 +3,7 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
|
#![cfg(target_arch = "arm")]
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
extern crate cortex_m_rt;
|
extern crate cortex_m_rt;
|
||||||
@ -12,12 +13,16 @@ use embassy_executor::Spawner;
|
|||||||
use embassy_rp::gpio::{Level, Output, Pin};
|
use embassy_rp::gpio::{Level, Output, Pin};
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
use log::error;
|
use log::error;
|
||||||
use tangentbord1::board::Board;
|
use tangentbord1::{
|
||||||
use tangentbord1::keyboard::{Half, KeyboardConfig};
|
board::Board,
|
||||||
use tangentbord1::logger::Logger;
|
event::Half,
|
||||||
use tangentbord1::util::{stall, wheel};
|
keyboard::KeyboardConfig,
|
||||||
use tangentbord1::ws2812::{Rgb, Ws2812};
|
logger::Logger,
|
||||||
use tangentbord1::{allocator, rtt, uart, usb};
|
rgb::Rgb,
|
||||||
|
util::{stall, wheel},
|
||||||
|
ws2812::Ws2812,
|
||||||
|
{allocator, rtt, uart, usb},
|
||||||
|
};
|
||||||
use tgnt::layer::Layer;
|
use tgnt::layer::Layer;
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
@ -48,7 +53,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
neopixel.write(&[Rgb::new(0xFF, 0x00, 0x00)]).await;
|
neopixel.write(&[Rgb::new(0xFF, 0x00, 0x00)]).await;
|
||||||
|
|
||||||
let layers = include_bytes!("layers-left.pc");
|
let layers = include_bytes!("layers.pc");
|
||||||
let Ok(layers): Result<Vec<Layer>, _> = postcard::from_bytes(layers) else {
|
let Ok(layers): Result<Vec<Layer>, _> = postcard::from_bytes(layers) else {
|
||||||
log::error!("Failed to deserialize layer config");
|
log::error!("Failed to deserialize layer config");
|
||||||
stall().await
|
stall().await
|
||||||
1781
lib/Cargo.lock
generated
Normal file
1781
lib/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tangentbord1"
|
name = "tangentbord1-lib"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Joakim Hulthe <joakim@hulthe.net>"]
|
authors = ["Joakim Hulthe <joakim@hulthe.net>"]
|
||||||
description = "Keyboard firmware"
|
description = "Keyboard firmware"
|
||||||
@ -14,15 +14,14 @@ usb-device = "0.2.9"
|
|||||||
usbd-hid = "0.6.1"
|
usbd-hid = "0.6.1"
|
||||||
static_cell = "1.0.0"
|
static_cell = "1.0.0"
|
||||||
embedded-io = { version = "*", features = ["async"] }
|
embedded-io = { version = "*", features = ["async"] }
|
||||||
futures = { version = "0.3", default-features = false }
|
futures = { version = "0.3", default-features = false, features = ["async-await"] }
|
||||||
embassy-executor = { git = "https://github.com/embassy-rs/embassy.git", features = ["log", "arch-cortex-m", "executor-thread", "nightly", "integrated-timers" ] }
|
embassy-executor = { git = "https://github.com/embassy-rs/embassy.git", features = ["log", "executor-thread", "nightly", "integrated-timers" ] }
|
||||||
embassy-sync = { git = "https://github.com/embassy-rs/embassy.git", features = ["log", "nightly"] }
|
embassy-sync = { git = "https://github.com/embassy-rs/embassy.git", features = ["log", "nightly"] }
|
||||||
embassy-time = { git = "https://github.com/embassy-rs/embassy.git", features = ["log"] }
|
embassy-time = { git = "https://github.com/embassy-rs/embassy.git", features = ["log"] }
|
||||||
embassy-futures = { git = "https://github.com/embassy-rs/embassy.git", features = ["log"] }
|
embassy-futures = { git = "https://github.com/embassy-rs/embassy.git", features = ["log"] }
|
||||||
embassy-usb = { git = "https://github.com/embassy-rs/embassy.git", features = ["usbd-hid"] }
|
embassy-usb = { git = "https://github.com/embassy-rs/embassy.git", features = ["usbd-hid"] }
|
||||||
embassy-usb-logger = { git = "https://github.com/embassy-rs/embassy.git", features = [] }
|
embassy-usb-logger = { git = "https://github.com/embassy-rs/embassy.git", features = [] }
|
||||||
embassy-usb-driver = { git = "https://github.com/embassy-rs/embassy.git", features = [] }
|
embassy-usb-driver = { git = "https://github.com/embassy-rs/embassy.git", features = [] }
|
||||||
embassy-rp = { git = "https://github.com/embassy-rs/embassy.git", features = ["log", "nightly", "unstable-traits", "unstable-pac", "time-driver", "critical-section-impl"] }
|
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
pio = "0.2.1"
|
pio = "0.2.1"
|
||||||
pio-proc = "0.2.1"
|
pio-proc = "0.2.1"
|
||||||
@ -39,8 +38,17 @@ crc-any = "2.4.3"
|
|||||||
serde = { version = "1.0.163", default-features = false, features = ["derive"] }
|
serde = { version = "1.0.163", default-features = false, features = ["derive"] }
|
||||||
bytemuck = { version = "1.13.1", features = ["derive"] }
|
bytemuck = { version = "1.13.1", features = ["derive"] }
|
||||||
|
|
||||||
#[patch."https://git.nubo.sh/hulthe/tgnt.git"]
|
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||||
#tgnt = { path = "../tgnt" }
|
embassy-executor = { git = "https://github.com/embassy-rs/embassy.git", features = ["arch-std"] }
|
||||||
|
embassy-time = { git = "https://github.com/embassy-rs/embassy.git", features = ["std", "generic-queue"] }
|
||||||
|
simple_logger = "4"
|
||||||
|
|
||||||
|
[target.thumbv6m-none-eabi.dependencies]
|
||||||
|
embassy-rp = { git = "https://github.com/embassy-rs/embassy.git", features = ["log", "nightly", "unstable-traits", "unstable-pac", "time-driver", "critical-section-impl"] }
|
||||||
|
embassy-executor = { git = "https://github.com/embassy-rs/embassy.git", features = ["arch-cortex-m"] }
|
||||||
|
|
||||||
|
[patch."https://git.nubo.sh/hulthe/tgnt.git"]
|
||||||
|
tgnt = { path = "../../tgnt" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tgnt = { git = "https://git.nubo.sh/hulthe/tgnt.git", default-features = false }
|
tgnt = { git = "https://git.nubo.sh/hulthe/tgnt.git", default-features = false }
|
||||||
15
lib/memory.x
Normal file
15
lib/memory.x
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
MEMORY {
|
||||||
|
BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
|
||||||
|
FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
|
||||||
|
RAM : ORIGIN = 0x20000000, LENGTH = 256K
|
||||||
|
}
|
||||||
|
|
||||||
|
EXTERN(BOOT2_FIRMWARE)
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
/* ### Boot loader */
|
||||||
|
.boot2 ORIGIN(BOOT2) :
|
||||||
|
{
|
||||||
|
KEEP(*(.boot2));
|
||||||
|
} > BOOT2
|
||||||
|
} INSERT BEFORE .text;
|
||||||
0
src/.gitignore → lib/src/.gitignore
vendored
0
src/.gitignore → lib/src/.gitignore
vendored
60
lib/src/event.rs
Normal file
60
lib/src/event.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
use core::time::Duration;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tgnt::{button::Button, keys::Key};
|
||||||
|
|
||||||
|
pub mod switch {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
/// A switch was pressed or released
|
||||||
|
///
|
||||||
|
/// This event is triggered by tasks that monitor switches.
|
||||||
|
pub struct Event {
|
||||||
|
/// The keyboard half that triggered the event.
|
||||||
|
pub source: Half,
|
||||||
|
|
||||||
|
/// The index of the button that triggered the event.
|
||||||
|
pub source_button: usize,
|
||||||
|
|
||||||
|
pub kind: EventKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub enum EventKind {
|
||||||
|
Press {
|
||||||
|
button: Button,
|
||||||
|
},
|
||||||
|
Release {
|
||||||
|
button: Button,
|
||||||
|
|
||||||
|
/// The duration that the button was held down for
|
||||||
|
after: Duration,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod button {
|
||||||
|
use tgnt::button::Modifier;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// A usb keyboard button was pressed or released.
|
||||||
|
///
|
||||||
|
/// This is a lower-level event than a [SwitchEvent], as things like ModTap and Compose are
|
||||||
|
/// converted to Presses and Releases.
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Event {
|
||||||
|
PressKey(Key),
|
||||||
|
ReleaseKey(Key),
|
||||||
|
PressMod(Modifier),
|
||||||
|
ReleaseMod(Modifier),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A keyboard half.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum Half {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
11
lib/src/interrupts.rs
Normal file
11
lib/src/interrupts.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use embassy_rp::{
|
||||||
|
bind_interrupts,
|
||||||
|
peripherals::{UART0, USB},
|
||||||
|
};
|
||||||
|
|
||||||
|
bind_interrupts! {
|
||||||
|
pub struct Irqs {
|
||||||
|
UART0_IRQ => embassy_rp::uart::BufferedInterruptHandler<UART0>;
|
||||||
|
USBCTRL_IRQ => embassy_rp::usb::InterruptHandler<USB>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,18 +9,20 @@ use embassy_rp::{
|
|||||||
peripherals::PIO1,
|
peripherals::PIO1,
|
||||||
};
|
};
|
||||||
use embassy_sync::pubsub::{ImmediatePublisher, PubSubChannel, Subscriber};
|
use embassy_sync::pubsub::{ImmediatePublisher, PubSubChannel, Subscriber};
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Instant};
|
||||||
use futures::{select_biased, FutureExt};
|
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
use tgnt::{
|
use tgnt::{button::Button, layer::Layer};
|
||||||
button::{Button, Modifier},
|
|
||||||
keys::Key,
|
|
||||||
layer::Layer,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{lights::Lights, util::CS, ws2812::Ws2812};
|
use crate::{
|
||||||
|
event::{
|
||||||
|
switch::{Event, EventKind},
|
||||||
|
Half,
|
||||||
|
},
|
||||||
|
lights::Lights,
|
||||||
|
util::CS,
|
||||||
|
ws2812::Ws2812,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct KeyboardConfig {
|
pub struct KeyboardConfig {
|
||||||
/// Which board is this.
|
/// Which board is this.
|
||||||
@ -43,41 +45,14 @@ struct State {
|
|||||||
lights: Lights<PIO1, SWITCH_COUNT>,
|
lights: Lights<PIO1, SWITCH_COUNT>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A keyboard half.
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub enum Half {
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Event {
|
|
||||||
/// The keyboard half that triggered the event.
|
|
||||||
pub source: Half,
|
|
||||||
|
|
||||||
/// The index of the button that triggered the event.
|
|
||||||
pub source_button: usize,
|
|
||||||
|
|
||||||
pub kind: EventKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub enum EventKind {
|
|
||||||
PressKey(Key),
|
|
||||||
ReleaseKey(Key),
|
|
||||||
PressModifier(Modifier),
|
|
||||||
ReleaseModifier(Modifier),
|
|
||||||
SetLayer(u16),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const KB_SUBSCRIBERS: usize = 2;
|
pub const KB_SUBSCRIBERS: usize = 2;
|
||||||
const ACTUAL_KB_SUBSCRIBERS: usize = KB_SUBSCRIBERS + 2;
|
const ACTUAL_KB_SUBSCRIBERS: usize = KB_SUBSCRIBERS + 2;
|
||||||
const KB_EVENT_CAP: usize = 128;
|
const KB_EVENT_CAP: usize = 128;
|
||||||
static KB_EVENTS: PubSubChannel<CS, Event, KB_EVENT_CAP, ACTUAL_KB_SUBSCRIBERS, 0> =
|
static KB_EVENTS: PubSubChannel<CS, Event, KB_EVENT_CAP, ACTUAL_KB_SUBSCRIBERS, 0> =
|
||||||
PubSubChannel::new();
|
PubSubChannel::new();
|
||||||
pub struct KbEvents {
|
pub struct KbEvents {
|
||||||
subscriber: Subscriber<'static, CS, Event, KB_EVENT_CAP, ACTUAL_KB_SUBSCRIBERS, 0>,
|
pub subscriber: Subscriber<'static, CS, Event, KB_EVENT_CAP, ACTUAL_KB_SUBSCRIBERS, 0>,
|
||||||
publisher: ImmediatePublisher<'static, CS, Event, KB_EVENT_CAP, ACTUAL_KB_SUBSCRIBERS, 0>,
|
pub publisher: ImmediatePublisher<'static, CS, Event, KB_EVENT_CAP, ACTUAL_KB_SUBSCRIBERS, 0>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct KbEventsTx<'a> {
|
pub struct KbEventsTx<'a> {
|
||||||
@ -183,8 +158,8 @@ impl KbEventsTx<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const MOD_TAP_TIME: Duration = Duration::from_millis(150);
|
pub const MOD_TAP_TIME: Duration = Duration::from_millis(150);
|
||||||
const SWITCH_COUNT: usize = 18;
|
pub const SWITCH_COUNT: usize = 18;
|
||||||
|
|
||||||
/// Task for monitoring a single switch pin, and handling button presses.
|
/// Task for monitoring a single switch pin, and handling button presses.
|
||||||
#[embassy_executor::task(pool_size = 18)]
|
#[embassy_executor::task(pool_size = 18)]
|
||||||
@ -195,6 +170,7 @@ async fn switch_task(switch_num: usize, pin: AnyPin, state: &'static State) -> !
|
|||||||
loop {
|
loop {
|
||||||
// pins are pull-up, so when the switch is pressed they are brought low.
|
// pins are pull-up, so when the switch is pressed they are brought low.
|
||||||
pin.wait_for_low().await;
|
pin.wait_for_low().await;
|
||||||
|
let pressed_at = Instant::now();
|
||||||
|
|
||||||
// TODO: do we need debouncing?
|
// TODO: do we need debouncing?
|
||||||
|
|
||||||
@ -219,72 +195,54 @@ async fn switch_task(switch_num: usize, pin: AnyPin, state: &'static State) -> !
|
|||||||
|
|
||||||
debug!("switch {switch_num} button {button:?} pressed");
|
debug!("switch {switch_num} button {button:?} pressed");
|
||||||
|
|
||||||
let wait_for_release = async {
|
|
||||||
pin.wait_for_high().await;
|
|
||||||
debug!("switch {switch_num} button {button:?} released");
|
|
||||||
};
|
|
||||||
|
|
||||||
let ev = |kind| Event {
|
let ev = |kind| Event {
|
||||||
source: state.half,
|
source: state.half,
|
||||||
source_button: switch_num,
|
source_button: switch_num,
|
||||||
kind,
|
kind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use EventKind::*;
|
events.publish_immediate(ev(EventKind::Press {
|
||||||
match button {
|
button: button.clone(),
|
||||||
&Button::Key(key) => {
|
}));
|
||||||
events.publish_immediate(ev(PressKey(key)));
|
|
||||||
wait_for_release.await;
|
|
||||||
events.publish_immediate(ev(ReleaseKey(key)));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
&Button::Mod(modifier) => {
|
|
||||||
events.publish_immediate(ev(PressModifier(modifier)));
|
|
||||||
wait_for_release.await;
|
|
||||||
events.publish_immediate(ev(ReleaseModifier(modifier)));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
&Button::ModTap(key, modifier) => {
|
|
||||||
select_biased! {
|
|
||||||
_ = Timer::after(MOD_TAP_TIME).fuse() => {
|
|
||||||
events.publish_immediate(ev(PressModifier(modifier)));
|
|
||||||
pin.wait_for_high().await;
|
|
||||||
events.publish_immediate(ev(ReleaseModifier(modifier)));
|
|
||||||
debug!("switch {switch_num} button {button:?} released");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
_ = wait_for_release.fuse() => {
|
|
||||||
events.publish_immediate(ev(PressKey(key)));
|
|
||||||
Timer::after(Duration::from_millis(20)).await;
|
|
||||||
events.publish_immediate(ev(ReleaseKey(key)));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button::NextLayer => {
|
|
||||||
let next_layer = (current_layer + 1) % layer_count;
|
|
||||||
events.publish_immediate(ev(SetLayer(next_layer)));
|
|
||||||
debug!("switched to layer {next_layer}");
|
|
||||||
}
|
|
||||||
Button::PrevLayer => {
|
|
||||||
let prev_layer = current_layer.checked_sub(1).unwrap_or(layer_count - 1);
|
|
||||||
events.publish_immediate(ev(SetLayer(prev_layer)));
|
|
||||||
debug!("switched to layer {prev_layer}");
|
|
||||||
}
|
|
||||||
Button::None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_for_release.await;
|
pin.wait_for_high().await;
|
||||||
|
let released_after = pressed_at.elapsed();
|
||||||
|
|
||||||
|
debug!("switch {switch_num} button {button:?} released");
|
||||||
|
|
||||||
|
events.publish_immediate(ev(EventKind::Release {
|
||||||
|
button: button.clone(),
|
||||||
|
after: released_after.into(),
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn layer_switch_task(mut events: KbEvents, state: &'static State) {
|
async fn layer_switch_task(mut events: KbEvents, state: &'static State) {
|
||||||
|
let layer_count = state.layers.len() as u16;
|
||||||
|
let Some(last_layer) = layer_count.checked_sub(1) else {
|
||||||
|
error!("no layers specified");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let event = events.recv().await;
|
let event = events.recv().await;
|
||||||
if let EventKind::SetLayer(new_layer) = event.kind {
|
let layer = state.current_layer.load(Ordering::Relaxed);
|
||||||
state.current_layer.store(new_layer, Ordering::Relaxed);
|
let new_layer = match event.kind {
|
||||||
}
|
EventKind::Press { button } => match button {
|
||||||
|
Button::NextLayer => layer.wrapping_add(1) % layer_count,
|
||||||
|
Button::PrevLayer => layer.checked_sub(1).unwrap_or(last_layer),
|
||||||
|
Button::HoldLayer(l) => layer.wrapping_add(l) % layer_count,
|
||||||
|
_ => continue,
|
||||||
|
},
|
||||||
|
EventKind::Release { button, .. } => match button {
|
||||||
|
Button::HoldLayer(l) => layer.checked_sub(l).unwrap_or(last_layer),
|
||||||
|
_ => continue,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
state.current_layer.store(new_layer, Ordering::Relaxed);
|
||||||
|
debug!("switched to layer {new_layer}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1,9 +1,12 @@
|
|||||||
use core::cmp::min;
|
use core::cmp::min;
|
||||||
|
|
||||||
|
use atomic_polyfill::Ordering;
|
||||||
|
use embassy_futures::yield_now;
|
||||||
use embassy_time::{Duration, Instant, Timer};
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
use futures::{select_biased, FutureExt};
|
use futures::{select_biased, FutureExt};
|
||||||
|
use tgnt::button::Button;
|
||||||
|
|
||||||
use crate::{util::wheel, ws2812::Rgb};
|
use crate::{rgb::Rgb, util::wheel};
|
||||||
|
|
||||||
use super::{Event, EventKind, KbEvents, State, SWITCH_COUNT};
|
use super::{Event, EventKind, KbEvents, State, SWITCH_COUNT};
|
||||||
|
|
||||||
@ -140,57 +143,54 @@ async fn handle_event(
|
|||||||
state: &'static State,
|
state: &'static State,
|
||||||
lights: &mut [LightState; SWITCH_COUNT],
|
lights: &mut [LightState; SWITCH_COUNT],
|
||||||
) {
|
) {
|
||||||
match event.kind {
|
let rgb = match event.kind {
|
||||||
EventKind::PressKey(_) => {
|
EventKind::Press { button } => match button {
|
||||||
if state.half != event.source {
|
Button::Key(..) => LightState::Solid(Rgb::new(0, 150, 0)),
|
||||||
return;
|
Button::Mod(..) => LightState::Solid(Rgb::new(0, 0, 150)),
|
||||||
}
|
Button::ModTap(..) => LightState::Solid(Rgb::new(0, 0, 150)),
|
||||||
let Some(light) = lights.get_mut(event.source_button) else { return; };
|
Button::Compose(..) => LightState::Solid(Rgb::new(0, 100, 100)),
|
||||||
*light = LightState::Solid(Rgb::new(0, 150, 0));
|
Button::NextLayer | Button::PrevLayer => {
|
||||||
}
|
yield_now().await; // dirty hack to make sure layer_switch_task gets to run first
|
||||||
EventKind::PressModifier(_) => {
|
let layer = state.current_layer.load(Ordering::Relaxed);
|
||||||
if state.half != event.source {
|
let layer = min(layer, state.layers.len().saturating_sub(1) as u16);
|
||||||
return;
|
let buttons_to_light_up = if state.layers.len() <= 3 {
|
||||||
}
|
match layer {
|
||||||
let Some(light) = lights.get_mut(event.source_button) else { return; };
|
0 => [0, 1, 2, 3, 4].as_ref(),
|
||||||
*light = LightState::Solid(Rgb::new(0, 0, 150));
|
1 => &[5, 6, 7, 8, 9],
|
||||||
}
|
2 => &[10, 11, 12, 13, 14],
|
||||||
EventKind::ReleaseKey(_) | EventKind::ReleaseModifier(_) => {
|
_ => &[],
|
||||||
if state.half != event.source {
|
}
|
||||||
return;
|
} else {
|
||||||
}
|
match layer {
|
||||||
let Some(light) = lights.get_mut(event.source_button) else { return; };
|
0 => [0, 5, 10].as_ref(),
|
||||||
*light = LightState::FadeBy(0.85);
|
1 => &[1, 6, 11],
|
||||||
}
|
2 => &[2, 7, 12],
|
||||||
EventKind::SetLayer(layer) => {
|
3 => &[3, 8, 13],
|
||||||
let layer = min(layer, state.layers.len().saturating_sub(1) as u16);
|
4 => &[4, 9, 14],
|
||||||
let buttons_to_light_up = if state.layers.len() <= 3 {
|
_ => &[],
|
||||||
match layer {
|
}
|
||||||
0 => [0, 1, 2, 3, 4].as_ref(),
|
};
|
||||||
1 => &[5, 6, 7, 8, 9],
|
|
||||||
2 => &[10, 11, 12, 13, 14],
|
|
||||||
_ => &[],
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match layer {
|
|
||||||
0 => [0, 5, 10].as_ref(),
|
|
||||||
1 => &[1, 6, 11],
|
|
||||||
2 => &[2, 7, 12],
|
|
||||||
3 => &[3, 8, 13],
|
|
||||||
4 => &[4, 9, 14],
|
|
||||||
_ => &[],
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let solid_until = Instant::now() + Duration::from_millis(200);
|
let solid_until = Instant::now() + Duration::from_millis(200);
|
||||||
for &button in buttons_to_light_up {
|
for &button in buttons_to_light_up {
|
||||||
let Some(light) = lights.get_mut(button) else { continue; };
|
let Some(light) = lights.get_mut(button) else { continue; };
|
||||||
*light = LightState::SolidThenFade {
|
*light = LightState::SolidThenFade {
|
||||||
color: Rgb::new(120, 0, 120),
|
color: Rgb::new(120, 0, 120),
|
||||||
solid_until,
|
solid_until,
|
||||||
fade_by: 0.85,
|
fade_by: 0.85,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
LightState::Solid(Rgb::new(100, 0, 100))
|
||||||
}
|
}
|
||||||
}
|
_ => LightState::Solid(Rgb::new(150, 0, 0)),
|
||||||
|
},
|
||||||
|
EventKind::Release { .. } => LightState::FadeBy(0.85),
|
||||||
|
};
|
||||||
|
|
||||||
|
if event.source != state.half {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let Some(light) = lights.get_mut(event.source_button) else { return; };
|
||||||
|
*light = rgb;
|
||||||
}
|
}
|
||||||
397
lib/src/keypress_handler.rs
Normal file
397
lib/src/keypress_handler.rs
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
use core::future::pending;
|
||||||
|
|
||||||
|
use embassy_sync::pubsub::{publisher::Pub, subscriber::Sub, PubSubBehavior, WaitResult};
|
||||||
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
|
use futures::FutureExt;
|
||||||
|
use heapless::Deque;
|
||||||
|
use log::{debug, error};
|
||||||
|
use tgnt::button::Button;
|
||||||
|
|
||||||
|
use crate::event::{button, switch, Half};
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
const MOD_TAP_TIME: Duration = Duration::from_millis(150);
|
||||||
|
const SWITCH_COUNT: usize = 18;
|
||||||
|
|
||||||
|
/// This function perpetually converts between [switch::Event]s and [button::Event]s.
|
||||||
|
///
|
||||||
|
/// Call it from a dedicated task.
|
||||||
|
pub async fn keypress_handler(
|
||||||
|
input: &mut Sub<'_, impl PubSubBehavior<switch::Event>, switch::Event>,
|
||||||
|
output: &mut Pub<'_, impl PubSubBehavior<button::Event>, button::Event>,
|
||||||
|
) -> ! {
|
||||||
|
type SwitchIndex = usize;
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct PressEvent {
|
||||||
|
source_button: SwitchIndex,
|
||||||
|
source_half: Half,
|
||||||
|
button: Button,
|
||||||
|
time: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn slow_press(
|
||||||
|
output: &mut Pub<'_, impl PubSubBehavior<button::Event>, button::Event>,
|
||||||
|
button: &Button,
|
||||||
|
) {
|
||||||
|
let event = match button {
|
||||||
|
&Button::Mod(m) | &Button::ModTap(_, m) => button::Event::PressMod(m),
|
||||||
|
&Button::Key(k) => button::Event::PressKey(k),
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
output.publish_immediate(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn slow_release(
|
||||||
|
output: &mut Pub<'_, impl PubSubBehavior<button::Event>, button::Event>,
|
||||||
|
button: &Button,
|
||||||
|
) {
|
||||||
|
let event = match button {
|
||||||
|
&Button::Mod(m) | &Button::ModTap(_, m) => button::Event::ReleaseMod(m),
|
||||||
|
&Button::Key(k) => button::Event::ReleaseKey(k),
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
output.publish_immediate(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// queue of button presses that are waiting for ModTap
|
||||||
|
let mut queue = Deque::<PressEvent, { 2 * SWITCH_COUNT }>::new();
|
||||||
|
let queue = &mut queue;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// create a future that waits for the next ModTap to time out
|
||||||
|
let modtap_timeout = async {
|
||||||
|
loop {
|
||||||
|
if let Some(event) = queue.front() {
|
||||||
|
let Button::ModTap(..) = event.button else {
|
||||||
|
error!("first element in queue wasn't a modtap, wtf?");
|
||||||
|
let _ = queue.pop_front();
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let timeout = event.time + MOD_TAP_TIME;
|
||||||
|
Timer::at(timeout).await;
|
||||||
|
return queue.pop_front().unwrap();
|
||||||
|
} else {
|
||||||
|
// if the queue is empty, never return.
|
||||||
|
return pending().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let event = futures::select_biased! {
|
||||||
|
event = input.next_message().fuse() => event,
|
||||||
|
event = modtap_timeout.fuse() => {
|
||||||
|
// first element in queue timed out, and will be treated as a Mod
|
||||||
|
let &Button::ModTap(..) = &event.button else {
|
||||||
|
error!("first element in queue wasn't a modtap, wtf?");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
slow_press(output, &event.button).await;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let Some(event) = queue.pop_front() else { break };
|
||||||
|
|
||||||
|
// resolve events until we encounter another ModTap,
|
||||||
|
// then put it back in the queue and stop
|
||||||
|
if let Button::ModTap(..) = &event.button {
|
||||||
|
queue.push_front(event).expect("we just popped, the queue can't be full");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
slow_press(output, &event.button).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let WaitResult::Message(event) = event else {
|
||||||
|
error!("lagged");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let time = Instant::now();
|
||||||
|
debug!("event: {:?}", event.kind);
|
||||||
|
match event.kind {
|
||||||
|
switch::EventKind::Press { button } => {
|
||||||
|
let insert = |queue: &mut Deque<_, 36>, button| {
|
||||||
|
if let Some(_queued) = queue
|
||||||
|
.iter()
|
||||||
|
.find(|queued: &&PressEvent| queued.source_button == event.source_button)
|
||||||
|
.filter(|queued| queued.source_half == event.source)
|
||||||
|
{
|
||||||
|
error!("tried to add PressEvent to queue twice");
|
||||||
|
} else if queue
|
||||||
|
.push_back(PressEvent {
|
||||||
|
source_half: event.source,
|
||||||
|
source_button: event.source_button,
|
||||||
|
button,
|
||||||
|
time,
|
||||||
|
})
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
error!("button queue full. this shouldn't happen.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match button {
|
||||||
|
Button::ModTap(_, _) => {
|
||||||
|
debug!("adding modtap to queue");
|
||||||
|
// add event to queue
|
||||||
|
insert(queue, button);
|
||||||
|
}
|
||||||
|
Button::Mod(m) => {
|
||||||
|
// if events in queue, also add to queue maybe?
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
Button::Key(_) => {
|
||||||
|
if queue.is_empty() {
|
||||||
|
debug!("sending key now");
|
||||||
|
// otherwise, send immediately
|
||||||
|
slow_press(output, &button).await;
|
||||||
|
} else {
|
||||||
|
debug!("adding key to queue");
|
||||||
|
// if events in queue, also add to queue
|
||||||
|
insert(queue, button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Button::Compose(..) => {
|
||||||
|
if queue.is_empty() {
|
||||||
|
// otherwise, send immediately
|
||||||
|
// TODO
|
||||||
|
} else {
|
||||||
|
// if events in queue, also add to queue
|
||||||
|
insert(queue, button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch::EventKind::Release { button, .. } => {
|
||||||
|
let position_in_queue = queue
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_, queued)| queued.source_button == event.source_button)
|
||||||
|
.map(|(i, _)| i);
|
||||||
|
|
||||||
|
match button {
|
||||||
|
Button::ModTap(k, _) => {
|
||||||
|
// check if modtap in queue
|
||||||
|
if let Some(position_in_queue) = position_in_queue {
|
||||||
|
// If the modtap was still in the queue, it hasn't been resolved as a mod
|
||||||
|
// yet. Therefore, it is a Tap. Resolve all ModTaps before this one as Mods
|
||||||
|
debug!("modtap was still in queue");
|
||||||
|
for _ in 0..position_in_queue {
|
||||||
|
let prev_event = queue.pop_front().unwrap();
|
||||||
|
debug!("pressing earlier event {:?}", prev_event);
|
||||||
|
slow_press(output, &prev_event.button).await;
|
||||||
|
}
|
||||||
|
let _ = queue.pop_front();
|
||||||
|
debug!("pressing modtap as key");
|
||||||
|
slow_press(output, &Button::Key(k)).await;
|
||||||
|
slow_release(output, &Button::Key(k)).await;
|
||||||
|
} else {
|
||||||
|
// If the ModTap wasn't in the queue, it has already been resolved as a Mod.
|
||||||
|
debug!("modtap wasn't in queue, releasing");
|
||||||
|
slow_release(output, &button).await;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Button::Mod(m) => {
|
||||||
|
debug!("mod not implemented yet");
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
Button::Key(_) => {
|
||||||
|
// if this press event was in queue, resolve all ModTaps before in queue as Mods
|
||||||
|
// otherwise, just resolve this
|
||||||
|
if let Some(position_in_queue) = position_in_queue {
|
||||||
|
debug!(
|
||||||
|
"key was in queue, pressing all events up to and including this"
|
||||||
|
);
|
||||||
|
for _ in 0..=position_in_queue {
|
||||||
|
let prev_event = queue.pop_front().unwrap();
|
||||||
|
debug!("pressing {prev_event:?}");
|
||||||
|
slow_press(output, &prev_event.button).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug!("releasing key {button:?}");
|
||||||
|
slow_release(output, &button).await;
|
||||||
|
}
|
||||||
|
Button::Compose(..) => {
|
||||||
|
// if this press event was in queue, resolve all ModTaps before in queue as Mods
|
||||||
|
// otherwise, just resolve this
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use core::time;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use alloc::vec;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use embassy_futures::{
|
||||||
|
join::join,
|
||||||
|
select::{select, Either},
|
||||||
|
};
|
||||||
|
use embassy_sync::{blocking_mutex::raw::NoopRawMutex, pubsub::PubSubChannel};
|
||||||
|
use embassy_time::with_timeout;
|
||||||
|
use log::info;
|
||||||
|
use tgnt::{button::Modifier, keys::Key};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_modtap_timings() {
|
||||||
|
simple_logger::SimpleLogger::new().init().unwrap();
|
||||||
|
|
||||||
|
let switch_events = PubSubChannel::<NoopRawMutex, switch::Event, 10, 1, 1>::new();
|
||||||
|
let button_events = PubSubChannel::<NoopRawMutex, button::Event, 10, 1, 1>::new();
|
||||||
|
let mut button_sub = button_events.subscriber().unwrap();
|
||||||
|
|
||||||
|
let buttons = [
|
||||||
|
Button::ModTap(Key::A, Modifier::LShift),
|
||||||
|
Button::ModTap(Key::B, Modifier::LCtrl),
|
||||||
|
Button::Key(Key::C),
|
||||||
|
];
|
||||||
|
|
||||||
|
struct Test {
|
||||||
|
description: &'static str,
|
||||||
|
// button index, pressed, delay
|
||||||
|
input: Vec<(usize, bool, Duration)>,
|
||||||
|
expected: Vec<button::Event>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let modtap_mod = Test {
|
||||||
|
description: "modtap mod",
|
||||||
|
input: vec![
|
||||||
|
(0, true, Duration::from_millis(25)),
|
||||||
|
(1, true, Duration::from_millis(25)),
|
||||||
|
(1, false, Duration::from_millis(25)),
|
||||||
|
(0, false, Duration::from_millis(25)),
|
||||||
|
],
|
||||||
|
expected: vec![
|
||||||
|
button::Event::PressMod(Modifier::LShift),
|
||||||
|
button::Event::PressKey(Key::B),
|
||||||
|
button::Event::ReleaseKey(Key::B),
|
||||||
|
button::Event::ReleaseMod(Modifier::LShift),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let modtap_tap = Test {
|
||||||
|
description: "modtap tap",
|
||||||
|
input: vec![
|
||||||
|
(0, true, Duration::from_millis(25)),
|
||||||
|
(1, true, Duration::from_millis(25)),
|
||||||
|
(0, false, Duration::from_millis(25)),
|
||||||
|
(1, false, Duration::from_millis(25)),
|
||||||
|
],
|
||||||
|
expected: vec![
|
||||||
|
button::Event::PressKey(Key::A),
|
||||||
|
button::Event::ReleaseKey(Key::A),
|
||||||
|
button::Event::PressKey(Key::B),
|
||||||
|
button::Event::ReleaseKey(Key::B),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
let modtap_tap_2x = Test {
|
||||||
|
description: "2x modtap tap",
|
||||||
|
input: vec![
|
||||||
|
(0, true, Duration::from_millis(25)),
|
||||||
|
(2, true, Duration::from_millis(25)),
|
||||||
|
(2, false, Duration::from_millis(25)),
|
||||||
|
(0, false, Duration::from_millis(25)),
|
||||||
|
(0, true, Duration::from_millis(25)),
|
||||||
|
(2, true, Duration::from_millis(25)),
|
||||||
|
(2, false, Duration::from_millis(25)),
|
||||||
|
(0, false, Duration::from_millis(25)),
|
||||||
|
],
|
||||||
|
expected: vec![
|
||||||
|
button::Event::PressMod(Modifier::LShift),
|
||||||
|
button::Event::PressKey(Key::C),
|
||||||
|
button::Event::ReleaseKey(Key::C),
|
||||||
|
button::Event::ReleaseMod(Modifier::LShift),
|
||||||
|
button::Event::PressMod(Modifier::LShift),
|
||||||
|
button::Event::PressKey(Key::C),
|
||||||
|
button::Event::ReleaseKey(Key::C),
|
||||||
|
button::Event::ReleaseMod(Modifier::LShift),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
for test in [modtap_tap, modtap_mod, modtap_tap_2x] {
|
||||||
|
info!("running timing test test {:?}", test.description);
|
||||||
|
embassy_futures::block_on(async {
|
||||||
|
let r = select(
|
||||||
|
join(
|
||||||
|
async {
|
||||||
|
for &(i, pressed, delay) in &test.input {
|
||||||
|
let kind = if pressed {
|
||||||
|
switch::EventKind::Press {
|
||||||
|
button: buttons[i].clone(),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch::EventKind::Release {
|
||||||
|
button: buttons[i].clone(),
|
||||||
|
after: time::Duration::ZERO, // ignore
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let event = switch::Event {
|
||||||
|
source: Half::Left,
|
||||||
|
source_button: i,
|
||||||
|
kind,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch_events.publish_immediate(event);
|
||||||
|
|
||||||
|
Timer::after(delay).await;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async {
|
||||||
|
let mut got = Vec::new();
|
||||||
|
for _expected in &test.expected {
|
||||||
|
let event = match with_timeout(
|
||||||
|
Duration::from_millis(200),
|
||||||
|
button_sub.next_message(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(WaitResult::Message(event)) => event,
|
||||||
|
Ok(WaitResult::Lagged(_)) => return Err(("lagged", got)),
|
||||||
|
Err(_) => return Err(("timeout", got)),
|
||||||
|
};
|
||||||
|
|
||||||
|
got.push(event.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (event, expected) in got.iter().zip(test.expected.iter()) {
|
||||||
|
if event != expected {
|
||||||
|
return Err(("unexpected event", got));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
),
|
||||||
|
convert_events(
|
||||||
|
&mut *switch_events.subscriber().unwrap(),
|
||||||
|
&mut *button_events.publisher().unwrap(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match r {
|
||||||
|
Either::First(((), Ok(()))) => {}
|
||||||
|
Either::First(((), Err((msg, got)))) => panic!(
|
||||||
|
"timing test {:?} failed due to {msg}.\nexpected={:#?} got={:#?}",
|
||||||
|
test.description, test.expected, got
|
||||||
|
),
|
||||||
|
Either::Second(never) => never,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
lib/src/lib.rs
Normal file
32
lib/src/lib.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
#![feature(split_array)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "arm")]
|
||||||
|
pub mod allocator;
|
||||||
|
#[cfg(target_arch = "arm")]
|
||||||
|
pub mod board;
|
||||||
|
#[cfg(target_arch = "arm")]
|
||||||
|
pub mod interrupts;
|
||||||
|
#[cfg(target_arch = "arm")]
|
||||||
|
pub mod keyboard;
|
||||||
|
#[cfg(target_arch = "arm")]
|
||||||
|
pub mod lights;
|
||||||
|
#[cfg(target_arch = "arm")]
|
||||||
|
pub mod panic_handler;
|
||||||
|
#[cfg(target_arch = "arm")]
|
||||||
|
pub mod uart;
|
||||||
|
#[cfg(target_arch = "arm")]
|
||||||
|
pub mod usb;
|
||||||
|
#[cfg(target_arch = "arm")]
|
||||||
|
pub mod ws2812;
|
||||||
|
|
||||||
|
pub mod event;
|
||||||
|
pub mod logger;
|
||||||
|
pub mod neopixel;
|
||||||
|
pub mod rgb;
|
||||||
|
pub mod rtt;
|
||||||
|
pub mod util;
|
||||||
|
pub mod keypress_handler;
|
||||||
@ -2,7 +2,7 @@ use crate::ws2812::Ws2812;
|
|||||||
use embassy_rp::pio;
|
use embassy_rp::pio;
|
||||||
use embassy_sync::mutex::Mutex;
|
use embassy_sync::mutex::Mutex;
|
||||||
|
|
||||||
use crate::{util::CS, ws2812::Rgb};
|
use crate::{rgb::Rgb, util::CS};
|
||||||
|
|
||||||
pub struct Lights<P: pio::Instance + 'static, const N: usize> {
|
pub struct Lights<P: pio::Instance + 'static, const N: usize> {
|
||||||
state: Mutex<CS, State<P, N>>,
|
state: Mutex<CS, State<P, N>>,
|
||||||
@ -24,8 +24,10 @@ impl Logger {
|
|||||||
|
|
||||||
static LOGGER: StaticCell<Logger> = StaticCell::new();
|
static LOGGER: StaticCell<Logger> = StaticCell::new();
|
||||||
let logger = LOGGER.init(self);
|
let logger = LOGGER.init(self);
|
||||||
unsafe { log::set_logger_racy(logger).unwrap() };
|
unsafe {
|
||||||
log::set_max_level(log::LevelFilter::Debug);
|
log::set_logger_racy(logger).unwrap();
|
||||||
|
log::set_max_level_racy(log::LevelFilter::Debug);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
58
lib/src/rgb.rs
Normal file
58
lib/src/rgb.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
use bytemuck::{cast_slice, Pod, Zeroable};
|
||||||
|
use core::{
|
||||||
|
fmt::{self, Debug},
|
||||||
|
ops::Div,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// An Rgb value that can be safely transmuted to u32 for use with Ws2812.
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
|
||||||
|
pub struct Rgb(u32);
|
||||||
|
|
||||||
|
impl Rgb {
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn new(r: u8, g: u8, b: u8) -> Self {
|
||||||
|
Self(u32::from_be_bytes([g, r, b, 0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the red, green, and blue components of this Rgb.
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn components(&self) -> [u8; 3] {
|
||||||
|
let [g, r, b, _] = self.0.to_be_bytes();
|
||||||
|
[r, g, b]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn slice_as_u32s(rgbs: &[Rgb]) -> &[u32] {
|
||||||
|
cast_slice(rgbs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for Rgb {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let [r, g, b] = self.components();
|
||||||
|
f.debug_tuple("Rgb").field(&r).field(&g).field(&b).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<u8> for Rgb {
|
||||||
|
type Output = Rgb;
|
||||||
|
|
||||||
|
fn div(self, d: u8) -> Self::Output {
|
||||||
|
let [r, g, b] = self.components();
|
||||||
|
Rgb::new(r / d, g / d, b / d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_arch = "x86_64", test))]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use bytemuck::cast;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rgb_as_u32() {
|
||||||
|
let rgb = Rgb::new(0x11, 0x22, 0xCC);
|
||||||
|
let rgb_u32: u32 = cast(rgb);
|
||||||
|
assert_eq!(rgb_u32, 0x2211CC00);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,12 +11,13 @@ use heapless::Vec;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
use crate::keyboard::{self, Half, KbEvents};
|
use crate::event::{switch, Half};
|
||||||
use crate::Irqs;
|
use crate::interrupts::Irqs;
|
||||||
|
use crate::keyboard::KbEvents;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
enum Message {
|
enum Message {
|
||||||
KeyboardEvent(keyboard::Event),
|
KeyboardEvent(switch::Event),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn start(tx: PIN_0, rx: PIN_1, uart: UART0, board: Half, events: KbEvents) {
|
pub async fn start(tx: PIN_0, rx: PIN_1, uart: UART0, board: Half, events: KbEvents) {
|
||||||
@ -3,7 +3,7 @@ use embassy_rp::{peripherals::USB, usb::Driver};
|
|||||||
use embassy_usb::{Builder, Config, UsbDevice};
|
use embassy_usb::{Builder, Config, UsbDevice};
|
||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
use crate::{keyboard::KbEvents, Irqs};
|
use crate::{interrupts::Irqs, keyboard::KbEvents};
|
||||||
|
|
||||||
pub mod keyboard;
|
pub mod keyboard;
|
||||||
pub mod logger;
|
pub mod logger;
|
||||||
@ -1,8 +1,13 @@
|
|||||||
pub mod report;
|
pub mod report;
|
||||||
|
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_futures::select::select;
|
||||||
use embassy_rp::{peripherals::USB, usb::Driver};
|
use embassy_rp::{peripherals::USB, usb::Driver};
|
||||||
use embassy_sync::mutex::Mutex;
|
use embassy_sync::{
|
||||||
|
blocking_mutex::raw::NoopRawMutex,
|
||||||
|
mutex::Mutex,
|
||||||
|
pubsub::{PubSubChannel, WaitResult},
|
||||||
|
};
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
use embassy_usb::{
|
use embassy_usb::{
|
||||||
class::hid::{self, HidReaderWriter, ReadError, ReportId, RequestHandler},
|
class::hid::{self, HidReaderWriter, ReadError, ReportId, RequestHandler},
|
||||||
@ -10,13 +15,15 @@ use embassy_usb::{
|
|||||||
Builder,
|
Builder,
|
||||||
};
|
};
|
||||||
use embassy_usb_driver::EndpointError;
|
use embassy_usb_driver::EndpointError;
|
||||||
|
use log::error;
|
||||||
use static_cell::StaticCell;
|
use static_cell::StaticCell;
|
||||||
use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
|
use usbd_hid::descriptor::SerializedDescriptor;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
keyboard::{Event, EventKind, KbEvents},
|
event::button,
|
||||||
|
keyboard::KbEvents,
|
||||||
usb::keyboard::report::{KeyboardReport, EMPTY_KEYBOARD_REPORT},
|
usb::keyboard::report::{KeyboardReport, EMPTY_KEYBOARD_REPORT},
|
||||||
util::CS,
|
util::CS, keypress_handler::keypress_handler,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::MAX_PACKET_SIZE;
|
use super::MAX_PACKET_SIZE;
|
||||||
@ -25,7 +32,18 @@ struct Handler;
|
|||||||
|
|
||||||
static CONTEXT: StaticCell<Context> = StaticCell::new();
|
static CONTEXT: StaticCell<Context> = StaticCell::new();
|
||||||
|
|
||||||
pub static KB_REPORT: Mutex<CS, KeyboardReport> = Mutex::new(EMPTY_KEYBOARD_REPORT);
|
static KB_REPORT: Mutex<CS, Reports> = Mutex::new(Reports {
|
||||||
|
actual: EMPTY_KEYBOARD_REPORT,
|
||||||
|
unsent: EMPTY_KEYBOARD_REPORT,
|
||||||
|
});
|
||||||
|
|
||||||
|
struct Reports {
|
||||||
|
/// The report to be sent to the host machine.
|
||||||
|
actual: KeyboardReport,
|
||||||
|
|
||||||
|
/// Key presses which hasn't been sent yet.
|
||||||
|
unsent: KeyboardReport,
|
||||||
|
}
|
||||||
|
|
||||||
struct Context {
|
struct Context {
|
||||||
handler: Handler,
|
handler: Handler,
|
||||||
@ -86,20 +104,53 @@ type HidStream = HidReaderWriter<'static, Driver<'static, USB>, 256, 256>;
|
|||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn listen_to_events(mut events: KbEvents) {
|
async fn listen_to_events(mut events: KbEvents) {
|
||||||
loop {
|
let button_events = PubSubChannel::<NoopRawMutex, button::Event, 10, 1, 1>::new();
|
||||||
let event = events.recv().await;
|
let mut button_pub = button_events.publisher().unwrap();
|
||||||
report_event(event).await;
|
let mut button_sub = button_events.subscriber().unwrap();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn report_event(event: Event) {
|
select(
|
||||||
match event.kind {
|
async {
|
||||||
EventKind::PressKey(key) => KB_REPORT.lock().await.press_key(key),
|
loop {
|
||||||
EventKind::ReleaseKey(key) => KB_REPORT.lock().await.release_key(key),
|
let WaitResult::Message(event) = button_sub.next_message().await else {
|
||||||
EventKind::PressModifier(modifier) => KB_REPORT.lock().await.press_modifier(modifier),
|
error!("lagged");
|
||||||
EventKind::ReleaseModifier(modifier) => KB_REPORT.lock().await.release_modifier(modifier),
|
continue;
|
||||||
EventKind::SetLayer(_) => {}
|
};
|
||||||
}
|
|
||||||
|
loop {
|
||||||
|
let mut r = KB_REPORT.lock().await;
|
||||||
|
match event {
|
||||||
|
button::Event::PressKey(k) => {
|
||||||
|
r.actual.press_key(k);
|
||||||
|
r.unsent.press_key(k);
|
||||||
|
}
|
||||||
|
button::Event::PressMod(m) => {
|
||||||
|
r.actual.press_modifier(m);
|
||||||
|
r.unsent.press_modifier(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we got a key release, but if the key press hasn't been sent yet, we
|
||||||
|
// wait for a bit until it has.
|
||||||
|
button::Event::ReleaseKey(k) if r.unsent.key_pressed(k) => {
|
||||||
|
drop(r);
|
||||||
|
Timer::after(Duration::from_millis(1)).await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
button::Event::ReleaseMod(m) if r.unsent.modifier_pressed(m) => {
|
||||||
|
drop(r);
|
||||||
|
Timer::after(Duration::from_millis(1)).await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
button::Event::ReleaseKey(k) => r.actual.release_key(k),
|
||||||
|
button::Event::ReleaseMod(m) => r.actual.release_modifier(m),
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
keypress_handler(&mut *events.subscriber, &mut *button_pub),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
@ -107,9 +158,6 @@ async fn task(stream: HidStream, handler: &'static Handler) {
|
|||||||
if let Err(e) = keyboard_report(stream, handler).await {
|
if let Err(e) = keyboard_report(stream, handler).await {
|
||||||
log::error!("keyboard error: {e:?}");
|
log::error!("keyboard error: {e:?}");
|
||||||
}
|
}
|
||||||
//if let Err(e) = mouse_wiggler(stream).await {
|
|
||||||
// log::error!("mouse wiggler: {e:?}");
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn keyboard_report(mut stream: HidStream, _handler: &'static Handler) -> Result<(), Error> {
|
async fn keyboard_report(mut stream: HidStream, _handler: &'static Handler) -> Result<(), Error> {
|
||||||
@ -117,7 +165,12 @@ async fn keyboard_report(mut stream: HidStream, _handler: &'static Handler) -> R
|
|||||||
loop {
|
loop {
|
||||||
Timer::after(Duration::from_millis(2)).await;
|
Timer::after(Duration::from_millis(2)).await;
|
||||||
|
|
||||||
let report = KB_REPORT.lock().await.clone();
|
let report = {
|
||||||
|
let mut reports = KB_REPORT.lock().await;
|
||||||
|
reports.unsent = EMPTY_KEYBOARD_REPORT;
|
||||||
|
reports.actual.clone()
|
||||||
|
};
|
||||||
|
|
||||||
if report.keycodes != EMPTY_KEYBOARD_REPORT.keycodes {
|
if report.keycodes != EMPTY_KEYBOARD_REPORT.keycodes {
|
||||||
log::trace!("keys: {:x?}", report.keycodes);
|
log::trace!("keys: {:x?}", report.keycodes);
|
||||||
}
|
}
|
||||||
@ -130,48 +183,6 @@ async fn keyboard_report(mut stream: HidStream, _handler: &'static Handler) -> R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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)]
|
#[derive(Debug)]
|
||||||
enum Error {
|
enum Error {
|
||||||
Read(ReadError),
|
Read(ReadError),
|
||||||
@ -5,7 +5,7 @@
|
|||||||
/// keyboard LEDs.
|
/// keyboard LEDs.
|
||||||
///
|
///
|
||||||
/// Unlike usbd_hids KeyboardReport, this one supports N-key rollover.
|
/// Unlike usbd_hids KeyboardReport, this one supports N-key rollover.
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq, Zeroable, Pod)]
|
||||||
#[cfg(feature = "n-key-rollover")]
|
#[cfg(feature = "n-key-rollover")]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
pub struct KeyboardReport {
|
pub struct KeyboardReport {
|
||||||
@ -15,7 +15,8 @@ pub struct KeyboardReport {
|
|||||||
pub keycodes: [u8; 27],
|
pub keycodes: [u8; 27],
|
||||||
}
|
}
|
||||||
|
|
||||||
use core::mem::{size_of, transmute};
|
use bytemuck::{cast_ref, Pod, Zeroable};
|
||||||
|
use core::mem::size_of;
|
||||||
use tgnt::{button::Modifier, keys::Key};
|
use tgnt::{button::Modifier, keys::Key};
|
||||||
|
|
||||||
#[cfg(not(feature = "n-key-rollover"))]
|
#[cfg(not(feature = "n-key-rollover"))]
|
||||||
@ -35,14 +36,21 @@ pub const EMPTY_KEYBOARD_REPORT: KeyboardReport = KeyboardReport {
|
|||||||
keycodes: [0; 6],
|
keycodes: [0; 6],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Get the byte index, and the mask for that byte, in the keycode bitmap.
|
||||||
|
fn key_to_byte_mask(key: Key) -> (usize, u8) {
|
||||||
|
let keycode = u8::from(key);
|
||||||
|
let byte = keycode >> 3;
|
||||||
|
let bit = keycode & 0b111;
|
||||||
|
let mask = 1 << bit;
|
||||||
|
|
||||||
|
(usize::from(byte), mask)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "n-key-rollover")]
|
#[cfg(feature = "n-key-rollover")]
|
||||||
impl KeyboardReport {
|
impl KeyboardReport {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_key(&mut self, key: Key, pressed: bool) {
|
pub fn set_key(&mut self, key: Key, pressed: bool) {
|
||||||
let keycode = u8::from(key);
|
let (byte, mask) = key_to_byte_mask(key);
|
||||||
let byte = keycode >> 3;
|
|
||||||
let bit = keycode & 0b111;
|
|
||||||
let mask = 1 << bit;
|
|
||||||
|
|
||||||
if let Some(k) = self.keycodes.get_mut(byte as usize) {
|
if let Some(k) = self.keycodes.get_mut(byte as usize) {
|
||||||
if pressed {
|
if pressed {
|
||||||
@ -51,7 +59,7 @@ impl KeyboardReport {
|
|||||||
*k &= !mask;
|
*k &= !mask;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log::warn!("Tried to set out-of-range keycode: 0x{keycode:x}");
|
log::warn!("Tried to set out-of-range keycode: 0x{:x}", u8::from(key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +73,17 @@ impl KeyboardReport {
|
|||||||
self.set_key(key, false)
|
self.set_key(key, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn key_pressed(&mut self, key: Key) -> bool {
|
||||||
|
let (byte, mask) = key_to_byte_mask(key);
|
||||||
|
if let Some(k) = self.keycodes.get_mut(byte as usize) {
|
||||||
|
(*k & mask) != 0
|
||||||
|
} else {
|
||||||
|
log::warn!("Tried to get out-of-range keycode: 0x{:x}", u8::from(key));
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_modifier(&mut self, modifier: Modifier, pressed: bool) {
|
pub fn set_modifier(&mut self, modifier: Modifier, pressed: bool) {
|
||||||
if pressed {
|
if pressed {
|
||||||
@ -84,10 +103,14 @@ impl KeyboardReport {
|
|||||||
self.set_modifier(modifier, false)
|
self.set_modifier(modifier, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn modifier_pressed(&mut self, modifier: Modifier) -> bool {
|
||||||
|
(self.modifier & u8::from(modifier)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_bytes(&self) -> &[u8; size_of::<KeyboardReport>()] {
|
pub fn as_bytes(&self) -> &[u8; size_of::<KeyboardReport>()] {
|
||||||
// SAFETY: KeyboardReport is repr(C, packed) and contains only u8s.
|
cast_ref(self)
|
||||||
unsafe { transmute(self) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2,39 +2,17 @@ use crate::util::CS;
|
|||||||
|
|
||||||
use super::MAX_PACKET_SIZE;
|
use super::MAX_PACKET_SIZE;
|
||||||
use core::fmt::Write as WriteFmt;
|
use core::fmt::Write as WriteFmt;
|
||||||
use embassy_executor::Spawner;
|
|
||||||
use embassy_rp::{peripherals::USB, usb::Driver};
|
use embassy_rp::{peripherals::USB, usb::Driver};
|
||||||
use embassy_sync::pipe::Pipe;
|
use embassy_sync::pipe::Pipe;
|
||||||
use embassy_time::Instant;
|
use embassy_time::Instant;
|
||||||
use embassy_usb::{
|
use embassy_usb::class::cdc_acm::CdcAcmClass;
|
||||||
class::cdc_acm::{self, CdcAcmClass},
|
|
||||||
Builder,
|
|
||||||
};
|
|
||||||
use log::{Metadata, Record};
|
use log::{Metadata, Record};
|
||||||
use static_cell::StaticCell;
|
|
||||||
|
|
||||||
pub const BUFFER_SIZE: usize = 16 * 1024;
|
pub const BUFFER_SIZE: usize = 16 * 1024;
|
||||||
static BUFFER: Pipe<CS, BUFFER_SIZE> = Pipe::new();
|
static BUFFER: Pipe<CS, BUFFER_SIZE> = Pipe::new();
|
||||||
|
|
||||||
struct UsbLogger;
|
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;
|
|
||||||
|
|
||||||
static STATE: StaticCell<cdc_acm::State<'static>> = StaticCell::new();
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn log_task(mut class: CdcAcmClass<'static, Driver<'static, USB>>) {
|
async fn log_task(mut class: CdcAcmClass<'static, Driver<'static, USB>>) {
|
||||||
let mut buf = [0u8; MAX_PACKET_SIZE as usize];
|
let mut buf = [0u8; MAX_PACKET_SIZE as usize];
|
||||||
@ -1,7 +1,7 @@
|
|||||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
|
|
||||||
use crate::ws2812::Rgb;
|
use crate::rgb::Rgb;
|
||||||
|
|
||||||
pub type CS = CriticalSectionRawMutex;
|
pub type CS = CriticalSectionRawMutex;
|
||||||
|
|
||||||
@ -1,23 +1,16 @@
|
|||||||
use core::fmt::{self, Debug};
|
|
||||||
use core::mem::transmute;
|
|
||||||
use core::ops::Div;
|
|
||||||
|
|
||||||
use embassy_rp::dma::{self, AnyChannel};
|
use embassy_rp::dma::{self, AnyChannel};
|
||||||
use embassy_rp::pio::{self, FifoJoin, Instance, Pio, PioPin, ShiftConfig, ShiftDirection};
|
use embassy_rp::pio::{self, FifoJoin, Instance, Pio, PioPin, ShiftConfig, ShiftDirection};
|
||||||
use embassy_rp::relocate::RelocatedProgram;
|
use embassy_rp::relocate::RelocatedProgram;
|
||||||
use embassy_rp::{Peripheral, PeripheralRef};
|
use embassy_rp::{Peripheral, PeripheralRef};
|
||||||
use fixed::FixedU32;
|
use fixed::FixedU32;
|
||||||
|
|
||||||
|
use crate::rgb::Rgb;
|
||||||
|
|
||||||
pub struct Ws2812<P: pio::Instance + 'static> {
|
pub struct Ws2812<P: pio::Instance + 'static> {
|
||||||
sm: pio::StateMachine<'static, P, 0>,
|
sm: pio::StateMachine<'static, P, 0>,
|
||||||
dma: PeripheralRef<'static, AnyChannel>,
|
dma: PeripheralRef<'static, AnyChannel>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An Rgb value that can be safely transmuted to u32 for use with Ws2812.
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub struct Rgb(u32);
|
|
||||||
|
|
||||||
impl<P: Instance> Ws2812<P> {
|
impl<P: Instance> Ws2812<P> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
pio: impl Peripheral<P = P> + 'static,
|
pio: impl Peripheral<P = P> + 'static,
|
||||||
@ -96,39 +89,3 @@ impl<P: Instance> Ws2812<P> {
|
|||||||
self.sm.tx().dma_push(self.dma.reborrow(), colors).await;
|
self.sm.tx().dma_push(self.dma.reborrow(), colors).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rgb {
|
|
||||||
#[inline(always)]
|
|
||||||
pub const fn new(r: u8, g: u8, b: u8) -> Self {
|
|
||||||
Self(u32::from_be_bytes([g, r, b, 0]))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the red, green, and blue components of this Rgb.
|
|
||||||
#[inline(always)]
|
|
||||||
pub const fn components(&self) -> [u8; 3] {
|
|
||||||
let [g, r, b, _] = self.0.to_be_bytes();
|
|
||||||
[r, g, b]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn slice_as_u32s(rgbs: &[Rgb]) -> &[u32] {
|
|
||||||
// SAFETY: Rgb contains only a u32, and is #[repr(transparent)]
|
|
||||||
unsafe { transmute(rgbs) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for Rgb {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let [r, g, b] = self.components();
|
|
||||||
f.debug_tuple("Rgb").field(&r).field(&g).field(&b).finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Div<u8> for Rgb {
|
|
||||||
type Output = Rgb;
|
|
||||||
|
|
||||||
fn div(self, d: u8) -> Self::Output {
|
|
||||||
let [r, g, b] = self.components();
|
|
||||||
Rgb::new(r / d, g / d, b / d)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
218
Cargo.lock → right/Cargo.lock
generated
218
Cargo.lock → right/Cargo.lock
generated
@ -44,6 +44,17 @@ dependencies = [
|
|||||||
"critical-section",
|
"critical-section",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi 0.1.19",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@ -115,7 +126,7 @@ checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.16",
|
"syn 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -152,6 +163,17 @@ dependencies = [
|
|||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colored"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"lazy_static",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cortex-m"
|
name = "cortex-m"
|
||||||
version = "0.7.7"
|
version = "0.7.7"
|
||||||
@ -282,7 +304,7 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "embassy-cortex-m"
|
name = "embassy-cortex-m"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/embassy-rs/embassy.git#d55b9bc6e2de528e1351b992bb30f54ec6f76b6d"
|
source = "git+https://github.com/embassy-rs/embassy.git#d414f4e4f7f243b4e52e6550ca616c180d731774"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-polyfill 1.0.2",
|
"atomic-polyfill 1.0.2",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@ -297,8 +319,9 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "embassy-embedded-hal"
|
name = "embassy-embedded-hal"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/embassy-rs/embassy.git#d55b9bc6e2de528e1351b992bb30f54ec6f76b6d"
|
source = "git+https://github.com/embassy-rs/embassy.git#d414f4e4f7f243b4e52e6550ca616c180d731774"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"embassy-futures",
|
||||||
"embassy-sync",
|
"embassy-sync",
|
||||||
"embedded-hal 0.2.7",
|
"embedded-hal 0.2.7",
|
||||||
"embedded-hal 1.0.0-alpha.10",
|
"embedded-hal 1.0.0-alpha.10",
|
||||||
@ -311,7 +334,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "embassy-executor"
|
name = "embassy-executor"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/embassy-rs/embassy.git#d55b9bc6e2de528e1351b992bb30f54ec6f76b6d"
|
source = "git+https://github.com/embassy-rs/embassy.git#d414f4e4f7f243b4e52e6550ca616c180d731774"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-polyfill 1.0.2",
|
"atomic-polyfill 1.0.2",
|
||||||
"cortex-m",
|
"cortex-m",
|
||||||
@ -326,7 +349,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "embassy-futures"
|
name = "embassy-futures"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/embassy-rs/embassy.git#d55b9bc6e2de528e1351b992bb30f54ec6f76b6d"
|
source = "git+https://github.com/embassy-rs/embassy.git#d414f4e4f7f243b4e52e6550ca616c180d731774"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
@ -334,7 +357,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "embassy-hal-common"
|
name = "embassy-hal-common"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/embassy-rs/embassy.git#d55b9bc6e2de528e1351b992bb30f54ec6f76b6d"
|
source = "git+https://github.com/embassy-rs/embassy.git#d414f4e4f7f243b4e52e6550ca616c180d731774"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
@ -342,7 +365,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "embassy-macros"
|
name = "embassy-macros"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/embassy-rs/embassy.git#d55b9bc6e2de528e1351b992bb30f54ec6f76b6d"
|
source = "git+https://github.com/embassy-rs/embassy.git#d414f4e4f7f243b4e52e6550ca616c180d731774"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -353,12 +376,12 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "embassy-net-driver"
|
name = "embassy-net-driver"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/embassy-rs/embassy.git#d55b9bc6e2de528e1351b992bb30f54ec6f76b6d"
|
source = "git+https://github.com/embassy-rs/embassy.git#d414f4e4f7f243b4e52e6550ca616c180d731774"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embassy-net-driver-channel"
|
name = "embassy-net-driver-channel"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/embassy-rs/embassy.git#d55b9bc6e2de528e1351b992bb30f54ec6f76b6d"
|
source = "git+https://github.com/embassy-rs/embassy.git#d414f4e4f7f243b4e52e6550ca616c180d731774"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"embassy-futures",
|
"embassy-futures",
|
||||||
"embassy-net-driver",
|
"embassy-net-driver",
|
||||||
@ -368,7 +391,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "embassy-rp"
|
name = "embassy-rp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/embassy-rs/embassy.git#d55b9bc6e2de528e1351b992bb30f54ec6f76b6d"
|
source = "git+https://github.com/embassy-rs/embassy.git#d414f4e4f7f243b4e52e6550ca616c180d731774"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-polyfill 1.0.2",
|
"atomic-polyfill 1.0.2",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@ -404,7 +427,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "embassy-sync"
|
name = "embassy-sync"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "git+https://github.com/embassy-rs/embassy.git#d55b9bc6e2de528e1351b992bb30f54ec6f76b6d"
|
source = "git+https://github.com/embassy-rs/embassy.git#d414f4e4f7f243b4e52e6550ca616c180d731774"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"critical-section",
|
"critical-section",
|
||||||
@ -417,7 +440,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "embassy-time"
|
name = "embassy-time"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "git+https://github.com/embassy-rs/embassy.git#d55b9bc6e2de528e1351b992bb30f54ec6f76b6d"
|
source = "git+https://github.com/embassy-rs/embassy.git#d414f4e4f7f243b4e52e6550ca616c180d731774"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-polyfill 1.0.2",
|
"atomic-polyfill 1.0.2",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
@ -431,7 +454,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "embassy-usb"
|
name = "embassy-usb"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/embassy-rs/embassy.git#d55b9bc6e2de528e1351b992bb30f54ec6f76b6d"
|
source = "git+https://github.com/embassy-rs/embassy.git#d414f4e4f7f243b4e52e6550ca616c180d731774"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"embassy-futures",
|
"embassy-futures",
|
||||||
"embassy-net-driver-channel",
|
"embassy-net-driver-channel",
|
||||||
@ -445,12 +468,12 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "embassy-usb-driver"
|
name = "embassy-usb-driver"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/embassy-rs/embassy.git#d55b9bc6e2de528e1351b992bb30f54ec6f76b6d"
|
source = "git+https://github.com/embassy-rs/embassy.git#d414f4e4f7f243b4e52e6550ca616c180d731774"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embassy-usb-logger"
|
name = "embassy-usb-logger"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/embassy-rs/embassy.git#d55b9bc6e2de528e1351b992bb30f54ec6f76b6d"
|
source = "git+https://github.com/embassy-rs/embassy.git#d414f4e4f7f243b4e52e6550ca616c180d731774"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"embassy-futures",
|
"embassy-futures",
|
||||||
"embassy-sync",
|
"embassy-sync",
|
||||||
@ -631,7 +654,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.16",
|
"syn 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -709,6 +732,15 @@ dependencies = [
|
|||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@ -733,11 +765,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "io-lifetimes"
|
name = "io-lifetimes"
|
||||||
version = "1.0.10"
|
version = "1.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
|
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi 0.3.1",
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
@ -748,7 +780,7 @@ version = "0.4.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
|
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"hermit-abi 0.3.1",
|
||||||
"io-lifetimes",
|
"io-lifetimes",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
@ -763,6 +795,12 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lalrpop"
|
name = "lalrpop"
|
||||||
version = "0.19.12"
|
version = "0.19.12"
|
||||||
@ -794,6 +832,12 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.144"
|
version = "0.2.144"
|
||||||
@ -824,12 +868,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.17"
|
version = "0.4.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de"
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
@ -888,10 +929,19 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "num_threads"
|
||||||
version = "1.17.1"
|
version = "0.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.17.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
@ -1036,18 +1086,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.58"
|
version = "1.0.59"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8"
|
checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.27"
|
version = "1.0.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
|
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@ -1080,13 +1130,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.8.1"
|
version = "1.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370"
|
checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-syntax 0.7.1",
|
"regex-syntax 0.7.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1097,9 +1147,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.7.1"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c"
|
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rgb"
|
name = "rgb"
|
||||||
@ -1232,7 +1282,20 @@ checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.16",
|
"syn 2.0.18",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simple_logger"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e78beb34673091ccf96a8816fce8bfd30d1292c7621ca2bcb5f2ba0fae4f558d"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"colored",
|
||||||
|
"log",
|
||||||
|
"time",
|
||||||
|
"windows-sys 0.42.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1292,9 +1355,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_cell"
|
name = "static_cell"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e4c37c250d21f53fa7165e76e5401d7e6539c211a8d2cf449e3962956a5cc2ce"
|
checksum = "ed6a851e9c00ce152b2bad24e8545a6db0b8646988267a6ea966d95c6932e8a2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-polyfill 1.0.2",
|
"atomic-polyfill 1.0.2",
|
||||||
]
|
]
|
||||||
@ -1331,9 +1394,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.16"
|
version = "2.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01"
|
checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1341,7 +1404,7 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tangentbord1"
|
name = "tangentbord1-lib"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-polyfill 1.0.2",
|
"atomic-polyfill 1.0.2",
|
||||||
@ -1372,6 +1435,7 @@ dependencies = [
|
|||||||
"ron",
|
"ron",
|
||||||
"rtt-target",
|
"rtt-target",
|
||||||
"serde",
|
"serde",
|
||||||
|
"simple_logger",
|
||||||
"smart-leds",
|
"smart-leds",
|
||||||
"static_cell",
|
"static_cell",
|
||||||
"tgnt",
|
"tgnt",
|
||||||
@ -1379,6 +1443,23 @@ dependencies = [
|
|||||||
"usbd-hid",
|
"usbd-hid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tangentbord1-right"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cortex-m-rt",
|
||||||
|
"embassy-executor",
|
||||||
|
"embassy-futures",
|
||||||
|
"embassy-rp",
|
||||||
|
"embassy-sync",
|
||||||
|
"embassy-time",
|
||||||
|
"log",
|
||||||
|
"postcard",
|
||||||
|
"ron",
|
||||||
|
"tangentbord1-lib",
|
||||||
|
"tgnt",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "term"
|
name = "term"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@ -1402,7 +1483,6 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tgnt"
|
name = "tgnt"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://git.nubo.sh/hulthe/tgnt.git#643f75cf0a33208ec18c38ea7d9ccf06f344ff35"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
@ -1424,7 +1504,36 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.16",
|
"syn 2.0.18",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"libc",
|
||||||
|
"num_threads",
|
||||||
|
"serde",
|
||||||
|
"time-core",
|
||||||
|
"time-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-core"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time-macros"
|
||||||
|
version = "0.2.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b"
|
||||||
|
dependencies = [
|
||||||
|
"time-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1450,9 +1559,9 @@ checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.8"
|
version = "1.0.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
@ -1571,6 +1680,21 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.42.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm 0.42.2",
|
||||||
|
"windows_aarch64_msvc 0.42.2",
|
||||||
|
"windows_i686_gnu 0.42.2",
|
||||||
|
"windows_i686_msvc 0.42.2",
|
||||||
|
"windows_x86_64_gnu 0.42.2",
|
||||||
|
"windows_x86_64_gnullvm 0.42.2",
|
||||||
|
"windows_x86_64_msvc 0.42.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.45.0"
|
version = "0.45.0"
|
||||||
29
right/Cargo.toml
Normal file
29
right/Cargo.toml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
[package]
|
||||||
|
name = "tangentbord1-right"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Joakim Hulthe <joakim@hulthe.net>"]
|
||||||
|
description = "Keyboard firmware"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies.tangentbord1]
|
||||||
|
path = "../lib"
|
||||||
|
package = "tangentbord1-lib"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tgnt = { git = "https://git.nubo.sh/hulthe/tgnt.git", default-features = false }
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
embassy-rp = { git = "https://github.com/embassy-rs/embassy.git", features = ["log", "nightly", "unstable-traits", "unstable-pac", "time-driver", "critical-section-impl"] }
|
||||||
|
embassy-executor = { git = "https://github.com/embassy-rs/embassy.git", features = ["arch-cortex-m", "log", "executor-thread", "nightly", "integrated-timers" ] }
|
||||||
|
embassy-sync = { git = "https://github.com/embassy-rs/embassy.git", features = ["log", "nightly"] }
|
||||||
|
embassy-time = { git = "https://github.com/embassy-rs/embassy.git", features = ["log"] }
|
||||||
|
embassy-futures = { git = "https://github.com/embassy-rs/embassy.git", features = ["log"] }
|
||||||
|
log = "0.4.17"
|
||||||
|
postcard = { version = "1.0.4", features = ["alloc"] }
|
||||||
|
|
||||||
|
[patch."https://git.nubo.sh/hulthe/tgnt.git"]
|
||||||
|
tgnt = { path = "../../tgnt" }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tgnt = { git = "https://git.nubo.sh/hulthe/tgnt.git", default-features = false }
|
||||||
|
ron = "0.8.0"
|
||||||
|
postcard = { version = "1", features = ["use-std"] }
|
||||||
59
right/build.rs
Normal file
59
right/build.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
//! This build script copies the `memory.x` file from the crate root into
|
||||||
|
//! a directory where the linker can always find it at build time.
|
||||||
|
//! For many projects this is optional, as the linker always searches the
|
||||||
|
//! project root directory -- wherever `Cargo.toml` is. However, if you
|
||||||
|
//! are using a workspace or have a more complicated build setup, this
|
||||||
|
//! build script becomes required. Additionally, by requesting that
|
||||||
|
//! Cargo re-run the build script whenever `memory.x` is changed,
|
||||||
|
//! updating `memory.x` ensures a rebuild of the application with the
|
||||||
|
//! new memory settings.
|
||||||
|
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::{env, fs};
|
||||||
|
|
||||||
|
use tgnt::layer::Layer;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
memory();
|
||||||
|
serialize_layout("./layers.ron", "./src/layers.pc");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn memory() {
|
||||||
|
// Put `memory.x` in our output directory and ensure it's
|
||||||
|
// on the linker search path.
|
||||||
|
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||||
|
File::create(out.join("memory.x"))
|
||||||
|
.unwrap()
|
||||||
|
.write_all(include_bytes!("../memory.x"))
|
||||||
|
.unwrap();
|
||||||
|
println!("cargo:rustc-link-search={}", out.display());
|
||||||
|
|
||||||
|
// By default, Cargo will re-run a build script whenever
|
||||||
|
// any file in the project changes. By specifying `memory.x`
|
||||||
|
// here, we ensure the build script is only re-run when
|
||||||
|
// `memory.x` is changed.
|
||||||
|
println!("cargo:rerun-if-changed=../memory.x");
|
||||||
|
|
||||||
|
// --nmagic turns off page alignment of sections (which saves flash space)
|
||||||
|
println!("cargo:rustc-link-arg-bins=--nmagic");
|
||||||
|
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
||||||
|
println!("cargo:rustc-link-arg-bins=-Tlink-rp.x");
|
||||||
|
//println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_layout(ron_path: &str, postcard_path: &str) {
|
||||||
|
println!("cargo:rerun-if-changed={ron_path}");
|
||||||
|
|
||||||
|
let layers = fs::read_to_string(ron_path).expect("Failed to read .ron");
|
||||||
|
let layers: Vec<Layer> = ron::from_str(&layers).expect("Failed to deserialize .ron");
|
||||||
|
|
||||||
|
let serialized = postcard::to_stdvec(&layers).expect("Failed to serialize layers");
|
||||||
|
|
||||||
|
File::create(postcard_path)
|
||||||
|
.expect("Failed to create .pc")
|
||||||
|
.write_all(&serialized)
|
||||||
|
.expect("Failed to write .pc");
|
||||||
|
}
|
||||||
@ -23,7 +23,7 @@
|
|||||||
Key(Z),
|
Key(Z),
|
||||||
|
|
||||||
// Thumbpad
|
// Thumbpad
|
||||||
PrevLayer,
|
HoldLayer(1),
|
||||||
Key(Return),
|
Key(Return),
|
||||||
Key(Delete),
|
Key(Delete),
|
||||||
],
|
],
|
||||||
@ -53,7 +53,7 @@
|
|||||||
None,
|
None,
|
||||||
|
|
||||||
// Thumbpad
|
// Thumbpad
|
||||||
PrevLayer,
|
HoldLayer(1),
|
||||||
Key(Return),
|
Key(Return),
|
||||||
Key(Delete),
|
Key(Delete),
|
||||||
],
|
],
|
||||||
@ -82,7 +82,7 @@
|
|||||||
None,
|
None,
|
||||||
|
|
||||||
// Thumbpad
|
// Thumbpad
|
||||||
PrevLayer,
|
HoldLayer(1),
|
||||||
Key(Return),
|
Key(Return),
|
||||||
Key(Delete),
|
Key(Delete),
|
||||||
],
|
],
|
||||||
BIN
right/src/layers.pc
Normal file
BIN
right/src/layers.pc
Normal file
Binary file not shown.
@ -12,12 +12,16 @@ use embassy_executor::Spawner;
|
|||||||
use embassy_rp::gpio::{Level, Output, Pin};
|
use embassy_rp::gpio::{Level, Output, Pin};
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
use log::error;
|
use log::error;
|
||||||
use tangentbord1::board::Board;
|
use tangentbord1::{
|
||||||
use tangentbord1::keyboard::{Half, KeyboardConfig};
|
board::Board,
|
||||||
use tangentbord1::logger::Logger;
|
event::Half,
|
||||||
use tangentbord1::util::{stall, wheel};
|
keyboard::KeyboardConfig,
|
||||||
use tangentbord1::ws2812::{Rgb, Ws2812};
|
logger::Logger,
|
||||||
use tangentbord1::{allocator, rtt, uart, usb};
|
rgb::Rgb,
|
||||||
|
util::{stall, wheel},
|
||||||
|
ws2812::Ws2812,
|
||||||
|
{allocator, rtt, uart, usb},
|
||||||
|
};
|
||||||
use tgnt::layer::Layer;
|
use tgnt::layer::Layer;
|
||||||
|
|
||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
@ -50,7 +54,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
|
|
||||||
//Timer::after(Duration::from_millis(3000)).await;
|
//Timer::after(Duration::from_millis(3000)).await;
|
||||||
|
|
||||||
let layers = include_bytes!("layers-right.pc");
|
let layers = include_bytes!("layers.pc");
|
||||||
let Ok(layers): Result<Vec<Layer>, _> = postcard::from_bytes(layers) else {
|
let Ok(layers): Result<Vec<Layer>, _> = postcard::from_bytes(layers) else {
|
||||||
log::error!("Failed to deserialize layer config");
|
log::error!("Failed to deserialize layer config");
|
||||||
stall().await
|
stall().await
|
||||||
1
src/bin/.gitignore
vendored
1
src/bin/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
*.pc
|
|
||||||
30
src/lib.rs
30
src/lib.rs
@ -1,30 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![feature(type_alias_impl_trait)]
|
|
||||||
#![feature(split_array)]
|
|
||||||
|
|
||||||
use embassy_rp::{
|
|
||||||
bind_interrupts,
|
|
||||||
peripherals::{UART0, USB},
|
|
||||||
};
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
pub mod allocator;
|
|
||||||
pub mod board;
|
|
||||||
pub mod keyboard;
|
|
||||||
pub mod lights;
|
|
||||||
pub mod logger;
|
|
||||||
pub mod neopixel;
|
|
||||||
pub mod panic_handler;
|
|
||||||
pub mod rtt;
|
|
||||||
pub mod uart;
|
|
||||||
pub mod usb;
|
|
||||||
pub mod util;
|
|
||||||
pub mod ws2812;
|
|
||||||
|
|
||||||
bind_interrupts! {
|
|
||||||
pub struct Irqs {
|
|
||||||
UART0_IRQ => embassy_rp::uart::BufferedInterruptHandler<UART0>;
|
|
||||||
USBCTRL_IRQ => embassy_rp::usb::InterruptHandler<USB>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user