Compare commits

8 Commits

Author SHA1 Message Date
34e6e50c61 Remove old broken run script 2024-07-14 17:42:39 +02:00
04949c4abd Make cargo check work in the top directory 2024-07-14 17:41:17 +02:00
f31e20c7ce Add a README 2024-07-14 17:40:19 +02:00
8a5fe69b5c Add 3d-files for case 2024-07-07 22:01:03 +02:00
e730fb9ec7 Remap layer 2,0 slightly.
Add arrow keys and move F-keys
2024-07-03 14:09:44 +02:00
a066b3c5dc Get Compose keys working with proper shifting 2024-06-23 14:43:19 +02:00
5a38ef7f78 Remove deprecated neopixel files 2024-05-29 23:16:28 +02:00
ae599f587a Remove old todos 2024-05-29 23:14:35 +02:00
27 changed files with 194 additions and 274 deletions

34
README.md Normal file
View 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

Binary file not shown.

BIN
case/frontplate.stl Normal file

Binary file not shown.

BIN
case/keycap-filled.stl Normal file

Binary file not shown.

BIN
case/keycap.stl Normal file

Binary file not shown.

BIN
case/leg-long.stl Normal file

Binary file not shown.

BIN
case/leg.stl Normal file

Binary file not shown.

BIN
case/thumb-backplate.stl Normal file

Binary file not shown.

BIN
case/thumb-frontplate.stl Normal file

Binary file not shown.

View File

@ -1,3 +0,0 @@
# tgnt
Keyboard configuration library and editor.

View File

@ -9,7 +9,7 @@ edition = "2021"
path = "../lib" path = "../lib"
package = "tangentbord1-lib" package = "tangentbord1-lib"
[dependencies] [target.thumbv6m-none-eabi.dependencies]
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
embassy-rp = { version = "0.1.0", features = ["time-driver", "critical-section-impl"] } 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"] } embassy-executor = { version = "0.5.0", features = ["nightly", "executor-thread", "integrated-timers", "arch-cortex-m"] }

View File

@ -61,24 +61,24 @@
Layer( Layer(
buttons: [ buttons: [
// Row 1 // Row 1
Key(Escape), Key(F1),
Key(Mute), Key(F4),
Key(VolumeDown), Key(F7),
Key(VolumeUp), Key(F10),
None, None,
// Row 2 // Row 2
Mod(LAlt), Key(F2),
Mod(LMod), Key(F5),
Mod(LShift), Key(F8),
Mod(LCtrl), Key(F11),
None, None,
// Row 3 // Row 3
None, Key(F3),
None, Key(F6),
None, Key(F9),
None, Key(F12),
None, None,
// Thumbpad // Thumbpad

View File

@ -1,9 +1,10 @@
//! Firmware for Tangentbord1, left half. //! Firmware for Tangentbord1, left half.
#![no_std] // NOTE: the order of attributes matters here..
#![no_main] #![no_main]
#![cfg(target_os = "none")] // only try to compile this for embedded
#![no_std]
#![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;

View File

@ -40,14 +40,14 @@ pub enum Button {
Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, MsgPack, MsgUnpack, Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, MsgPack, MsgUnpack,
)] )]
pub enum CompShift { pub enum CompShift {
/// Do not shift the key. /// Do not shift the key. [Key::A] becomes 'a'.
#[default] #[default]
Lower, Lower,
/// Shift the key. /// Shift the key. [Key::A] becomes 'A'.
Upper, 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, Variable,
} }

View File

@ -137,7 +137,7 @@ async fn play_shader(state: &'static State, shader: &impl Shader) {
(4, 4), (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)); let switch_coords = SWITCH_COORDS.map(|(x, y)| (f32::from(x) / 4.0, f32::from(y) / 4.0));

View File

@ -1,7 +1,7 @@
use core::future::pending; use core::future::pending;
use crate::{ use crate::{
button::Button, button::{Button, CompShift, Modifier},
event::{button, switch, Half}, event::{button, switch, Half},
keys::Key, keys::Key,
}; };
@ -28,6 +28,7 @@ pub async fn keypress_handler(
output: &mut Pub<'_, impl PubSubBehavior<button::Event>, button::Event>, output: &mut Pub<'_, impl PubSubBehavior<button::Event>, button::Event>,
) -> ! { ) -> ! {
type SwitchIndex = usize; type SwitchIndex = usize;
#[derive(Debug)] #[derive(Debug)]
struct PressEvent { struct PressEvent {
source_button: SwitchIndex, source_button: SwitchIndex,
@ -36,51 +37,88 @@ pub async fn keypress_handler(
time: Instant, 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( async fn slow_press(
output: &mut Pub<'_, impl PubSubBehavior<button::Event>, button::Event>, output: &mut Pub<'_, impl PubSubBehavior<button::Event>, button::Event>,
shift_held: &mut ShiftHeld,
button: &Button, button: &Button,
) { ) {
let event = match button { let do_compose = |keys: &[(CompShift, Key)]| {
&Button::Mod(m) | &Button::ModTap(_, m) => button::Event::PressMod(m), for &(comp_shift, key) in keys {
&Button::Key(k) => button::Event::PressKey(k), match comp_shift {
&Button::Compose2(csa, a, csb, b) => { // Release any pressed shift keys
let events = [ CompShift::Lower => {
button::Event::PressKey(COMPOSE_KEY), if shift_held.left {
button::Event::ReleaseKey(COMPOSE_KEY), output.publish_immediate(button::Event::ReleaseMod(Modifier::LShift));
button::Event::Wait, }
button::Event::PressKey(a), if shift_held.right {
button::Event::ReleaseKey(a), output.publish_immediate(button::Event::ReleaseMod(Modifier::RShift));
button::Event::Wait, }
button::Event::PressKey(b), }
button::Event::ReleaseKey(b),
button::Event::Wait,
];
for event in events { // Press shift if not already pressed
output.publish_immediate(event); 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; return;
} }
&Button::Compose3(csa, a, csb, b, csc, c) => { &Button::Compose3(csa, a, csb, b, csc, c) => {
let events = [ do_compose(&[
button::Event::PressKey(COMPOSE_KEY), (CompShift::Variable, COMPOSE_KEY),
button::Event::ReleaseKey(COMPOSE_KEY), (csa, a),
button::Event::Wait, (csb, b),
button::Event::PressKey(a), (csc, c),
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);
}
return; return;
} }
_ => return, _ => return,
@ -90,10 +128,19 @@ pub async fn keypress_handler(
async fn slow_release( async fn slow_release(
output: &mut Pub<'_, impl PubSubBehavior<button::Event>, button::Event>, output: &mut Pub<'_, impl PubSubBehavior<button::Event>, button::Event>,
shift_held: &mut ShiftHeld,
button: &Button, button: &Button,
) { ) {
let event = match 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), &Button::Key(k) => button::Event::ReleaseKey(k),
_ => return, _ => return,
}; };
@ -104,6 +151,10 @@ pub async fn keypress_handler(
let mut queue = Deque::<PressEvent, { 2 * SWITCH_COUNT }>::new(); let mut queue = Deque::<PressEvent, { 2 * SWITCH_COUNT }>::new();
let queue = &mut queue; 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 { loop {
// create a future that waits for the next ModTap to time out // create a future that waits for the next ModTap to time out
let modtap_timeout = async { let modtap_timeout = async {
@ -134,7 +185,7 @@ pub async fn keypress_handler(
continue; continue;
}; };
debug!("modtap resolved as mod: {:?}", event.button); debug!("modtap resolved as mod: {:?}", event.button);
slow_press(output, &event.button).await; slow_press(output, &mut shift_held, &event.button).await;
loop { loop {
let Some(event) = queue.pop_front() else { break }; 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"); queue.push_front(event).expect("we just popped, the queue can't be full");
break; break;
} }
slow_press(output, &event.button).await; slow_press(output, &mut shift_held, &event.button).await;
} }
continue; continue;
@ -194,7 +245,7 @@ pub async fn keypress_handler(
if queue.is_empty() { if queue.is_empty() {
debug!("sending key now"); debug!("sending key now");
// otherwise, send immediately // otherwise, send immediately
slow_press(output, &button).await; slow_press(output, &mut shift_held, &button).await;
} else { } else {
debug!("adding key to queue"); debug!("adding key to queue");
// if events in queue, also add 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 { for _ in 0..position_in_queue {
let prev_event = queue.pop_front().unwrap(); let prev_event = queue.pop_front().unwrap();
debug!("pressing earlier event {:?}", prev_event); 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(); let _ = queue.pop_front();
debug!("pressing modtap as key"); debug!("pressing modtap as key");
slow_press(output, &Button::Key(k)).await; slow_press(output, &mut shift_held, &Button::Key(k)).await;
slow_release(output, &Button::Key(k)).await; slow_release(output, &mut shift_held, &Button::Key(k)).await;
} else { } else {
// If the ModTap wasn't in the queue, it has already been resolved as a Mod. // If the ModTap wasn't in the queue, it has already been resolved as a Mod.
debug!("modtap wasn't in queue, releasing"); debug!("modtap wasn't in queue, releasing");
slow_release(output, &button).await; slow_release(output, &mut shift_held, &button).await;
}; };
} }
Button::Mod(..) Button::Mod(..)
@ -247,11 +298,11 @@ pub async fn keypress_handler(
for _ in 0..=position_in_queue { for _ in 0..=position_in_queue {
let prev_event = queue.pop_front().unwrap(); let prev_event = queue.pop_front().unwrap();
debug!("pressing {prev_event:?}"); 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:?}"); debug!("releasing key {button:?}");
slow_release(output, &button).await; slow_release(output, &mut shift_held, &button).await;
} }
_ => {} _ => {}
} }

View File

@ -115,19 +115,19 @@ pub enum Key {
PageUp = 0x4B, PageUp = 0x4B,
/// Keyboard Delete Forward /// Keyboard Delete Forward
Delete = 0x4C, Delete = 0x4C,
/// KeyboardEnd7 /// Keyboard End
End = 0x4D, End = 0x4D,
/// Keyboard PageDown7 /// Keyboard PageDown
PageDown7 = 0x4E, PageDown = 0x4E,
/// Keyboard RightArrow7 /// Keyboard RightArrow
RightArrow7 = 0x4F, RightArrow = 0x4F,
/// Keyboard LeftArrow7 /// Keyboard LeftArrow
LeftArrow7 = 0x50, LeftArrow = 0x50,
/// Keyboard DownArrow7 /// Keyboard DownArrow
DownArrow7 = 0x51, DownArrow = 0x51,
/// Keyboard UpArrow7 /// Keyboard UpArrow
UpArrow7 = 0x52, UpArrow = 0x52,
/// Keypad Num Lock and Clear6 /// Keypad Num Lock and Clear
KeypadNumLock = 0x53, KeypadNumLock = 0x53,
/// Keypad / /// Keypad /
KeypadSlash = 0x54, KeypadSlash = 0x54,

View File

@ -31,7 +31,6 @@ pub mod keys;
pub mod layer; pub mod layer;
pub mod layout; pub mod layout;
pub mod logger; pub mod logger;
pub mod neopixel;
pub mod rgb; pub mod rgb;
pub mod rtt; pub mod rtt;
pub mod serial_proto; pub mod serial_proto;

View File

@ -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

View File

@ -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!");
}
}
*/

View File

@ -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

View File

@ -116,9 +116,14 @@ impl RequestHandler for Handler {
} }
type HidStream = HidReaderWriter<'static, Driver<'static, USB>, 256, 256>; 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] #[embassy_executor::task]
async fn event_listener_task(mut events: KbEvents, ctx: &'static Context) -> ! { 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_pub = button_events.publisher().unwrap();
let mut button_sub = button_events.subscriber().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; .await;
// match hack because rust isn't smart enough.
match r { match r {
Either::First(never) | Either::Second(never) => match never {}, Either::First(never) | Either::Second(never) => match never {},
} }

View File

@ -1,7 +1,6 @@
use core::{ use core::{
cell::UnsafeCell, cell::UnsafeCell,
fmt::{self, Debug, Display, Write}, fmt::{self, Debug, Display, Write},
iter,
mem::MaybeUninit, mem::MaybeUninit,
ops::Deref, ops::Deref,
}; };
@ -169,11 +168,12 @@ pub fn display_len(t: &impl Display) -> usize {
pub struct DisplayPack<T>(pub T); pub struct DisplayPack<T>(pub T);
impl<T: Display> MsgPack for DisplayPack<T> { impl<T: Display> MsgPack for DisplayPack<T> {
fn pack(&self) -> impl Iterator<Item = msgpck::Piece<'_>> { 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()] //[msgpck::Marker::Str32.into(), len.into()]
.into_iter() // .into_iter()
.chain(iter::from_fn(move || None)) // TODO // .chain(iter::from_fn(move || None)) // TODO
} }
fn pack_with_writer(&self, w: &mut dyn msgpck::Write) -> Result<usize, PackErr> { fn pack_with_writer(&self, w: &mut dyn msgpck::Write) -> Result<usize, PackErr> {

View File

@ -9,7 +9,7 @@ edition = "2021"
path = "../lib" path = "../lib"
package = "tangentbord1-lib" package = "tangentbord1-lib"
[dependencies] [target.thumbv6m-none-eabi.dependencies]
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
embassy-rp = { version = "0.1.0", features = ["time-driver", "critical-section-impl"] } 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"] } embassy-executor = { version = "0.5.0", features = ["nightly", "executor-thread", "integrated-timers", "arch-cortex-m"] }

View File

@ -39,7 +39,7 @@
Key(D0), Key(D0),
// Row 2 // Row 2
Compose2(Lower, Apostrophe, Variable, A), // ä Compose2(Upper, Apostrophe, Variable, A), // ä
ModTap(D4, RCtrl), ModTap(D4, RCtrl),
ModTap(D5, RShift), ModTap(D5, RShift),
ModTap(D6, RMod), ModTap(D6, RMod),
@ -47,7 +47,7 @@
Mod(RAlt), Mod(RAlt),
// Row 3 // Row 3
Compose2(Lower, Apostrophe, Variable, O), // ö Compose2(Upper, Apostrophe, Variable, O), // ö
Key(D1), Key(D1),
Key(D2), Key(D2),
Key(D3), Key(D3),
@ -62,24 +62,24 @@
Layer( Layer(
buttons: [ buttons: [
// Row 1 // Row 1
Key(F1), None,
Key(F4), Key(Mute),
Key(F7), Key(VolumeDown),
Key(F10), Key(VolumeUp),
Layer(Move, Down, 1), Layer(Move, Down, 1),
// Row 2 // Row 2
Key(F2), Key(LeftArrow),
ModTap(F5, RCtrl), Key(DownArrow),
ModTap(F8, RShift), Key(UpArrow),
ModTap(F11, RMod), Key(RightArrow),
Mod(RAlt), None,
// Row 3 // Row 3
Key(F3), Key(Home),
Key(F6), Key(PageDown),
Key(F9), Key(PageUp),
Key(F12), Key(End),
None, None,
// Thumbpad // Thumbpad

View File

@ -1,7 +1,9 @@
//! Firmware for Tangentbord1, right half. //! Firmware for Tangentbord1, right half.
#![no_std] // NOTE: the order of attributes matters here..
#![no_main] #![no_main]
#![cfg(target_os = "none")] // only try to compile this for embedded
#![no_std]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
extern crate alloc; extern crate alloc;
@ -43,8 +45,6 @@ async fn main(_spawner: Spawner) {
neopixel.write(&[Rgb::new(0xFF, 0x00, 0x00)]).await; neopixel.write(&[Rgb::new(0xFF, 0x00, 0x00)]).await;
//Timer::after(Duration::from_millis(3000)).await;
let layers = include_bytes!("layers.pc"); let layers = include_bytes!("layers.pc");
let Ok(layers): Result<Vec<Vec<Layer>>, _> = postcard::from_bytes(layers) else { let Ok(layers): Result<Vec<Vec<Layer>>, _> = postcard::from_bytes(layers) else {
log::error!("Failed to deserialize layer config"); log::error!("Failed to deserialize layer config");
@ -54,7 +54,6 @@ async fn main(_spawner: Spawner) {
let keyboard = KeyboardConfig { let keyboard = KeyboardConfig {
half, half,
pins: [ pins: [
// TODO: reconfigure these for right PCB
// row 1 // row 1
board.d12.degrade(), board.d12.degrade(),
board.d11.degrade(), board.d11.degrade(),

26
run
View File

@ -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
}
}