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"
|
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"] }
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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));
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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>;
|
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 {},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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> {
|
||||||
|
|||||||
@ -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"] }
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
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