Compare commits
8 Commits
58b4bc4004
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
34e6e50c61
|
|||
|
04949c4abd
|
|||
|
f31e20c7ce
|
|||
|
8a5fe69b5c
|
|||
|
e730fb9ec7
|
|||
|
a066b3c5dc
|
|||
|
5a38ef7f78
|
|||
|
ae599f587a
|
34
README.md
Normal file
34
README.md
Normal file
@ -0,0 +1,34 @@
|
||||
A DIY split keyboard.
|
||||
|
||||
## Directory
|
||||
|
||||
### `/lib`
|
||||
A rust library, and the bulk of the keyboard firmware implementation.
|
||||
Written using the Embassy framework.
|
||||
|
||||
### `/left` and `/right`
|
||||
Rust binaries, one for each half of the split keyboard.
|
||||
Most of the actual logic is implemented in `/lib`.
|
||||
|
||||
### `/schematic`
|
||||
Kicad schematics of the keyboard PCBs.
|
||||
|
||||
### `/case`
|
||||
3d-printable files of the keyboard case and related plastics.
|
||||
|
||||
### `/editor`
|
||||
A graphical program for configuring the keyboard layout. Work in progress.
|
||||
|
||||
|
||||
## Developing
|
||||
|
||||
Required tools:
|
||||
- Rust, obviously.
|
||||
- `probe-rs` for flashing the firmware. Follow instructions at https://probe.rs/
|
||||
- [`flip-link`](https://github.com/knurling-rs/flip-link/)
|
||||
- A debug probe of some kind, I recommend a [Pitaya-Link](https://github.com/makerdiary/pitaya-link)
|
||||
|
||||
To flash:
|
||||
- plug in the probe,
|
||||
- `cd` into either `left` or `right`
|
||||
- `cargo run`
|
||||
BIN
case/backplate.stl
Normal file
BIN
case/backplate.stl
Normal file
Binary file not shown.
BIN
case/frontplate.stl
Normal file
BIN
case/frontplate.stl
Normal file
Binary file not shown.
BIN
case/keycap-filled.stl
Normal file
BIN
case/keycap-filled.stl
Normal file
Binary file not shown.
BIN
case/keycap.stl
Normal file
BIN
case/keycap.stl
Normal file
Binary file not shown.
BIN
case/leg-long.stl
Normal file
BIN
case/leg-long.stl
Normal file
Binary file not shown.
BIN
case/leg.stl
Normal file
BIN
case/leg.stl
Normal file
Binary file not shown.
BIN
case/thumb-backplate.stl
Normal file
BIN
case/thumb-backplate.stl
Normal file
Binary file not shown.
BIN
case/thumb-frontplate.stl
Normal file
BIN
case/thumb-frontplate.stl
Normal file
Binary file not shown.
@ -1,3 +0,0 @@
|
||||
# tgnt
|
||||
|
||||
Keyboard configuration library and editor.
|
||||
@ -9,7 +9,7 @@ edition = "2021"
|
||||
path = "../lib"
|
||||
package = "tangentbord1-lib"
|
||||
|
||||
[dependencies]
|
||||
[target.thumbv6m-none-eabi.dependencies]
|
||||
cortex-m-rt = "0.7"
|
||||
embassy-rp = { version = "0.1.0", features = ["time-driver", "critical-section-impl"] }
|
||||
embassy-executor = { version = "0.5.0", features = ["nightly", "executor-thread", "integrated-timers", "arch-cortex-m"] }
|
||||
|
||||
@ -61,24 +61,24 @@
|
||||
Layer(
|
||||
buttons: [
|
||||
// Row 1
|
||||
Key(Escape),
|
||||
Key(Mute),
|
||||
Key(VolumeDown),
|
||||
Key(VolumeUp),
|
||||
Key(F1),
|
||||
Key(F4),
|
||||
Key(F7),
|
||||
Key(F10),
|
||||
None,
|
||||
|
||||
// Row 2
|
||||
Mod(LAlt),
|
||||
Mod(LMod),
|
||||
Mod(LShift),
|
||||
Mod(LCtrl),
|
||||
Key(F2),
|
||||
Key(F5),
|
||||
Key(F8),
|
||||
Key(F11),
|
||||
None,
|
||||
|
||||
// Row 3
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Key(F3),
|
||||
Key(F6),
|
||||
Key(F9),
|
||||
Key(F12),
|
||||
None,
|
||||
|
||||
// Thumbpad
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
//! Firmware for Tangentbord1, left half.
|
||||
|
||||
#![no_std]
|
||||
// NOTE: the order of attributes matters here..
|
||||
#![no_main]
|
||||
#![cfg(target_os = "none")] // only try to compile this for embedded
|
||||
#![no_std]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![cfg(target_arch = "arm")]
|
||||
|
||||
extern crate alloc;
|
||||
extern crate cortex_m_rt;
|
||||
|
||||
@ -40,14 +40,14 @@ pub enum Button {
|
||||
Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, MsgPack, MsgUnpack,
|
||||
)]
|
||||
pub enum CompShift {
|
||||
/// Do not shift the key.
|
||||
/// Do not shift the key. [Key::A] becomes 'a'.
|
||||
#[default]
|
||||
Lower,
|
||||
|
||||
/// Shift the key.
|
||||
/// Shift the key. [Key::A] becomes 'A'.
|
||||
Upper,
|
||||
|
||||
/// Shift the key if shift is being held down.
|
||||
/// Shift the key if shift is being held down. [Key::A] becomes 'a' or 'A' depending.
|
||||
Variable,
|
||||
}
|
||||
|
||||
|
||||
@ -137,7 +137,7 @@ async fn play_shader(state: &'static State, shader: &impl Shader) {
|
||||
(4, 4),
|
||||
];
|
||||
|
||||
const BRIGHTNESS: f32 = 0.15;
|
||||
const BRIGHTNESS: f32 = 1.00;
|
||||
|
||||
let switch_coords = SWITCH_COORDS.map(|(x, y)| (f32::from(x) / 4.0, f32::from(y) / 4.0));
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use core::future::pending;
|
||||
|
||||
use crate::{
|
||||
button::Button,
|
||||
button::{Button, CompShift, Modifier},
|
||||
event::{button, switch, Half},
|
||||
keys::Key,
|
||||
};
|
||||
@ -28,6 +28,7 @@ pub async fn keypress_handler(
|
||||
output: &mut Pub<'_, impl PubSubBehavior<button::Event>, button::Event>,
|
||||
) -> ! {
|
||||
type SwitchIndex = usize;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PressEvent {
|
||||
source_button: SwitchIndex,
|
||||
@ -36,51 +37,88 @@ pub async fn keypress_handler(
|
||||
time: Instant,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ShiftHeld {
|
||||
left: bool,
|
||||
right: bool,
|
||||
}
|
||||
|
||||
impl ShiftHeld {
|
||||
pub fn either(&self) -> bool {
|
||||
self.left || self.right
|
||||
}
|
||||
}
|
||||
|
||||
async fn slow_press(
|
||||
output: &mut Pub<'_, impl PubSubBehavior<button::Event>, button::Event>,
|
||||
shift_held: &mut ShiftHeld,
|
||||
button: &Button,
|
||||
) {
|
||||
let event = match button {
|
||||
&Button::Mod(m) | &Button::ModTap(_, m) => button::Event::PressMod(m),
|
||||
&Button::Key(k) => button::Event::PressKey(k),
|
||||
&Button::Compose2(csa, a, csb, b) => {
|
||||
let events = [
|
||||
button::Event::PressKey(COMPOSE_KEY),
|
||||
button::Event::ReleaseKey(COMPOSE_KEY),
|
||||
button::Event::Wait,
|
||||
button::Event::PressKey(a),
|
||||
button::Event::ReleaseKey(a),
|
||||
button::Event::Wait,
|
||||
button::Event::PressKey(b),
|
||||
button::Event::ReleaseKey(b),
|
||||
button::Event::Wait,
|
||||
];
|
||||
let do_compose = |keys: &[(CompShift, Key)]| {
|
||||
for &(comp_shift, key) in keys {
|
||||
match comp_shift {
|
||||
// Release any pressed shift keys
|
||||
CompShift::Lower => {
|
||||
if shift_held.left {
|
||||
output.publish_immediate(button::Event::ReleaseMod(Modifier::LShift));
|
||||
}
|
||||
if shift_held.right {
|
||||
output.publish_immediate(button::Event::ReleaseMod(Modifier::RShift));
|
||||
}
|
||||
}
|
||||
|
||||
for event in events {
|
||||
output.publish_immediate(event);
|
||||
// Press shift if not already pressed
|
||||
CompShift::Upper if !shift_held.either() => {
|
||||
output.publish_immediate(button::Event::PressMod(Modifier::LShift));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
output.publish_immediate(button::Event::PressKey(key));
|
||||
output.publish_immediate(button::Event::ReleaseKey(key));
|
||||
output.publish_immediate(button::Event::Wait);
|
||||
|
||||
match comp_shift {
|
||||
// Re-press any shift keys we released
|
||||
CompShift::Lower => {
|
||||
if shift_held.left {
|
||||
output.publish_immediate(button::Event::PressMod(Modifier::LShift));
|
||||
}
|
||||
if shift_held.right {
|
||||
output.publish_immediate(button::Event::PressMod(Modifier::RShift));
|
||||
}
|
||||
}
|
||||
|
||||
// Release the shift keys we pressed
|
||||
CompShift::Upper if !shift_held.either() => {
|
||||
output.publish_immediate(button::Event::ReleaseMod(Modifier::LShift));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let event = match button {
|
||||
&Button::Mod(m) | &Button::ModTap(_, m) => {
|
||||
match m {
|
||||
Modifier::LShift => shift_held.left = true,
|
||||
Modifier::RShift => shift_held.right = true,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
button::Event::PressMod(m)
|
||||
}
|
||||
&Button::Key(k) => button::Event::PressKey(k),
|
||||
&Button::Compose2(csa, a, csb, b) => {
|
||||
do_compose(&[(CompShift::Variable, COMPOSE_KEY), (csa, a), (csb, b)]);
|
||||
return;
|
||||
}
|
||||
&Button::Compose3(csa, a, csb, b, csc, c) => {
|
||||
let events = [
|
||||
button::Event::PressKey(COMPOSE_KEY),
|
||||
button::Event::ReleaseKey(COMPOSE_KEY),
|
||||
button::Event::Wait,
|
||||
button::Event::PressKey(a),
|
||||
button::Event::ReleaseKey(a),
|
||||
button::Event::Wait,
|
||||
button::Event::PressKey(b),
|
||||
button::Event::ReleaseKey(b),
|
||||
button::Event::Wait,
|
||||
button::Event::PressKey(c),
|
||||
button::Event::ReleaseKey(c),
|
||||
];
|
||||
|
||||
for event in events {
|
||||
output.publish_immediate(event);
|
||||
}
|
||||
|
||||
do_compose(&[
|
||||
(CompShift::Variable, COMPOSE_KEY),
|
||||
(csa, a),
|
||||
(csb, b),
|
||||
(csc, c),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
_ => return,
|
||||
@ -90,10 +128,19 @@ pub async fn keypress_handler(
|
||||
|
||||
async fn slow_release(
|
||||
output: &mut Pub<'_, impl PubSubBehavior<button::Event>, button::Event>,
|
||||
shift_held: &mut ShiftHeld,
|
||||
button: &Button,
|
||||
) {
|
||||
let event = match button {
|
||||
&Button::Mod(m) | &Button::ModTap(_, m) => button::Event::ReleaseMod(m),
|
||||
&Button::Mod(m) | &Button::ModTap(_, m) => {
|
||||
match m {
|
||||
Modifier::LShift => shift_held.left = false,
|
||||
Modifier::RShift => shift_held.right = false,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
button::Event::ReleaseMod(m)
|
||||
}
|
||||
&Button::Key(k) => button::Event::ReleaseKey(k),
|
||||
_ => return,
|
||||
};
|
||||
@ -104,6 +151,10 @@ pub async fn keypress_handler(
|
||||
let mut queue = Deque::<PressEvent, { 2 * SWITCH_COUNT }>::new();
|
||||
let queue = &mut queue;
|
||||
|
||||
// tracks whether a shift-key is being held down.
|
||||
// required to properly play compose macros.
|
||||
let mut shift_held = ShiftHeld::default();
|
||||
|
||||
loop {
|
||||
// create a future that waits for the next ModTap to time out
|
||||
let modtap_timeout = async {
|
||||
@ -134,7 +185,7 @@ pub async fn keypress_handler(
|
||||
continue;
|
||||
};
|
||||
debug!("modtap resolved as mod: {:?}", event.button);
|
||||
slow_press(output, &event.button).await;
|
||||
slow_press(output, &mut shift_held, &event.button).await;
|
||||
|
||||
loop {
|
||||
let Some(event) = queue.pop_front() else { break };
|
||||
@ -145,7 +196,7 @@ pub async fn keypress_handler(
|
||||
queue.push_front(event).expect("we just popped, the queue can't be full");
|
||||
break;
|
||||
}
|
||||
slow_press(output, &event.button).await;
|
||||
slow_press(output, &mut shift_held, &event.button).await;
|
||||
}
|
||||
|
||||
continue;
|
||||
@ -194,7 +245,7 @@ pub async fn keypress_handler(
|
||||
if queue.is_empty() {
|
||||
debug!("sending key now");
|
||||
// otherwise, send immediately
|
||||
slow_press(output, &button).await;
|
||||
slow_press(output, &mut shift_held, &button).await;
|
||||
} else {
|
||||
debug!("adding key to queue");
|
||||
// if events in queue, also add to queue
|
||||
@ -222,16 +273,16 @@ pub async fn keypress_handler(
|
||||
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;
|
||||
slow_press(output, &mut shift_held, &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;
|
||||
slow_press(output, &mut shift_held, &Button::Key(k)).await;
|
||||
slow_release(output, &mut shift_held, &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;
|
||||
slow_release(output, &mut shift_held, &button).await;
|
||||
};
|
||||
}
|
||||
Button::Mod(..)
|
||||
@ -247,11 +298,11 @@ pub async fn keypress_handler(
|
||||
for _ in 0..=position_in_queue {
|
||||
let prev_event = queue.pop_front().unwrap();
|
||||
debug!("pressing {prev_event:?}");
|
||||
slow_press(output, &prev_event.button).await;
|
||||
slow_press(output, &mut shift_held, &prev_event.button).await;
|
||||
}
|
||||
}
|
||||
debug!("releasing key {button:?}");
|
||||
slow_release(output, &button).await;
|
||||
slow_release(output, &mut shift_held, &button).await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@ -115,19 +115,19 @@ pub enum Key {
|
||||
PageUp = 0x4B,
|
||||
/// Keyboard Delete Forward
|
||||
Delete = 0x4C,
|
||||
/// KeyboardEnd7
|
||||
/// Keyboard End
|
||||
End = 0x4D,
|
||||
/// Keyboard PageDown7
|
||||
PageDown7 = 0x4E,
|
||||
/// Keyboard RightArrow7
|
||||
RightArrow7 = 0x4F,
|
||||
/// Keyboard LeftArrow7
|
||||
LeftArrow7 = 0x50,
|
||||
/// Keyboard DownArrow7
|
||||
DownArrow7 = 0x51,
|
||||
/// Keyboard UpArrow7
|
||||
UpArrow7 = 0x52,
|
||||
/// Keypad Num Lock and Clear6
|
||||
/// Keyboard PageDown
|
||||
PageDown = 0x4E,
|
||||
/// Keyboard RightArrow
|
||||
RightArrow = 0x4F,
|
||||
/// Keyboard LeftArrow
|
||||
LeftArrow = 0x50,
|
||||
/// Keyboard DownArrow
|
||||
DownArrow = 0x51,
|
||||
/// Keyboard UpArrow
|
||||
UpArrow = 0x52,
|
||||
/// Keypad Num Lock and Clear
|
||||
KeypadNumLock = 0x53,
|
||||
/// Keypad /
|
||||
KeypadSlash = 0x54,
|
||||
|
||||
@ -31,7 +31,6 @@ pub mod keys;
|
||||
pub mod layer;
|
||||
pub mod layout;
|
||||
pub mod logger;
|
||||
pub mod neopixel;
|
||||
pub mod rgb;
|
||||
pub mod rtt;
|
||||
pub mod serial_proto;
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
.program ws2812
|
||||
.side_set 1
|
||||
.wrap_target
|
||||
bitloop:
|
||||
out x 1 side 0 [6]; Drive low. Side-set still takes place before instruction stalls.
|
||||
jmp !x do_zero side 1 [3]; Branch on the bit we shifted out previous delay. Drive high.
|
||||
do_one:
|
||||
jmp bitloop side 1 [4]; Continue driving high, for a one (long pulse)
|
||||
do_zero:
|
||||
nop side 0 [4]; Or drive low, for a zero (short pulse)
|
||||
.wrap
|
||||
@ -1,122 +0,0 @@
|
||||
/*
|
||||
use embassy_rp::{
|
||||
gpio::{AnyPin, Drive, SlewRate},
|
||||
peripherals::{DMA_CH0, PIO0},
|
||||
pio::{FifoJoin, PioPeripheral, PioStateMachine, ShiftDirection},
|
||||
pio_instr_util,
|
||||
relocate::RelocatedProgram,
|
||||
PeripheralRef,
|
||||
};
|
||||
use embassy_time::{Duration, Timer};
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn test(pio: PIO0, pin: AnyPin, dma: DMA_CH0) {
|
||||
let (_common, mut sm, ..) = pio.split();
|
||||
let mut dma = PeripheralRef::new(dma);
|
||||
|
||||
let pio_program = pio_proc::pio_file!("src/neopixel.pio");
|
||||
|
||||
let relocated = RelocatedProgram::new(&pio_program.program);
|
||||
sm.write_instr(relocated.origin() as usize, relocated.code());
|
||||
pio_instr_util::exec_jmp(&mut sm, relocated.origin());
|
||||
|
||||
let pin = sm.make_pio_pin(pin);
|
||||
sm.set_set_pins(&[&pin]);
|
||||
sm.set_sideset_base_pin(&pin);
|
||||
sm.set_sideset_count(1);
|
||||
|
||||
// Clock config
|
||||
// TODO CLOCK_FREQ should come from embassy_rp
|
||||
const CLOCK_FREQ: u32 = 125_000_000;
|
||||
const WS2812_FREQ: u32 = 800_000;
|
||||
const CYCLES_PER_BIT: u32 = 16;
|
||||
|
||||
let bit_freq = WS2812_FREQ * CYCLES_PER_BIT;
|
||||
let mut int = CLOCK_FREQ / bit_freq;
|
||||
let rem = CLOCK_FREQ - (int * bit_freq);
|
||||
let frac = (rem * 256) / bit_freq;
|
||||
// 65536.0 is represented as 0 in the pio's clock divider
|
||||
if int == 65536 {
|
||||
int = 0;
|
||||
}
|
||||
sm.set_clkdiv((int << 8) | frac);
|
||||
let pio::Wrap { source, target } = relocated.wrap();
|
||||
sm.set_wrap(source, target);
|
||||
|
||||
sm.set_autopull(true);
|
||||
sm.set_fifo_join(FifoJoin::TxOnly);
|
||||
sm.set_pull_threshold(8); // 24?
|
||||
sm.set_out_shift_dir(ShiftDirection::Left);
|
||||
|
||||
sm.set_enable(true);
|
||||
|
||||
log::info!("wrap: {:?}", sm.get_wrap());
|
||||
log::info!("addr: {:?}", sm.get_addr());
|
||||
log::info!("sideset_base: {:?}", sm.get_sideset_base());
|
||||
log::info!("sideset_count: {:?}", sm.get_sideset_count());
|
||||
log::info!("in_base: {:?}", sm.get_in_base());
|
||||
log::info!("jmp_pin: {:?}", sm.get_jmp_pin());
|
||||
log::info!("set_range: {:?}", sm.get_set_range());
|
||||
log::info!("out_range: {:?}", sm.get_out_range());
|
||||
log::info!("pull_threshold: {:?}", sm.get_pull_threshold());
|
||||
log::info!("push_threshold: {:?}", sm.get_push_threshold());
|
||||
|
||||
//sm = rp2pio.StateMachine(
|
||||
// assembled,
|
||||
// frequency=12_800_000, # to get appropriate sub-bit times in PIO program
|
||||
// first_sideset_pin=NEOPIXEL,
|
||||
// auto_pull=True,
|
||||
// out_shift_right=False,
|
||||
// pull_threshold=8,
|
||||
//)
|
||||
|
||||
loop {
|
||||
log::info!("sending dma");
|
||||
|
||||
sm.dma_push(dma.reborrow(), &[0x0a, 0x00, 0x00]).await;
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
sm.dma_push(dma.reborrow(), &[0x00, 0x0a, 0x00]).await;
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
sm.dma_push(dma.reborrow(), &[0x00, 0x00, 0x0a]).await;
|
||||
Timer::after(Duration::from_millis(500)).await;
|
||||
|
||||
//sm0.set_enable(true);
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
pub async fn test_blink(pio: PIO0, pin: AnyPin) {
|
||||
log::info!("test blink hehe");
|
||||
let (_, mut sm, ..) = pio.split();
|
||||
// Setup sm2
|
||||
|
||||
// blink
|
||||
let prg = pio_proc::pio_file!("src/blink.pio");
|
||||
|
||||
let relocated = RelocatedProgram::new(&prg.program);
|
||||
let out_pin = sm.make_pio_pin(pin);
|
||||
let pio_pins = [&out_pin];
|
||||
sm.set_set_pins(&pio_pins);
|
||||
sm.set_set_range(25, 1);
|
||||
|
||||
sm.write_instr(relocated.origin() as usize, relocated.code());
|
||||
pio_instr_util::exec_jmp(&mut sm, relocated.origin());
|
||||
// sm.set_clkdiv((65535 << 8) + 255 as u32);
|
||||
// sm.set_clkdiv(0);
|
||||
|
||||
let pio::Wrap { source, target } = relocated.wrap();
|
||||
sm.set_wrap(source, target);
|
||||
|
||||
// sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32);
|
||||
sm.set_enable(true);
|
||||
// sm.wait_push().await as i32;
|
||||
// sm.push_tx(1);
|
||||
sm.wait_push(125_000_000).await;
|
||||
log::info!("started");
|
||||
|
||||
loop {
|
||||
sm.wait_irq(3).await;
|
||||
log::info!("did it!");
|
||||
}
|
||||
}
|
||||
*/
|
||||
@ -1,8 +0,0 @@
|
||||
.program ws2812
|
||||
.origin 0
|
||||
.wrap_target
|
||||
out x 1
|
||||
set pins,1 [1]
|
||||
mov pins,x [1]
|
||||
set pins,0
|
||||
.wrap
|
||||
@ -116,9 +116,14 @@ impl RequestHandler for Handler {
|
||||
}
|
||||
type HidStream = HidReaderWriter<'static, Driver<'static, USB>, 256, 256>;
|
||||
|
||||
/// Capacity of the [button::Event] buffer in [event_listener_task]. A too small capacity may cause
|
||||
/// events to be dropped.
|
||||
pub const BUTTON_EVENT_BUF_LEN: usize = 32;
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn event_listener_task(mut events: KbEvents, ctx: &'static Context) -> ! {
|
||||
let button_events = PubSubChannel::<NoopRawMutex, button::Event, 10, 1, 1>::new();
|
||||
let button_events =
|
||||
PubSubChannel::<NoopRawMutex, button::Event, BUTTON_EVENT_BUF_LEN, 1, 1>::new();
|
||||
let mut button_pub = button_events.publisher().unwrap();
|
||||
let mut button_sub = button_events.subscriber().unwrap();
|
||||
|
||||
@ -128,6 +133,7 @@ async fn event_listener_task(mut events: KbEvents, ctx: &'static Context) -> ! {
|
||||
)
|
||||
.await;
|
||||
|
||||
// match hack because rust isn't smart enough.
|
||||
match r {
|
||||
Either::First(never) | Either::Second(never) => match never {},
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
use core::{
|
||||
cell::UnsafeCell,
|
||||
fmt::{self, Debug, Display, Write},
|
||||
iter,
|
||||
mem::MaybeUninit,
|
||||
ops::Deref,
|
||||
};
|
||||
@ -169,11 +168,12 @@ pub fn display_len(t: &impl Display) -> usize {
|
||||
pub struct DisplayPack<T>(pub T);
|
||||
impl<T: Display> MsgPack for DisplayPack<T> {
|
||||
fn pack(&self) -> impl Iterator<Item = msgpck::Piece<'_>> {
|
||||
let len = display_len(&self.0) as u32;
|
||||
"TODO".pack()
|
||||
//let len = display_len(&self.0) as u32;
|
||||
|
||||
[msgpck::Marker::Str32.into(), len.into()]
|
||||
.into_iter()
|
||||
.chain(iter::from_fn(move || None)) // TODO
|
||||
//[msgpck::Marker::Str32.into(), len.into()]
|
||||
// .into_iter()
|
||||
// .chain(iter::from_fn(move || None)) // TODO
|
||||
}
|
||||
|
||||
fn pack_with_writer(&self, w: &mut dyn msgpck::Write) -> Result<usize, PackErr> {
|
||||
|
||||
@ -9,7 +9,7 @@ edition = "2021"
|
||||
path = "../lib"
|
||||
package = "tangentbord1-lib"
|
||||
|
||||
[dependencies]
|
||||
[target.thumbv6m-none-eabi.dependencies]
|
||||
cortex-m-rt = "0.7"
|
||||
embassy-rp = { version = "0.1.0", features = ["time-driver", "critical-section-impl"] }
|
||||
embassy-executor = { version = "0.5.0", features = ["nightly", "executor-thread", "integrated-timers", "arch-cortex-m"] }
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
Key(D0),
|
||||
|
||||
// Row 2
|
||||
Compose2(Lower, Apostrophe, Variable, A), // ä
|
||||
Compose2(Upper, Apostrophe, Variable, A), // ä
|
||||
ModTap(D4, RCtrl),
|
||||
ModTap(D5, RShift),
|
||||
ModTap(D6, RMod),
|
||||
@ -47,7 +47,7 @@
|
||||
Mod(RAlt),
|
||||
|
||||
// Row 3
|
||||
Compose2(Lower, Apostrophe, Variable, O), // ö
|
||||
Compose2(Upper, Apostrophe, Variable, O), // ö
|
||||
Key(D1),
|
||||
Key(D2),
|
||||
Key(D3),
|
||||
@ -62,24 +62,24 @@
|
||||
Layer(
|
||||
buttons: [
|
||||
// Row 1
|
||||
Key(F1),
|
||||
Key(F4),
|
||||
Key(F7),
|
||||
Key(F10),
|
||||
None,
|
||||
Key(Mute),
|
||||
Key(VolumeDown),
|
||||
Key(VolumeUp),
|
||||
Layer(Move, Down, 1),
|
||||
|
||||
// Row 2
|
||||
Key(F2),
|
||||
ModTap(F5, RCtrl),
|
||||
ModTap(F8, RShift),
|
||||
ModTap(F11, RMod),
|
||||
Mod(RAlt),
|
||||
Key(LeftArrow),
|
||||
Key(DownArrow),
|
||||
Key(UpArrow),
|
||||
Key(RightArrow),
|
||||
None,
|
||||
|
||||
// Row 3
|
||||
Key(F3),
|
||||
Key(F6),
|
||||
Key(F9),
|
||||
Key(F12),
|
||||
Key(Home),
|
||||
Key(PageDown),
|
||||
Key(PageUp),
|
||||
Key(End),
|
||||
None,
|
||||
|
||||
// Thumbpad
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
//! Firmware for Tangentbord1, right half.
|
||||
|
||||
#![no_std]
|
||||
// NOTE: the order of attributes matters here..
|
||||
#![no_main]
|
||||
#![cfg(target_os = "none")] // only try to compile this for embedded
|
||||
#![no_std]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
extern crate alloc;
|
||||
@ -43,8 +45,6 @@ async fn main(_spawner: Spawner) {
|
||||
|
||||
neopixel.write(&[Rgb::new(0xFF, 0x00, 0x00)]).await;
|
||||
|
||||
//Timer::after(Duration::from_millis(3000)).await;
|
||||
|
||||
let layers = include_bytes!("layers.pc");
|
||||
let Ok(layers): Result<Vec<Vec<Layer>>, _> = postcard::from_bytes(layers) else {
|
||||
log::error!("Failed to deserialize layer config");
|
||||
@ -54,7 +54,6 @@ async fn main(_spawner: Spawner) {
|
||||
let keyboard = KeyboardConfig {
|
||||
half,
|
||||
pins: [
|
||||
// TODO: reconfigure these for right PCB
|
||||
// row 1
|
||||
board.d12.degrade(),
|
||||
board.d11.degrade(),
|
||||
|
||||
26
run
26
run
@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env nu
|
||||
|
||||
# Build, flash, and run the firmware.
|
||||
#
|
||||
# Will also attach to serial logs
|
||||
def main [
|
||||
bin: string # Which firmware to run, 'left' or 'right'.
|
||||
--serial (-s): string # The serial device to get logs from.
|
||||
--probe (-p): bool # Flash & run using probe-run instead of elf2uf2
|
||||
] {
|
||||
if $probe == true {
|
||||
cargo build --bin $bin
|
||||
probe-run --chip RP2040 --speed 4000 ("./target/thumbv6m-none-eabi/debug/" + $bin)
|
||||
} else {
|
||||
cargo run --bin $bin
|
||||
}
|
||||
|
||||
let log_script = ($env.FILE_PWD | path join "serial-logs")
|
||||
|
||||
if $serial == null {
|
||||
nu $log_script
|
||||
} else {
|
||||
nu $log_script --serial $serial
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user