Compare commits
44 Commits
pcb-left-v
...
04949c4abd
| Author | SHA1 | Date | |
|---|---|---|---|
|
04949c4abd
|
|||
|
f31e20c7ce
|
|||
|
8a5fe69b5c
|
|||
|
e730fb9ec7
|
|||
|
a066b3c5dc
|
|||
|
5a38ef7f78
|
|||
|
ae599f587a
|
|||
|
58b4bc4004
|
|||
|
4a528eb4b7
|
|||
|
84f8222b30
|
|||
|
9892ae1d70
|
|||
|
4f8ed8c10f
|
|||
|
800874c653
|
|||
|
e185e5524b
|
|||
|
abb4df15f0
|
|||
|
7f6971b329
|
|||
|
00b3b51b36
|
|||
|
7d9cfe9557
|
|||
|
fb57b0bdc6
|
|||
|
607e5b02ef
|
|||
|
8066837cc9
|
|||
|
f265898dda
|
|||
|
e68d25afa8
|
|||
|
dcf950cc66
|
|||
|
2c239b6248
|
|||
|
9854e0c0a8
|
|||
|
59f91e2f6b
|
|||
|
fa3b1502d7
|
|||
|
5c68b483af
|
|||
|
481066a343
|
|||
|
8a14c13bf1
|
|||
|
c5c27c487e
|
|||
|
78db14f88d
|
|||
|
251fe2d79b
|
|||
|
2714d549b0
|
|||
|
c39893a067
|
|||
|
2b66c022f7
|
|||
|
4f59a59693
|
|||
|
2fc050eb4f
|
|||
|
05951e1b77
|
|||
|
e544e86cfa
|
|||
|
dd613adf49
|
|||
|
3c0e7d48d9
|
|||
|
aa201036c6
|
@ -1,35 +0,0 @@
|
|||||||
#
|
|
||||||
# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository.
|
|
||||||
#
|
|
||||||
# Copyright (c) The RP-RS Developers, 2021
|
|
||||||
#
|
|
||||||
# You might want to make a similar file in your own repository if you are
|
|
||||||
# writing programs for Raspberry Silicon microcontrollers.
|
|
||||||
#
|
|
||||||
# This file is MIT or Apache-2.0 as per the repository README.md file
|
|
||||||
#
|
|
||||||
|
|
||||||
[build]
|
|
||||||
# Set the default target to match the Cortex-M0+ in the RP2040
|
|
||||||
target = "thumbv6m-none-eabi"
|
|
||||||
|
|
||||||
# Target specific options
|
|
||||||
[target.thumbv6m-none-eabi]
|
|
||||||
# Pass some extra options to rustc, some of which get passed on to the linker.
|
|
||||||
#
|
|
||||||
# * inline-threshold=5 makes the compiler more aggressive and inlining functions
|
|
||||||
# * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't
|
|
||||||
# have SIMD)
|
|
||||||
rustflags = [
|
|
||||||
"-C", "linker=flip-link",
|
|
||||||
"-C", "inline-threshold=5",
|
|
||||||
"-C", "no-vectorize-loops",
|
|
||||||
]
|
|
||||||
|
|
||||||
# This runner will make a UF2 file and then copy it to a mounted RP2040 in USB
|
|
||||||
# Bootloader mode:
|
|
||||||
runner = "elf2uf2-rs -d"
|
|
||||||
|
|
||||||
# This runner will find a supported SWD debug probe and flash your RP2040 over
|
|
||||||
# SWD:
|
|
||||||
# runner = "probe-run --chip RP2040"
|
|
||||||
12
.cargo/config.toml
Normal file
12
.cargo/config.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[build]
|
||||||
|
#target = "thumbv6m-none-eabi"
|
||||||
|
|
||||||
|
[target.thumbv6m-none-eabi]
|
||||||
|
rustflags = [
|
||||||
|
"-C", "linker=flip-link",
|
||||||
|
"-C", "inline-threshold=5", # inline functions more aggressively
|
||||||
|
"-C", "no-vectorize-loops",
|
||||||
|
]
|
||||||
|
|
||||||
|
#runner = "elf2uf2-rs -d"
|
||||||
|
runner = "probe-rs run --chip RP2040"
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
/target
|
target
|
||||||
|
*.pc
|
||||||
|
|||||||
4377
Cargo.lock
generated
4377
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
47
Cargo.toml
47
Cargo.toml
@ -1,41 +1,10 @@
|
|||||||
[package]
|
[workspace]
|
||||||
name = "tangentbord1"
|
members = [
|
||||||
version = "0.1.0"
|
"lib",
|
||||||
authors = ["Joakim Hulthe <joakim@hulthe.net>"]
|
"left",
|
||||||
description = "Keyboard firmware"
|
"right",
|
||||||
edition = "2021"
|
"editor",
|
||||||
|
]
|
||||||
|
|
||||||
[dependencies]
|
resolver = "2"
|
||||||
tgnt = { git = "https://git.nubo.sh/hulthe/tgnt.git", default-features = false }
|
|
||||||
cortex-m = "0.7.6"
|
|
||||||
cortex-m-rt = "0.7"
|
|
||||||
embedded-hal = "0.2.5"
|
|
||||||
#panic-halt = "0.2.0"
|
|
||||||
usb-device = "0.2.9"
|
|
||||||
usbd-hid = "0.6.1"
|
|
||||||
static_cell = "1.0.0"
|
|
||||||
embedded-io = { version = "*", features = ["async"] }
|
|
||||||
futures = { version = "0.3", default-features = false }
|
|
||||||
embassy-executor = { git = "https://github.com/embassy-rs/embassy.git", features = ["log", "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"] }
|
|
||||||
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-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", "pio", "critical-section-impl"] }
|
|
||||||
log = "0.4.17"
|
|
||||||
pio = "0.2.1"
|
|
||||||
pio-proc = "0.2.1"
|
|
||||||
smart-leds = "0.3.0"
|
|
||||||
embedded-alloc = "0.5.0"
|
|
||||||
postcard = { version = "1.0.4", features = ["alloc"] }
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
tgnt = { git = "https://git.nubo.sh/hulthe/tgnt.git", default-features = false }
|
|
||||||
ron = "0.8.0"
|
|
||||||
postcard = { version = "1", features = ["use-std"] }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["n-key-rollover"]
|
|
||||||
n-key-rollover=[]
|
|
||||||
|
|||||||
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.
2
editor/.gitignore
vendored
Normal file
2
editor/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/Cargo.lock
|
||||||
22
editor/Cargo.toml
Normal file
22
editor/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "editor"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies.tangentbord1]
|
||||||
|
path = "../lib"
|
||||||
|
package = "tangentbord1-lib"
|
||||||
|
features = ["std"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.5.1", features = ["derive"] }
|
||||||
|
eframe = { version = "0.26.2", features = ["persistence", "ron", "serde"] }
|
||||||
|
egui = { version = "0.26.2", features = ["persistence", "ron", "serde"] }
|
||||||
|
egui_extras = { version = "0.26.2" }
|
||||||
|
msgpck = { version = "0.2.8", features = ["alloc"] }
|
||||||
|
eyre = "0.6.12"
|
||||||
|
log = "0.4.20"
|
||||||
|
pretty_env_logger = "0.5.0"
|
||||||
|
ron = { version = "0.8.0" }
|
||||||
|
serde = { version = "1.0.156", features = ["derive"] }
|
||||||
|
tokio = { version = "1.36.0", features = ["full"] }
|
||||||
25
editor/src/default_layer.ron
Normal file
25
editor/src/default_layer.ron
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
Layer(
|
||||||
|
buttons: [
|
||||||
|
Keycode(0x04),
|
||||||
|
Keycode(0x05),
|
||||||
|
Keycode(0x06),
|
||||||
|
Keycode(0x07),
|
||||||
|
Keycode(0x08),
|
||||||
|
|
||||||
|
Keycode(0x09),
|
||||||
|
Keycode(0x0A),
|
||||||
|
Keycode(0x0B),
|
||||||
|
Keycode(0x0C),
|
||||||
|
Keycode(0x0D),
|
||||||
|
|
||||||
|
Keycode(0x0E),
|
||||||
|
Keycode(0x0F),
|
||||||
|
Keycode(0x10),
|
||||||
|
Keycode(0x11),
|
||||||
|
ModTap( keycode: 0x12, modifier: LMod),
|
||||||
|
|
||||||
|
Modifier(LShift),
|
||||||
|
Modifier(LCtrl),
|
||||||
|
NextLayer,
|
||||||
|
],
|
||||||
|
)
|
||||||
25
editor/src/default_layout.ron
Normal file
25
editor/src/default_layout.ron
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
Layout(
|
||||||
|
buttons: [
|
||||||
|
(x: 0.0, y: 0.4),
|
||||||
|
(x: 1.0, y: 0.2),
|
||||||
|
(x: 2.0, y: 0.0),
|
||||||
|
(x: 3.0, y: 0.2),
|
||||||
|
(x: 4.0, y: 0.4),
|
||||||
|
|
||||||
|
(x: 0.0, y: 1.4),
|
||||||
|
(x: 1.0, y: 1.2),
|
||||||
|
(x: 2.0, y: 1.0),
|
||||||
|
(x: 3.0, y: 1.2),
|
||||||
|
(x: 4.0, y: 1.4),
|
||||||
|
|
||||||
|
(x: 0.0, y: 2.4),
|
||||||
|
(x: 1.0, y: 2.2),
|
||||||
|
(x: 2.0, y: 2.0),
|
||||||
|
(x: 3.0, y: 2.2),
|
||||||
|
(x: 4.0, y: 2.4),
|
||||||
|
|
||||||
|
(x: 3.0, y: 3.2),
|
||||||
|
(x: 4.0, y: 3.4),
|
||||||
|
(x: 5.0, y: 3.6),
|
||||||
|
]
|
||||||
|
)
|
||||||
395
editor/src/main.rs
Normal file
395
editor/src/main.rs
Normal file
@ -0,0 +1,395 @@
|
|||||||
|
mod mat;
|
||||||
|
mod serial;
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
use egui::{Button, Color32, Frame, Rect, ScrollArea, Slider, TextEdit, Vec2};
|
||||||
|
use eyre::eyre;
|
||||||
|
use mat::Mat;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serial::{connect_to_serial, scan_for_serial};
|
||||||
|
use std::{collections::VecDeque, path::PathBuf};
|
||||||
|
use tangentbord1::{
|
||||||
|
layer::Layer,
|
||||||
|
layout::Layout,
|
||||||
|
serial_proto::owned::{ChangeLayer, DeviceMsg, LogRecord},
|
||||||
|
};
|
||||||
|
use tokio::{
|
||||||
|
runtime::Runtime,
|
||||||
|
sync::{
|
||||||
|
mpsc::{self, Receiver},
|
||||||
|
oneshot::{self, error::TryRecvError},
|
||||||
|
},
|
||||||
|
task::spawn_blocking,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
struct Opt {
|
||||||
|
/// Path to device serial port
|
||||||
|
device: Option<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> eyre::Result<()> {
|
||||||
|
let _opt = Opt::parse();
|
||||||
|
|
||||||
|
pretty_env_logger::init();
|
||||||
|
|
||||||
|
let rt = Runtime::new().unwrap();
|
||||||
|
let _enter_rt = rt.enter();
|
||||||
|
|
||||||
|
log::info!("starting application");
|
||||||
|
let native_options = eframe::NativeOptions::default();
|
||||||
|
eframe::run_native(
|
||||||
|
"tgnt keyboard editor",
|
||||||
|
native_options,
|
||||||
|
Box::new(|cc| Box::new(App::new(cc))),
|
||||||
|
)
|
||||||
|
.map_err(|e| eyre!("Failed to run program: {e:?}"))?;
|
||||||
|
|
||||||
|
rt.shutdown_background();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
#[serde(default)] // if we add new fields, give them default values when deserializing old state
|
||||||
|
pub struct App {
|
||||||
|
layout: RonEdit<Layout>,
|
||||||
|
|
||||||
|
layers: Mat<RonEdit<Layer>>,
|
||||||
|
|
||||||
|
u1: f32,
|
||||||
|
margin: f32,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
serial: SerialState,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SerialState {
|
||||||
|
scan_task: Option<oneshot::Receiver<Result<Option<PathBuf>, String>>>,
|
||||||
|
dev: Result<Option<PathBuf>, String>,
|
||||||
|
reader: Option<Receiver<DeviceMsg>>,
|
||||||
|
logs: VecDeque<LogRecord>,
|
||||||
|
active_layer: Option<(u16, u16)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
struct RonEdit<T> {
|
||||||
|
pub t: T,
|
||||||
|
pub ron: String,
|
||||||
|
pub error: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Serialize> RonEdit<T> {
|
||||||
|
pub fn new(t: T) -> Self {
|
||||||
|
RonEdit {
|
||||||
|
ron: ron::ser::to_string_pretty(&t, Default::default()).unwrap(),
|
||||||
|
error: String::new(),
|
||||||
|
t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default + Serialize> Default for RonEdit<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
RonEdit::new(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for App {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
layers: Default::default(),
|
||||||
|
layout: Default::default(),
|
||||||
|
u1: 75.0,
|
||||||
|
margin: 0.05,
|
||||||
|
|
||||||
|
serial: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SerialState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
scan_task: Default::default(),
|
||||||
|
dev: Ok(None),
|
||||||
|
reader: Default::default(),
|
||||||
|
logs: Default::default(),
|
||||||
|
active_layer: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App {
|
||||||
|
/// Called once before the first frame.
|
||||||
|
pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
|
||||||
|
// This is also where you can customize the look and feel of egui using
|
||||||
|
// `cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`.
|
||||||
|
|
||||||
|
// Load previous app state (if any).
|
||||||
|
// Note that you must enable the `persistence` feature for this to work.
|
||||||
|
if let Some(storage) = cc.storage {
|
||||||
|
return eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default();
|
||||||
|
}
|
||||||
|
|
||||||
|
let layer_str = include_str!("default_layer.ron");
|
||||||
|
let layer: Layer = ron::from_str(&layer_str).expect("Failed to deserialize default layer");
|
||||||
|
|
||||||
|
let layout_str = include_str!("default_layout.ron");
|
||||||
|
let layout: Layout =
|
||||||
|
ron::from_str(&layout_str).expect("Failed to deserialize default layout");
|
||||||
|
|
||||||
|
let mut layers = Mat::default();
|
||||||
|
layers.push_row(RonEdit::new(layer));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
layers,
|
||||||
|
layout: RonEdit::new(layout),
|
||||||
|
..Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl eframe::App for App {
|
||||||
|
/// Called by the frame work to save state before shutdown.
|
||||||
|
fn save(&mut self, storage: &mut dyn eframe::Storage) {
|
||||||
|
eframe::set_value(storage, eframe::APP_KEY, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called each time the UI needs repainting, which may be many times per second.
|
||||||
|
/// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
|
||||||
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
|
let Self {
|
||||||
|
margin,
|
||||||
|
u1,
|
||||||
|
layout,
|
||||||
|
layers,
|
||||||
|
serial:
|
||||||
|
SerialState {
|
||||||
|
scan_task: scan_serial_task,
|
||||||
|
dev: serial_devs,
|
||||||
|
reader: serial_reader,
|
||||||
|
logs: serial_logs,
|
||||||
|
active_layer,
|
||||||
|
},
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
while let Some(rx) = scan_serial_task {
|
||||||
|
match rx.try_recv() {
|
||||||
|
Ok(r) => {
|
||||||
|
*serial_devs = r;
|
||||||
|
*scan_serial_task = None;
|
||||||
|
}
|
||||||
|
Err(TryRecvError::Empty) => break,
|
||||||
|
Err(TryRecvError::Closed) => {
|
||||||
|
*scan_serial_task = None;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while let Some(rx) = serial_reader {
|
||||||
|
match rx.try_recv() {
|
||||||
|
Ok(DeviceMsg::Log(record)) => {
|
||||||
|
serial_logs.push_back(record);
|
||||||
|
if serial_logs.len() > 10 {
|
||||||
|
serial_logs.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(DeviceMsg::ChangeLayer(ChangeLayer { x, y })) => {
|
||||||
|
*active_layer = Some((x, y));
|
||||||
|
}
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(mpsc::error::TryRecvError::Empty) => break,
|
||||||
|
Err(mpsc::error::TryRecvError::Disconnected) => {
|
||||||
|
*serial_reader = None;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "wasm32"))] // no File->Quit on web pages!
|
||||||
|
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||||
|
// The top panel is often a good place for a menu bar:
|
||||||
|
egui::menu::bar(ui, |ui| {
|
||||||
|
ui.menu_button("File", |ui| {
|
||||||
|
if ui.button("Quit").clicked() {
|
||||||
|
todo!("implement quit")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
egui::SidePanel::left("side_panel")
|
||||||
|
.resizable(true)
|
||||||
|
.width_range(250.0..=500.0)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ScrollArea::vertical().show(ui, |ui| {
|
||||||
|
ui.heading("Side Panel");
|
||||||
|
|
||||||
|
ui.collapsing("Serial", |ui| {
|
||||||
|
match serial_devs {
|
||||||
|
Ok(Some(dev)) => {
|
||||||
|
if serial_reader.is_none()
|
||||||
|
&& ui.button(format!("Connect to {dev:?}")).clicked()
|
||||||
|
{
|
||||||
|
*serial_reader =
|
||||||
|
Some(connect_to_serial(dev.clone(), ctx.clone()));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
ui.label("No devices found.");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
ui.code_editor(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.button("scan for serial device").clicked()
|
||||||
|
&& scan_serial_task.is_none()
|
||||||
|
{
|
||||||
|
let (tx, rx) = oneshot::channel();
|
||||||
|
let ctx = ctx.clone();
|
||||||
|
spawn_blocking(move || {
|
||||||
|
let r = scan_for_serial().map_err(|e| e.to_string());
|
||||||
|
let _ = tx.send(r);
|
||||||
|
ctx.request_repaint();
|
||||||
|
});
|
||||||
|
|
||||||
|
*scan_serial_task = Some(rx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if scan_serial_task.is_some() {
|
||||||
|
ui.label("Scanning...");
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollArea::both().show(ui, |ui| {
|
||||||
|
for log in &*serial_logs {
|
||||||
|
ui.group(|ui| {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label(&log.level);
|
||||||
|
ui.label(&log.message);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.label("u1");
|
||||||
|
ui.add(Slider::new(u1, 20.0..=150.0));
|
||||||
|
ui.label("margin");
|
||||||
|
ui.add(Slider::new(margin, 0.0..=0.4));
|
||||||
|
|
||||||
|
ui.collapsing("Layout", |ui| {
|
||||||
|
if ui
|
||||||
|
.add(TextEdit::multiline(&mut layout.ron).code_editor())
|
||||||
|
.changed()
|
||||||
|
{
|
||||||
|
layout.error.clear();
|
||||||
|
match ron::from_str(&layout.ron) {
|
||||||
|
Ok(new) => layout.t = new,
|
||||||
|
Err(e) => layout.error = e.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !layout.error.is_empty() {
|
||||||
|
ui.add(
|
||||||
|
TextEdit::multiline(&mut layout.error)
|
||||||
|
.interactive(false)
|
||||||
|
.text_color(Color32::RED),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for x in 0..layers.width() {
|
||||||
|
for y in 0..layers.height() {
|
||||||
|
let layer = layers.get_mut(x, y).unwrap();
|
||||||
|
ui.collapsing(format!("Layer {x},{y}"), |ui| {
|
||||||
|
if ui
|
||||||
|
.add(TextEdit::multiline(&mut layer.ron).code_editor())
|
||||||
|
.changed()
|
||||||
|
{
|
||||||
|
layer.error.clear();
|
||||||
|
match ron::from_str(&layer.ron) {
|
||||||
|
Ok(new) => layer.t = new,
|
||||||
|
Err(e) => layer.error = e.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !layer.error.is_empty() {
|
||||||
|
ui.add(
|
||||||
|
TextEdit::multiline(&mut layer.error)
|
||||||
|
.interactive(false)
|
||||||
|
.text_color(Color32::RED),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.button("Add layer row").clicked() {
|
||||||
|
layers.push_row(RonEdit::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.menu_button("Delete layer row", |ui| {
|
||||||
|
for r in 0..layers.height() {
|
||||||
|
if ui.button(format!("row {r}")).clicked() {
|
||||||
|
layers.remove_row(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if ui.button("Add layer column").clicked() {
|
||||||
|
layers.push_col(RonEdit::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.menu_button("Delete layer column", |ui| {
|
||||||
|
for c in 0..layers.width() {
|
||||||
|
if ui.button(format!("column {c}")).clicked() {
|
||||||
|
layers.remove_col(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
ScrollArea::both().show(ui, |ui| {
|
||||||
|
for (i, layer) in layers.iter_cf().enumerate() {
|
||||||
|
let x = (i / layers.height()) as u16;
|
||||||
|
let y = (i % layers.height()) as u16;
|
||||||
|
|
||||||
|
if Some((x, y)) == *active_layer {
|
||||||
|
ui.visuals_mut().widgets.inactive.bg_stroke =
|
||||||
|
(1.0, Color32::DARK_GREEN).into();
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame::none().show(ui, |ui| {
|
||||||
|
for (i, geometry) in layout.t.buttons.iter().enumerate() {
|
||||||
|
let margin = *margin * *u1;
|
||||||
|
let size = Vec2::new(
|
||||||
|
*u1 * geometry.w - margin * 2.0,
|
||||||
|
*u1 * geometry.h - margin * 2.0,
|
||||||
|
);
|
||||||
|
let offset =
|
||||||
|
Vec2::new(*u1 * geometry.x + margin, *u1 * geometry.y + margin);
|
||||||
|
let rect = Rect::from_min_size(ui.min_rect().min + offset, size);
|
||||||
|
|
||||||
|
let button = if let Some(button) = layer.t.buttons.get(i) {
|
||||||
|
Button::new(button.to_string())
|
||||||
|
} else {
|
||||||
|
Button::new("")
|
||||||
|
};
|
||||||
|
|
||||||
|
ui.put(rect, button);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.visuals_mut().widgets = Default::default();
|
||||||
|
ui.separator();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
98
editor/src/mat.rs
Normal file
98
editor/src/mat.rs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// 2-dimensional dynamically sized matrix
|
||||||
|
#[derive(Default, Deserialize, Serialize)]
|
||||||
|
pub struct Mat<T> {
|
||||||
|
rows: Vec<Vec<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl<T> Mat<T> {
|
||||||
|
/// Iterater over entries, rows first.
|
||||||
|
pub fn iter_rf(&self) -> impl Iterator<Item = &T> {
|
||||||
|
todo!();
|
||||||
|
#[allow(unreachable_code)]
|
||||||
|
[].into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterater over entries, columns first.
|
||||||
|
pub fn iter_cf(&self) -> impl Iterator<Item = &T> {
|
||||||
|
let height = self.height();
|
||||||
|
(0..self.width())
|
||||||
|
.flat_map(move |x| (0..height).map(move |y| (x, y)))
|
||||||
|
.map(|(x, y)| &self.rows[y][x])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self, x: usize, y: usize) -> Option<&mut T> {
|
||||||
|
if x >= self.width() || y >= self.height() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(&mut self.rows[y][x])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the width & height of the matrix
|
||||||
|
pub fn height(&self) -> usize {
|
||||||
|
self.rows.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the width & height of the matrix
|
||||||
|
pub fn width(&self) -> usize {
|
||||||
|
self.rows.get(0).map(|row| row.len()).unwrap_or(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the width & height of the matrix
|
||||||
|
pub fn size(&self) -> (usize, usize) {
|
||||||
|
(self.width(), self.height())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_row(&mut self, t: T)
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
let width = self.width();
|
||||||
|
if width == 0 {
|
||||||
|
self.rows.push(vec![t]);
|
||||||
|
} else {
|
||||||
|
self.rows.push(vec![t; width])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_col(&mut self, t: T)
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
let height = self.height();
|
||||||
|
if height == 0 {
|
||||||
|
self.rows.push(vec![t]);
|
||||||
|
} else {
|
||||||
|
self.rows.iter_mut().for_each(|row| row.push(t.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_row(&mut self, y: usize) {
|
||||||
|
if y >= self.height() {
|
||||||
|
panic!("row {y} out of matrix bounds");
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.height() == 1 {
|
||||||
|
self.rows.clear();
|
||||||
|
} else {
|
||||||
|
self.rows.remove(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_col(&mut self, x: usize) {
|
||||||
|
if x >= self.width() {
|
||||||
|
panic!("col {x} out of matrix bounds");
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.width() == 1 {
|
||||||
|
self.rows.clear();
|
||||||
|
} else {
|
||||||
|
for row in self.rows.iter_mut() {
|
||||||
|
row.remove(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
editor/src/serial.rs
Normal file
5
editor/src/serial.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
mod port;
|
||||||
|
mod scan;
|
||||||
|
|
||||||
|
pub use port::connect_to_serial;
|
||||||
|
pub use scan::scan_for_serial;
|
||||||
144
editor/src/serial/port.rs
Normal file
144
editor/src/serial/port.rs
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
use std::{future::pending, path::PathBuf, time::Duration};
|
||||||
|
|
||||||
|
use egui::Context;
|
||||||
|
use eyre::{bail, Context as EyreContext};
|
||||||
|
use msgpck::{MsgPack, MsgUnpack, UnpackErr};
|
||||||
|
use tangentbord1::serial_proto::owned::{DeviceMsg, HostMsg};
|
||||||
|
use tokio::{
|
||||||
|
fs::File,
|
||||||
|
io::{AsyncReadExt, AsyncWriteExt},
|
||||||
|
process::Command,
|
||||||
|
select,
|
||||||
|
sync::mpsc::{self, Receiver, Sender},
|
||||||
|
time::{sleep, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
|
const MAX_MESSAGE_SIZE: usize = 16 * 1024;
|
||||||
|
const MESSAGE_TIMEOUT: Duration = Duration::from_millis(30);
|
||||||
|
|
||||||
|
pub fn connect_to_serial(dev: PathBuf, ctx: Context) -> Receiver<DeviceMsg> {
|
||||||
|
let (tx, rx) = mpsc::channel(12);
|
||||||
|
tokio::spawn(async {
|
||||||
|
if let Err(e) = read_serial(dev, tx, ctx).await {
|
||||||
|
log::error!("serial read task exited with error: {e:#?}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
rx
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read_serial(dev: PathBuf, tx: Sender<DeviceMsg>, ctx: Context) -> eyre::Result<()> {
|
||||||
|
log::debug!("configuring keyboard serial device");
|
||||||
|
let out = Command::new("stty")
|
||||||
|
.arg("-F")
|
||||||
|
.arg(&dev)
|
||||||
|
.args(["115200", "raw", "-clocal", "-echo"])
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.wrap_err("failed to configure serial device, couldn't execute stty")?;
|
||||||
|
|
||||||
|
if !out.status.success() {
|
||||||
|
bail!("failed to configure serial device");
|
||||||
|
}
|
||||||
|
|
||||||
|
log::debug!("opening keyboard serial device");
|
||||||
|
let mut file = File::options()
|
||||||
|
.create(false)
|
||||||
|
.read(true)
|
||||||
|
.append(true)
|
||||||
|
.open(dev)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
log::debug!("requesting keyboard layers");
|
||||||
|
for p in HostMsg::GetLayers.pack() {
|
||||||
|
log::debug!("packing {:x?}", p);
|
||||||
|
file.write_all(p.as_bytes()).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf = Vec::with_capacity(MAX_MESSAGE_SIZE);
|
||||||
|
let mut last_read = Instant::now();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// if buffer is not empty, this future will sleep until the pending message times out
|
||||||
|
let timeout = async {
|
||||||
|
if buf.is_empty() {
|
||||||
|
pending().await
|
||||||
|
} else {
|
||||||
|
let timeout_at = last_read + MESSAGE_TIMEOUT;
|
||||||
|
sleep(Instant::now() - timeout_at).await;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// try to read some bytes from the file
|
||||||
|
let mut tmp = [0u8; 1024];
|
||||||
|
let n = select! {
|
||||||
|
n = file.read(&mut tmp) => n?,
|
||||||
|
|
||||||
|
// need to continuously poll read if nothing is happening
|
||||||
|
_ = sleep(Duration::from_millis(20)) => continue,
|
||||||
|
|
||||||
|
_ = timeout => {
|
||||||
|
log::warn!("message timeout, clearing buffer");
|
||||||
|
buf.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// exit on eof
|
||||||
|
if n == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_read = Instant::now();
|
||||||
|
buf.extend_from_slice(&tmp[..n]);
|
||||||
|
|
||||||
|
// make sure we're not just reading garbage forever
|
||||||
|
if buf.len() > MAX_MESSAGE_SIZE {
|
||||||
|
log::warn!("max message size exceeded");
|
||||||
|
buf.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to parse messages from the read bytes
|
||||||
|
loop {
|
||||||
|
let mut reader = &mut &buf[..];
|
||||||
|
let record = match DeviceMsg::unpack(&mut reader) {
|
||||||
|
Ok(r) => r,
|
||||||
|
|
||||||
|
// we probably have not gotten the entire message yet, go back to reading bytes.
|
||||||
|
// if the message is corrupted, we will eventually hit MESSAGE_TIMEOUT or
|
||||||
|
// MAX_MESSAGE_SIZE.
|
||||||
|
Err(UnpackErr::UnexpectedEof) => break,
|
||||||
|
|
||||||
|
// on any other error, the message is corrupt. clear the buffer.
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("corrupt message: {e:?}");
|
||||||
|
buf.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// remove the decoded bytes from buf
|
||||||
|
if reader.is_empty() {
|
||||||
|
buf.clear();
|
||||||
|
} else {
|
||||||
|
let bytes_read = buf.len() - reader.len();
|
||||||
|
buf.rotate_left(bytes_read);
|
||||||
|
buf.truncate(buf.len() - bytes_read);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(_) = tx.send(record).await {
|
||||||
|
log::info!("channel closed, closing serial thingy");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there are no more bytes, stop trying to decode messages.
|
||||||
|
if buf.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.request_repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
141
editor/src/serial/scan.rs
Normal file
141
editor/src/serial/scan.rs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fs::read_dir,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::Command,
|
||||||
|
str,
|
||||||
|
};
|
||||||
|
|
||||||
|
use eyre::Context;
|
||||||
|
|
||||||
|
/// Scan for the keyboard serial device
|
||||||
|
pub fn scan_for_serial() -> eyre::Result<Option<PathBuf>> {
|
||||||
|
log::info!("scanning for keyboard serial device");
|
||||||
|
|
||||||
|
let mut syspaths = vec![];
|
||||||
|
|
||||||
|
// Reqursively scan all "/sys/bus/usb/devices/usb*" folders for files called "dev"
|
||||||
|
// and get the paths to the parent folders of those "dev" files.
|
||||||
|
for f in read_dir("/sys/bus/usb/devices/")? {
|
||||||
|
let f = f?;
|
||||||
|
let file_name = f.file_name();
|
||||||
|
let Some(name) = file_name.to_str() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if !name.starts_with("usb") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut paths = scan_usb_for_devs(&f.path())?;
|
||||||
|
syspaths.append(&mut paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
log::debug!("checking these devices: {syspaths:#?}");
|
||||||
|
|
||||||
|
for syspath in syspaths {
|
||||||
|
let syspath = syspath.to_string_lossy();
|
||||||
|
let devname = Command::new("udevadm")
|
||||||
|
.args(["info", "-q", "name", "-p", &syspath])
|
||||||
|
.output()
|
||||||
|
.wrap_err("failed to run udevadmn to query dev name")?;
|
||||||
|
|
||||||
|
let devname = str::from_utf8(&devname.stdout)
|
||||||
|
.wrap_err("failed to parse udevadm output as utf-8")?
|
||||||
|
.trim();
|
||||||
|
|
||||||
|
let devname = Path::new(devname);
|
||||||
|
|
||||||
|
// Ignore USB hubs and such
|
||||||
|
if devname.starts_with("bus/") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let devpath = Path::new("/dev").join(devname);
|
||||||
|
|
||||||
|
let properties = Command::new("udevadm")
|
||||||
|
.args(["info", "-q", "property", "--export", "-p", &syspath])
|
||||||
|
.output()
|
||||||
|
.wrap_err("failed to run udevadmn to query properities")?;
|
||||||
|
|
||||||
|
let properties = str::from_utf8(&properties.stdout)
|
||||||
|
.wrap_err("failed to parse udevadm output as utf-8")?;
|
||||||
|
|
||||||
|
let properties = parse_env(properties);
|
||||||
|
let Some(properties) = parse_properties(&properties) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
log::debug!("{devpath:?}: {properties:#?}");
|
||||||
|
|
||||||
|
let is_serial_device = [
|
||||||
|
properties.model == "Tangentbord1",
|
||||||
|
properties.vendor == "Tux",
|
||||||
|
properties.vendor_id == "b00b",
|
||||||
|
properties.usb_type == "generic",
|
||||||
|
properties.usb_driver == "cdc_acm",
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.all(|b| b);
|
||||||
|
|
||||||
|
if is_serial_device {
|
||||||
|
return Ok(Some(devpath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Properties<'a> {
|
||||||
|
model: &'a str,
|
||||||
|
serial: &'a str,
|
||||||
|
serial_short: &'a str,
|
||||||
|
vendor: &'a str,
|
||||||
|
vendor_id: &'a str,
|
||||||
|
usb_type: &'a str,
|
||||||
|
usb_driver: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_properties<'a>(properties: &HashMap<&'a str, &'a str>) -> Option<Properties<'a>> {
|
||||||
|
Some(Properties {
|
||||||
|
model: *properties.get("ID_MODEL")?,
|
||||||
|
serial: *properties.get("ID_SERIAL")?,
|
||||||
|
serial_short: *properties.get("ID_SERIAL_SHORT")?,
|
||||||
|
vendor: *properties.get("ID_VENDOR")?,
|
||||||
|
vendor_id: *properties.get("ID_VENDOR_ID")?,
|
||||||
|
usb_type: *properties.get("ID_USB_TYPE")?,
|
||||||
|
usb_driver: *properties.get("ID_USB_DRIVER")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_env(s: &str) -> HashMap<&str, &str> {
|
||||||
|
s.lines()
|
||||||
|
.filter_map(|line| {
|
||||||
|
let (key, val) = line.split_once('=')?;
|
||||||
|
let val = val.trim_matches('\'');
|
||||||
|
Some((key, val))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn scan_usb_for_devs(p: &Path) -> eyre::Result<Vec<PathBuf>> {
|
||||||
|
let mut out = vec![];
|
||||||
|
|
||||||
|
for f in read_dir(p)? {
|
||||||
|
let f = f?;
|
||||||
|
let meta = f.metadata()?;
|
||||||
|
let path = f.path();
|
||||||
|
if meta.is_dir() {
|
||||||
|
let mut results = scan_usb_for_devs(&path)?;
|
||||||
|
out.append(&mut results);
|
||||||
|
} else if meta.is_file() && f.file_name() == "dev" {
|
||||||
|
if let Some(parent) = path.parent() {
|
||||||
|
out.push(parent.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
@ -1,31 +0,0 @@
|
|||||||
[
|
|
||||||
Layer(
|
|
||||||
buttons: [
|
|
||||||
// Row 1
|
|
||||||
Key(Apostrophe),
|
|
||||||
Key(Comma),
|
|
||||||
Key(Period),
|
|
||||||
Key(P),
|
|
||||||
Key(Y),
|
|
||||||
|
|
||||||
// Row 2
|
|
||||||
Key(A),
|
|
||||||
Key(O),
|
|
||||||
Key(E),
|
|
||||||
Key(U),
|
|
||||||
Key(I),
|
|
||||||
|
|
||||||
// Row 3
|
|
||||||
Key(Colon),
|
|
||||||
Key(Q),
|
|
||||||
Key(J),
|
|
||||||
Key(K),
|
|
||||||
Key(X),
|
|
||||||
|
|
||||||
// Thumbpad
|
|
||||||
Mod(LShift),
|
|
||||||
Mod(LCtrl),
|
|
||||||
NextLayer,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
]
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
[
|
|
||||||
Layer(
|
|
||||||
buttons: [
|
|
||||||
// Row 1
|
|
||||||
Key(F),
|
|
||||||
Key(G),
|
|
||||||
Key(C),
|
|
||||||
Key(R),
|
|
||||||
Key(L),
|
|
||||||
|
|
||||||
// Row 2
|
|
||||||
Key(D),
|
|
||||||
Key(H),
|
|
||||||
Key(T),
|
|
||||||
Key(N),
|
|
||||||
Key(S),
|
|
||||||
|
|
||||||
// Row 3
|
|
||||||
Key(B),
|
|
||||||
Key(M),
|
|
||||||
Key(W),
|
|
||||||
Key(V),
|
|
||||||
Key(Z),
|
|
||||||
|
|
||||||
// Thumbpad
|
|
||||||
PrevLayer,
|
|
||||||
Mod(RAlt),
|
|
||||||
Mod(RMod),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
]
|
|
||||||
2
left/.cargo/config.toml
Normal file
2
left/.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[build]
|
||||||
|
target = "thumbv6m-none-eabi"
|
||||||
28
left/Cargo.toml
Normal file
28
left/Cargo.toml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
[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"
|
||||||
|
|
||||||
|
[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"] }
|
||||||
|
embassy-sync = "0.5.0"
|
||||||
|
embassy-time = "0.3.0"
|
||||||
|
embassy-futures = "0.1.1"
|
||||||
|
log = "0.4.17"
|
||||||
|
postcard = { version = "1.0.4", features = ["alloc"] }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
ron = "0.8.0"
|
||||||
|
postcard = { version = "1", features = ["use-std"] }
|
||||||
|
|
||||||
|
[build-dependencies.tangentbord1]
|
||||||
|
path = "../lib"
|
||||||
|
package = "tangentbord1-lib"
|
||||||
@ -13,12 +13,11 @@ use std::io::Write;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
|
|
||||||
use tgnt::layer::Layer;
|
use tangentbord1::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");
|
||||||
@ -47,9 +46,10 @@ fn memory() {
|
|||||||
|
|
||||||
fn serialize_layout(ron_path: &str, postcard_path: &str) {
|
fn serialize_layout(ron_path: &str, postcard_path: &str) {
|
||||||
println!("cargo:rerun-if-changed={ron_path}");
|
println!("cargo:rerun-if-changed={ron_path}");
|
||||||
|
println!("cargo:rerun-if-changed={postcard_path}");
|
||||||
|
|
||||||
let layers = fs::read_to_string(ron_path).expect("Failed to read .ron");
|
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 layers: Vec<Vec<Layer>> = ron::from_str(&layers).expect("Failed to deserialize .ron");
|
||||||
|
|
||||||
let serialized = postcard::to_stdvec(&layers).expect("Failed to serialize layers");
|
let serialized = postcard::to_stdvec(&layers).expect("Failed to serialize layers");
|
||||||
|
|
||||||
180
left/layers.ron
Normal file
180
left/layers.ron
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
[
|
||||||
|
[
|
||||||
|
Layer(
|
||||||
|
buttons: [
|
||||||
|
// Row 1
|
||||||
|
Key(Escape),
|
||||||
|
Key(Comma),
|
||||||
|
Key(Period),
|
||||||
|
Key(P),
|
||||||
|
Key(Y),
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
ModTap(A, LAlt),
|
||||||
|
ModTap(O, LMod),
|
||||||
|
ModTap(E, LShift),
|
||||||
|
ModTap(U, LCtrl),
|
||||||
|
Key(I),
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
Key(Colon),
|
||||||
|
Key(Q),
|
||||||
|
Key(J),
|
||||||
|
Key(K),
|
||||||
|
Key(X),
|
||||||
|
|
||||||
|
// Thumbpad
|
||||||
|
Key(Backspace),
|
||||||
|
Key(Space),
|
||||||
|
Layer(Peek, Right, 1),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Layer(
|
||||||
|
buttons: [
|
||||||
|
// Row 1
|
||||||
|
Key(Escape),
|
||||||
|
Key(Slash),
|
||||||
|
Key(Equal),
|
||||||
|
Key(Accent),
|
||||||
|
Key(Period),
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
ModTap(BackslashPipe, LAlt),
|
||||||
|
ModTap(Dash, LMod),
|
||||||
|
ModTap(LBracket, LShift),
|
||||||
|
ModTap(RBracket, LCtrl),
|
||||||
|
Key(Tab),
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Key(Apostrophe),
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Thumbpad
|
||||||
|
Key(Backspace),
|
||||||
|
Key(Space),
|
||||||
|
Layer(Peek, Right, 1),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Layer(
|
||||||
|
buttons: [
|
||||||
|
// Row 1
|
||||||
|
Key(F1),
|
||||||
|
Key(F4),
|
||||||
|
Key(F7),
|
||||||
|
Key(F10),
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
Key(F2),
|
||||||
|
Key(F5),
|
||||||
|
Key(F8),
|
||||||
|
Key(F11),
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
Key(F3),
|
||||||
|
Key(F6),
|
||||||
|
Key(F9),
|
||||||
|
Key(F12),
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Thumbpad
|
||||||
|
Key(Backspace),
|
||||||
|
Key(Space),
|
||||||
|
Layer(Peek, Right, 1),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
[ // gaming row
|
||||||
|
Layer(
|
||||||
|
buttons: [
|
||||||
|
// Row 1
|
||||||
|
Key(Escape),
|
||||||
|
Key(Q),
|
||||||
|
Key(W),
|
||||||
|
Key(E),
|
||||||
|
Key(R),
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
Mod(LShift),
|
||||||
|
Key(A),
|
||||||
|
Key(S),
|
||||||
|
Key(D),
|
||||||
|
Key(F),
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
Mod(LCtrl),
|
||||||
|
Key(Z),
|
||||||
|
Key(X),
|
||||||
|
Key(C),
|
||||||
|
Key(V),
|
||||||
|
|
||||||
|
// Thumbpad
|
||||||
|
Key(Backspace),
|
||||||
|
Key(Space),
|
||||||
|
Layer(Peek, Right, 1),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Layer(
|
||||||
|
buttons: [
|
||||||
|
// Row 1
|
||||||
|
Key(D1),
|
||||||
|
Key(D2),
|
||||||
|
Key(D3),
|
||||||
|
Key(D4),
|
||||||
|
Key(D5),
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
Key(D6),
|
||||||
|
Key(D7),
|
||||||
|
Key(D8),
|
||||||
|
Key(D9),
|
||||||
|
Key(D0),
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Thumbpad
|
||||||
|
Key(Backspace),
|
||||||
|
Key(Space),
|
||||||
|
Layer(Peek, Right, 1),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Layer(
|
||||||
|
buttons: [
|
||||||
|
// Row 1
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Thumbpad
|
||||||
|
Key(Backspace),
|
||||||
|
Key(Space),
|
||||||
|
Layer(Peek, Right, 1),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
]
|
||||||
118
left/src/main.rs
Normal file
118
left/src/main.rs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
//! Firmware for Tangentbord1, left half.
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
extern crate cortex_m_rt;
|
||||||
|
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::gpio::{Level, Output, Pin};
|
||||||
|
use embassy_time::Timer;
|
||||||
|
use log::error;
|
||||||
|
use tangentbord1::{
|
||||||
|
board::Board,
|
||||||
|
event::Half,
|
||||||
|
interrupts::Irqs,
|
||||||
|
keyboard::KeyboardConfig,
|
||||||
|
layer::Layer,
|
||||||
|
logger::LogMultiplexer,
|
||||||
|
rgb::Rgb,
|
||||||
|
util::stall,
|
||||||
|
ws2812::Ws2812,
|
||||||
|
{allocator, rtt, uart, usb},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
let half = Half::Left;
|
||||||
|
|
||||||
|
let rtt_write = rtt::init_rtt_logger();
|
||||||
|
|
||||||
|
allocator::init();
|
||||||
|
|
||||||
|
let p = embassy_rp::init(Default::default());
|
||||||
|
let board = Board::from(p);
|
||||||
|
|
||||||
|
let _neopixel_power = Output::new(board.neopixel_power, Level::High);
|
||||||
|
|
||||||
|
let mut neopixel = Ws2812::new(board.PIO0, Irqs, board.DMA_CH0, board.neopixel);
|
||||||
|
let neopixels_d5 = Ws2812::new(board.PIO1, Irqs, board.DMA_CH1, board.d5);
|
||||||
|
|
||||||
|
neopixel.write(&[Rgb::new(0xFF, 0x00, 0x00)]).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");
|
||||||
|
stall().await
|
||||||
|
};
|
||||||
|
|
||||||
|
let keyboard = KeyboardConfig {
|
||||||
|
half,
|
||||||
|
pins: [
|
||||||
|
// row 1
|
||||||
|
board.d24.degrade(),
|
||||||
|
board.a3.degrade(),
|
||||||
|
board.a2.degrade(),
|
||||||
|
board.a1.degrade(),
|
||||||
|
board.a0.degrade(),
|
||||||
|
// row 2
|
||||||
|
board.d25.degrade(),
|
||||||
|
board.sck.degrade(),
|
||||||
|
board.mosi.degrade(),
|
||||||
|
board.miso.degrade(),
|
||||||
|
board.d2.degrade(),
|
||||||
|
// row 3
|
||||||
|
board.d12.degrade(),
|
||||||
|
board.d11.degrade(),
|
||||||
|
board.d10.degrade(),
|
||||||
|
board.d9.degrade(),
|
||||||
|
board.d3.degrade(),
|
||||||
|
// thumbpad
|
||||||
|
board.d7.degrade(),
|
||||||
|
board.scl.degrade(),
|
||||||
|
board.sda.degrade(),
|
||||||
|
],
|
||||||
|
// the index of the LEDs is different than the switch index.
|
||||||
|
// each number is the index of the LED for the switch of that index.
|
||||||
|
led_map: [4, 3, 2, 1, 0, 5, 6, 7, 8, 9, 14, 13, 12, 11, 10, 15, 16, 17],
|
||||||
|
led_driver: neopixels_d5,
|
||||||
|
layers,
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some([events1, events2]) = keyboard.create().await else {
|
||||||
|
error!("failed to create keyboard");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
uart::start(board.tx, board.rx, board.UART0, half, events2).await;
|
||||||
|
|
||||||
|
neopixel.write(&[Rgb::new(0x00, 0x99, 0x99)]).await;
|
||||||
|
|
||||||
|
let usb_logger = usb::setup_logger_and_keyboard(board.USB, events1).await;
|
||||||
|
|
||||||
|
let logger = LogMultiplexer {
|
||||||
|
outputs: [rtt_write, usb_logger],
|
||||||
|
};
|
||||||
|
logger.init();
|
||||||
|
|
||||||
|
log::error!("log_level: error");
|
||||||
|
log::warn!("log_level: warn");
|
||||||
|
log::info!("log_level: info");
|
||||||
|
log::debug!("log_level: debug");
|
||||||
|
log::trace!("log_level: trace");
|
||||||
|
|
||||||
|
neopixel.write(&[Rgb::new(0x00, 0x00, 0xFF)]).await;
|
||||||
|
Timer::after_secs(5).await;
|
||||||
|
|
||||||
|
for b in (0u8..0xff).rev() {
|
||||||
|
neopixel.write(&[Rgb::new(0, 0, b)]).await;
|
||||||
|
Timer::after_millis(10).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
stall().await
|
||||||
|
}
|
||||||
2
lib/.cargo/config.toml
Normal file
2
lib/.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[build]
|
||||||
|
target = "thumbv6m-none-eabi"
|
||||||
62
lib/Cargo.toml
Normal file
62
lib/Cargo.toml
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
[package]
|
||||||
|
name = "tangentbord1-lib"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Joakim Hulthe <joakim@hulthe.net>"]
|
||||||
|
description = "Keyboard firmware"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
#tgnt = { git = "https://git.nubo.sh/hulthe/tgnt.git", default-features = false }
|
||||||
|
cortex-m = "0.7.6"
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
embedded-hal = "0.2.5"
|
||||||
|
static_cell = "1.0.0"
|
||||||
|
embedded-io-async = "*"
|
||||||
|
futures = { version = "0.3", default-features = false, features = ["async-await"] }
|
||||||
|
embassy-executor = { version = "0.5.0", features = ["nightly", "nightly", "executor-thread", "integrated-timers"] }
|
||||||
|
embassy-sync = "0.5.0"
|
||||||
|
embassy-time = "0.3.0"
|
||||||
|
embassy-futures = "0.1.1"
|
||||||
|
log = "0.4.17"
|
||||||
|
pio = "0.2.1"
|
||||||
|
pio-proc = "0.2.1"
|
||||||
|
smart-leds = "0.3.0"
|
||||||
|
embedded-alloc = "0.5.0"
|
||||||
|
postcard = { version = "1.0.4", features = ["alloc"] }
|
||||||
|
fixed = "1.23.1"
|
||||||
|
rtt-target = "0.4.0"
|
||||||
|
heapless = "0.7.16"
|
||||||
|
once_cell = { version = "1.17.1", default-features = false }
|
||||||
|
critical-section = "1.1.1"
|
||||||
|
crc-any = "2.4.3"
|
||||||
|
serde = { version = "1.0.163", default-features = false, features = ["derive"] }
|
||||||
|
bytemuck = { version = "1.13.1", features = ["derive"] }
|
||||||
|
libm = "0.2.8"
|
||||||
|
glam = { version = "0.25.0", default-features = false, features = ["libm"] }
|
||||||
|
msgpck = { version = "0.2.8", features = ["alloc"] }
|
||||||
|
#atomic-polyfill = "1.0.2"
|
||||||
|
portable-atomic = { version = "1.6.0", default-features = false }
|
||||||
|
|
||||||
|
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||||
|
embassy-executor = { version = "0.5.0", features = ["arch-std"] }
|
||||||
|
embassy-time = { version = "0.3.0", features = ["std", "generic-queue"] }
|
||||||
|
simple_logger = "4"
|
||||||
|
|
||||||
|
[target.thumbv6m-none-eabi.dependencies]
|
||||||
|
embassy-rp = { version = "0.1.0", features = ["time-driver", "critical-section-impl"] }
|
||||||
|
embassy-executor = { version = "0.5.0", features = ["arch-cortex-m"] }
|
||||||
|
embassy-usb = { version = "0.1.0", features = ["usbd-hid"] }
|
||||||
|
embassy-usb-logger = "0.1.0"
|
||||||
|
embassy-usb-driver = "0.1.0"
|
||||||
|
usb-device = "0.2.9"
|
||||||
|
usbd-hid = "0.6.1"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
#tgnt = { git = "https://git.nubo.sh/hulthe/tgnt.git", default-features = false }
|
||||||
|
ron = "0.8.0"
|
||||||
|
postcard = { version = "1", features = ["use-std"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
std = []
|
||||||
|
default = ["n-key-rollover"]
|
||||||
|
n-key-rollover=[]
|
||||||
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
@ -4,11 +4,12 @@ use core::mem::MaybeUninit;
|
|||||||
|
|
||||||
use embedded_alloc::Heap;
|
use embedded_alloc::Heap;
|
||||||
|
|
||||||
|
pub const HEAP_SIZE: usize = 4096;
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static HEAP: Heap = Heap::empty();
|
static HEAP: Heap = Heap::empty();
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
const HEAP_SIZE: usize = 4096;
|
|
||||||
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
|
static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
|
||||||
unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) }
|
unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) }
|
||||||
}
|
}
|
||||||
28
lib/src/atomics.rs
Normal file
28
lib/src/atomics.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
use portable_atomic::{AtomicU32, Ordering};
|
||||||
|
|
||||||
|
pub struct AtomicCoord {
|
||||||
|
inner: AtomicU32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AtomicCoord {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
AtomicCoord {
|
||||||
|
inner: AtomicU32::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(&self, ordering: Ordering) -> (u16, u16) {
|
||||||
|
let [a, b, c, d] = self.inner.load(ordering).to_ne_bytes();
|
||||||
|
let x = u16::from_ne_bytes([a, b]);
|
||||||
|
let y = u16::from_ne_bytes([c, d]);
|
||||||
|
|
||||||
|
(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store(&self, x: u16, y: u16, ordering: Ordering) {
|
||||||
|
let [a, b] = x.to_ne_bytes();
|
||||||
|
let [c, d] = y.to_ne_bytes();
|
||||||
|
let xy = u32::from_ne_bytes([a, b, c, d]);
|
||||||
|
self.inner.store(xy, ordering);
|
||||||
|
}
|
||||||
|
}
|
||||||
107
lib/src/button.rs
Normal file
107
lib/src/button.rs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
use core::fmt::{self, Debug, Display};
|
||||||
|
use msgpck::{MsgPack, MsgUnpack};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::keys::Key;
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, MsgPack, MsgUnpack)]
|
||||||
|
pub enum Modifier {
|
||||||
|
LCtrl = 0x01,
|
||||||
|
LShift = 0x02,
|
||||||
|
LAlt = 0x04,
|
||||||
|
LMod = 0x08,
|
||||||
|
RCtrl = 0x10,
|
||||||
|
RShift = 0x20,
|
||||||
|
RAlt = 0x40,
|
||||||
|
RMod = 0x80,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Modifier> for u8 {
|
||||||
|
fn from(modifier: Modifier) -> Self {
|
||||||
|
modifier as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, MsgPack, MsgUnpack)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum Button {
|
||||||
|
Mod(Modifier),
|
||||||
|
Key(Key),
|
||||||
|
ModTap(Key, Modifier),
|
||||||
|
Compose2(CompShift, Key, CompShift, Key),
|
||||||
|
Compose3(CompShift, Key, CompShift, Key, CompShift, Key),
|
||||||
|
Layer(LayerShift, LayerDir, u16),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether a key should be shift modified as part of a compose chain.
|
||||||
|
#[derive(
|
||||||
|
Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, MsgPack, MsgUnpack,
|
||||||
|
)]
|
||||||
|
pub enum CompShift {
|
||||||
|
/// Do not shift the key. [Key::A] becomes 'a'.
|
||||||
|
#[default]
|
||||||
|
Lower,
|
||||||
|
|
||||||
|
/// Shift the key. [Key::A] becomes 'A'.
|
||||||
|
Upper,
|
||||||
|
|
||||||
|
/// Shift the key if shift is being held down. [Key::A] becomes 'a' or 'A' depending.
|
||||||
|
Variable,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, MsgPack, MsgUnpack)]
|
||||||
|
pub enum LayerDir {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, MsgPack, MsgUnpack)]
|
||||||
|
pub enum LayerShift {
|
||||||
|
Move,
|
||||||
|
Peek,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Button {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let cs = |cs: &CompShift| match cs {
|
||||||
|
CompShift::Lower => "",
|
||||||
|
CompShift::Upper => "⇧",
|
||||||
|
CompShift::Variable => "⇧?",
|
||||||
|
};
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Button::Mod(modifier) => Debug::fmt(&modifier, f),
|
||||||
|
Button::Key(key) => write!(f, "{key:?}"),
|
||||||
|
Button::ModTap(key, modifier) => write!(f, "{key:?}/{modifier}"),
|
||||||
|
Button::Compose2(cs1, k1, cs2, k2) => {
|
||||||
|
write!(f, "⎄ {}{k1:?} {}{k2:?}", cs(cs1), cs(cs2))
|
||||||
|
}
|
||||||
|
Button::Compose3(cs1, k1, cs2, k2, cs3, k3) => {
|
||||||
|
write!(f, "⎄ {}{k1:?} {}{k2:?} {}{k3:?}", cs(cs1), cs(cs2), cs(cs3))
|
||||||
|
}
|
||||||
|
Button::Layer(..) => write!(f, "Lr"),
|
||||||
|
Button::None => write!(f, "Ø"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Modifier {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let s = match self {
|
||||||
|
Modifier::LCtrl => "⎈",
|
||||||
|
Modifier::LShift => "⇧",
|
||||||
|
Modifier::LAlt => "⎇",
|
||||||
|
Modifier::LMod => "◆",
|
||||||
|
Modifier::RCtrl => "⎈",
|
||||||
|
Modifier::RShift => "⇧",
|
||||||
|
Modifier::RAlt => "⎇",
|
||||||
|
Modifier::RMod => "◆",
|
||||||
|
};
|
||||||
|
|
||||||
|
write!(f, "{s}")
|
||||||
|
}
|
||||||
|
}
|
||||||
0
lib/src/defmt.rs
Normal file
0
lib/src/defmt.rs
Normal file
59
lib/src/event.rs
Normal file
59
lib/src/event.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
use crate::{button::Button, keys::Key};
|
||||||
|
use core::time::Duration;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
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 super::*;
|
||||||
|
use crate::button::Modifier;
|
||||||
|
|
||||||
|
/// A usb keyboard button was pressed or released.
|
||||||
|
///
|
||||||
|
/// This is a lower-level event than a [switch::Event], 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),
|
||||||
|
Wait,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A keyboard half.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum Half {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
}
|
||||||
13
lib/src/interrupts.rs
Normal file
13
lib/src/interrupts.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use embassy_rp::{
|
||||||
|
bind_interrupts,
|
||||||
|
peripherals::{PIO0, PIO1, UART0, USB},
|
||||||
|
};
|
||||||
|
|
||||||
|
bind_interrupts! {
|
||||||
|
pub struct Irqs {
|
||||||
|
UART0_IRQ => embassy_rp::uart::BufferedInterruptHandler<UART0>;
|
||||||
|
USBCTRL_IRQ => embassy_rp::usb::InterruptHandler<USB>;
|
||||||
|
PIO0_IRQ_0 => embassy_rp::pio::InterruptHandler<PIO0>;
|
||||||
|
PIO1_IRQ_0 => embassy_rp::pio::InterruptHandler<PIO1>;
|
||||||
|
}
|
||||||
|
}
|
||||||
358
lib/src/keyboard.rs
Normal file
358
lib/src/keyboard.rs
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
mod lights;
|
||||||
|
|
||||||
|
use core::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::{
|
||||||
|
gpio::{AnyPin, Input, Pin, Pull},
|
||||||
|
peripherals::PIO1,
|
||||||
|
};
|
||||||
|
use embassy_sync::pubsub::{ImmediatePublisher, PubSubChannel, Subscriber};
|
||||||
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
|
use log::{debug, error, info, warn};
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
atomics::AtomicCoord,
|
||||||
|
button::{Button, LayerDir, LayerShift},
|
||||||
|
event::{
|
||||||
|
switch::{Event, EventKind},
|
||||||
|
Half,
|
||||||
|
},
|
||||||
|
layer::{Layer, Layers},
|
||||||
|
lights::Lights,
|
||||||
|
serial_proto::{
|
||||||
|
borrowed::DeviceMsg,
|
||||||
|
owned::{ChangeLayer, SwitchPress, SwitchRelease},
|
||||||
|
},
|
||||||
|
usb::serial::serial_send,
|
||||||
|
util::{SwapCell, SwapCellRead, CS},
|
||||||
|
ws2812::Ws2812,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct KeyboardConfig {
|
||||||
|
/// Which board is this.
|
||||||
|
pub half: Half,
|
||||||
|
/// Array of input pins of each switch
|
||||||
|
pub pins: [AnyPin; SWITCH_COUNT],
|
||||||
|
/// Array of LED indices of each switch
|
||||||
|
pub led_map: [usize; SWITCH_COUNT],
|
||||||
|
pub led_driver: Ws2812<PIO1>,
|
||||||
|
/// Matrix of layers. Stored as rows of columns.
|
||||||
|
pub layers: Vec<Vec<Layer>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
/// Which board is this.
|
||||||
|
half: Half,
|
||||||
|
current_layer: AtomicCoord,
|
||||||
|
layer_cols: usize,
|
||||||
|
layer_rows: usize,
|
||||||
|
layers: SwapCellRead<Layers>,
|
||||||
|
/// Array of LED indices of each switch
|
||||||
|
led_map: [usize; SWITCH_COUNT],
|
||||||
|
lights: Lights<PIO1, SWITCH_COUNT>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Number of [KbEvents] returned by [KeyboardConfig::create].
|
||||||
|
pub const KB_SUBSCRIBERS: usize = 2;
|
||||||
|
|
||||||
|
/// Max number of subscribers for [KB_EVENTS]. Equals [KB_SUBSCRIBERS] plus the two used by
|
||||||
|
/// [layer_switch_task] and [lights::task].
|
||||||
|
const ACTUAL_KB_SUBSCRIBERS: usize = KB_SUBSCRIBERS + 2;
|
||||||
|
|
||||||
|
const KB_EVENT_CAP: usize = 128;
|
||||||
|
static KB_EVENTS: PubSubChannel<CS, Event, KB_EVENT_CAP, ACTUAL_KB_SUBSCRIBERS, 0> =
|
||||||
|
PubSubChannel::new();
|
||||||
|
pub struct KbEvents {
|
||||||
|
pub subscriber: Subscriber<'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> {
|
||||||
|
publisher:
|
||||||
|
&'a mut ImmediatePublisher<'static, CS, Event, KB_EVENT_CAP, ACTUAL_KB_SUBSCRIBERS, 0>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct KbEventsRx<'a> {
|
||||||
|
subscriber: &'a mut Subscriber<'static, CS, Event, KB_EVENT_CAP, ACTUAL_KB_SUBSCRIBERS, 0>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyboardConfig {
|
||||||
|
pub async fn create(self) -> Option<[KbEvents; KB_SUBSCRIBERS]> {
|
||||||
|
let spawner = Spawner::for_current_executor().await;
|
||||||
|
|
||||||
|
if self.layers.is_empty() {
|
||||||
|
error!("no layers defined");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"setting up keyboard layout with {} layer(s)",
|
||||||
|
self.layers.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
for (y, row) in self.layers.iter().enumerate() {
|
||||||
|
for (x, layer) in row.iter().enumerate() {
|
||||||
|
if layer.buttons.len() != SWITCH_COUNT {
|
||||||
|
warn!(
|
||||||
|
"layer ({x}, {y}) defines {} buttons, but there are {SWITCH_COUNT} switches",
|
||||||
|
layer.buttons.len(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let layer_cols = self.layers.iter().map(|row| row.len()).max().unwrap_or(0);
|
||||||
|
let layer_rows = self.layers.len();
|
||||||
|
|
||||||
|
static LAYERS: StaticCell<SwapCell<Layers>> = StaticCell::new();
|
||||||
|
let layers = LAYERS.init_with(|| SwapCell::new(self.layers));
|
||||||
|
let (layers_read, layers_write) = layers.split();
|
||||||
|
|
||||||
|
static STATE: StaticCell<State> = StaticCell::new();
|
||||||
|
let state = STATE.init_with(|| State {
|
||||||
|
half: self.half,
|
||||||
|
current_layer: AtomicCoord::new(),
|
||||||
|
layer_cols,
|
||||||
|
layer_rows,
|
||||||
|
layers: layers_read,
|
||||||
|
lights: Lights::new(self.led_driver),
|
||||||
|
led_map: self.led_map,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (i, pin) in self.pins.into_iter().enumerate() {
|
||||||
|
if spawner.spawn(switch_task(i, pin, state)).is_err() {
|
||||||
|
error!("failed to spawn switch task, pool_size mismatch?");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spawner.must_spawn(layer_switch_task(
|
||||||
|
KbEvents {
|
||||||
|
publisher: KB_EVENTS.immediate_publisher(),
|
||||||
|
subscriber: KB_EVENTS
|
||||||
|
.subscriber()
|
||||||
|
.expect("Not enough KbEvents allocated"),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
));
|
||||||
|
|
||||||
|
spawner.must_spawn(lights::task(
|
||||||
|
KbEvents {
|
||||||
|
publisher: KB_EVENTS.immediate_publisher(),
|
||||||
|
subscriber: KB_EVENTS
|
||||||
|
.subscriber()
|
||||||
|
.expect("Not enough KbEvents allocated"),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
));
|
||||||
|
|
||||||
|
Some([(); KB_SUBSCRIBERS].map(|_| {
|
||||||
|
KbEvents {
|
||||||
|
publisher: KB_EVENTS.immediate_publisher(),
|
||||||
|
subscriber: KB_EVENTS
|
||||||
|
.subscriber()
|
||||||
|
.expect("Not enough KbEvents allocated"),
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KbEvents {
|
||||||
|
pub async fn send(&mut self, event: Event) {
|
||||||
|
self.publisher.publish_immediate(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn recv(&mut self) -> Event {
|
||||||
|
self.subscriber.next_message_pure().await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn split(&mut self) -> (KbEventsRx, KbEventsTx) {
|
||||||
|
let tx = KbEventsTx {
|
||||||
|
publisher: &mut self.publisher,
|
||||||
|
};
|
||||||
|
let rx = KbEventsRx {
|
||||||
|
subscriber: &mut self.subscriber,
|
||||||
|
};
|
||||||
|
(rx, tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KbEventsRx<'_> {
|
||||||
|
pub async fn recv(&mut self) -> Event {
|
||||||
|
self.subscriber.next_message_pure().await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KbEventsTx<'_> {
|
||||||
|
pub fn send(&mut self, event: Event) {
|
||||||
|
self.publisher.publish_immediate(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const MOD_TAP_TIME: Duration = Duration::from_millis(200);
|
||||||
|
pub const SWITCH_COUNT: usize = 18;
|
||||||
|
pub const DEBOUNCE_THRESHOLD: Duration = Duration::from_millis(15);
|
||||||
|
|
||||||
|
/// Task for monitoring a single switch pin, and handling button presses.
|
||||||
|
#[embassy_executor::task(pool_size = 18)]
|
||||||
|
async fn switch_task(switch_num: usize, pin: AnyPin, state: &'static State) -> ! {
|
||||||
|
let _pin_nr = pin.pin();
|
||||||
|
let mut pin = Input::new(pin, Pull::Up);
|
||||||
|
let events = KB_EVENTS.immediate_publisher();
|
||||||
|
loop {
|
||||||
|
// pins are pull-up, so when the switch is pressed they are brought low.
|
||||||
|
pin.wait_for_low().await;
|
||||||
|
let pressed_at = Instant::now();
|
||||||
|
|
||||||
|
// TODO: do we need debouncing?
|
||||||
|
|
||||||
|
// get current layer
|
||||||
|
let (x, y) = state.current_layer.load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
let layers = state.layers.read();
|
||||||
|
let Some(Layer { buttons }) = layers
|
||||||
|
.get(usize::from(y))
|
||||||
|
.and_then(|row| row.get(usize::from(x)))
|
||||||
|
else {
|
||||||
|
// currently layer is null, do nothing
|
||||||
|
pin.wait_for_high().await;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let button = buttons.get(switch_num).cloned();
|
||||||
|
drop(layers);
|
||||||
|
|
||||||
|
// and current button
|
||||||
|
let Some(button) = button else {
|
||||||
|
warn!("no button defined for switch {switch_num}");
|
||||||
|
pin.wait_for_high().await;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("switch {switch_num} button {button:?} pressed");
|
||||||
|
serial_send(&DeviceMsg::SwitchPress(SwitchPress(switch_num as u16)));
|
||||||
|
|
||||||
|
let ev = |kind| Event {
|
||||||
|
source: state.half,
|
||||||
|
source_button: switch_num,
|
||||||
|
kind,
|
||||||
|
};
|
||||||
|
|
||||||
|
events.publish_immediate(ev(EventKind::Press {
|
||||||
|
button: button.clone(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
Timer::after(DEBOUNCE_THRESHOLD).await;
|
||||||
|
pin.wait_for_high().await;
|
||||||
|
let released_after = pressed_at.elapsed();
|
||||||
|
|
||||||
|
debug!("switch {switch_num} button {button:?} released");
|
||||||
|
serial_send(&DeviceMsg::SwitchRelease(SwitchRelease(switch_num as u16)));
|
||||||
|
|
||||||
|
events.publish_immediate(ev(EventKind::Release {
|
||||||
|
button: button.clone(),
|
||||||
|
after: released_after.into(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
Timer::after(DEBOUNCE_THRESHOLD).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn layer_switch_task(mut events: KbEvents, state: &'static State) {
|
||||||
|
let col_count = state.layer_cols as u16;
|
||||||
|
let row_count = state.layer_rows as u16;
|
||||||
|
let Some(last_row) = row_count.checked_sub(1) else {
|
||||||
|
error!("no layers specified");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(last_col) = col_count.checked_sub(1) else {
|
||||||
|
error!("no layers specified");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
use LayerDir::*;
|
||||||
|
use LayerShift::*;
|
||||||
|
|
||||||
|
let event = events.recv().await;
|
||||||
|
let (x, y) = state.current_layer.load(Ordering::Relaxed);
|
||||||
|
let (nx, ny) = match event.kind {
|
||||||
|
EventKind::Press { button } => match button {
|
||||||
|
Button::Layer(_, Up, n) => (x, y.checked_sub(n).unwrap_or(last_row)),
|
||||||
|
Button::Layer(_, Down, n) => (x, y.wrapping_add(n) % row_count),
|
||||||
|
Button::Layer(_, Left, n) => (x.checked_sub(n).unwrap_or(last_col), y),
|
||||||
|
Button::Layer(_, Right, n) => (x.wrapping_add(n) % col_count, y),
|
||||||
|
_ => continue,
|
||||||
|
},
|
||||||
|
EventKind::Release { button, .. } => match button {
|
||||||
|
Button::Layer(Peek, Up, n) => (x, y.wrapping_add(n) % row_count),
|
||||||
|
Button::Layer(Peek, Down, n) => (x, y.checked_sub(n).unwrap_or(last_row)),
|
||||||
|
Button::Layer(Peek, Left, n) => (x.wrapping_add(n) % col_count, y),
|
||||||
|
Button::Layer(Peek, Right, n) => (x.checked_sub(n).unwrap_or(last_col), y),
|
||||||
|
_ => continue,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
state.current_layer.store(nx, ny, Ordering::Relaxed);
|
||||||
|
serial_send(&DeviceMsg::ChangeLayer(ChangeLayer { x: nx, y: ny }));
|
||||||
|
debug!("switched to layer ({nx}, {ny})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Random functions for testing
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub mod test {
|
||||||
|
use crate::{button::Button, keys::Key};
|
||||||
|
|
||||||
|
pub fn letter_to_key(c: char) -> Button {
|
||||||
|
if !c.is_ascii() {
|
||||||
|
return Button::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let c = c.to_ascii_uppercase();
|
||||||
|
|
||||||
|
let key = match c {
|
||||||
|
'A' => Key::A,
|
||||||
|
'B' => Key::B,
|
||||||
|
'C' => Key::C,
|
||||||
|
'D' => Key::D,
|
||||||
|
'E' => Key::E,
|
||||||
|
'F' => Key::F,
|
||||||
|
'G' => Key::G,
|
||||||
|
'H' => Key::H,
|
||||||
|
'I' => Key::I,
|
||||||
|
'J' => Key::J,
|
||||||
|
'K' => Key::K,
|
||||||
|
'L' => Key::L,
|
||||||
|
'M' => Key::M,
|
||||||
|
'N' => Key::N,
|
||||||
|
'O' => Key::O,
|
||||||
|
'P' => Key::P,
|
||||||
|
'Q' => Key::Q,
|
||||||
|
'R' => Key::R,
|
||||||
|
'S' => Key::S,
|
||||||
|
'T' => Key::T,
|
||||||
|
'U' => Key::U,
|
||||||
|
'V' => Key::V,
|
||||||
|
'W' => Key::W,
|
||||||
|
'X' => Key::X,
|
||||||
|
'Y' => Key::Y,
|
||||||
|
'Z' => Key::Z,
|
||||||
|
' ' => Key::Space,
|
||||||
|
'\n' => Key::Return,
|
||||||
|
_ => {
|
||||||
|
log::info!("char {c:?} -> None");
|
||||||
|
return Button::None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
log::info!("char {c:?} -> {key:?}");
|
||||||
|
|
||||||
|
Button::Key(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
247
lib/src/keyboard/lights.rs
Normal file
247
lib/src/keyboard/lights.rs
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
use core::future::pending;
|
||||||
|
|
||||||
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
|
use futures::{select_biased, FutureExt};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
button::Button,
|
||||||
|
lights::shaders::{PowerOffAnim, PowerOnAnim, Shader, Shaders},
|
||||||
|
rgb::Rgb,
|
||||||
|
usb::{UsbEvent, USB_EVENTS},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{Event, EventKind, KbEvents, State, SWITCH_COUNT};
|
||||||
|
|
||||||
|
/// Duration until the keyboard starts the idle animation
|
||||||
|
const UNTIL_IDLE: Duration = Duration::from_secs(30);
|
||||||
|
|
||||||
|
/// DUration between each animation frame.
|
||||||
|
const FRAMETIME: Duration = Duration::from_millis(16);
|
||||||
|
|
||||||
|
const IDLE_ANIM: Shaders = Shaders::OrthoRainbow;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
enum LightsState {
|
||||||
|
Active {
|
||||||
|
keys: [KeyLedState; SWITCH_COUNT],
|
||||||
|
next_frame: Instant,
|
||||||
|
idle_at: Instant,
|
||||||
|
},
|
||||||
|
PoweringOff(PowerOffAnim),
|
||||||
|
PoweringOn(PowerOnAnim),
|
||||||
|
#[default]
|
||||||
|
PoweredOff,
|
||||||
|
Idle(Shaders),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightsState {
|
||||||
|
pub fn active_default() -> Self {
|
||||||
|
let now = Instant::now();
|
||||||
|
LightsState::Active {
|
||||||
|
keys: Default::default(),
|
||||||
|
next_frame: now,
|
||||||
|
idle_at: now + UNTIL_IDLE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default)]
|
||||||
|
enum KeyLedState {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
Solid(Rgb),
|
||||||
|
FadeBy(f32),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
pub(super) async fn task(mut events: KbEvents, state: &'static State) {
|
||||||
|
let mut lights = LightsState::default();
|
||||||
|
let mut usb_events = USB_EVENTS
|
||||||
|
.dyn_subscriber()
|
||||||
|
.expect("USB_EVENTS: out of subscribers");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match &mut lights {
|
||||||
|
LightsState::Active {
|
||||||
|
keys,
|
||||||
|
next_frame,
|
||||||
|
idle_at,
|
||||||
|
} => {
|
||||||
|
select_biased! {
|
||||||
|
event = events.recv().fuse() => {
|
||||||
|
*idle_at = Instant::now() + UNTIL_IDLE;
|
||||||
|
handle_event(event, state, keys).await;
|
||||||
|
}
|
||||||
|
ev = usb_events.next_message_pure().fuse() => {
|
||||||
|
*idle_at = Instant::now() + UNTIL_IDLE;
|
||||||
|
handle_usb_event(ev, &mut lights).await;
|
||||||
|
}
|
||||||
|
_ = Timer::at(*idle_at).fuse() => lights = LightsState::Idle(IDLE_ANIM),
|
||||||
|
_ = Timer::at(*next_frame).fuse() => {
|
||||||
|
keypress_tick(state, keys).await;
|
||||||
|
*next_frame = Instant::now() + FRAMETIME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LightsState::PoweringOff(anim) => {
|
||||||
|
select_biased! {
|
||||||
|
ev = usb_events.next_message_pure().fuse() => handle_usb_event(ev, &mut lights).await,
|
||||||
|
_ = play_shader(state, anim).fuse() => lights = LightsState::PoweredOff,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LightsState::PoweringOn(anim) => {
|
||||||
|
select_biased! {
|
||||||
|
ev = usb_events.next_message_pure().fuse() => handle_usb_event(ev, &mut lights).await,
|
||||||
|
_ = play_shader(state, anim).fuse() => lights = LightsState::active_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LightsState::PoweredOff => {
|
||||||
|
let ev = usb_events.next_message_pure().await;
|
||||||
|
handle_usb_event(ev, &mut lights).await;
|
||||||
|
}
|
||||||
|
LightsState::Idle(anim) => {
|
||||||
|
select_biased! {
|
||||||
|
ev = usb_events.next_message_pure().fuse() => handle_usb_event(ev, &mut lights).await,
|
||||||
|
event = events.recv().fuse() => {
|
||||||
|
let now = Instant::now();
|
||||||
|
let mut keys = Default::default();
|
||||||
|
handle_event(event, state, &mut keys).await;
|
||||||
|
lights = LightsState::Active { keys, next_frame: now, idle_at: now + UNTIL_IDLE};
|
||||||
|
}
|
||||||
|
_ = play_shader(state, anim).fuse() => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn play_shader(state: &'static State, shader: &impl Shader) {
|
||||||
|
const SWITCH_COORDS: [(u16, u16); SWITCH_COUNT] = [
|
||||||
|
(0, 1),
|
||||||
|
(1, 1),
|
||||||
|
(2, 1),
|
||||||
|
(3, 1),
|
||||||
|
(4, 1),
|
||||||
|
(4, 2),
|
||||||
|
(3, 2),
|
||||||
|
(2, 2),
|
||||||
|
(1, 2),
|
||||||
|
(0, 2),
|
||||||
|
(0, 3),
|
||||||
|
(1, 3),
|
||||||
|
(2, 3),
|
||||||
|
(3, 3),
|
||||||
|
(4, 3),
|
||||||
|
(2, 4),
|
||||||
|
(3, 4),
|
||||||
|
(4, 4),
|
||||||
|
];
|
||||||
|
|
||||||
|
const BRIGHTNESS: f32 = 1.00;
|
||||||
|
|
||||||
|
let switch_coords = SWITCH_COORDS.map(|(x, y)| (f32::from(x) / 4.0, f32::from(y) / 4.0));
|
||||||
|
|
||||||
|
let animate_shader = async {
|
||||||
|
loop {
|
||||||
|
let now = Instant::now();
|
||||||
|
|
||||||
|
state
|
||||||
|
.lights
|
||||||
|
.update(|rgbs| {
|
||||||
|
(switch_coords.into_iter().zip(rgbs.iter_mut()))
|
||||||
|
.for_each(|(uv, rgb)| *rgb = shader.sample(now, uv) * BRIGHTNESS)
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Timer::after(FRAMETIME).await;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let end_animation = async {
|
||||||
|
match shader.end_time() {
|
||||||
|
Some(end_time) => Timer::at(end_time).await,
|
||||||
|
None => pending().await,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
select_biased! {
|
||||||
|
_ = animate_shader.fuse() => {}
|
||||||
|
_ = end_animation.fuse() => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn keypress_tick(state: &'static State, lights: &mut [KeyLedState; SWITCH_COUNT]) {
|
||||||
|
state
|
||||||
|
.lights
|
||||||
|
.update(|rgbs| {
|
||||||
|
for (button, light) in lights.iter_mut().enumerate() {
|
||||||
|
let Some(&led_id) = state.led_map.get(button) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(rgb) = rgbs.get_mut(led_id) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
match &*light {
|
||||||
|
KeyLedState::None => *rgb = Rgb::new(0, 0, 0),
|
||||||
|
KeyLedState::FadeBy(fade) => {
|
||||||
|
let [r, g, b] = rgb
|
||||||
|
.components()
|
||||||
|
.map(|c| ((c as f32) * fade.clamp(0.0, 1.0)) as u8);
|
||||||
|
*rgb = Rgb::new(r, g, b);
|
||||||
|
if *rgb == Rgb::new(0, 0, 0) {
|
||||||
|
*light = KeyLedState::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&KeyLedState::Solid(color) => *rgb = color,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_event(
|
||||||
|
event: Event,
|
||||||
|
state: &'static State,
|
||||||
|
lights: &mut [KeyLedState; SWITCH_COUNT],
|
||||||
|
) {
|
||||||
|
let rgb = match event.kind {
|
||||||
|
EventKind::Press { button } => match button {
|
||||||
|
Button::Key(..) => KeyLedState::Solid(Rgb::new(0, 150, 0)),
|
||||||
|
Button::Mod(..) => KeyLedState::Solid(Rgb::new(0, 0, 150)),
|
||||||
|
Button::ModTap(..) => KeyLedState::Solid(Rgb::new(0, 0, 150)),
|
||||||
|
Button::Compose2(..) | Button::Compose3(..) => {
|
||||||
|
KeyLedState::Solid(Rgb::new(0, 100, 100))
|
||||||
|
}
|
||||||
|
Button::Layer(..) => KeyLedState::Solid(Rgb::new(120, 0, 120)),
|
||||||
|
_ => KeyLedState::Solid(Rgb::new(150, 0, 0)),
|
||||||
|
},
|
||||||
|
EventKind::Release { .. } => KeyLedState::FadeBy(0.85),
|
||||||
|
};
|
||||||
|
|
||||||
|
if event.source != state.half {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(light) = lights.get_mut(event.source_button) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
*light = rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_usb_event(event: UsbEvent, state: &mut LightsState) {
|
||||||
|
let usb_enabled = match event {
|
||||||
|
UsbEvent::Suspended(false) | UsbEvent::Configured(true) => true,
|
||||||
|
UsbEvent::Configured(false) | UsbEvent::Suspended(true) | UsbEvent::Reset => false,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
*state = match (&state, usb_enabled) {
|
||||||
|
(LightsState::PoweringOn(..), true) => return,
|
||||||
|
(LightsState::PoweringOff(..), false) => return,
|
||||||
|
(LightsState::PoweredOff, false) => return,
|
||||||
|
(_, true) => LightsState::PoweringOn(PowerOnAnim { start }),
|
||||||
|
(_, false) => LightsState::PoweringOff(PowerOffAnim { start }),
|
||||||
|
};
|
||||||
|
}
|
||||||
505
lib/src/keypress_handler.rs
Normal file
505
lib/src/keypress_handler.rs
Normal file
@ -0,0 +1,505 @@
|
|||||||
|
use core::future::pending;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
button::{Button, CompShift, Modifier},
|
||||||
|
event::{button, switch, Half},
|
||||||
|
keys::Key,
|
||||||
|
};
|
||||||
|
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};
|
||||||
|
|
||||||
|
/// The time a ModTap button takes to resolve as a Mod while being held down.
|
||||||
|
const MOD_TAP_TIME: Duration = Duration::from_millis(150);
|
||||||
|
|
||||||
|
/// The number of switches on this keyboard half.
|
||||||
|
const SWITCH_COUNT: usize = 18;
|
||||||
|
|
||||||
|
/// The usb keycode mapped to Compose.
|
||||||
|
const COMPOSE_KEY: Key = Key::Application;
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) => {
|
||||||
|
do_compose(&[
|
||||||
|
(CompShift::Variable, COMPOSE_KEY),
|
||||||
|
(csa, a),
|
||||||
|
(csb, b),
|
||||||
|
(csc, c),
|
||||||
|
]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
output.publish_immediate(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
debug!("modtap resolved as mod: {:?}", event.button);
|
||||||
|
slow_press(output, &mut shift_held, &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, &mut shift_held, &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(..)
|
||||||
|
| Button::Key(..)
|
||||||
|
| Button::Compose2(..)
|
||||||
|
| Button::Compose3(..) => {
|
||||||
|
if queue.is_empty() {
|
||||||
|
debug!("sending key now");
|
||||||
|
// otherwise, send immediately
|
||||||
|
slow_press(output, &mut shift_held, &button).await;
|
||||||
|
} else {
|
||||||
|
debug!("adding key to queue");
|
||||||
|
// if events in queue, also add to queue
|
||||||
|
insert(queue, button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch::EventKind::Release { button, .. } => {
|
||||||
|
let position_in_queue = queue
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, queued)| queued.source_half == event.source)
|
||||||
|
.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: {k:?}");
|
||||||
|
for _ in 0..position_in_queue {
|
||||||
|
let prev_event = queue.pop_front().unwrap();
|
||||||
|
debug!("pressing earlier event {:?}", prev_event);
|
||||||
|
slow_press(output, &mut shift_held, &prev_event.button).await;
|
||||||
|
}
|
||||||
|
let _ = queue.pop_front();
|
||||||
|
debug!("pressing modtap as key");
|
||||||
|
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, &mut shift_held, &button).await;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Button::Mod(..)
|
||||||
|
| Button::Key(..)
|
||||||
|
| Button::Compose2(..)
|
||||||
|
| Button::Compose3(..) => {
|
||||||
|
// 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, &mut shift_held, &prev_event.button).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
debug!("releasing key {button:?}");
|
||||||
|
slow_release(output, &mut shift_held, &button).await;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(target_arch = "x86_64", 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 tgnt::{button::Modifier, keys::Key};
|
||||||
|
|
||||||
|
struct Test {
|
||||||
|
// button index, pressed, delay
|
||||||
|
input: Vec<(usize, bool, Duration)>,
|
||||||
|
expected: Vec<button::Event>,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! timing_test {
|
||||||
|
($name:ident, $test:expr) => {
|
||||||
|
#[test]
|
||||||
|
fn $name() {
|
||||||
|
let test: Test = $test;
|
||||||
|
run_test(test);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const SHORT: Duration = Duration::from_millis(1);
|
||||||
|
const LONG: Duration = Duration::from_millis(160);
|
||||||
|
|
||||||
|
timing_test! {
|
||||||
|
modtap_mod,
|
||||||
|
Test {
|
||||||
|
input: vec![
|
||||||
|
(0, true, SHORT),
|
||||||
|
(1, true, SHORT),
|
||||||
|
(1, false, SHORT),
|
||||||
|
(0, false, SHORT),
|
||||||
|
],
|
||||||
|
expected: vec![
|
||||||
|
button::Event::PressMod(Modifier::LShift),
|
||||||
|
button::Event::PressKey(Key::B),
|
||||||
|
button::Event::ReleaseKey(Key::B),
|
||||||
|
button::Event::ReleaseMod(Modifier::LShift),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timing_test! {
|
||||||
|
modtap_tap,
|
||||||
|
Test {
|
||||||
|
input: vec![
|
||||||
|
(0, true, SHORT),
|
||||||
|
(1, true, SHORT),
|
||||||
|
(0, false, SHORT),
|
||||||
|
(1, false, SHORT),
|
||||||
|
],
|
||||||
|
expected: vec![
|
||||||
|
button::Event::PressKey(Key::A),
|
||||||
|
button::Event::ReleaseKey(Key::A),
|
||||||
|
button::Event::PressKey(Key::B),
|
||||||
|
button::Event::ReleaseKey(Key::B),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timing_test! { modtap_tap_2x, Test {
|
||||||
|
input: vec![
|
||||||
|
(0, true, SHORT),
|
||||||
|
(2, true, SHORT),
|
||||||
|
(2, false, SHORT),
|
||||||
|
(0, false, SHORT),
|
||||||
|
(0, true, SHORT),
|
||||||
|
(2, true, SHORT),
|
||||||
|
(2, false, SHORT),
|
||||||
|
(0, false, SHORT),
|
||||||
|
],
|
||||||
|
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),
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
|
||||||
|
timing_test! { modtap_double_shift, Test {
|
||||||
|
input: vec![
|
||||||
|
(0, true, LONG),
|
||||||
|
(3, true, SHORT),
|
||||||
|
(0, false, LONG),
|
||||||
|
(3, false, SHORT),
|
||||||
|
],
|
||||||
|
expected: vec![
|
||||||
|
button::Event::PressMod(Modifier::LShift),
|
||||||
|
button::Event::ReleaseMod(Modifier::LShift),
|
||||||
|
button::Event::PressMod(Modifier::RShift),
|
||||||
|
button::Event::ReleaseMod(Modifier::RShift),
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
|
||||||
|
fn run_test(test: Test) {
|
||||||
|
let _ = simple_logger::SimpleLogger::new().init();
|
||||||
|
|
||||||
|
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),
|
||||||
|
Button::ModTap(Key::D, Modifier::RShift),
|
||||||
|
];
|
||||||
|
|
||||||
|
// future that iterates over the test input and sends switch events.
|
||||||
|
let clicker = 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let output_listener = async {
|
||||||
|
let mut got = Vec::new();
|
||||||
|
for _expected in &test.expected {
|
||||||
|
let timeout = LONG + Duration::from_millis(50);
|
||||||
|
let event = match with_timeout(timeout, 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(())
|
||||||
|
};
|
||||||
|
|
||||||
|
embassy_futures::block_on(async {
|
||||||
|
let r = select(
|
||||||
|
join(clicker, output_listener),
|
||||||
|
keypress_handler(
|
||||||
|
&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.expected, got
|
||||||
|
),
|
||||||
|
Either::Second(never) => never,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
panic!();
|
||||||
|
}
|
||||||
|
}
|
||||||
269
lib/src/keys.rs
Normal file
269
lib/src/keys.rs
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
use msgpck::{MsgPack, MsgUnpack};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, MsgPack, MsgUnpack)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
// https://usb.org/sites/default/files/hut1_3_0.pdf
|
||||||
|
pub enum Key {
|
||||||
|
A = 0x04,
|
||||||
|
B = 0x05,
|
||||||
|
C = 0x06,
|
||||||
|
D = 0x07,
|
||||||
|
E = 0x08,
|
||||||
|
F = 0x09,
|
||||||
|
G = 0x0A,
|
||||||
|
H = 0x0B,
|
||||||
|
I = 0x0C,
|
||||||
|
J = 0x0D,
|
||||||
|
K = 0x0E,
|
||||||
|
L = 0x0F,
|
||||||
|
M = 0x10,
|
||||||
|
N = 0x11,
|
||||||
|
O = 0x12,
|
||||||
|
P = 0x13,
|
||||||
|
Q = 0x14,
|
||||||
|
R = 0x15,
|
||||||
|
S = 0x16,
|
||||||
|
T = 0x17,
|
||||||
|
U = 0x18,
|
||||||
|
V = 0x19,
|
||||||
|
W = 0x1A,
|
||||||
|
X = 0x1B,
|
||||||
|
Y = 0x1C,
|
||||||
|
Z = 0x1D,
|
||||||
|
|
||||||
|
/// Keyboard 1 and !
|
||||||
|
D1 = 0x1E,
|
||||||
|
/// Keyboard 2 and @
|
||||||
|
D2 = 0x1F,
|
||||||
|
/// Keyboard 3 and #
|
||||||
|
D3 = 0x20,
|
||||||
|
/// Keyboard 4 and $
|
||||||
|
D4 = 0x21,
|
||||||
|
/// Keyboard 5 and %
|
||||||
|
D5 = 0x22,
|
||||||
|
/// Keyboard 6 and ∧
|
||||||
|
D6 = 0x23,
|
||||||
|
/// Keyboard 7 and &
|
||||||
|
D7 = 0x24,
|
||||||
|
/// Keyboard 8 and *
|
||||||
|
D8 = 0x25,
|
||||||
|
/// Keyboard 9 and (
|
||||||
|
D9 = 0x26,
|
||||||
|
/// Keyboard 0 and )
|
||||||
|
D0 = 0x27,
|
||||||
|
|
||||||
|
Return = 0x28,
|
||||||
|
Escape = 0x29,
|
||||||
|
/// Keyboard DELETE (Backspace)
|
||||||
|
Backspace = 0x2A,
|
||||||
|
Tab = 0x2B,
|
||||||
|
|
||||||
|
/// Keyboard Spacebar
|
||||||
|
Space = 0x2C,
|
||||||
|
/// Keyboard - and (underscore)
|
||||||
|
Dash = 0x2D,
|
||||||
|
/// Keyboard = and +2
|
||||||
|
Equal = 0x2E,
|
||||||
|
/// Keyboard [ and {
|
||||||
|
LBracket = 0x2F,
|
||||||
|
/// Keyboard ] and }
|
||||||
|
RBracket = 0x30,
|
||||||
|
/// Keyboard \and |
|
||||||
|
BackslashPipe = 0x31,
|
||||||
|
/// Keyboard Non-US # and ̃
|
||||||
|
Pound = 0x32,
|
||||||
|
/// Keyboard ; and :
|
||||||
|
Colon = 0x33,
|
||||||
|
/// Keyboard ‘ and “
|
||||||
|
Apostrophe = 0x34,
|
||||||
|
/// Keyboard Grave Accent and Tilde
|
||||||
|
Accent = 0x35,
|
||||||
|
/// Keyboard , and <
|
||||||
|
Comma = 0x36,
|
||||||
|
/// Keyboard . and >
|
||||||
|
Period = 0x37,
|
||||||
|
/// Keyboard / and ?
|
||||||
|
Slash = 0x38,
|
||||||
|
CapsLock = 0x39,
|
||||||
|
|
||||||
|
F1 = 0x3A,
|
||||||
|
F2 = 0x3B,
|
||||||
|
F3 = 0x3C,
|
||||||
|
F4 = 0x3D,
|
||||||
|
F5 = 0x3E,
|
||||||
|
F6 = 0x3F,
|
||||||
|
F7 = 0x40,
|
||||||
|
F8 = 0x41,
|
||||||
|
F9 = 0x42,
|
||||||
|
F10 = 0x43,
|
||||||
|
F11 = 0x44,
|
||||||
|
F12 = 0x45,
|
||||||
|
|
||||||
|
/// Keyboard PrintScreen
|
||||||
|
PrintScreen = 0x46,
|
||||||
|
/// Keyboard Scroll Lock
|
||||||
|
ScrollLock = 0x47,
|
||||||
|
/// Keyboard Pause
|
||||||
|
Pause = 0x48,
|
||||||
|
/// Keyboard Insert
|
||||||
|
Insert = 0x49,
|
||||||
|
/// Keyboard Home
|
||||||
|
Home = 0x4A,
|
||||||
|
/// Keyboard PageUp
|
||||||
|
PageUp = 0x4B,
|
||||||
|
/// Keyboard Delete Forward
|
||||||
|
Delete = 0x4C,
|
||||||
|
/// Keyboard End
|
||||||
|
End = 0x4D,
|
||||||
|
/// 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,
|
||||||
|
/// Keypad *
|
||||||
|
KeypadAsterisk = 0x55,
|
||||||
|
/// Keypad -
|
||||||
|
KeypadMinus = 0x56,
|
||||||
|
/// Keypad +
|
||||||
|
KeypadPlus = 0x57,
|
||||||
|
/// KeypadEnter
|
||||||
|
KeypadEnter = 0x58,
|
||||||
|
/// Keypad 1 and End
|
||||||
|
Keypad1 = 0x59,
|
||||||
|
/// Keypad 2 and Down Arrow
|
||||||
|
Keypad2 = 0x5A,
|
||||||
|
/// Keypad 3 and PageDn
|
||||||
|
Keypad3 = 0x5B,
|
||||||
|
/// Keypad 4 and Left Arrow
|
||||||
|
Keypad4 = 0x5C,
|
||||||
|
/// Keypad 5
|
||||||
|
Keypad5 = 0x5D,
|
||||||
|
/// Keypad 6 and Right Arrow
|
||||||
|
Keypad6 = 0x5E,
|
||||||
|
/// Keypad 7 and Home
|
||||||
|
Keypad7 = 0x5F,
|
||||||
|
/// Keypad 8 and Up Arrow
|
||||||
|
Keypad8 = 0x60,
|
||||||
|
/// Keypad 9 and PageUp
|
||||||
|
Keypad9 = 0x61,
|
||||||
|
/// Keypad 0 and Insert
|
||||||
|
Keypad0 = 0x62,
|
||||||
|
|
||||||
|
/// Keypad . and Delete
|
||||||
|
KbPeriod = 0x63,
|
||||||
|
/// Keyboard Non-US \ and |
|
||||||
|
NonUsBackslashPipe = 0x64,
|
||||||
|
Application = 0x65,
|
||||||
|
Power = 0x66,
|
||||||
|
/// Keypad =
|
||||||
|
KpEqual = 0x67,
|
||||||
|
F13 = 0x68,
|
||||||
|
F14 = 0x69,
|
||||||
|
F15 = 0x6A,
|
||||||
|
F16 = 0x6B,
|
||||||
|
F17 = 0x6C,
|
||||||
|
F18 = 0x6D,
|
||||||
|
F19 = 0x6E,
|
||||||
|
F20 = 0x6F,
|
||||||
|
F21 = 0x70,
|
||||||
|
F22 = 0x71,
|
||||||
|
F23 = 0x72,
|
||||||
|
F24 = 0x73,
|
||||||
|
Execute = 0x74,
|
||||||
|
Help = 0x75,
|
||||||
|
Menu = 0x76,
|
||||||
|
Select = 0x77,
|
||||||
|
Stop = 0x78,
|
||||||
|
Again = 0x79,
|
||||||
|
Undo = 0x7A,
|
||||||
|
Cut = 0x7B,
|
||||||
|
Copy = 0x7C,
|
||||||
|
Paste = 0x7D,
|
||||||
|
Find = 0x7E,
|
||||||
|
Mute = 0x7F,
|
||||||
|
VolumeUp = 0x80,
|
||||||
|
VolumeDown = 0x81,
|
||||||
|
/// Keyboard Locking Caps Lock
|
||||||
|
LockingCapsLock = 0x82,
|
||||||
|
/// Keyboard Locking Num Lock
|
||||||
|
LockingNumLock = 0x83,
|
||||||
|
/// Keyboard Locking Scroll Lock
|
||||||
|
LockingScrollLock = 0x84,
|
||||||
|
/// Keypad ,
|
||||||
|
KpComma = 0x85,
|
||||||
|
/// Keypad Equal Sign
|
||||||
|
KpEqualSIgn = 0x86,
|
||||||
|
|
||||||
|
International1 = 0x87,
|
||||||
|
International2 = 0x88,
|
||||||
|
International3 = 0x89,
|
||||||
|
International4 = 0x8A,
|
||||||
|
International5 = 0x8B,
|
||||||
|
International6 = 0x8C,
|
||||||
|
International7 = 0x8D,
|
||||||
|
International8 = 0x8E,
|
||||||
|
International9 = 0x8F,
|
||||||
|
Lang1 = 0x90,
|
||||||
|
Lang2 = 0x91,
|
||||||
|
Lang3 = 0x92,
|
||||||
|
Lang4 = 0x93,
|
||||||
|
Lang5 = 0x94,
|
||||||
|
Lang6 = 0x95,
|
||||||
|
Lang7 = 0x96,
|
||||||
|
Lang8 = 0x97,
|
||||||
|
Lang9 = 0x98,
|
||||||
|
/// Keyboard Alternative Erase
|
||||||
|
AltErase = 0x99,
|
||||||
|
/// Keyboard SysReq/Attention
|
||||||
|
SysReq = 0x9A,
|
||||||
|
Cancel = 0x9B,
|
||||||
|
Clear = 0x9C,
|
||||||
|
Prior = 0x9D,
|
||||||
|
/// Keyboard Return
|
||||||
|
Return2 = 0x9E,
|
||||||
|
/// Keyboard Separator
|
||||||
|
Separator = 0x9F,
|
||||||
|
|
||||||
|
/// Keyboard Out
|
||||||
|
Out = 0xA0,
|
||||||
|
/// Keyboard Oper
|
||||||
|
Oper = 0xA1,
|
||||||
|
/// Keyboard Clear/Again
|
||||||
|
ClearAgain = 0xA2,
|
||||||
|
/// Keyboard CrSel/Props
|
||||||
|
CrSel = 0xA3,
|
||||||
|
/// Keyboard ExSel
|
||||||
|
ExSel = 0xA4,
|
||||||
|
//
|
||||||
|
// NOTE: the usb keyboard/keypad page allegedly har characters in the rage above A4,
|
||||||
|
// like the ones below, but they don't seem to work
|
||||||
|
///// Keypad {
|
||||||
|
//LCurly = 0xB8,
|
||||||
|
///// Keypad }
|
||||||
|
//RCurly = 0xB9,
|
||||||
|
//
|
||||||
|
// NOTE: Not sure if these are required
|
||||||
|
//LCtrl = 0xE0,
|
||||||
|
//LShift = 0xE1,
|
||||||
|
//LAlt = 0xE2,
|
||||||
|
//RCtrl = 0xE4,
|
||||||
|
//RShift = 0xE5,
|
||||||
|
//RAlt = 0xE6,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Key> for u8 {
|
||||||
|
fn from(key: Key) -> u8 {
|
||||||
|
key as u8
|
||||||
|
}
|
||||||
|
}
|
||||||
12
lib/src/layer.rs
Normal file
12
lib/src/layer.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use alloc::vec::Vec;
|
||||||
|
use msgpck::{MsgPack, MsgUnpack};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::button::Button;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Deserialize, Clone, MsgPack, MsgUnpack)]
|
||||||
|
pub struct Layer {
|
||||||
|
pub buttons: Vec<Button>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Layers = Vec<Vec<Layer>>;
|
||||||
36
lib/src/layout.rs
Normal file
36
lib/src/layout.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
use alloc::vec::Vec;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Layout {
|
||||||
|
pub buttons: Vec<Rect>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Rect {
|
||||||
|
pub x: f32,
|
||||||
|
pub y: f32,
|
||||||
|
|
||||||
|
#[serde(default = "default_width")]
|
||||||
|
pub w: f32,
|
||||||
|
#[serde(default = "default_height")]
|
||||||
|
pub h: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Rect {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
w: default_width(),
|
||||||
|
h: default_height(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_width() -> f32 {
|
||||||
|
1.0
|
||||||
|
}
|
||||||
|
fn default_height() -> f32 {
|
||||||
|
1.0
|
||||||
|
}
|
||||||
37
lib/src/lib.rs
Normal file
37
lib/src/lib.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#![cfg_attr(not(feature = "std"), 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 atomics;
|
||||||
|
pub mod button;
|
||||||
|
pub mod event;
|
||||||
|
pub mod keypress_handler;
|
||||||
|
pub mod keys;
|
||||||
|
pub mod layer;
|
||||||
|
pub mod layout;
|
||||||
|
pub mod logger;
|
||||||
|
pub mod rgb;
|
||||||
|
pub mod rtt;
|
||||||
|
pub mod serial_proto;
|
||||||
|
pub mod util;
|
||||||
43
lib/src/lights.rs
Normal file
43
lib/src/lights.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
pub mod shaders;
|
||||||
|
|
||||||
|
use crate::ws2812::Ws2812;
|
||||||
|
use embassy_rp::pio;
|
||||||
|
use embassy_sync::mutex::Mutex;
|
||||||
|
|
||||||
|
use crate::{rgb::Rgb, util::CS};
|
||||||
|
|
||||||
|
pub struct Lights<P: pio::Instance + 'static, const N: usize> {
|
||||||
|
state: Mutex<CS, State<P, N>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State<P: pio::Instance + 'static, const N: usize> {
|
||||||
|
colors: [Rgb; N],
|
||||||
|
driver: Ws2812<P>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: pio::Instance, const N: usize> Lights<P, N> {
|
||||||
|
pub const fn new(driver: Ws2812<P>) -> Self {
|
||||||
|
Lights {
|
||||||
|
state: Mutex::new(State {
|
||||||
|
colors: [Rgb::new(0, 0, 0); N],
|
||||||
|
driver,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn colors_mut(&mut self) -> &[Rgb; N] {
|
||||||
|
&self.state.get_mut().colors
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run a function to update the colors, and then immediately refresh the LEDs.
|
||||||
|
pub async fn update(&self, f: impl FnOnce(&mut [Rgb; N])) {
|
||||||
|
let State { colors, driver } = &mut *self.state.lock().await;
|
||||||
|
f(colors);
|
||||||
|
driver.write(colors).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the LEDs with the currently set colors.
|
||||||
|
pub async fn refresh(&self) {
|
||||||
|
self.update(|_| ()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
117
lib/src/lights/shaders.rs
Normal file
117
lib/src/lights/shaders.rs
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
use core::f32::consts::PI;
|
||||||
|
|
||||||
|
use embassy_time::{Duration, Instant};
|
||||||
|
use glam::{vec2, vec3, Vec3};
|
||||||
|
use libm::cosf;
|
||||||
|
|
||||||
|
use crate::rgb::Rgb;
|
||||||
|
|
||||||
|
/// A fragment shader.
|
||||||
|
pub trait Shader {
|
||||||
|
/// Sample a normalized coordinate (0 to 1) using the shader function and return a color.
|
||||||
|
fn sample(&self, time: Instant, uv: (f32, f32)) -> Rgb;
|
||||||
|
|
||||||
|
fn end_time(&self) -> Option<Instant> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Shaders {
|
||||||
|
OrthoRainbow,
|
||||||
|
LsdHyperspace,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shader for Shaders {
|
||||||
|
fn sample(&self, time: Instant, uv: (f32, f32)) -> Rgb {
|
||||||
|
match self {
|
||||||
|
Shaders::OrthoRainbow => OrthoRainbow.sample(time, uv),
|
||||||
|
Shaders::LsdHyperspace => LsdHyperspace.sample(time, uv),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct OrthoRainbow;
|
||||||
|
impl Shader for OrthoRainbow {
|
||||||
|
fn sample(&self, time: Instant, (x, y): (f32, f32)) -> Rgb {
|
||||||
|
let time = time.as_millis() as f32 / 1000.0;
|
||||||
|
let r = 0.5 + 0.5 * cosf(time + x + 0.0);
|
||||||
|
let g = 0.5 + 0.5 * cosf(time + y + 2.0);
|
||||||
|
let b = 0.5 + 0.5 * cosf(time + x + 4.0);
|
||||||
|
|
||||||
|
Rgb::from_f32s(r, g, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LsdHyperspace;
|
||||||
|
impl Shader for LsdHyperspace {
|
||||||
|
fn sample(&self, time: Instant, uv: (f32, f32)) -> Rgb {
|
||||||
|
let time = time.as_millis() as f32 / 1000.0 * 3.0;
|
||||||
|
let uv = vec2(uv.0, uv.1);
|
||||||
|
|
||||||
|
let center = vec2(0.5, 0.5);
|
||||||
|
|
||||||
|
let dist = (uv - center).length();
|
||||||
|
|
||||||
|
let fac = dist * PI + vec3(0.0, 1.0, 4.0) - time;
|
||||||
|
let col = cos3(fac) * cos3(fac * 0.5);
|
||||||
|
|
||||||
|
Rgb::from_f32s(col.x, col.y, col.z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PowerOffAnim {
|
||||||
|
/// Animation starting time.
|
||||||
|
pub start: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PowerOffAnim {
|
||||||
|
const FADE_FACTOR: f32 = 0.5;
|
||||||
|
|
||||||
|
/// Animation duration, in seconds.
|
||||||
|
const DURATION_SEC: u16 = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shader for PowerOffAnim {
|
||||||
|
fn sample(&self, time: Instant, (_, y): (f32, f32)) -> Rgb {
|
||||||
|
let time = time.as_millis().saturating_sub(self.start.as_millis());
|
||||||
|
let time = time as f32 / 1000.0;
|
||||||
|
|
||||||
|
let duration: f32 = Self::DURATION_SEC.into();
|
||||||
|
let r = Self::FADE_FACTOR * (duration - time - 1.0 + y);
|
||||||
|
Rgb::from_f32s(r, 0.0, 0.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end_time(&self) -> Option<Instant> {
|
||||||
|
Some(self.start + Duration::from_secs(Self::DURATION_SEC.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PowerOnAnim {
|
||||||
|
/// Animation starting time.
|
||||||
|
pub start: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PowerOnAnim {
|
||||||
|
const FADE_FACTOR: f32 = 1.0;
|
||||||
|
|
||||||
|
/// Animation duration, in seconds.
|
||||||
|
const DURATION_SEC: u16 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cos3(v: Vec3) -> Vec3 {
|
||||||
|
vec3(cosf(v.x), cosf(v.y), cosf(v.z))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shader for PowerOnAnim {
|
||||||
|
fn sample(&self, time: Instant, (_, y): (f32, f32)) -> Rgb {
|
||||||
|
let time = time.as_millis().saturating_sub(self.start.as_millis());
|
||||||
|
let time = time as f32 / 1000.0;
|
||||||
|
|
||||||
|
let duration: f32 = Self::DURATION_SEC.into();
|
||||||
|
let g = Self::FADE_FACTOR * (duration - time - y);
|
||||||
|
Rgb::from_f32s(0.0, g, 0.0)
|
||||||
|
}
|
||||||
|
fn end_time(&self) -> Option<Instant> {
|
||||||
|
Some(self.start + Duration::from_secs(Self::DURATION_SEC.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
81
lib/src/logger.rs
Normal file
81
lib/src/logger.rs
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
use core::{fmt::Arguments, sync::atomic::Ordering};
|
||||||
|
|
||||||
|
use embassy_time::Instant;
|
||||||
|
use log::{Level, Metadata, Record};
|
||||||
|
use portable_atomic::AtomicBool;
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
|
pub const LOGGER_OUTPUTS: usize = 2;
|
||||||
|
|
||||||
|
/// A logger which timestamps logs and forwards them to mutiple other loggers.
|
||||||
|
pub struct LogMultiplexer {
|
||||||
|
pub outputs: [&'static dyn LogOutput; LOGGER_OUTPUTS],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TimestampedRecord<'a> {
|
||||||
|
pub record: Record<'a>,
|
||||||
|
|
||||||
|
/// Timestamp
|
||||||
|
pub timestamp: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LogMultiplexer {
|
||||||
|
/// Set this as the global logger.
|
||||||
|
///
|
||||||
|
/// Calling this function more than once does nothing.
|
||||||
|
pub fn init(self) {
|
||||||
|
// guard against calling this multiple times
|
||||||
|
static INITIALIZED: AtomicBool = AtomicBool::new(false);
|
||||||
|
if INITIALIZED.fetch_or(true, Ordering::SeqCst) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LOGGER: StaticCell<LogMultiplexer> = StaticCell::new();
|
||||||
|
let logger = LOGGER.init(self);
|
||||||
|
unsafe {
|
||||||
|
log::set_logger_racy(logger).unwrap();
|
||||||
|
log::set_max_level_racy(log::LevelFilter::Debug);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl log::Log for LogMultiplexer {
|
||||||
|
fn enabled(&self, _metadata: &Metadata) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&self, record: &Record) {
|
||||||
|
if self.enabled(record.metadata()) {
|
||||||
|
let timestamp = Instant::now();
|
||||||
|
|
||||||
|
let record = TimestampedRecord {
|
||||||
|
timestamp,
|
||||||
|
record: record.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for output in &self.outputs {
|
||||||
|
output.log(&record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait LogOutput: Send + Sync {
|
||||||
|
fn log(&self, record: &TimestampedRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TimestampedRecord<'_> {
|
||||||
|
pub fn args(&self) -> &Arguments<'_> {
|
||||||
|
self.record.args()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn level(&self) -> Level {
|
||||||
|
self.metadata().level()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn metadata(&self) -> &Metadata<'_> {
|
||||||
|
self.record.metadata()
|
||||||
|
}
|
||||||
|
}
|
||||||
29
lib/src/panic_handler.rs
Normal file
29
lib/src/panic_handler.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
use core::{fmt::Write, panic::PanicInfo};
|
||||||
|
|
||||||
|
use cortex_m::asm;
|
||||||
|
use embassy_rp::gpio::{Level, Output};
|
||||||
|
|
||||||
|
use crate::rtt::rtt_write;
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic_blink(info: &PanicInfo) -> ! {
|
||||||
|
cortex_m::interrupt::disable();
|
||||||
|
|
||||||
|
let _ = write!(&mut Writer, "{info}");
|
||||||
|
|
||||||
|
// SAFETY: we panicked, so no other code will be running.
|
||||||
|
let p = unsafe { embassy_rp::Peripherals::steal() };
|
||||||
|
let _led = Output::new(p.PIN_11, Level::High);
|
||||||
|
|
||||||
|
asm::udf()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write to RTT
|
||||||
|
struct Writer;
|
||||||
|
|
||||||
|
impl core::fmt::Write for Writer {
|
||||||
|
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
|
||||||
|
rtt_write(s);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
85
lib/src/rgb.rs
Normal file
85
lib/src/rgb.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
use bytemuck::{cast_slice, Pod, Zeroable};
|
||||||
|
use core::{
|
||||||
|
fmt::{self, Debug},
|
||||||
|
ops::{Div, Mul},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 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]))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_f64s(r: f64, g: f64, b: f64) -> Self {
|
||||||
|
let r = r.clamp(0.0, 1.0) * 255.0;
|
||||||
|
let g = g.clamp(0.0, 1.0) * 255.0;
|
||||||
|
let b = b.clamp(0.0, 1.0) * 255.0;
|
||||||
|
Self::new(r as u8, g as u8, b as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_f32s(r: f32, g: f32, b: f32) -> Self {
|
||||||
|
let r = r.clamp(0.0, 1.0) * 255.0;
|
||||||
|
let g = g.clamp(0.0, 1.0) * 255.0;
|
||||||
|
let b = b.clamp(0.0, 1.0) * 255.0;
|
||||||
|
Self::new(r as u8, g as u8, b as u8)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<f32> for Rgb {
|
||||||
|
type Output = Rgb;
|
||||||
|
|
||||||
|
fn mul(self, factor: f32) -> Self::Output {
|
||||||
|
let [r, g, b] = self.components();
|
||||||
|
Rgb::new(
|
||||||
|
((r as f32) * factor) as u8,
|
||||||
|
((g as f32) * factor) as u8,
|
||||||
|
((b as f32) * factor) as u8,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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);
|
||||||
|
}
|
||||||
|
}
|
||||||
64
lib/src/rtt.rs
Normal file
64
lib/src/rtt.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
use core::{cell::RefCell, fmt::Write};
|
||||||
|
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use rtt_target::{rtt_init, UpChannel};
|
||||||
|
|
||||||
|
use crate::logger::{LogOutput, TimestampedRecord};
|
||||||
|
|
||||||
|
pub type RttWriteFn = fn(&str);
|
||||||
|
|
||||||
|
static CHANNEL: Mutex<RefCell<Option<UpChannel>>> = Mutex::new(RefCell::new(None));
|
||||||
|
|
||||||
|
/// Write directly to the rtt output. Must call [init_rtt_logger] first.
|
||||||
|
pub fn rtt_write(s: &str) {
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut slot = CHANNEL.borrow_ref_mut(cs);
|
||||||
|
if let Some(channel) = slot.as_mut() {
|
||||||
|
channel.write(s.as_bytes());
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_rtt_logger() -> &'static RttLogger {
|
||||||
|
let channels = rtt_init! {
|
||||||
|
up: {
|
||||||
|
0: {
|
||||||
|
size: 1024
|
||||||
|
mode: NoBlockSkip
|
||||||
|
name: "Terminal"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
let mut slot = CHANNEL.borrow_ref_mut(cs);
|
||||||
|
|
||||||
|
if slot.is_none() {
|
||||||
|
*slot = Some(channels.up.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RTT_LOGGER: RttLogger = RttLogger;
|
||||||
|
&RTT_LOGGER
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RttLogger;
|
||||||
|
|
||||||
|
impl LogOutput for RttLogger {
|
||||||
|
fn log(&self, record: &TimestampedRecord) {
|
||||||
|
let s = record.timestamp.as_secs();
|
||||||
|
let ms = record.timestamp.as_millis() % 1000;
|
||||||
|
let level = record.level();
|
||||||
|
let mut w = &mut Writer;
|
||||||
|
let _ = writeln!(&mut w, "[{s}.{ms:04}] ({level}) {}", record.args());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Writer;
|
||||||
|
|
||||||
|
impl Write for Writer {
|
||||||
|
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
|
||||||
|
rtt_write(s);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
71
lib/src/serial_proto.rs
Normal file
71
lib/src/serial_proto.rs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
pub mod owned {
|
||||||
|
use alloc::string::String;
|
||||||
|
use msgpck::{MsgPack, MsgUnpack};
|
||||||
|
|
||||||
|
use crate::layer::Layers;
|
||||||
|
|
||||||
|
#[derive(Debug, MsgPack, MsgUnpack)]
|
||||||
|
pub struct LogRecord {
|
||||||
|
/// Milliseconds since boot
|
||||||
|
pub timestamp: u64,
|
||||||
|
|
||||||
|
pub level: String,
|
||||||
|
|
||||||
|
pub message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Press the switch with the provided index.
|
||||||
|
#[derive(Debug, MsgPack, MsgUnpack)]
|
||||||
|
pub struct SwitchPress(pub u16);
|
||||||
|
|
||||||
|
/// Release the switch with the provided index.
|
||||||
|
#[derive(Debug, MsgPack, MsgUnpack)]
|
||||||
|
pub struct SwitchRelease(pub u16);
|
||||||
|
|
||||||
|
/// Change to the layer at the provided coordinates in the layer matrix.
|
||||||
|
#[derive(Debug, MsgPack, MsgUnpack)]
|
||||||
|
pub struct ChangeLayer {
|
||||||
|
pub x: u16,
|
||||||
|
pub y: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, MsgPack, MsgUnpack)]
|
||||||
|
pub enum DeviceMsg {
|
||||||
|
Log(LogRecord),
|
||||||
|
SwitchPress(SwitchPress),
|
||||||
|
SwitchRelease(SwitchRelease),
|
||||||
|
ChangeLayer(ChangeLayer),
|
||||||
|
Layers(Layers),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, MsgPack, MsgUnpack)]
|
||||||
|
pub enum HostMsg {
|
||||||
|
GetLayers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod borrowed {
|
||||||
|
use msgpck::MsgPack;
|
||||||
|
|
||||||
|
use super::owned::*;
|
||||||
|
use crate::{layer::Layers, util::DisplayPack};
|
||||||
|
|
||||||
|
#[derive(Debug, MsgPack)]
|
||||||
|
pub struct LogRecord<'a> {
|
||||||
|
/// Milliseconds since boot
|
||||||
|
pub timestamp: u64,
|
||||||
|
|
||||||
|
pub level: DisplayPack<log::Level>,
|
||||||
|
|
||||||
|
pub message: DisplayPack<&'a core::fmt::Arguments<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, MsgPack)]
|
||||||
|
pub enum DeviceMsg<'a> {
|
||||||
|
Log(LogRecord<'a>),
|
||||||
|
SwitchPress(SwitchPress),
|
||||||
|
SwitchRelease(SwitchRelease),
|
||||||
|
ChangeLayer(ChangeLayer),
|
||||||
|
Layers(&'a Layers),
|
||||||
|
}
|
||||||
|
}
|
||||||
189
lib/src/uart.rs
Normal file
189
lib/src/uart.rs
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
use core::mem::size_of;
|
||||||
|
|
||||||
|
use bytemuck::{cast, AnyBitPattern, NoUninit};
|
||||||
|
use crc_any::CRCu16;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::peripherals::{PIN_0, PIN_1, UART0};
|
||||||
|
use embassy_rp::uart::{self, BufferedUart, DataBits, Parity, StopBits};
|
||||||
|
use embassy_sync::channel::Channel;
|
||||||
|
use embassy_sync::pubsub::PubSubBehavior;
|
||||||
|
use embedded_io_async::{Read, Write};
|
||||||
|
use futures::{select_biased, FutureExt};
|
||||||
|
use heapless::Vec;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
|
use crate::event::{switch, Half};
|
||||||
|
use crate::interrupts::Irqs;
|
||||||
|
use crate::keyboard::KbEvents;
|
||||||
|
use crate::usb::{UsbEvent, USB_EVENTS};
|
||||||
|
use crate::util::CS;
|
||||||
|
|
||||||
|
/// Channel for [UsbEvent]s to be sent on the uart line.
|
||||||
|
pub static UART_USB_EVENTS_OUT: Channel<CS, UsbEvent, 8> = Channel::new();
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
enum Message {
|
||||||
|
KeyboardEvent(switch::Event),
|
||||||
|
UsbEvent(UsbEvent),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn start(tx: PIN_0, rx: PIN_1, uart: UART0, board: Half, events: KbEvents) {
|
||||||
|
static TX_BUF: StaticCell<[u8; 256]> = StaticCell::new();
|
||||||
|
static RX_BUF: StaticCell<[u8; 256]> = StaticCell::new();
|
||||||
|
|
||||||
|
let mut config = uart::Config::default();
|
||||||
|
config.baudrate = 115200;
|
||||||
|
config.data_bits = DataBits::DataBits8;
|
||||||
|
config.stop_bits = StopBits::STOP1;
|
||||||
|
config.parity = Parity::ParityNone;
|
||||||
|
|
||||||
|
let uart = embassy_rp::uart::BufferedUart::new(
|
||||||
|
uart,
|
||||||
|
Irqs,
|
||||||
|
tx,
|
||||||
|
rx,
|
||||||
|
TX_BUF.init_with(|| [0u8; 256]),
|
||||||
|
RX_BUF.init_with(|| [0u8; 256]),
|
||||||
|
config,
|
||||||
|
);
|
||||||
|
|
||||||
|
Spawner::for_current_executor()
|
||||||
|
.await
|
||||||
|
.must_spawn(uart_task(uart, board, events))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn uart_task(uart: BufferedUart<'static, UART0>, this_half: Half, mut events: KbEvents) {
|
||||||
|
let (mut rx, mut tx) = uart.split();
|
||||||
|
let (mut events_rx, mut events_tx) = events.split();
|
||||||
|
|
||||||
|
/// The header of a UART packet.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, NoUninit, AnyBitPattern)]
|
||||||
|
struct Header {
|
||||||
|
/// The length of the payload.
|
||||||
|
len: u8,
|
||||||
|
|
||||||
|
/// An arbitrary value to feed the crc, should be different for each message.
|
||||||
|
random: u8,
|
||||||
|
|
||||||
|
/// A little-endian crc16.
|
||||||
|
crc: [u8; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
const HEADER_LEN: usize = size_of::<Header>();
|
||||||
|
|
||||||
|
let rx_task = async {
|
||||||
|
let mut buf: heapless::Vec<u8, 1024> = Vec::new();
|
||||||
|
loop {
|
||||||
|
if buf.len() >= HEADER_LEN {
|
||||||
|
let (&header, rest) = buf.split_first_chunk::<HEADER_LEN>().unwrap();
|
||||||
|
let header: Header = cast(header);
|
||||||
|
let crc = u16::from_le_bytes(header.crc);
|
||||||
|
let mut calculated_crc = CRCu16::crc16();
|
||||||
|
calculated_crc.digest(&[header.len, header.random]);
|
||||||
|
let calculated_crc = calculated_crc.get_crc();
|
||||||
|
|
||||||
|
if calculated_crc != crc {
|
||||||
|
log::error!("invalid uart header crc: {header:x?}");
|
||||||
|
buf.remove(0); // drop the first byte and hope we find a good packet header
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
log::trace!(
|
||||||
|
"reading from uart, header={header:?}, bytes_received={}",
|
||||||
|
rest.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
let len = usize::from(header.len);
|
||||||
|
if rest.len() >= len {
|
||||||
|
let r = postcard::from_bytes(&rest[..len]);
|
||||||
|
|
||||||
|
// drop packet from buffer
|
||||||
|
buf.rotate_left(len + HEADER_LEN);
|
||||||
|
buf.truncate(buf.len() - len - HEADER_LEN);
|
||||||
|
|
||||||
|
let message: Message = match r {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("failed to deserialize message: {e}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match &message {
|
||||||
|
Message::KeyboardEvent(event) => events_tx.send(event.clone()),
|
||||||
|
&Message::UsbEvent(event) => USB_EVENTS.publish_immediate(event),
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("got msg: {:?}", message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut chunk = [0u8; 128];
|
||||||
|
let n = match rx.read(&mut chunk).await {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("uart error: {:?}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if buf.extend_from_slice(&chunk[..n]).is_err() {
|
||||||
|
log::error!("uart buffer full");
|
||||||
|
buf.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let tx_task = async {
|
||||||
|
let mut buf = [0u8; 256 + HEADER_LEN];
|
||||||
|
let mut counter = 0u8;
|
||||||
|
|
||||||
|
// forward messages to the other keyboard half
|
||||||
|
loop {
|
||||||
|
let message = select_biased! {
|
||||||
|
event = events_rx.recv().fuse() => {
|
||||||
|
if event.source != this_half {
|
||||||
|
continue; // do not forward messages from the other half back to it
|
||||||
|
}
|
||||||
|
Message::KeyboardEvent(event)
|
||||||
|
}
|
||||||
|
event = UART_USB_EVENTS_OUT.receive().fuse() => Message::UsbEvent(event),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (buf_header, body) = buf.split_array_mut();
|
||||||
|
let serialized = match postcard::to_slice(&message, body) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("failed to serialize uart message: {e}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// add a "random" value to feed the crc
|
||||||
|
counter = counter.wrapping_add(1);
|
||||||
|
let random = counter;
|
||||||
|
|
||||||
|
let len = serialized.len() as u8;
|
||||||
|
|
||||||
|
let mut crc = CRCu16::crc16();
|
||||||
|
crc.digest(&[len, random]);
|
||||||
|
let header = Header {
|
||||||
|
len: serialized.len() as u8,
|
||||||
|
random,
|
||||||
|
crc: crc.get_crc().to_le_bytes(),
|
||||||
|
};
|
||||||
|
let header: [u8; HEADER_LEN] = cast(header);
|
||||||
|
*buf_header = header;
|
||||||
|
|
||||||
|
let package = &buf[..HEADER_LEN + usize::from(len)];
|
||||||
|
tx.write_all(package).await.ok();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
select_biased! {
|
||||||
|
_ = tx_task.fuse() => log::error!("uart tx_task exited"),
|
||||||
|
_ = rx_task.fuse() => log::error!("eart rx_task exited"),
|
||||||
|
}
|
||||||
|
}
|
||||||
136
lib/src/usb.rs
Normal file
136
lib/src/usb.rs
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::{peripherals::USB, usb::Driver};
|
||||||
|
use embassy_sync::pubsub::{PubSubBehavior, PubSubChannel};
|
||||||
|
use embassy_usb::{Builder, Config, Handler, UsbDevice};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
|
use crate::{interrupts::Irqs, keyboard::KbEvents, uart::UART_USB_EVENTS_OUT, util::CS};
|
||||||
|
|
||||||
|
use self::serial::UsbSerial;
|
||||||
|
|
||||||
|
pub mod keyboard;
|
||||||
|
pub mod serial;
|
||||||
|
|
||||||
|
pub const MAX_PACKET_SIZE: u8 = 64;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum UsbEvent {
|
||||||
|
Enabled(bool),
|
||||||
|
Suspended(bool),
|
||||||
|
Configured(bool),
|
||||||
|
Addressed(u8),
|
||||||
|
Reset,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type UsbEventChannel = PubSubChannel<CS, UsbEvent, 8, 24, 0>;
|
||||||
|
pub static USB_EVENTS: UsbEventChannel = UsbEventChannel::new();
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
device_descriptor: [u8; 256],
|
||||||
|
config_descriptor: [u8; 256],
|
||||||
|
bos_descriptor: [u8; 256],
|
||||||
|
msos_descriptor: [u8; 256],
|
||||||
|
control_buf: [u8; 64],
|
||||||
|
handler: UsbHandler,
|
||||||
|
}
|
||||||
|
|
||||||
|
static STATE: StaticCell<State> = StaticCell::new();
|
||||||
|
|
||||||
|
pub async fn setup_logger_and_keyboard(usb: USB, events: KbEvents) -> &'static UsbSerial {
|
||||||
|
let mut builder = builder(usb);
|
||||||
|
|
||||||
|
let usb_serial = serial::setup(&mut builder).await;
|
||||||
|
keyboard::setup(&mut builder, events).await;
|
||||||
|
|
||||||
|
log::info!("building usb device");
|
||||||
|
let usb = builder.build();
|
||||||
|
log::info!("spawning usb task");
|
||||||
|
Spawner::for_current_executor().await.must_spawn(run(usb));
|
||||||
|
|
||||||
|
usb_serial
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn builder(usb: USB) -> Builder<'static, Driver<'static, USB>> {
|
||||||
|
// calling init here can't panic because this function can't be called
|
||||||
|
// twice since we are taking ownership of the only USB peripheral.
|
||||||
|
let state = STATE.init_with(|| State {
|
||||||
|
device_descriptor: [0; 256],
|
||||||
|
config_descriptor: [0; 256],
|
||||||
|
bos_descriptor: [0; 256],
|
||||||
|
msos_descriptor: [0; 256],
|
||||||
|
control_buf: [0; 64],
|
||||||
|
handler: UsbHandler,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create embassy-usb Config
|
||||||
|
let mut config = Config::new(0xb00b, 0x1355);
|
||||||
|
config.manufacturer = Some("Tux");
|
||||||
|
config.product = Some("Tangentbord1");
|
||||||
|
config.serial_number = Some("42069");
|
||||||
|
config.max_power = 100;
|
||||||
|
config.max_packet_size_0 = MAX_PACKET_SIZE;
|
||||||
|
|
||||||
|
// Required for windows compatiblity.
|
||||||
|
// https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
|
||||||
|
config.device_class = 0xEF;
|
||||||
|
config.device_sub_class = 0x02;
|
||||||
|
config.device_protocol = 0x01;
|
||||||
|
config.composite_with_iads = true;
|
||||||
|
|
||||||
|
let driver = Driver::new(usb, Irqs);
|
||||||
|
|
||||||
|
let mut builder = Builder::new(
|
||||||
|
driver,
|
||||||
|
config,
|
||||||
|
&mut state.device_descriptor,
|
||||||
|
&mut state.config_descriptor,
|
||||||
|
&mut state.bos_descriptor,
|
||||||
|
&mut state.msos_descriptor,
|
||||||
|
&mut state.control_buf,
|
||||||
|
);
|
||||||
|
|
||||||
|
builder.handler(&mut state.handler);
|
||||||
|
|
||||||
|
builder
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
pub async fn run(mut device: UsbDevice<'static, Driver<'static, USB>>) {
|
||||||
|
log::info!("running usb device");
|
||||||
|
device.run().await
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UsbHandler;
|
||||||
|
|
||||||
|
impl Handler for UsbHandler {
|
||||||
|
fn enabled(&mut self, enabled: bool) {
|
||||||
|
USB_EVENTS.publish_immediate(UsbEvent::Enabled(enabled));
|
||||||
|
let _ = UART_USB_EVENTS_OUT.try_send(UsbEvent::Enabled(enabled));
|
||||||
|
log::debug!("usb enabled({enabled})");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
USB_EVENTS.publish_immediate(UsbEvent::Reset);
|
||||||
|
let _ = UART_USB_EVENTS_OUT.try_send(UsbEvent::Reset);
|
||||||
|
log::debug!("usb reset()");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addressed(&mut self, addr: u8) {
|
||||||
|
USB_EVENTS.publish_immediate(UsbEvent::Addressed(addr));
|
||||||
|
let _ = UART_USB_EVENTS_OUT.try_send(UsbEvent::Addressed(addr));
|
||||||
|
log::debug!("usb addressed({addr})");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configured(&mut self, configured: bool) {
|
||||||
|
USB_EVENTS.publish_immediate(UsbEvent::Configured(configured));
|
||||||
|
let _ = UART_USB_EVENTS_OUT.try_send(UsbEvent::Configured(configured));
|
||||||
|
log::debug!("usb configured({configured})");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn suspended(&mut self, suspended: bool) {
|
||||||
|
USB_EVENTS.publish_immediate(UsbEvent::Suspended(suspended));
|
||||||
|
let _ = UART_USB_EVENTS_OUT.try_send(UsbEvent::Suspended(suspended));
|
||||||
|
log::debug!("usb suspended({suspended})");
|
||||||
|
}
|
||||||
|
}
|
||||||
242
lib/src/usb/keyboard.rs
Normal file
242
lib/src/usb/keyboard.rs
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
pub mod report;
|
||||||
|
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_futures::select::{select, Either};
|
||||||
|
use embassy_rp::{peripherals::USB, usb::Driver};
|
||||||
|
use embassy_sync::{
|
||||||
|
blocking_mutex::raw::NoopRawMutex,
|
||||||
|
mutex::Mutex,
|
||||||
|
pubsub::{subscriber::Sub, PubSubBehavior, PubSubChannel, WaitResult},
|
||||||
|
signal::Signal,
|
||||||
|
};
|
||||||
|
use embassy_time::{Duration, Timer};
|
||||||
|
use embassy_usb::{
|
||||||
|
class::hid::{self, HidReaderWriter, ReadError, ReportId, RequestHandler},
|
||||||
|
control::OutResponse,
|
||||||
|
Builder,
|
||||||
|
};
|
||||||
|
use embassy_usb_driver::EndpointError;
|
||||||
|
use log::error;
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
use usbd_hid::descriptor::SerializedDescriptor;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
event::button,
|
||||||
|
keyboard::KbEvents,
|
||||||
|
keypress_handler::keypress_handler,
|
||||||
|
usb::keyboard::report::{KeyboardReport, EMPTY_KEYBOARD_REPORT},
|
||||||
|
util::CS,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::MAX_PACKET_SIZE;
|
||||||
|
|
||||||
|
struct Handler;
|
||||||
|
|
||||||
|
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 {
|
||||||
|
reports: Mutex<CS, Reports>,
|
||||||
|
|
||||||
|
/// Signalled by [report_task] when a report is sent.
|
||||||
|
report_signal: Signal<CS, ()>,
|
||||||
|
|
||||||
|
handler: Handler,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set up a USB HID keyboard. This function panics if called more than once.
|
||||||
|
pub async fn setup(builder: &mut Builder<'static, Driver<'static, USB>>, events: KbEvents) {
|
||||||
|
log::info!("setting up usb hid");
|
||||||
|
|
||||||
|
let context = {
|
||||||
|
static CONTEXT: StaticCell<Context> = StaticCell::new();
|
||||||
|
// this panics if the functon is called twice
|
||||||
|
CONTEXT.init(Context {
|
||||||
|
reports: Mutex::new(Reports {
|
||||||
|
actual: EMPTY_KEYBOARD_REPORT,
|
||||||
|
unsent: EMPTY_KEYBOARD_REPORT,
|
||||||
|
}),
|
||||||
|
report_signal: Signal::new(),
|
||||||
|
handler: Handler,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let hid_state = {
|
||||||
|
static HID_STATE: StaticCell<hid::State<'static>> = StaticCell::new();
|
||||||
|
// this panics if the functon is called twice
|
||||||
|
HID_STATE.init(hid::State::new())
|
||||||
|
};
|
||||||
|
|
||||||
|
let config = hid::Config {
|
||||||
|
//report_descriptor: MouseReport::desc(),
|
||||||
|
report_descriptor: KeyboardReport::desc(),
|
||||||
|
request_handler: Some(&context.handler),
|
||||||
|
poll_ms: 2,
|
||||||
|
max_packet_size: MAX_PACKET_SIZE as u16,
|
||||||
|
};
|
||||||
|
|
||||||
|
let stream = HidStream::new(builder, hid_state, config);
|
||||||
|
|
||||||
|
let spawner = Spawner::for_current_executor().await;
|
||||||
|
|
||||||
|
spawner.must_spawn(report_task(stream, context));
|
||||||
|
spawner.must_spawn(event_listener_task(events, context));
|
||||||
|
|
||||||
|
log::info!("done setting up usb keyboard");
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RequestHandler for Handler {
|
||||||
|
fn get_report(&self, id: ReportId, buf: &mut [u8]) -> Option<usize> {
|
||||||
|
log::info!("get_report({id:?}, {buf:?})");
|
||||||
|
let _ = (id, buf);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_report(&self, id: ReportId, data: &[u8]) -> embassy_usb::control::OutResponse {
|
||||||
|
log::info!("set_report({id:?}, {data:?})");
|
||||||
|
let _ = (id, data);
|
||||||
|
OutResponse::Rejected
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_idle_ms(&self, id: Option<ReportId>) -> Option<u32> {
|
||||||
|
log::info!("get_idle_ms({id:?})");
|
||||||
|
let _ = id;
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_idle_ms(&self, id: Option<ReportId>, duration_ms: u32) {
|
||||||
|
log::info!("set_idle_ms({id:?}, {duration_ms})");
|
||||||
|
let _ = (id, duration_ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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, BUTTON_EVENT_BUF_LEN, 1, 1>::new();
|
||||||
|
let mut button_pub = button_events.publisher().unwrap();
|
||||||
|
let mut button_sub = button_events.subscriber().unwrap();
|
||||||
|
|
||||||
|
let r = select(
|
||||||
|
button_events_to_report(ctx, &mut *button_sub),
|
||||||
|
keypress_handler(&mut *events.subscriber, &mut *button_pub),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// match hack because rust isn't smart enough.
|
||||||
|
match r {
|
||||||
|
Either::First(never) | Either::Second(never) => match never {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Listen for [button::Event]s and add them to the keyboard [Reports].
|
||||||
|
async fn button_events_to_report(
|
||||||
|
ctx: &'static Context,
|
||||||
|
button_sub: &mut Sub<'_, impl PubSubBehavior<button::Event>, button::Event>,
|
||||||
|
) -> ! {
|
||||||
|
loop {
|
||||||
|
let WaitResult::Message(event) = button_sub.next_message().await else {
|
||||||
|
error!("lagged");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut r = ctx.reports.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) => {
|
||||||
|
ctx.report_signal.reset();
|
||||||
|
drop(r);
|
||||||
|
ctx.report_signal.wait().await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
button::Event::ReleaseMod(m) if r.unsent.modifier_pressed(m) => {
|
||||||
|
ctx.report_signal.reset();
|
||||||
|
drop(r);
|
||||||
|
ctx.report_signal.wait().await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
button::Event::ReleaseKey(k) => r.actual.release_key(k),
|
||||||
|
button::Event::ReleaseMod(m) => r.actual.release_modifier(m),
|
||||||
|
|
||||||
|
button::Event::Wait => {
|
||||||
|
ctx.report_signal.reset();
|
||||||
|
drop(r);
|
||||||
|
ctx.report_signal.wait().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn report_task(stream: HidStream, ctx: &'static Context) {
|
||||||
|
if let Err(e) = write_reports(stream, ctx).await {
|
||||||
|
log::error!("keyboard error: {e:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write keyboard reports.
|
||||||
|
async fn write_reports(mut stream: HidStream, ctx: &'static Context) -> Result<(), Error> {
|
||||||
|
stream.ready().await;
|
||||||
|
loop {
|
||||||
|
Timer::after(Duration::from_millis(2)).await;
|
||||||
|
|
||||||
|
let report = {
|
||||||
|
let mut reports = ctx.reports.lock().await;
|
||||||
|
ctx.report_signal.signal(());
|
||||||
|
reports.unsent = EMPTY_KEYBOARD_REPORT;
|
||||||
|
reports.actual
|
||||||
|
};
|
||||||
|
|
||||||
|
if report.keycodes != EMPTY_KEYBOARD_REPORT.keycodes {
|
||||||
|
log::trace!("keys: {:x?}", report.keycodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "n-key-rollover")]
|
||||||
|
stream.write(report.as_bytes()).await?;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "n-key-rollover"))]
|
||||||
|
stream.write_serialize(&report).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
enum Error {
|
||||||
|
Read(ReadError),
|
||||||
|
Endpoint(EndpointError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ReadError> for Error {
|
||||||
|
fn from(value: ReadError) -> Self {
|
||||||
|
Error::Read(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<EndpointError> for Error {
|
||||||
|
fn from(value: EndpointError) -> Self {
|
||||||
|
Error::Endpoint(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,15 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use crate::{button::Modifier, keys::Key};
|
||||||
|
use bytemuck::{cast_ref, Pod, Zeroable};
|
||||||
|
use core::mem::size_of;
|
||||||
|
|
||||||
/// KeyboardReport describes a report and its companion descriptor that can be
|
/// KeyboardReport describes a report and its companion descriptor that can be
|
||||||
/// used to send keyboard button presses to a host and receive the status of the
|
/// used to send keyboard button presses to a host and receive the status of the
|
||||||
/// 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,9 +19,6 @@ pub struct KeyboardReport {
|
|||||||
pub keycodes: [u8; 27],
|
pub keycodes: [u8; 27],
|
||||||
}
|
}
|
||||||
|
|
||||||
use core::mem::{size_of, transmute};
|
|
||||||
use tgnt::{button::Modifier, keys::Key};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "n-key-rollover"))]
|
#[cfg(not(feature = "n-key-rollover"))]
|
||||||
pub use ::usbd_hid::descriptor::KeyboardReport;
|
pub use ::usbd_hid::descriptor::KeyboardReport;
|
||||||
|
|
||||||
@ -35,23 +36,30 @@ 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_i, 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_i) {
|
||||||
if pressed {
|
if pressed {
|
||||||
*k |= mask;
|
*k |= mask;
|
||||||
} else {
|
} else {
|
||||||
*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_i, mask) = key_to_byte_mask(key);
|
||||||
|
if let Some(k) = self.keycodes.get_mut(byte_i) {
|
||||||
|
(*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) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
219
lib/src/usb/serial.rs
Normal file
219
lib/src/usb/serial.rs
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
use crate::{
|
||||||
|
logger::{LogOutput, TimestampedRecord},
|
||||||
|
serial_proto::borrowed::{DeviceMsg, LogRecord},
|
||||||
|
serial_proto::owned::HostMsg,
|
||||||
|
util::{DisplayPack, CS},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::MAX_PACKET_SIZE;
|
||||||
|
use core::future::pending;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::{peripherals::USB, usb::Driver};
|
||||||
|
use embassy_sync::{channel::Channel, pipe::Pipe};
|
||||||
|
use embassy_time::{Duration, Timer};
|
||||||
|
use embassy_usb::{
|
||||||
|
class::cdc_acm::{self, CdcAcmClass},
|
||||||
|
Builder,
|
||||||
|
};
|
||||||
|
use embassy_usb_driver::EndpointError;
|
||||||
|
use futures::{select_biased, FutureExt};
|
||||||
|
use msgpck::{MsgPack, MsgUnpack, PackErr, UnpackErr};
|
||||||
|
use static_cell::StaticCell;
|
||||||
|
|
||||||
|
pub const BUFFER_SIZE: usize = 16 * 1024;
|
||||||
|
static OUT: Pipe<CS, BUFFER_SIZE> = Pipe::new();
|
||||||
|
pub static IN: Channel<CS, HostMsg, 16> = Channel::new(); // TODO: read from this guy
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct UsbSerial;
|
||||||
|
|
||||||
|
pub async fn setup(usb_builder: &mut Builder<'static, Driver<'static, USB>>) -> &'static UsbSerial {
|
||||||
|
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(serial_task(class));
|
||||||
|
|
||||||
|
static USB_SERIAL: UsbSerial = UsbSerial;
|
||||||
|
&USB_SERIAL
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn wait_connection(class: &mut CdcAcmClass<'static, Driver<'static, USB>>) {
|
||||||
|
class.wait_connection().await;
|
||||||
|
OUT.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn serial_task(mut class: CdcAcmClass<'static, Driver<'static, USB>>) {
|
||||||
|
let mut write_buf = [0u8; MAX_PACKET_SIZE as usize];
|
||||||
|
let mut read_buf = [0u8; 1024 as usize];
|
||||||
|
let mut message_parser = MessageParser::new(&mut read_buf);
|
||||||
|
|
||||||
|
class.wait_connection().await;
|
||||||
|
loop {
|
||||||
|
select_biased! {
|
||||||
|
n = OUT.read(&mut write_buf).fuse() => {
|
||||||
|
let write = async {
|
||||||
|
class.write_packet(&write_buf[..n]).await?;
|
||||||
|
|
||||||
|
// if we send a packet containing exactly MAX_PACKET_SIZE bytes, we need to send another
|
||||||
|
// packet to "flush" the buffer.
|
||||||
|
if OUT.len() == 0 && n == usize::from(MAX_PACKET_SIZE) {
|
||||||
|
let _ = class.write_packet(&[]).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
match write.await {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(EndpointError::Disabled) => wait_connection(&mut class).await,
|
||||||
|
Err(EndpointError::BufferOverflow) => {
|
||||||
|
// not much we can do if this happens, just ignore the error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//r = class.read_packet(&mut read_buf).fuse() => {
|
||||||
|
r = message_parser.read(&mut class).fuse() => {
|
||||||
|
match r {
|
||||||
|
Ok(Some(message)) => {
|
||||||
|
log::info!("Got message!!!: {:?}", message);
|
||||||
|
if IN.try_send(message).is_err() {
|
||||||
|
log::error!("USB serial in buffer is full");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None) |
|
||||||
|
Err(EndpointError::Disabled) => wait_connection(&mut class).await,
|
||||||
|
Err(EndpointError::BufferOverflow) => {
|
||||||
|
// wtf, this shouldn't happen?
|
||||||
|
panic!("usb serial buffer overflow on read");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a [DeviceMsg] over the serial connection.
|
||||||
|
///
|
||||||
|
/// If the OUT buffer is full, the message may be partially dropped.
|
||||||
|
///
|
||||||
|
/// If no serial connection is active, the message may not be ever be sent.
|
||||||
|
pub fn serial_send(message: &DeviceMsg<'_>) {
|
||||||
|
struct UsbWriter;
|
||||||
|
impl msgpck::Write for UsbWriter {
|
||||||
|
fn write_all(&mut self, bytes: &[u8]) -> Result<(), PackErr> {
|
||||||
|
OUT.try_write(bytes).map_err(|_| PackErr::BufferOverflow)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = message.pack_with_writer(&mut UsbWriter);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LogOutput for UsbSerial {
|
||||||
|
fn log(&self, record: &TimestampedRecord) {
|
||||||
|
let ms = record.timestamp.as_millis();
|
||||||
|
let level = record.metadata().level();
|
||||||
|
|
||||||
|
let record = LogRecord {
|
||||||
|
timestamp: ms,
|
||||||
|
level: DisplayPack(level),
|
||||||
|
message: DisplayPack(record.args()),
|
||||||
|
};
|
||||||
|
|
||||||
|
serial_send(&DeviceMsg::Log(record));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MessageParser<'a> {
|
||||||
|
pub buf: &'a mut [u8],
|
||||||
|
pub len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'buf> MessageParser<'buf> {
|
||||||
|
pub fn new(buf: &'buf mut [u8]) -> Self {
|
||||||
|
Self { buf, len: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn read(
|
||||||
|
&mut self,
|
||||||
|
class: &mut CdcAcmClass<'static, Driver<'static, USB>>,
|
||||||
|
) -> Result<Option<HostMsg>, EndpointError> {
|
||||||
|
loop {
|
||||||
|
// try to parse messages from the buffer
|
||||||
|
if self.len > 0 {
|
||||||
|
log::debug!("buf: {:x?}", &self.buf[..self.len]);
|
||||||
|
}
|
||||||
|
let mut reader = &mut &self.buf[..self.len];
|
||||||
|
match HostMsg::unpack(&mut reader) {
|
||||||
|
Ok(r) => {
|
||||||
|
// remove the decoded bytes from buf
|
||||||
|
if reader.is_empty() {
|
||||||
|
self.len = 0;
|
||||||
|
} else {
|
||||||
|
let bytes_read = self.len - reader.len();
|
||||||
|
self.buf.rotate_left(bytes_read);
|
||||||
|
self.len -= bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
log::debug!("received message: {r:?}");
|
||||||
|
|
||||||
|
return Ok(Some(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
// we probably have not gotten the entire message yet, go back to reading bytes.
|
||||||
|
// if the message is corrupted, we will eventually hit MESSAGE_TIMEOUT or
|
||||||
|
// max buffer size.
|
||||||
|
Err(UnpackErr::UnexpectedEof) => {}
|
||||||
|
|
||||||
|
// on any other error, the message is corrupt. clear the buffer.
|
||||||
|
Err(_e) => {
|
||||||
|
log::error!("{:?}", _e);
|
||||||
|
self.len = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// if buffer is not empty, this future will sleep until the pending message times out
|
||||||
|
let buf_is_empty = self.len == 0;
|
||||||
|
let timeout = async {
|
||||||
|
if buf_is_empty {
|
||||||
|
pending().await
|
||||||
|
} else {
|
||||||
|
const MESSAGE_TIMEOUT: Duration = Duration::from_millis(30);
|
||||||
|
Timer::after(MESSAGE_TIMEOUT).await
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// try to read some bytes from the file
|
||||||
|
let n = select_biased! {
|
||||||
|
n = class.read_packet(&mut self.buf[self.len..]).fuse() => n?,
|
||||||
|
_ = timeout.fuse() => {
|
||||||
|
log::debug!("clearing buffer");
|
||||||
|
self.len = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// make sure we're not just reading garbage forever
|
||||||
|
if self.len >= self.buf.len() {
|
||||||
|
log::debug!("max message size exceeded");
|
||||||
|
self.len = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.len += n;
|
||||||
|
|
||||||
|
log::info!("read {} bytes, buf: {:x?}", n, &self.buf[..self.len]);
|
||||||
|
|
||||||
|
// exit on eof
|
||||||
|
if n == 0 {
|
||||||
|
log::error!("read 0 bytes, fuck.");
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
214
lib/src/util.rs
Normal file
214
lib/src/util.rs
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
use core::{
|
||||||
|
cell::UnsafeCell,
|
||||||
|
fmt::{self, Debug, Display, Write},
|
||||||
|
mem::MaybeUninit,
|
||||||
|
ops::Deref,
|
||||||
|
};
|
||||||
|
|
||||||
|
use embassy_futures::yield_now;
|
||||||
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
|
use embassy_time::{Duration, Timer};
|
||||||
|
use msgpck::{Marker, MsgPack, PackErr, Piece};
|
||||||
|
use portable_atomic::{AtomicU32, Ordering};
|
||||||
|
|
||||||
|
use crate::rgb::Rgb;
|
||||||
|
|
||||||
|
pub type CS = CriticalSectionRawMutex;
|
||||||
|
|
||||||
|
#[derive()]
|
||||||
|
pub struct SwapCell<T> {
|
||||||
|
// active_slot: 1 bit
|
||||||
|
// readers: 31 bits
|
||||||
|
entries: AtomicU32,
|
||||||
|
exits: AtomicU32,
|
||||||
|
|
||||||
|
slot0: UnsafeCell<MaybeUninit<T>>,
|
||||||
|
slot1: UnsafeCell<MaybeUninit<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SwapCellWrite<T: 'static> {
|
||||||
|
cell: &'static SwapCell<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SwapCellRead<T: 'static> {
|
||||||
|
cell: &'static SwapCell<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SwapCellGuard<'a, T> {
|
||||||
|
cell: &'a SwapCell<T>,
|
||||||
|
slot: &'a T,
|
||||||
|
}
|
||||||
|
|
||||||
|
const SLOT_MASK: u32 = 0x80000000;
|
||||||
|
const ENTRIES_MASK: u32 = 0x7FFFFFFF;
|
||||||
|
|
||||||
|
impl<T> SwapCell<T> {
|
||||||
|
pub const fn new(initial: T) -> Self {
|
||||||
|
SwapCell {
|
||||||
|
entries: AtomicU32::new(0),
|
||||||
|
exits: AtomicU32::new(0),
|
||||||
|
slot0: UnsafeCell::new(MaybeUninit::new(initial)),
|
||||||
|
slot1: UnsafeCell::new(MaybeUninit::uninit()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn split(&'static mut self) -> (SwapCellRead<T>, SwapCellWrite<T>) {
|
||||||
|
(SwapCellRead { cell: self }, SwapCellWrite { cell: self })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SwapCellWrite<T> {
|
||||||
|
pub async fn write(&mut self, t: T) {
|
||||||
|
let x = self.cell.entries.load(Ordering::SeqCst);
|
||||||
|
let active_slot = (x & SLOT_MASK).rotate_left(1) as usize;
|
||||||
|
|
||||||
|
let mut slots = [&self.cell.slot0, &self.cell.slot1];
|
||||||
|
slots.rotate_left(active_slot);
|
||||||
|
let [active_slot, inactive_slot] = slots;
|
||||||
|
|
||||||
|
// SAFETY: no one elase is allowed to touch the inactive slot.
|
||||||
|
unsafe { inactive_slot.get().write(MaybeUninit::new(t)) };
|
||||||
|
|
||||||
|
// swap active/inactive slots
|
||||||
|
let x = self.cell.entries.fetch_xor(SLOT_MASK, Ordering::SeqCst);
|
||||||
|
let (_active_slot, inactive_slot) = (inactive_slot, active_slot);
|
||||||
|
|
||||||
|
// wait until there are no more readers in the previously active slot
|
||||||
|
let entries = x & ENTRIES_MASK;
|
||||||
|
loop {
|
||||||
|
let exits = self.cell.exits.load(Ordering::SeqCst);
|
||||||
|
if exits >= entries {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
yield_now().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop the now inactive slot
|
||||||
|
// SAFETY: we waited until everyone else stopped using this slot.
|
||||||
|
unsafe { inactive_slot.get().drop_in_place() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> SwapCellRead<T> {
|
||||||
|
pub fn read(&self) -> SwapCellGuard<'_, T> {
|
||||||
|
let x = self.cell.entries.fetch_add(1, Ordering::SeqCst);
|
||||||
|
|
||||||
|
let entries = x & ENTRIES_MASK;
|
||||||
|
debug_assert!(entries < ENTRIES_MASK, "SwapCell overflowed");
|
||||||
|
|
||||||
|
let slot = (x & SLOT_MASK).rotate_left(1) as usize;
|
||||||
|
let slot = [&self.cell.slot0, &self.cell.slot1][slot];
|
||||||
|
let slot = unsafe { &*slot.get() };
|
||||||
|
let slot = unsafe { slot.assume_init_ref() };
|
||||||
|
|
||||||
|
SwapCellGuard {
|
||||||
|
cell: self.cell,
|
||||||
|
slot,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Drop for SwapCellGuard<'_, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.cell.exits.fetch_add(1, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for SwapCellGuard<'_, T> {
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.slot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn stall() -> ! {
|
||||||
|
loop {
|
||||||
|
Timer::after(Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Input a value 0 to 255 to get a color value
|
||||||
|
// The colours are a transition r - g - b - back to r.
|
||||||
|
pub fn wheel(mut wheel_pos: u8) -> Rgb {
|
||||||
|
wheel_pos = 255 - wheel_pos;
|
||||||
|
if wheel_pos < 85 {
|
||||||
|
Rgb::new(255 - wheel_pos * 3, 0, wheel_pos * 3)
|
||||||
|
} else if wheel_pos < 170 {
|
||||||
|
wheel_pos -= 85;
|
||||||
|
Rgb::new(0, wheel_pos * 3, 255 - wheel_pos * 3)
|
||||||
|
} else {
|
||||||
|
wheel_pos -= 170;
|
||||||
|
Rgb::new(wheel_pos * 3, 255 - wheel_pos * 3, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate the length of the formatted string of a type that impls Display.
|
||||||
|
pub fn display_len(t: &impl Display) -> usize {
|
||||||
|
// impl fmt::Write for a dummy struct that just increments a length
|
||||||
|
struct Write<'a>(&'a mut usize);
|
||||||
|
impl fmt::Write for Write<'_> {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
*self.0 += s.len();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut n = 0;
|
||||||
|
|
||||||
|
let mut w = Write(&mut n);
|
||||||
|
write!(&mut w, "{}", t).expect("Write impl is infallible");
|
||||||
|
|
||||||
|
n
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrapper type that impls [MsgPack] for `T: Display`, by serializing `T` as a msgpck string.
|
||||||
|
pub struct DisplayPack<T>(pub T);
|
||||||
|
impl<T: Display> MsgPack for DisplayPack<T> {
|
||||||
|
fn pack(&self) -> impl Iterator<Item = msgpck::Piece<'_>> {
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pack_with_writer(&self, w: &mut dyn msgpck::Write) -> Result<usize, PackErr> {
|
||||||
|
let mut n = 0;
|
||||||
|
|
||||||
|
let str_len = display_len(&self.0);
|
||||||
|
|
||||||
|
for bytes in [
|
||||||
|
Piece::from(Marker::Str32).as_bytes(),
|
||||||
|
Piece::from(str_len as u32).as_bytes(),
|
||||||
|
] {
|
||||||
|
w.write_all(bytes)?;
|
||||||
|
n += bytes.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is advanced stupid.
|
||||||
|
struct Write<'a>(&'a mut dyn msgpck::Write);
|
||||||
|
impl fmt::Write for Write<'_> {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
self.0
|
||||||
|
.write_all(s.as_bytes())
|
||||||
|
.map_err(|_| fmt::Error::default())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(&mut Write(w), "{}", self.0).map_err(|_| PackErr::BufferOverflow)?;
|
||||||
|
n += str_len;
|
||||||
|
|
||||||
|
Ok(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Debug> Debug for DisplayPack<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
Debug::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
92
lib/src/ws2812.rs
Normal file
92
lib/src/ws2812.rs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
use embassy_rp::dma::{self, AnyChannel};
|
||||||
|
use embassy_rp::interrupt::typelevel::Binding;
|
||||||
|
use embassy_rp::pio::{self, FifoJoin, Instance, Pio, PioPin, ShiftConfig, ShiftDirection};
|
||||||
|
use embassy_rp::{Peripheral, PeripheralRef};
|
||||||
|
use fixed::FixedU32;
|
||||||
|
|
||||||
|
use crate::rgb::Rgb;
|
||||||
|
|
||||||
|
pub struct Ws2812<P: pio::Instance + 'static> {
|
||||||
|
sm: pio::StateMachine<'static, P, 0>,
|
||||||
|
dma: PeripheralRef<'static, AnyChannel>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: Instance> Ws2812<P> {
|
||||||
|
pub fn new(
|
||||||
|
pio: impl Peripheral<P = P> + 'static,
|
||||||
|
irqs: impl Binding<P::Interrupt, pio::InterruptHandler<P>>,
|
||||||
|
dma: impl dma::Channel,
|
||||||
|
pin: impl PioPin,
|
||||||
|
) -> Self {
|
||||||
|
let mut pio = Pio::new(pio, irqs);
|
||||||
|
let mut sm = pio.sm0;
|
||||||
|
// prepare the PIO program
|
||||||
|
let side_set = ::pio::SideSet::new(false, 1, false);
|
||||||
|
let mut a: ::pio::Assembler<32> = ::pio::Assembler::new_with_side_set(side_set);
|
||||||
|
|
||||||
|
const T1: u8 = 2; // start bit
|
||||||
|
const T2: u8 = 5; // data bit
|
||||||
|
const T3: u8 = 3; // stop bit
|
||||||
|
const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32;
|
||||||
|
|
||||||
|
let mut wrap_target = a.label();
|
||||||
|
let mut wrap_source = a.label();
|
||||||
|
let mut do_zero = a.label();
|
||||||
|
a.set_with_side_set(::pio::SetDestination::PINDIRS, 1, 0);
|
||||||
|
a.bind(&mut wrap_target);
|
||||||
|
// Do stop bit
|
||||||
|
a.out_with_delay_and_side_set(::pio::OutDestination::X, 1, T3 - 1, 0);
|
||||||
|
// Do start bit
|
||||||
|
a.jmp_with_delay_and_side_set(::pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1);
|
||||||
|
// Do data bit = 1
|
||||||
|
a.jmp_with_delay_and_side_set(::pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1);
|
||||||
|
a.bind(&mut do_zero);
|
||||||
|
// Do data bit = 0
|
||||||
|
a.nop_with_delay_and_side_set(T2 - 1, 0);
|
||||||
|
a.bind(&mut wrap_source);
|
||||||
|
|
||||||
|
let prg = a.assemble_with_wrap(wrap_source, wrap_target);
|
||||||
|
|
||||||
|
//let relocated_prg = RelocatedProgram::new(&prg);
|
||||||
|
let loaded_prg = pio.common.load_program(&prg);
|
||||||
|
|
||||||
|
// Clock config
|
||||||
|
// TODO CLOCK_FREQ should come from embassy_rp
|
||||||
|
const CLOCK_FREQ: u32 = 125_000_000;
|
||||||
|
const WS2812_FREQ: u32 = 800_000;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut config = pio::Config::default();
|
||||||
|
config.clock_divider = FixedU32::from_bits((int << 8) | frac);
|
||||||
|
config.fifo_join = FifoJoin::TxOnly;
|
||||||
|
config.shift_out = ShiftConfig {
|
||||||
|
threshold: 24,
|
||||||
|
direction: ShiftDirection::Left,
|
||||||
|
auto_fill: true,
|
||||||
|
};
|
||||||
|
let out_pin = pio.common.make_pio_pin(pin);
|
||||||
|
config.set_set_pins(&[&out_pin]);
|
||||||
|
config.use_program(&loaded_prg, &[&out_pin]);
|
||||||
|
|
||||||
|
sm.set_config(&config);
|
||||||
|
sm.set_enable(true);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
sm,
|
||||||
|
dma: PeripheralRef::new(dma.degrade()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn write(&mut self, colors: &[Rgb]) {
|
||||||
|
let colors = Rgb::slice_as_u32s(colors);
|
||||||
|
self.sm.tx().dma_push(self.dma.reborrow(), colors).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
2
right/.cargo/config.toml
Normal file
2
right/.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[build]
|
||||||
|
target = "thumbv6m-none-eabi"
|
||||||
28
right/Cargo.toml
Normal file
28
right/Cargo.toml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
[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"
|
||||||
|
|
||||||
|
[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"] }
|
||||||
|
embassy-sync = "0.5.0"
|
||||||
|
embassy-time = "0.3.0"
|
||||||
|
embassy-futures = "0.1.1"
|
||||||
|
log = "0.4.17"
|
||||||
|
postcard = { version = "1.0.4", features = ["alloc"] }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
ron = "0.8.0"
|
||||||
|
postcard = { version = "1", features = ["use-std"] }
|
||||||
|
|
||||||
|
[build-dependencies.tangentbord1]
|
||||||
|
path = "../lib"
|
||||||
|
package = "tangentbord1-lib"
|
||||||
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 tangentbord1::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<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");
|
||||||
|
}
|
||||||
181
right/layers.ron
Normal file
181
right/layers.ron
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
[
|
||||||
|
[
|
||||||
|
Layer(
|
||||||
|
buttons: [
|
||||||
|
// Row 1
|
||||||
|
Key(F),
|
||||||
|
Key(G),
|
||||||
|
Key(C),
|
||||||
|
Key(R),
|
||||||
|
Key(L),
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
Key(D),
|
||||||
|
ModTap(H, RCtrl),
|
||||||
|
ModTap(T, RShift),
|
||||||
|
ModTap(N, RMod),
|
||||||
|
ModTap(S, RAlt),
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
Key(B),
|
||||||
|
Key(M),
|
||||||
|
Key(W),
|
||||||
|
Key(V),
|
||||||
|
Key(Z),
|
||||||
|
|
||||||
|
// Thumbpad
|
||||||
|
Layer(Peek, Right, 1),
|
||||||
|
Key(Return),
|
||||||
|
Key(Delete),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Layer(
|
||||||
|
buttons: [
|
||||||
|
// Row 1
|
||||||
|
Compose2(Lower, O, Variable, A), // å
|
||||||
|
Key(D7),
|
||||||
|
Key(D8),
|
||||||
|
Key(D9),
|
||||||
|
Key(D0),
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
Compose2(Upper, Apostrophe, Variable, A), // ä
|
||||||
|
ModTap(D4, RCtrl),
|
||||||
|
ModTap(D5, RShift),
|
||||||
|
ModTap(D6, RMod),
|
||||||
|
//ModTap(, RAlt),
|
||||||
|
Mod(RAlt),
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
Compose2(Upper, Apostrophe, Variable, O), // ö
|
||||||
|
Key(D1),
|
||||||
|
Key(D2),
|
||||||
|
Key(D3),
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Thumbpad
|
||||||
|
Layer(Peek, Right, 1),
|
||||||
|
Key(Return),
|
||||||
|
Key(Delete),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Layer(
|
||||||
|
buttons: [
|
||||||
|
// Row 1
|
||||||
|
None,
|
||||||
|
Key(Mute),
|
||||||
|
Key(VolumeDown),
|
||||||
|
Key(VolumeUp),
|
||||||
|
Layer(Move, Down, 1),
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
Key(LeftArrow),
|
||||||
|
Key(DownArrow),
|
||||||
|
Key(UpArrow),
|
||||||
|
Key(RightArrow),
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
Key(Home),
|
||||||
|
Key(PageDown),
|
||||||
|
Key(PageUp),
|
||||||
|
Key(End),
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Thumbpad
|
||||||
|
Layer(Peek, Right, 1),
|
||||||
|
Key(Return),
|
||||||
|
Key(Delete),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
[ // gaming row
|
||||||
|
Layer(
|
||||||
|
buttons: [
|
||||||
|
// Row 1
|
||||||
|
Key(Y),
|
||||||
|
Key(U),
|
||||||
|
Key(I),
|
||||||
|
Key(O),
|
||||||
|
Key(P),
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
Key(H),
|
||||||
|
Key(J),
|
||||||
|
Key(K),
|
||||||
|
Key(L),
|
||||||
|
Key(G),
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
Key(B),
|
||||||
|
Key(N),
|
||||||
|
Key(M),
|
||||||
|
Key(Comma),
|
||||||
|
Key(Period),
|
||||||
|
|
||||||
|
// Thumbpad
|
||||||
|
Layer(Peek, Right, 1),
|
||||||
|
Key(Return),
|
||||||
|
Key(Delete),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Layer(
|
||||||
|
buttons: [
|
||||||
|
// Row 1
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Key(CapsLock),
|
||||||
|
Key(Tab),
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Thumbpad
|
||||||
|
Layer(Peek, Right, 1),
|
||||||
|
Key(Return),
|
||||||
|
Key(Delete),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Layer(
|
||||||
|
buttons: [
|
||||||
|
// Row 1
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Layer(Move, Up, 1),
|
||||||
|
|
||||||
|
// Row 2
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Row 3
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
|
||||||
|
// Thumbpad
|
||||||
|
Layer(Peek, Right, 1),
|
||||||
|
Key(Return),
|
||||||
|
Key(Delete),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
]
|
||||||
115
right/src/main.rs
Normal file
115
right/src/main.rs
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
//! Firmware for Tangentbord1, right half.
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
extern crate cortex_m_rt;
|
||||||
|
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::gpio::{Level, Output, Pin};
|
||||||
|
use embassy_time::Timer;
|
||||||
|
use log::error;
|
||||||
|
use tangentbord1::{
|
||||||
|
board::Board,
|
||||||
|
event::Half,
|
||||||
|
interrupts::Irqs,
|
||||||
|
keyboard::KeyboardConfig,
|
||||||
|
layer::Layer,
|
||||||
|
logger::LogMultiplexer,
|
||||||
|
rgb::Rgb,
|
||||||
|
util::stall,
|
||||||
|
ws2812::Ws2812,
|
||||||
|
{allocator, rtt, uart, usb},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
let half = Half::Right;
|
||||||
|
|
||||||
|
let rtt_logger = rtt::init_rtt_logger();
|
||||||
|
|
||||||
|
allocator::init();
|
||||||
|
|
||||||
|
let p = embassy_rp::init(Default::default());
|
||||||
|
let board = Board::from(p);
|
||||||
|
|
||||||
|
let _neopixel_power = Output::new(board.neopixel_power, Level::High);
|
||||||
|
|
||||||
|
let mut neopixel = Ws2812::new(board.PIO0, Irqs, board.DMA_CH0, board.neopixel);
|
||||||
|
let neopixels_d5 = Ws2812::new(board.PIO1, Irqs, board.DMA_CH1, board.d5);
|
||||||
|
|
||||||
|
neopixel.write(&[Rgb::new(0xFF, 0x00, 0x00)]).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");
|
||||||
|
stall().await
|
||||||
|
};
|
||||||
|
|
||||||
|
let keyboard = KeyboardConfig {
|
||||||
|
half,
|
||||||
|
pins: [
|
||||||
|
// row 1
|
||||||
|
board.d12.degrade(),
|
||||||
|
board.d11.degrade(),
|
||||||
|
board.d10.degrade(),
|
||||||
|
board.d9.degrade(),
|
||||||
|
board.d7.degrade(),
|
||||||
|
// row 2
|
||||||
|
board.a2.degrade(),
|
||||||
|
board.a1.degrade(),
|
||||||
|
board.a0.degrade(),
|
||||||
|
board.sda.degrade(),
|
||||||
|
board.scl.degrade(),
|
||||||
|
// row 3
|
||||||
|
board.mosi.degrade(),
|
||||||
|
board.sck.degrade(),
|
||||||
|
board.d25.degrade(),
|
||||||
|
board.d24.degrade(),
|
||||||
|
board.a3.degrade(),
|
||||||
|
// thumbpad
|
||||||
|
board.d3.degrade(),
|
||||||
|
board.d2.degrade(),
|
||||||
|
board.miso.degrade(),
|
||||||
|
],
|
||||||
|
led_map: [0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14, 15, 16, 17],
|
||||||
|
led_driver: neopixels_d5,
|
||||||
|
layers,
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some([events1, events2]) = keyboard.create().await else {
|
||||||
|
error!("failed to create keyboard");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
uart::start(board.tx, board.rx, board.UART0, half, events2).await;
|
||||||
|
|
||||||
|
// TODO: delaying the logger until here is not ideal
|
||||||
|
let usb_logger = usb::setup_logger_and_keyboard(board.USB, events1).await;
|
||||||
|
|
||||||
|
let logger = LogMultiplexer {
|
||||||
|
outputs: [rtt_logger, usb_logger],
|
||||||
|
};
|
||||||
|
logger.init();
|
||||||
|
|
||||||
|
log::error!("log_level: error");
|
||||||
|
log::warn!("log_level: warn");
|
||||||
|
log::info!("log_level: info");
|
||||||
|
log::debug!("log_level: debug");
|
||||||
|
log::trace!("log_level: trace");
|
||||||
|
|
||||||
|
neopixel.write(&[Rgb::new(0x00, 0x00, 0xFF)]).await;
|
||||||
|
Timer::after_secs(5).await;
|
||||||
|
|
||||||
|
for b in (0u8..0xff).rev() {
|
||||||
|
neopixel.write(&[Rgb::new(0, 0, b)]).await;
|
||||||
|
Timer::after_millis(10).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
stall().await
|
||||||
|
}
|
||||||
37
run
37
run
@ -1,19 +1,26 @@
|
|||||||
#!/bin/sh
|
#!/usr/bin/env nu
|
||||||
|
|
||||||
set -e
|
# 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
|
||||||
|
}
|
||||||
|
|
||||||
cargo run --bin $1
|
let log_script = ($env.FILE_PWD | path join "serial-logs")
|
||||||
|
|
||||||
printf "waiting for serial log"
|
if $serial == null {
|
||||||
|
nu $log_script
|
||||||
|
} else {
|
||||||
|
nu $log_script --serial $serial
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SERIAL=/dev/ttyACM0
|
|
||||||
while [ ! -e $SERIAL ]
|
|
||||||
do
|
|
||||||
printf "."
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
echo
|
|
||||||
|
|
||||||
sleep 1
|
|
||||||
|
|
||||||
cat $SERIAL
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "nightly-2023-02-01"
|
channel = "nightly-2024-02-01"
|
||||||
components = ["rust-std", "rust-src", "rustfmt", "cargo", "clippy"]
|
components = ["rust-std", "rust-src", "rustfmt", "cargo", "clippy"]
|
||||||
targets = ["thumbv6m-none-eabi"]
|
targets = ["thumbv6m-none-eabi"]
|
||||||
|
|||||||
35
schematic/.gitignore
vendored
Normal file
35
schematic/.gitignore
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# For PCBs designed using KiCad: https://www.kicad.org/
|
||||||
|
# Format documentation: https://kicad.org/help/file-formats/
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.000
|
||||||
|
*.bak
|
||||||
|
*.bck
|
||||||
|
*.kicad_pcb-bak
|
||||||
|
*.kicad_sch-bak
|
||||||
|
*-backups
|
||||||
|
*.kicad_prl
|
||||||
|
*.sch-bak
|
||||||
|
*~
|
||||||
|
_autosave-*
|
||||||
|
*.tmp
|
||||||
|
*-save.pro
|
||||||
|
*-save.kicad_pcb
|
||||||
|
fp-info-cache
|
||||||
|
|
||||||
|
# Gerber- & Drillfiles
|
||||||
|
*.gbr
|
||||||
|
*.gbrjob
|
||||||
|
*.drl
|
||||||
|
*.zip
|
||||||
|
|
||||||
|
# Netlist files (exported from Eeschema)
|
||||||
|
*.net
|
||||||
|
|
||||||
|
# Autorouter files (exported from Pcbnew)
|
||||||
|
*.dsn
|
||||||
|
*.ses
|
||||||
|
|
||||||
|
# Exported BOM files
|
||||||
|
*.xml
|
||||||
|
*.csv
|
||||||
@ -1,239 +1,74 @@
|
|||||||
(footprint "MX_SW_SOCKET_LED" (version 20221018) (generator pcbnew)
|
(footprint "MX_SW_SOCKET_LED" (version 20221018) (generator pcbnew)
|
||||||
(layer "F.Cu")
|
(layer "F.Cu")
|
||||||
(property "Sheetfile" "tangentbord1.kicad_sch")
|
(property "Sheetfile" "tangentbord1-right.kicad_sch")
|
||||||
(property "Sheetname" "")
|
(property "Sheetname" "")
|
||||||
(attr smd)
|
(attr smd)
|
||||||
(fp_text reference "S5" (at -3.093788 -5.083025) (layer "F.SilkS")
|
(fp_text reference "S6" (at -3.093788 -5.083025) (layer "F.SilkS")
|
||||||
(effects (font (size 1.143 1.143) (thickness 0.153)) (justify left))
|
(effects (font (size 1.143 1.143) (thickness 0.153)) (justify left))
|
||||||
(tstamp c99c09e1-07a6-4954-a7af-6c2a5b589b0d)
|
(tstamp 9b953bc3-ea93-406d-923b-6747a9f95f01)
|
||||||
)
|
)
|
||||||
(fp_text value "MX_SW_SOCKET_LED" (at 7.62 7.62) (layer "B.SilkS") hide
|
(fp_text value "MX_SW_SOCKET_RGB_REV" (at 7.62 7.62) (layer "B.SilkS") hide
|
||||||
(effects (font (size 1 1) (thickness 0.153)) (justify left mirror))
|
(effects (font (size 1 1) (thickness 0.153)) (justify left mirror))
|
||||||
(tstamp 0eb8349b-27fb-449f-97f6-4d5ec12a7760)
|
(tstamp b1cdcf15-7789-4949-9c30-421f8f3dead7)
|
||||||
)
|
)
|
||||||
(fp_line (start -5.5 -0.8) (end -5.5 -4.6)
|
(fp_line (start -5.5 -0.8) (end -5.5 -4.6)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp dd364529-0a04-4b96-82b7-8807d7e82d8a))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp a5ebf1ca-f2f3-456d-8734-3ec67a5d2f0e))
|
||||||
(fp_line (start -3.3 -6.8) (end 4.3 -6.8)
|
(fp_line (start -3.3 -6.8) (end 4.3 -6.8)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 08655067-c738-4d88-a5d0-c4c0f7629304))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 7ced5247-932f-414c-a8f1-3aaa8bb0c55a))
|
||||||
(fp_line (start -2.65 -0.8) (end -5.5 -0.8)
|
(fp_line (start -2.65 -0.8) (end -5.5 -0.8)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 4947607d-146d-4a42-abcd-e160b3de2d29))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp c55e07b1-7c40-428a-b498-6706e77f218d))
|
||||||
(fp_line (start 3.7 -3.3) (end -0.3 -3.3)
|
(fp_line (start 3.7 -3.3) (end -0.3 -3.3)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp f8cae5ff-b325-4003-83ca-884503a4c43e))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 29f4d1e2-095c-411d-b1cb-06bcf2a3f062))
|
||||||
(fp_line (start 4.3 -6.8) (end 4.3 -3.85)
|
(fp_line (start 4.3 -6.8) (end 4.3 -3.85)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 07a5e303-3bf3-4775-8180-a44d35021fb7))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 3956e555-800f-42c1-8cad-a159e2f9b25a))
|
||||||
(fp_arc (start -5.5 -4.6) (mid -4.85554 -6.15573) (end -3.299731 -6.8)
|
(fp_arc (start -5.5 -4.6) (mid -4.85554 -6.15573) (end -3.299731 -6.8)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 5501ad11-67d2-4e0d-9d57-e1dc79618cd8))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp d160ddef-571f-4c18-85e1-2736e64d36a5))
|
||||||
(fp_arc (start -2.3 -1.5) (mid -1.672817 -2.814191) (end -0.300066 -3.300003)
|
(fp_arc (start -2.3 -1.5) (mid -1.672817 -2.814191) (end -0.300066 -3.300003)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp fb8d98d8-d3a1-4299-a626-0d1cf8426f2d))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 122a7c4a-463a-48bb-8538-9161cff84d87))
|
||||||
(fp_arc (start -2.3 -1.35) (mid -2.361128 -1.002455) (end -2.650134 -0.79997)
|
(fp_arc (start -2.3 -1.35) (mid -2.361128 -1.002455) (end -2.650134 -0.79997)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp cbd40b41-9802-4e49-8b6c-78cae9a593b5))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 7034dbdb-7824-4fa3-acc9-e1649675d12e))
|
||||||
(fp_arc (start 4.3 -3.85) (mid 4.113976 -3.450797) (end 3.700181 -3.299992)
|
(fp_arc (start 4.3 -3.85) (mid 4.113976 -3.450797) (end 3.700181 -3.299992)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 08d8d9f5-1e44-4e79-89ee-bb77c66df22c))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 69fbfb3c-18fc-4aef-b3df-299a8c260ede))
|
||||||
(fp_line (start -6.35 -6.35) (end -4.572 -6.35)
|
(fp_line (start -6.35 -6.35) (end -4.572 -6.35)
|
||||||
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp a66a6650-24b3-401c-b98a-58f7f82eef2a))
|
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 354484b7-1b55-4067-ba9c-340e06f8350f))
|
||||||
(fp_line (start -6.35 -4.572) (end -6.35 -6.35)
|
(fp_line (start -6.35 -4.572) (end -6.35 -6.35)
|
||||||
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp f4b59714-80ed-4b0a-a558-d7a1fede57c8))
|
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp a47f18e5-3ac4-46e4-a70c-12d8ce481418))
|
||||||
(fp_line (start -6.35 6.35) (end -6.35 4.572)
|
(fp_line (start -6.35 6.35) (end -6.35 4.572)
|
||||||
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 1f85adf5-e0b0-4b00-bb7e-976f0adbf1ab))
|
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 2cbd506a-2707-4adc-953f-92ad6aba9aad))
|
||||||
(fp_line (start -4.572 6.35) (end -6.35 6.35)
|
(fp_line (start -4.572 6.35) (end -6.35 6.35)
|
||||||
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp ef248f10-08fe-46fb-b351-c8c94c627f21))
|
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 70126239-96e4-45d8-b257-2e1e3c4cdee8))
|
||||||
(fp_line (start 4.572 -6.35) (end 6.35 -6.35)
|
(fp_line (start 4.572 -6.35) (end 6.35 -6.35)
|
||||||
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp dec0b7c7-509b-4474-831f-b377198753eb))
|
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 3e8ceb0d-878e-4acd-bdc9-cf5341e242d6))
|
||||||
(fp_line (start 6.35 -6.35) (end 6.35 -4.572)
|
(fp_line (start 6.35 -6.35) (end 6.35 -4.572)
|
||||||
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp dff0b461-eb3c-4f93-8c8b-1f5c3adf7894))
|
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 56a62407-0bab-4ec4-8642-dec42ffc2bd5))
|
||||||
(fp_line (start 6.35 4.572) (end 6.35 6.35)
|
(fp_line (start 6.35 4.572) (end 6.35 6.35)
|
||||||
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp e09052b6-9b0f-4f5f-ac4b-7ed8a36df7dd))
|
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 57c6a53d-df50-4568-89a3-53d71e8a8962))
|
||||||
(fp_line (start 6.35 6.35) (end 4.572 6.35)
|
(fp_line (start 6.35 6.35) (end 4.572 6.35)
|
||||||
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp dd36f99c-3376-4e5c-be45-e63b36a3673a))
|
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 348f11b8-2e3a-44f1-8a7c-98d2e83b8024))
|
||||||
(fp_line (start -1.846447 5.8) (end -1.846447 3.4)
|
(fp_line (start -1.846447 5.8) (end -1.846447 3.4)
|
||||||
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 8baa4deb-ea5a-41ab-b57e-bda9f345e6b2))
|
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp ef9e386f-f2c9-4f49-ac08-c41d014e6bda))
|
||||||
(fp_line (start -1.346447 2.9) (end 1.346447 2.9)
|
(fp_line (start -1.346447 2.9) (end 1.346447 2.9)
|
||||||
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 7e6e4779-d7e7-4991-859f-e5e6c4cff890))
|
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 9e78b888-2b3b-4811-93d9-19e6fb1cd304))
|
||||||
(fp_line (start 1.346447 6.3) (end -1.346447 6.3)
|
(fp_line (start 1.346447 6.3) (end -1.346447 6.3)
|
||||||
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 5287847c-4f93-462a-88d7-b18c71d10c09))
|
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp aa230b2c-3860-4cb6-bbda-916dbd000f7c))
|
||||||
(fp_line (start 1.846447 3.4) (end 1.846447 5.8)
|
(fp_line (start 1.846447 3.4) (end 1.846447 5.8)
|
||||||
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 7fc78158-3999-4f4c-839b-82ec0f54d966))
|
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 0fe096a0-9bc0-41c1-befc-70affedd1ba0))
|
||||||
(fp_arc (start -1.846447 3.4) (mid -1.7 3.046447) (end -1.346447 2.9)
|
(fp_arc (start -1.846447 3.4) (mid -1.7 3.046447) (end -1.346447 2.9)
|
||||||
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 5fbefb93-92ae-4b85-8993-54448b01c05b))
|
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 10a14cff-3cf9-4872-9c4f-e04bc7341d03))
|
||||||
(fp_arc (start -1.346447 6.3) (mid -1.7 6.153553) (end -1.846447 5.8)
|
(fp_arc (start -1.346447 6.3) (mid -1.7 6.153553) (end -1.846447 5.8)
|
||||||
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 54d8d00a-cecb-45ec-a21d-b655ec46397d))
|
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 7e0322e6-b043-44d5-b935-28285a470555))
|
||||||
(fp_arc (start 1.346447 2.9) (mid 1.700002 3.046446) (end 1.846447 3.4)
|
(fp_arc (start 1.346447 2.9) (mid 1.700002 3.046446) (end 1.846447 3.4)
|
||||||
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 45fb1dff-0d05-44d5-a542-5e508d9c68e8))
|
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 482de27e-e79f-4e7e-a88b-d9b3e3efcd65))
|
||||||
(fp_arc (start 1.846447 5.8) (mid 1.700002 6.153555) (end 1.346447 6.3)
|
(fp_arc (start 1.846447 5.8) (mid 1.700002 6.153555) (end 1.346447 6.3)
|
||||||
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 927850ff-52e7-4c5e-b339-00cd0ef614a6))
|
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 50813e33-58c5-43a0-8507-99783fb5ed29))
|
||||||
(fp_circle (center -3.813266 -2.54) (end -4.591727 -2.726047)
|
|
||||||
(stroke (width 0.12) (type solid)) (fill none) (layer "Edge.Cuts") (tstamp 47642d60-74b8-4690-ae54-d672695e75df))
|
|
||||||
(fp_circle (center 0 0) (end 2.000488 0.00118)
|
(fp_circle (center 0 0) (end 2.000488 0.00118)
|
||||||
(stroke (width 0.12) (type solid)) (fill none) (layer "Edge.Cuts") (tstamp 7daf9df0-c3e3-4d1d-b8d0-2a0e88d32779))
|
(stroke (width 0.12) (type solid)) (fill none) (layer "Edge.Cuts") (tstamp 92f95dcb-5a8f-4a5d-8c60-0b5a8ca91475))
|
||||||
(fp_circle (center 2.54 -5.08) (end 1.761539 -5.266047)
|
(pad "1" smd rect (at -6.858 -2.54) (size 3 2.5) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp 3ac8e64a-f3cd-43b9-9976-0adf1cd0fb34))
|
||||||
(stroke (width 0.12) (type solid)) (fill none) (layer "Edge.Cuts") (tstamp 7e70b781-b896-4b77-b2a5-0258e333d860))
|
(pad "1" thru_hole circle (at -3.81 -2.54) (size 4 4) (drill 3) (layers "*.Cu" "*.Mask") (tstamp d97b456e-2de2-44b2-ae7f-cdee6ae19a6c))
|
||||||
(pad "" np_thru_hole circle (at 0 0) (size 4 4) (drill 4) (layers "*.Cu" "*.Mask") (tstamp f6710873-9657-47b3-9054-ff113c49baab))
|
(pad "2" thru_hole circle (at 2.54 -5.08) (size 4 4) (drill 3) (layers "*.Cu" "*.Mask") (tstamp 3d4aa232-7809-4883-a57d-f26c10982857))
|
||||||
(pad "1" smd rect (at -7.8 -2.54) (size 3 2.5) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp f860b5a1-115f-409e-8132-b8192f3df02d))
|
(pad "2" smd rect (at 5.588 -5.08) (size 3 2.5) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp 8f8c6fe0-bdfa-4b12-92ee-d5b513cd306a))
|
||||||
(pad "1" smd custom (at -5.805 -2.54) (size 4.49 4.49) (layers "B.Cu" "B.Paste" "B.Mask")
|
(pad "3" smd rect (at 2.7245 5.38) (size 1.249 0.84) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp 73266c4e-02a3-4ac8-8fff-dd1a22c26373))
|
||||||
(thermal_bridge_angle 45)
|
(pad "4" smd rect (at -2.7 3.88) (size 1.2 0.84) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp f08ce53c-be0a-4dfb-b912-990bce1cb23c))
|
||||||
(options (clearance outline) (anchor circle))
|
(pad "5" smd rect (at 2.7245 3.88) (size 1.249 0.84) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp c1a766d8-0e8b-41e1-ae67-dbfe8b675c21))
|
||||||
(primitives
|
(pad "6" smd rect (at -2.7 5.38) (size 1.2 0.84) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp 8928140e-0e12-40d6-8b6b-ea860573ec6d))
|
||||||
(gr_poly
|
|
||||||
(pts
|
|
||||||
(xy -1.995 -0.25)
|
|
||||||
(xy 1.995 -0.25)
|
|
||||||
(xy 2.018 -0.249)
|
|
||||||
(xy 2.041 -0.246)
|
|
||||||
(xy 2.063 -0.24)
|
|
||||||
(xy 2.085 -0.233)
|
|
||||||
(xy 2.106 -0.224)
|
|
||||||
(xy 2.126 -0.213)
|
|
||||||
(xy 2.145 -0.2)
|
|
||||||
(xy 2.163 -0.185)
|
|
||||||
(xy 2.18 -0.168)
|
|
||||||
(xy 2.194 -0.151)
|
|
||||||
(xy 2.207 -0.132)
|
|
||||||
(xy 2.219 -0.111)
|
|
||||||
(xy 2.228 -0.09)
|
|
||||||
(xy 2.235 -0.068)
|
|
||||||
(xy 2.241 -0.046)
|
|
||||||
(xy 2.244 -0.023)
|
|
||||||
(xy 2.245 0)
|
|
||||||
(xy 2.244 0.023)
|
|
||||||
(xy 2.241 0.046)
|
|
||||||
(xy 2.235 0.068)
|
|
||||||
(xy 2.228 0.09)
|
|
||||||
(xy 2.219 0.111)
|
|
||||||
(xy 2.207 0.132)
|
|
||||||
(xy 2.194 0.151)
|
|
||||||
(xy 2.18 0.168)
|
|
||||||
(xy 2.163 0.185)
|
|
||||||
(xy 2.145 0.2)
|
|
||||||
(xy 2.126 0.213)
|
|
||||||
(xy 2.106 0.224)
|
|
||||||
(xy 2.085 0.233)
|
|
||||||
(xy 2.063 0.24)
|
|
||||||
(xy 2.041 0.246)
|
|
||||||
(xy 2.018 0.249)
|
|
||||||
(xy 1.995 0.25)
|
|
||||||
(xy -1.995 0.25)
|
|
||||||
(xy -2.018 0.249)
|
|
||||||
(xy -2.041 0.246)
|
|
||||||
(xy -2.064 0.24)
|
|
||||||
(xy -2.086 0.233)
|
|
||||||
(xy -2.107 0.224)
|
|
||||||
(xy -2.127 0.213)
|
|
||||||
(xy -2.146 0.2)
|
|
||||||
(xy -2.164 0.185)
|
|
||||||
(xy -2.18 0.168)
|
|
||||||
(xy -2.195 0.151)
|
|
||||||
(xy -2.208 0.132)
|
|
||||||
(xy -2.219 0.111)
|
|
||||||
(xy -2.228 0.09)
|
|
||||||
(xy -2.236 0.068)
|
|
||||||
(xy -2.241 0.046)
|
|
||||||
(xy -2.244 0.023)
|
|
||||||
(xy -2.245 0)
|
|
||||||
(xy -2.244 -0.023)
|
|
||||||
(xy -2.241 -0.046)
|
|
||||||
(xy -2.236 -0.068)
|
|
||||||
(xy -2.228 -0.09)
|
|
||||||
(xy -2.219 -0.111)
|
|
||||||
(xy -2.208 -0.132)
|
|
||||||
(xy -2.195 -0.151)
|
|
||||||
(xy -2.18 -0.168)
|
|
||||||
(xy -2.164 -0.185)
|
|
||||||
(xy -2.146 -0.2)
|
|
||||||
(xy -2.127 -0.213)
|
|
||||||
(xy -2.107 -0.224)
|
|
||||||
(xy -2.086 -0.233)
|
|
||||||
(xy -2.064 -0.24)
|
|
||||||
(xy -2.041 -0.246)
|
|
||||||
(xy -2.018 -0.249)
|
|
||||||
(xy -1.995 -0.25)
|
|
||||||
)
|
|
||||||
(width 0.1) (fill yes))
|
|
||||||
) (tstamp f976e214-f230-417a-bc46-ae3340533700))
|
|
||||||
(pad "1" thru_hole circle (at -3.81 -2.54) (size 3 3) (drill 1.5) (layers "*.Cu" "*.Paste" "*.Mask") (tstamp 56eae806-bca1-4355-af21-323b2e1f98a4))
|
|
||||||
(pad "2" thru_hole circle (at 2.54 -5.08) (size 3 3) (drill 1.5) (layers "*.Cu" "*.Paste" "*.Mask") (tstamp c17be6d2-1d3b-4536-aa82-0edd3efc323f))
|
|
||||||
(pad "2" smd custom (at 4.57 -5.08) (size 4.56 4.56) (layers "B.Cu" "B.Paste" "B.Mask")
|
|
||||||
(thermal_bridge_angle 45)
|
|
||||||
(options (clearance outline) (anchor circle))
|
|
||||||
(primitives
|
|
||||||
(gr_poly
|
|
||||||
(pts
|
|
||||||
(xy 2.03 0.25)
|
|
||||||
(xy -2.03 0.25)
|
|
||||||
(xy -2.053 0.249)
|
|
||||||
(xy -2.076 0.246)
|
|
||||||
(xy -2.098 0.24)
|
|
||||||
(xy -2.12 0.233)
|
|
||||||
(xy -2.141 0.224)
|
|
||||||
(xy -2.162 0.213)
|
|
||||||
(xy -2.181 0.2)
|
|
||||||
(xy -2.198 0.185)
|
|
||||||
(xy -2.215 0.168)
|
|
||||||
(xy -2.23 0.151)
|
|
||||||
(xy -2.243 0.132)
|
|
||||||
(xy -2.254 0.111)
|
|
||||||
(xy -2.263 0.09)
|
|
||||||
(xy -2.271 0.068)
|
|
||||||
(xy -2.276 0.046)
|
|
||||||
(xy -2.279 0.023)
|
|
||||||
(xy -2.28 0)
|
|
||||||
(xy -2.279 -0.023)
|
|
||||||
(xy -2.276 -0.046)
|
|
||||||
(xy -2.271 -0.068)
|
|
||||||
(xy -2.263 -0.09)
|
|
||||||
(xy -2.254 -0.111)
|
|
||||||
(xy -2.243 -0.132)
|
|
||||||
(xy -2.23 -0.151)
|
|
||||||
(xy -2.215 -0.168)
|
|
||||||
(xy -2.198 -0.185)
|
|
||||||
(xy -2.181 -0.2)
|
|
||||||
(xy -2.162 -0.213)
|
|
||||||
(xy -2.141 -0.224)
|
|
||||||
(xy -2.12 -0.233)
|
|
||||||
(xy -2.098 -0.24)
|
|
||||||
(xy -2.076 -0.246)
|
|
||||||
(xy -2.053 -0.249)
|
|
||||||
(xy -2.03 -0.25)
|
|
||||||
(xy 2.03 -0.25)
|
|
||||||
(xy 2.053 -0.249)
|
|
||||||
(xy 2.076 -0.246)
|
|
||||||
(xy 2.098 -0.24)
|
|
||||||
(xy 2.12 -0.233)
|
|
||||||
(xy 2.141 -0.224)
|
|
||||||
(xy 2.161 -0.213)
|
|
||||||
(xy 2.181 -0.2)
|
|
||||||
(xy 2.198 -0.185)
|
|
||||||
(xy 2.215 -0.168)
|
|
||||||
(xy 2.229 -0.151)
|
|
||||||
(xy 2.242 -0.132)
|
|
||||||
(xy 2.254 -0.111)
|
|
||||||
(xy 2.263 -0.09)
|
|
||||||
(xy 2.27 -0.068)
|
|
||||||
(xy 2.276 -0.046)
|
|
||||||
(xy 2.279 -0.023)
|
|
||||||
(xy 2.28 0)
|
|
||||||
(xy 2.279 0.023)
|
|
||||||
(xy 2.276 0.046)
|
|
||||||
(xy 2.27 0.068)
|
|
||||||
(xy 2.263 0.09)
|
|
||||||
(xy 2.254 0.111)
|
|
||||||
(xy 2.242 0.132)
|
|
||||||
(xy 2.229 0.151)
|
|
||||||
(xy 2.215 0.168)
|
|
||||||
(xy 2.198 0.185)
|
|
||||||
(xy 2.181 0.2)
|
|
||||||
(xy 2.161 0.213)
|
|
||||||
(xy 2.141 0.224)
|
|
||||||
(xy 2.12 0.233)
|
|
||||||
(xy 2.098 0.24)
|
|
||||||
(xy 2.076 0.246)
|
|
||||||
(xy 2.053 0.249)
|
|
||||||
(xy 2.03 0.25)
|
|
||||||
)
|
|
||||||
(width 0.1) (fill yes))
|
|
||||||
) (tstamp a04456fc-8484-4f55-ad69-dfde5c4f3919))
|
|
||||||
(pad "2" smd rect (at 6.6 -5.08) (size 3 2.5) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp 20d31537-450c-45d1-ac69-983ce16fd9bf))
|
|
||||||
(pad "3" smd rect (at 2.7245 5.38) (size 1.249 0.84) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp d919cc46-9b76-48ca-a4cf-2790f61156bf))
|
|
||||||
(pad "4" smd rect (at -2.7 3.88) (size 1.2 0.84) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp ec9c3096-d5b3-459c-ab86-4fc80b53e923))
|
|
||||||
(pad "5" smd rect (at 2.7245 3.88) (size 1.249 0.84) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp 30128a88-284d-4f8f-b381-2e55713b43db))
|
|
||||||
(pad "6" smd rect (at -2.7 5.38) (size 1.2 0.84) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp cd7031d3-6331-4338-86a9-1f61ab1d0b10))
|
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,245 +1,80 @@
|
|||||||
(footprint "MX_SW_SOCKET_LED_REV" (version 20221018) (generator pcbnew)
|
(footprint "MX_SW_SOCKET_LED_REV" (version 20221018) (generator pcbnew)
|
||||||
(layer "F.Cu")
|
(layer "F.Cu")
|
||||||
(property "Sheetfile" "tangentbord1.kicad_sch")
|
(property "Sheetfile" "tangentbord1-right.kicad_sch")
|
||||||
(property "Sheetname" "")
|
(property "Sheetname" "")
|
||||||
(attr smd)
|
(attr smd)
|
||||||
(fp_text reference "S6" (at -3.093788 -5.083025) (layer "F.SilkS")
|
(fp_text reference "S1" (at -3.093788 -5.083025) (layer "F.SilkS")
|
||||||
(effects (font (size 1.143 1.143) (thickness 0.152)) (justify left))
|
(effects (font (size 1.143 1.143) (thickness 0.152)) (justify left))
|
||||||
(tstamp 23ee0aaa-12c9-42fb-ae66-7f0a3183cff4)
|
(tstamp c951d81d-7d33-4ee1-acff-5a043206cce1)
|
||||||
)
|
)
|
||||||
(fp_text value "MX_SW_SOCKET_RGB_REV" (at -3.5 4) (layer "B.SilkS") hide
|
(fp_text value "~" (at -3.5 4) (layer "B.SilkS") hide
|
||||||
(effects (font (size 1 1) (thickness 0.153)) (justify left mirror))
|
(effects (font (size 1 1) (thickness 0.153)) (justify left mirror))
|
||||||
(tstamp 91a51d58-72b4-49a7-abbe-fd460b6e3968)
|
(tstamp 9ea61c81-9881-4f00-9bdf-c4012c9b670c)
|
||||||
)
|
)
|
||||||
(fp_line (start -5.5 -0.8) (end -5.5 -4.6)
|
(fp_line (start -5.5 -0.8) (end -5.5 -4.6)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 33d2cef6-95d4-4c10-8312-5a64ddaa387b))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp a961b2d6-9221-4d75-9afb-15ce0006a223))
|
||||||
(fp_line (start -3.3 -6.8) (end 4.3 -6.8)
|
(fp_line (start -3.3 -6.8) (end 4.3 -6.8)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp c7e0ebf9-9c2f-41ad-b231-868696a649c7))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp abb16534-df49-45c7-ad13-1fe878f3facb))
|
||||||
(fp_line (start -2.65 -0.8) (end -5.5 -0.8)
|
(fp_line (start -2.65 -0.8) (end -5.5 -0.8)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 80a48fbf-33a1-459e-9736-cc5e015f80d5))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp f9ea1e6d-1713-4248-9259-344c020c453f))
|
||||||
(fp_line (start 3.7 -3.3) (end -0.3 -3.3)
|
(fp_line (start 3.7 -3.3) (end -0.3 -3.3)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 45d1b653-030d-4572-8505-a8fc7d6f0520))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp d285743e-730b-4850-93ae-61fdd20d584e))
|
||||||
(fp_line (start 4.3 -6.8) (end 4.3 -3.85)
|
(fp_line (start 4.3 -6.8) (end 4.3 -3.85)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 4054fbde-e36b-4625-a0a0-330266f048c8))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 43bc396d-dd13-49e7-93c4-aa065bf4a303))
|
||||||
(fp_arc (start -5.5 -4.6) (mid -4.85554 -6.15573) (end -3.299731 -6.8)
|
(fp_arc (start -5.5 -4.6) (mid -4.85554 -6.15573) (end -3.299731 -6.8)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp a6afb57e-e564-4f04-82ce-c47c0bf41719))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 3d2d6137-3030-4eb5-9e4c-4c2ba3635f3e))
|
||||||
(fp_arc (start -2.3 -1.5) (mid -1.672817 -2.814191) (end -0.300066 -3.300003)
|
(fp_arc (start -2.3 -1.5) (mid -1.672817 -2.814191) (end -0.300066 -3.300003)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 7c59301b-e9f4-472a-b13a-be957ebffae1))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 32eaf36e-de38-4b42-bacf-4446fe011d0e))
|
||||||
(fp_arc (start -2.3 -1.35) (mid -2.361128 -1.002455) (end -2.650134 -0.79997)
|
(fp_arc (start -2.3 -1.35) (mid -2.361128 -1.002455) (end -2.650134 -0.79997)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 2119ec85-0c2e-468a-abf0-c707e4f34681))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 76de69f2-3422-4ba1-abe2-c47d8de8c589))
|
||||||
(fp_arc (start 4.3 -3.85) (mid 4.113976 -3.450797) (end 3.700181 -3.299992)
|
(fp_arc (start 4.3 -3.85) (mid 4.113976 -3.450797) (end 3.700181 -3.299992)
|
||||||
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp 8cc58c47-9f19-4fdd-bc67-a50c830a2dbb))
|
(stroke (width 0.15) (type solid)) (layer "B.SilkS") (tstamp cf50b328-e1e3-4fc0-a83e-546e2a5b1f68))
|
||||||
(fp_line (start -6.35 -6.35) (end -4.572 -6.35)
|
(fp_line (start -6.35 -6.35) (end -4.572 -6.35)
|
||||||
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp c7f0a6c1-5579-461d-869c-3a83cbcf03b1))
|
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp a200cd9e-218b-4e15-b45e-02fef761a8f2))
|
||||||
(fp_line (start -6.35 -4.572) (end -6.35 -6.35)
|
(fp_line (start -6.35 -4.572) (end -6.35 -6.35)
|
||||||
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 3e946efe-dd9e-4783-9257-2ad2b5a1b66d))
|
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 87a0415e-ac6c-4e20-ab32-2d46a4f79f86))
|
||||||
(fp_line (start -6.35 6.35) (end -6.35 4.572)
|
(fp_line (start -6.35 6.35) (end -6.35 4.572)
|
||||||
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 3ab665b5-c6c6-4f1b-b851-dbfc94c51387))
|
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp dcd45561-e8fd-4eb7-88fd-15c80ad071b5))
|
||||||
(fp_line (start -4.572 6.35) (end -6.35 6.35)
|
(fp_line (start -4.572 6.35) (end -6.35 6.35)
|
||||||
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 0c0c78f8-4a65-4cc2-96ab-d1a537c44d86))
|
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 04f1e907-f3eb-41d3-8601-694bbf3a9580))
|
||||||
(fp_line (start 4.572 -6.35) (end 6.35 -6.35)
|
(fp_line (start 4.572 -6.35) (end 6.35 -6.35)
|
||||||
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 9910dd4e-0368-4727-a256-452bfb3d4cf1))
|
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 7ee18b97-a118-4dab-bf4a-c82bc2437e0c))
|
||||||
(fp_line (start 6.35 -6.35) (end 6.35 -4.572)
|
(fp_line (start 6.35 -6.35) (end 6.35 -4.572)
|
||||||
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp c8bb65eb-0896-4688-97e3-cd6fd662c35d))
|
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 376d743f-9c70-45ef-9f4e-e07c8ba338ae))
|
||||||
(fp_line (start 6.35 4.572) (end 6.35 6.35)
|
(fp_line (start 6.35 4.572) (end 6.35 6.35)
|
||||||
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 09fe94aa-9bf3-4740-b589-a9d0d0acb085))
|
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp fac7f922-5d2b-4456-a0fd-77e3752ebfd1))
|
||||||
(fp_line (start 6.35 6.35) (end 4.572 6.35)
|
(fp_line (start 6.35 6.35) (end 4.572 6.35)
|
||||||
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp 216fd815-0d17-47b0-bc2f-2c697338ea34))
|
(stroke (width 0.381) (type solid)) (layer "F.SilkS") (tstamp b6a51e1d-2e29-427a-be3a-da4ca4248340))
|
||||||
(fp_rect (start -2.95 3.5) (end 2.95 4.3)
|
(fp_rect (start -2.95 3.5) (end 2.95 4.3)
|
||||||
(stroke (width 0.1) (type default)) (fill none) (layer "Cmts.User") (tstamp 4f13337b-adae-4710-a2d9-b1bbe154a89f))
|
(stroke (width 0.1) (type default)) (fill none) (layer "Cmts.User") (tstamp d63c090b-46f9-4040-a8bc-36e0868c4969))
|
||||||
(fp_rect (start -2.95 5) (end 2.95 5.8)
|
(fp_rect (start -2.95 5) (end 2.95 5.8)
|
||||||
(stroke (width 0.1) (type default)) (fill none) (layer "Cmts.User") (tstamp 1ce6af92-9f8e-497a-8c09-34d04976f799))
|
(stroke (width 0.1) (type default)) (fill none) (layer "Cmts.User") (tstamp 208cd57f-f65c-4067-8e0c-da37a5cab57e))
|
||||||
(fp_rect (start -1.6 3.2) (end 1.6 6)
|
(fp_rect (start -1.6 3.2) (end 1.6 6)
|
||||||
(stroke (width 0.1) (type default)) (fill none) (layer "Cmts.User") (tstamp 79321da0-2e79-439b-830f-d03e7f76a2ac))
|
(stroke (width 0.1) (type default)) (fill none) (layer "Cmts.User") (tstamp e06176dc-3f52-4442-a19c-15658b823697))
|
||||||
(fp_line (start -1.846447 5.8) (end -1.846447 3.4)
|
(fp_line (start -1.846447 5.8) (end -1.846447 3.4)
|
||||||
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 0d48ac6a-06b6-4945-8233-c0214bf9f9a8))
|
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 0dd5872a-1df3-4114-968d-34e29de87bce))
|
||||||
(fp_line (start -1.346447 2.9) (end 1.346447 2.9)
|
(fp_line (start -1.346447 2.9) (end 1.346447 2.9)
|
||||||
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 57b0cfbb-0103-4295-a684-7e9e7193e79d))
|
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 700be36d-72cb-48a5-b60a-72d927b88181))
|
||||||
(fp_line (start 1.346447 6.3) (end -1.346447 6.3)
|
(fp_line (start 1.346447 6.3) (end -1.346447 6.3)
|
||||||
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 977684e4-e2a3-4e38-b508-dd1b729fff34))
|
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 91e654e0-2bb0-4070-b218-4134b5bc93be))
|
||||||
(fp_line (start 1.846447 3.4) (end 1.846447 5.8)
|
(fp_line (start 1.846447 3.4) (end 1.846447 5.8)
|
||||||
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 251ac7dc-08e2-406f-8aab-c39ab202741c))
|
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 603b83e1-8c93-4967-879b-9e49a9921d37))
|
||||||
(fp_arc (start -1.846447 3.4) (mid -1.7 3.046447) (end -1.346447 2.9)
|
(fp_arc (start -1.846447 3.4) (mid -1.7 3.046447) (end -1.346447 2.9)
|
||||||
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 0182b6aa-a64d-4f09-a411-0cb51e823631))
|
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 3e50b49b-6257-44ff-ade1-fd4536a65368))
|
||||||
(fp_arc (start -1.346447 6.3) (mid -1.7 6.153553) (end -1.846447 5.8)
|
(fp_arc (start -1.346447 6.3) (mid -1.7 6.153553) (end -1.846447 5.8)
|
||||||
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp c720a28f-fd75-4475-81bf-90a81f15f95b))
|
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 2e2b00a0-e066-4fc3-865b-a0cc694c0043))
|
||||||
(fp_arc (start 1.346447 2.9) (mid 1.700002 3.046446) (end 1.846447 3.4)
|
(fp_arc (start 1.346447 2.9) (mid 1.700002 3.046446) (end 1.846447 3.4)
|
||||||
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp 36365749-c26a-4b6a-9d99-9c00929e2d02))
|
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp a97bf4b9-65e6-4537-8460-472528404085))
|
||||||
(fp_arc (start 1.846447 5.8) (mid 1.700002 6.153555) (end 1.346447 6.3)
|
(fp_arc (start 1.846447 5.8) (mid 1.700002 6.153555) (end 1.346447 6.3)
|
||||||
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp ed794652-3494-45bd-91b7-328da952ad11))
|
(stroke (width 0.05) (type default)) (layer "Edge.Cuts") (tstamp bf60bac6-b012-4dcf-b635-811bd1090f32))
|
||||||
(fp_circle (center -3.813266 -2.54) (end -4.591727 -2.726047)
|
|
||||||
(stroke (width 0.12) (type solid)) (fill none) (layer "Edge.Cuts") (tstamp 2e62aa8c-b7ac-44a8-8779-c039bfe6fe6b))
|
|
||||||
(fp_circle (center 0 0) (end 2.000488 0.00118)
|
(fp_circle (center 0 0) (end 2.000488 0.00118)
|
||||||
(stroke (width 0.12) (type solid)) (fill none) (layer "Edge.Cuts") (tstamp 91718e0c-59ea-40f8-a025-66654f765f1a))
|
(stroke (width 0.12) (type solid)) (fill none) (layer "Edge.Cuts") (tstamp 8795b4e2-afe2-45fe-87e0-5dbff4bcc40d))
|
||||||
(fp_circle (center 2.54 -5.08) (end 1.761539 -5.266047)
|
(pad "1" smd rect (at -6.858 -2.54) (size 3 2.5) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp e186fb07-db0c-467c-a14c-c6fb27d0c580))
|
||||||
(stroke (width 0.12) (type solid)) (fill none) (layer "Edge.Cuts") (tstamp 2aa02e7f-c47a-47df-86e0-01e2b57a31b4))
|
(pad "1" thru_hole circle (at -3.81 -2.54) (size 4 4) (drill 3) (layers "*.Cu" "*.Mask") (tstamp 61bebd94-df6f-4db4-a881-63a99c8b0651))
|
||||||
(pad "" np_thru_hole circle (at 0 0) (size 4 4) (drill 4) (layers "*.Cu" "*.Mask") (tstamp 5698588a-74a7-47c5-9575-ffed4e21626a))
|
(pad "2" thru_hole circle (at 2.54 -5.08) (size 4 4) (drill 3) (layers "*.Cu" "*.Mask") (tstamp 58eab195-be7b-480c-9fa9-5219fd9b7c35))
|
||||||
(pad "1" smd rect (at -7.8 -2.54) (size 3 2.5) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp e1112d11-a547-4a5f-92b0-d28e706a3a2e))
|
(pad "2" smd rect (at 5.588 -5.08) (size 3 2.5) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp d8041b85-518d-4ef3-a183-613c8b1d6f9d))
|
||||||
(pad "1" smd custom (at -5.805 -2.54) (size 4.49 4.49) (layers "B.Cu" "B.Paste" "B.Mask")
|
(pad "3" smd rect (at -2.7 3.88) (size 1.2 0.84) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp 22e85ded-aa6f-4f51-b5d8-72b96328eef5))
|
||||||
(thermal_bridge_angle 45)
|
(pad "4" smd rect (at 2.7245 5.38) (size 1.249 0.84) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp ac5ee3b0-3c6e-4e8c-be5f-63f3bb134ba2))
|
||||||
(options (clearance outline) (anchor circle))
|
(pad "5" smd rect (at -2.7 5.38) (size 1.2 0.84) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp 95932dac-b87b-40b0-b8a4-8044cbd4935f))
|
||||||
(primitives
|
(pad "6" smd rect (at 2.7245 3.88) (size 1.249 0.84) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp 0d5f9241-a3ff-43aa-b713-0ffc73867583))
|
||||||
(gr_poly
|
|
||||||
(pts
|
|
||||||
(xy -1.995 -0.25)
|
|
||||||
(xy 1.995 -0.25)
|
|
||||||
(xy 2.018 -0.249)
|
|
||||||
(xy 2.041 -0.246)
|
|
||||||
(xy 2.063 -0.24)
|
|
||||||
(xy 2.085 -0.233)
|
|
||||||
(xy 2.106 -0.224)
|
|
||||||
(xy 2.126 -0.213)
|
|
||||||
(xy 2.145 -0.2)
|
|
||||||
(xy 2.163 -0.185)
|
|
||||||
(xy 2.18 -0.168)
|
|
||||||
(xy 2.194 -0.151)
|
|
||||||
(xy 2.207 -0.132)
|
|
||||||
(xy 2.219 -0.111)
|
|
||||||
(xy 2.228 -0.09)
|
|
||||||
(xy 2.235 -0.068)
|
|
||||||
(xy 2.241 -0.046)
|
|
||||||
(xy 2.244 -0.023)
|
|
||||||
(xy 2.245 0)
|
|
||||||
(xy 2.244 0.023)
|
|
||||||
(xy 2.241 0.046)
|
|
||||||
(xy 2.235 0.068)
|
|
||||||
(xy 2.228 0.09)
|
|
||||||
(xy 2.219 0.111)
|
|
||||||
(xy 2.207 0.132)
|
|
||||||
(xy 2.194 0.151)
|
|
||||||
(xy 2.18 0.168)
|
|
||||||
(xy 2.163 0.185)
|
|
||||||
(xy 2.145 0.2)
|
|
||||||
(xy 2.126 0.213)
|
|
||||||
(xy 2.106 0.224)
|
|
||||||
(xy 2.085 0.233)
|
|
||||||
(xy 2.063 0.24)
|
|
||||||
(xy 2.041 0.246)
|
|
||||||
(xy 2.018 0.249)
|
|
||||||
(xy 1.995 0.25)
|
|
||||||
(xy -1.995 0.25)
|
|
||||||
(xy -2.018 0.249)
|
|
||||||
(xy -2.041 0.246)
|
|
||||||
(xy -2.064 0.24)
|
|
||||||
(xy -2.086 0.233)
|
|
||||||
(xy -2.107 0.224)
|
|
||||||
(xy -2.127 0.213)
|
|
||||||
(xy -2.146 0.2)
|
|
||||||
(xy -2.164 0.185)
|
|
||||||
(xy -2.18 0.168)
|
|
||||||
(xy -2.195 0.151)
|
|
||||||
(xy -2.208 0.132)
|
|
||||||
(xy -2.219 0.111)
|
|
||||||
(xy -2.228 0.09)
|
|
||||||
(xy -2.236 0.068)
|
|
||||||
(xy -2.241 0.046)
|
|
||||||
(xy -2.244 0.023)
|
|
||||||
(xy -2.245 0)
|
|
||||||
(xy -2.244 -0.023)
|
|
||||||
(xy -2.241 -0.046)
|
|
||||||
(xy -2.236 -0.068)
|
|
||||||
(xy -2.228 -0.09)
|
|
||||||
(xy -2.219 -0.111)
|
|
||||||
(xy -2.208 -0.132)
|
|
||||||
(xy -2.195 -0.151)
|
|
||||||
(xy -2.18 -0.168)
|
|
||||||
(xy -2.164 -0.185)
|
|
||||||
(xy -2.146 -0.2)
|
|
||||||
(xy -2.127 -0.213)
|
|
||||||
(xy -2.107 -0.224)
|
|
||||||
(xy -2.086 -0.233)
|
|
||||||
(xy -2.064 -0.24)
|
|
||||||
(xy -2.041 -0.246)
|
|
||||||
(xy -2.018 -0.249)
|
|
||||||
(xy -1.995 -0.25)
|
|
||||||
)
|
|
||||||
(width 0.1) (fill yes))
|
|
||||||
) (tstamp f4273132-0f0b-43ae-9662-0a1b2272edbd))
|
|
||||||
(pad "1" thru_hole circle (at -3.81 -2.54) (size 3 3) (drill 1.5) (layers "*.Cu" "*.Paste" "*.Mask") (tstamp 37e7f97e-6dee-4da2-95ec-6115231a9dee))
|
|
||||||
(pad "2" thru_hole circle (at 2.54 -5.08) (size 3 3) (drill 1.5) (layers "*.Cu" "*.Paste" "*.Mask") (tstamp 53129d49-d48d-432b-842c-2ee5c8814421))
|
|
||||||
(pad "2" smd custom (at 4.57 -5.08) (size 4.56 4.56) (layers "B.Cu" "B.Paste" "B.Mask")
|
|
||||||
(thermal_bridge_angle 45)
|
|
||||||
(options (clearance outline) (anchor circle))
|
|
||||||
(primitives
|
|
||||||
(gr_poly
|
|
||||||
(pts
|
|
||||||
(xy 2.03 0.25)
|
|
||||||
(xy -2.03 0.25)
|
|
||||||
(xy -2.053 0.249)
|
|
||||||
(xy -2.076 0.246)
|
|
||||||
(xy -2.098 0.24)
|
|
||||||
(xy -2.12 0.233)
|
|
||||||
(xy -2.141 0.224)
|
|
||||||
(xy -2.162 0.213)
|
|
||||||
(xy -2.181 0.2)
|
|
||||||
(xy -2.198 0.185)
|
|
||||||
(xy -2.215 0.168)
|
|
||||||
(xy -2.23 0.151)
|
|
||||||
(xy -2.243 0.132)
|
|
||||||
(xy -2.254 0.111)
|
|
||||||
(xy -2.263 0.09)
|
|
||||||
(xy -2.271 0.068)
|
|
||||||
(xy -2.276 0.046)
|
|
||||||
(xy -2.279 0.023)
|
|
||||||
(xy -2.28 0)
|
|
||||||
(xy -2.279 -0.023)
|
|
||||||
(xy -2.276 -0.046)
|
|
||||||
(xy -2.271 -0.068)
|
|
||||||
(xy -2.263 -0.09)
|
|
||||||
(xy -2.254 -0.111)
|
|
||||||
(xy -2.243 -0.132)
|
|
||||||
(xy -2.23 -0.151)
|
|
||||||
(xy -2.215 -0.168)
|
|
||||||
(xy -2.198 -0.185)
|
|
||||||
(xy -2.181 -0.2)
|
|
||||||
(xy -2.162 -0.213)
|
|
||||||
(xy -2.141 -0.224)
|
|
||||||
(xy -2.12 -0.233)
|
|
||||||
(xy -2.098 -0.24)
|
|
||||||
(xy -2.076 -0.246)
|
|
||||||
(xy -2.053 -0.249)
|
|
||||||
(xy -2.03 -0.25)
|
|
||||||
(xy 2.03 -0.25)
|
|
||||||
(xy 2.053 -0.249)
|
|
||||||
(xy 2.076 -0.246)
|
|
||||||
(xy 2.098 -0.24)
|
|
||||||
(xy 2.12 -0.233)
|
|
||||||
(xy 2.141 -0.224)
|
|
||||||
(xy 2.161 -0.213)
|
|
||||||
(xy 2.181 -0.2)
|
|
||||||
(xy 2.198 -0.185)
|
|
||||||
(xy 2.215 -0.168)
|
|
||||||
(xy 2.229 -0.151)
|
|
||||||
(xy 2.242 -0.132)
|
|
||||||
(xy 2.254 -0.111)
|
|
||||||
(xy 2.263 -0.09)
|
|
||||||
(xy 2.27 -0.068)
|
|
||||||
(xy 2.276 -0.046)
|
|
||||||
(xy 2.279 -0.023)
|
|
||||||
(xy 2.28 0)
|
|
||||||
(xy 2.279 0.023)
|
|
||||||
(xy 2.276 0.046)
|
|
||||||
(xy 2.27 0.068)
|
|
||||||
(xy 2.263 0.09)
|
|
||||||
(xy 2.254 0.111)
|
|
||||||
(xy 2.242 0.132)
|
|
||||||
(xy 2.229 0.151)
|
|
||||||
(xy 2.215 0.168)
|
|
||||||
(xy 2.198 0.185)
|
|
||||||
(xy 2.181 0.2)
|
|
||||||
(xy 2.161 0.213)
|
|
||||||
(xy 2.141 0.224)
|
|
||||||
(xy 2.12 0.233)
|
|
||||||
(xy 2.098 0.24)
|
|
||||||
(xy 2.076 0.246)
|
|
||||||
(xy 2.053 0.249)
|
|
||||||
(xy 2.03 0.25)
|
|
||||||
)
|
|
||||||
(width 0.1) (fill yes))
|
|
||||||
) (tstamp 22ddeb7d-6f62-4f5d-bef8-59c9303d43a6))
|
|
||||||
(pad "2" smd rect (at 6.6 -5.08) (size 3 2.5) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp ec1470bb-6cc4-4827-8a67-be405e79fed6))
|
|
||||||
(pad "3" smd rect (at -2.7 3.88) (size 1.2 0.84) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp 6ded5b95-fcd6-45ef-a218-42abc33f8229))
|
|
||||||
(pad "4" smd rect (at 2.7245 5.38) (size 1.249 0.84) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp 6f1d9c92-fe48-4919-a638-7dd0893fdb4a))
|
|
||||||
(pad "5" smd rect (at -2.7 5.38) (size 1.2 0.84) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp c81a963b-9726-4419-b939-5c19ad5fc8df))
|
|
||||||
(pad "6" smd rect (at 2.7245 3.88) (size 1.249 0.84) (layers "B.Cu" "B.Paste" "B.Mask") (tstamp 002e09e3-d3a5-4eaf-ad6a-06c235a242c8))
|
|
||||||
)
|
)
|
||||||
|
|||||||
30
schematic/ferris-stencil.svg
Normal file
30
schematic/ferris-stencil.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 12 KiB |
31
schematic/ferris-stencil2.svg
Normal file
31
schematic/ferris-stencil2.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 12 KiB |
File diff suppressed because it is too large
Load Diff
@ -770,6 +770,10 @@
|
|||||||
(stroke (width 0) (type default))
|
(stroke (width 0) (type default))
|
||||||
(uuid 45ce9686-37ac-46c8-81f1-9d5d317cb4f6)
|
(uuid 45ce9686-37ac-46c8-81f1-9d5d317cb4f6)
|
||||||
)
|
)
|
||||||
|
(wire (pts (xy 222.25 106.68) (xy 218.44 106.68))
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(uuid 67f4605a-03f4-46d7-a4ea-494957469e27)
|
||||||
|
)
|
||||||
(wire (pts (xy 121.92 39.37) (xy 129.54 39.37))
|
(wire (pts (xy 121.92 39.37) (xy 129.54 39.37))
|
||||||
(stroke (width 0) (type default))
|
(stroke (width 0) (type default))
|
||||||
(uuid 71019c66-8c11-49ed-bab5-1f93549d4e48)
|
(uuid 71019c66-8c11-49ed-bab5-1f93549d4e48)
|
||||||
@ -978,7 +982,7 @@
|
|||||||
(effects (font (size 1.27 1.27)) (justify left bottom))
|
(effects (font (size 1.27 1.27)) (justify left bottom))
|
||||||
(uuid c1d3efc7-7edf-4d2e-883f-9dad286118cd)
|
(uuid c1d3efc7-7edf-4d2e-883f-9dad286118cd)
|
||||||
)
|
)
|
||||||
(label "VBUS" (at 218.44 106.68 0) (fields_autoplaced)
|
(label "VBUS" (at 218.44 114.3 0) (fields_autoplaced)
|
||||||
(effects (font (size 1.27 1.27)) (justify left bottom))
|
(effects (font (size 1.27 1.27)) (justify left bottom))
|
||||||
(uuid c640bbf0-6243-4f46-a54e-8485d0b145ab)
|
(uuid c640bbf0-6243-4f46-a54e-8485d0b145ab)
|
||||||
)
|
)
|
||||||
@ -1045,7 +1049,7 @@
|
|||||||
(pin "5" (uuid 901eeacd-9093-41b1-b6d9-9c756d167a12))
|
(pin "5" (uuid 901eeacd-9093-41b1-b6d9-9c756d167a12))
|
||||||
(pin "6" (uuid 31bf21ee-d6cc-4e8e-a9ec-6bdc5bd0c77c))
|
(pin "6" (uuid 31bf21ee-d6cc-4e8e-a9ec-6bdc5bd0c77c))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "S4") (unit 1)
|
(reference "S4") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1070,7 +1074,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 4d17d4e5-e52b-4790-846e-aaf1d37be294))
|
(pin "1" (uuid 4d17d4e5-e52b-4790-846e-aaf1d37be294))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR027") (unit 1)
|
(reference "#PWR027") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1095,7 +1099,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 4f3f9a16-99dd-4bee-89f2-c88052ffd9cc))
|
(pin "1" (uuid 4f3f9a16-99dd-4bee-89f2-c88052ffd9cc))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR03") (unit 1)
|
(reference "#PWR03") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1120,7 +1124,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 71819f22-ee10-406b-b127-d1e67d9afa67))
|
(pin "1" (uuid 71819f22-ee10-406b-b127-d1e67d9afa67))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR026") (unit 1)
|
(reference "#PWR026") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1150,7 +1154,7 @@
|
|||||||
(pin "5" (uuid 5db46715-aeb6-4096-b03b-1c03af015d3a))
|
(pin "5" (uuid 5db46715-aeb6-4096-b03b-1c03af015d3a))
|
||||||
(pin "6" (uuid 824b7a5c-31f3-4424-a63c-2ce378d62561))
|
(pin "6" (uuid 824b7a5c-31f3-4424-a63c-2ce378d62561))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "S6") (unit 1)
|
(reference "S6") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1175,7 +1179,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid a657d869-b2f6-4564-882e-f52060cef501))
|
(pin "1" (uuid a657d869-b2f6-4564-882e-f52060cef501))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR031") (unit 1)
|
(reference "#PWR031") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1200,7 +1204,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid ea4ae56c-2fa8-42b5-a79f-463270dae1ea))
|
(pin "1" (uuid ea4ae56c-2fa8-42b5-a79f-463270dae1ea))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR035") (unit 1)
|
(reference "#PWR035") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1225,7 +1229,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 8da05b0c-7df2-47a2-b1b6-226792cb91ef))
|
(pin "1" (uuid 8da05b0c-7df2-47a2-b1b6-226792cb91ef))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR025") (unit 1)
|
(reference "#PWR025") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1242,7 +1246,7 @@
|
|||||||
(property "Value" "Conn_01x01_Pin" (at 62.23 153.035 0)
|
(property "Value" "Conn_01x01_Pin" (at 62.23 153.035 0)
|
||||||
(effects (font (size 1.27 1.27)) hide)
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
)
|
)
|
||||||
(property "Footprint" "Connector_Pin:Pin_D0.9mm_L10.0mm_W2.4mm_FlatFork" (at 63.5 153.67 0)
|
(property "Footprint" "Footprints:Pin0.9mm" (at 63.5 153.67 0)
|
||||||
(effects (font (size 1.27 1.27)) hide)
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
)
|
)
|
||||||
(property "Datasheet" "~" (at 63.5 153.67 0)
|
(property "Datasheet" "~" (at 63.5 153.67 0)
|
||||||
@ -1250,7 +1254,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid e22fd5b3-112d-45f1-9a6b-1b23278633a5))
|
(pin "1" (uuid e22fd5b3-112d-45f1-9a6b-1b23278633a5))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "E5") (unit 1)
|
(reference "E5") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1275,7 +1279,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid fb3c3808-52db-427c-a2ae-fdb4def984b5))
|
(pin "1" (uuid fb3c3808-52db-427c-a2ae-fdb4def984b5))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR012") (unit 1)
|
(reference "#PWR012") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1300,7 +1304,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 11de2bea-9b89-4c92-a584-b9a9bcdd50d2))
|
(pin "1" (uuid 11de2bea-9b89-4c92-a584-b9a9bcdd50d2))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR029") (unit 1)
|
(reference "#PWR029") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1325,7 +1329,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 8ae632b7-247f-4e3d-8c72-2fbe9725c6ea))
|
(pin "1" (uuid 8ae632b7-247f-4e3d-8c72-2fbe9725c6ea))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR014") (unit 1)
|
(reference "#PWR014") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1350,7 +1354,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 4464d849-528a-40f2-ae28-f56e9a29fc7c))
|
(pin "1" (uuid 4464d849-528a-40f2-ae28-f56e9a29fc7c))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR033") (unit 1)
|
(reference "#PWR033") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1375,7 +1379,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid d073f559-095c-4684-aeb5-add338265662))
|
(pin "1" (uuid d073f559-095c-4684-aeb5-add338265662))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR022") (unit 1)
|
(reference "#PWR022") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1400,7 +1404,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid e7f4a9c3-3b87-47b2-89fe-8d079678feb4))
|
(pin "1" (uuid e7f4a9c3-3b87-47b2-89fe-8d079678feb4))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR047") (unit 1)
|
(reference "#PWR047") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1425,7 +1429,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 4bbdb04f-2fcc-4bd2-82ca-7aa32cab0273))
|
(pin "1" (uuid 4bbdb04f-2fcc-4bd2-82ca-7aa32cab0273))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR013") (unit 1)
|
(reference "#PWR013") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1450,7 +1454,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 776ac4d5-c528-4da9-a4c5-5fc66e0ed55b))
|
(pin "1" (uuid 776ac4d5-c528-4da9-a4c5-5fc66e0ed55b))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR010") (unit 1)
|
(reference "#PWR010") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1475,7 +1479,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 25793923-078a-4ff3-a881-35ccf0ae6f0c))
|
(pin "1" (uuid 25793923-078a-4ff3-a881-35ccf0ae6f0c))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR039") (unit 1)
|
(reference "#PWR039") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1500,7 +1504,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid a0dae1f6-1876-4acb-ac0d-881607108f0c))
|
(pin "1" (uuid a0dae1f6-1876-4acb-ac0d-881607108f0c))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR07") (unit 1)
|
(reference "#PWR07") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1525,7 +1529,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid d95dbad1-4b9b-4d59-9e11-5b60eddca0f1))
|
(pin "1" (uuid d95dbad1-4b9b-4d59-9e11-5b60eddca0f1))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR049") (unit 1)
|
(reference "#PWR049") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1550,7 +1554,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 42cd2d71-918a-4fb3-a50a-128d210c71fa))
|
(pin "1" (uuid 42cd2d71-918a-4fb3-a50a-128d210c71fa))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR034") (unit 1)
|
(reference "#PWR034") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1575,7 +1579,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid f772d4c4-4943-4bc1-87bc-26b73d02e13e))
|
(pin "1" (uuid f772d4c4-4943-4bc1-87bc-26b73d02e13e))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR050") (unit 1)
|
(reference "#PWR050") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1600,7 +1604,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 79d17ebf-ccce-42b2-8a94-837c9e5a1b50))
|
(pin "1" (uuid 79d17ebf-ccce-42b2-8a94-837c9e5a1b50))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR04") (unit 1)
|
(reference "#PWR04") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1625,7 +1629,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 25c844b5-dc37-46ea-ad27-3bb60f129178))
|
(pin "1" (uuid 25c844b5-dc37-46ea-ad27-3bb60f129178))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR042") (unit 1)
|
(reference "#PWR042") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1650,7 +1654,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 01d9dbc9-e70e-4689-82d3-d354073788d7))
|
(pin "1" (uuid 01d9dbc9-e70e-4689-82d3-d354073788d7))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR043") (unit 1)
|
(reference "#PWR043") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1675,7 +1679,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid f7d04c4a-8102-4471-b930-f5c3f52b830d))
|
(pin "1" (uuid f7d04c4a-8102-4471-b930-f5c3f52b830d))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR015") (unit 1)
|
(reference "#PWR015") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1700,7 +1704,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 51549ee1-e211-47c9-b912-7c1288b4e045))
|
(pin "1" (uuid 51549ee1-e211-47c9-b912-7c1288b4e045))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR06") (unit 1)
|
(reference "#PWR06") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1725,7 +1729,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 7df12373-9727-42d5-804a-5d5fbe225cd2))
|
(pin "1" (uuid 7df12373-9727-42d5-804a-5d5fbe225cd2))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR011") (unit 1)
|
(reference "#PWR011") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1750,7 +1754,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid a14f9277-ab7b-454d-904c-a2ed4193c628))
|
(pin "1" (uuid a14f9277-ab7b-454d-904c-a2ed4193c628))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR037") (unit 1)
|
(reference "#PWR037") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1780,7 +1784,7 @@
|
|||||||
(pin "5" (uuid 4a3dd2f1-ff65-4cfe-82a0-1294a38f03ef))
|
(pin "5" (uuid 4a3dd2f1-ff65-4cfe-82a0-1294a38f03ef))
|
||||||
(pin "6" (uuid 5790b68a-a855-43ad-8c83-aa27759ebd69))
|
(pin "6" (uuid 5790b68a-a855-43ad-8c83-aa27759ebd69))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "S14") (unit 1)
|
(reference "S14") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1807,7 +1811,7 @@
|
|||||||
(pin "2" (uuid c4442656-d86b-47ae-8468-b312cd7be263))
|
(pin "2" (uuid c4442656-d86b-47ae-8468-b312cd7be263))
|
||||||
(pin "3" (uuid 05989648-1d94-4a4c-8d46-a0fd0cc198ca))
|
(pin "3" (uuid 05989648-1d94-4a4c-8d46-a0fd0cc198ca))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "J2") (unit 1)
|
(reference "J2") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1832,7 +1836,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid c2ffa5e3-a642-43e1-a6b4-75834de138a8))
|
(pin "1" (uuid c2ffa5e3-a642-43e1-a6b4-75834de138a8))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR08") (unit 1)
|
(reference "#PWR08") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1857,7 +1861,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 046c3ff8-4838-4a67-8633-1cc025056b02))
|
(pin "1" (uuid 046c3ff8-4838-4a67-8633-1cc025056b02))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR016") (unit 1)
|
(reference "#PWR016") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1874,7 +1878,7 @@
|
|||||||
(property "Value" "Conn_01x01_Pin" (at 57.15 158.115 0)
|
(property "Value" "Conn_01x01_Pin" (at 57.15 158.115 0)
|
||||||
(effects (font (size 1.27 1.27)) hide)
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
)
|
)
|
||||||
(property "Footprint" "Connector_Pin:Pin_D0.9mm_L10.0mm_W2.4mm_FlatFork" (at 58.42 158.75 0)
|
(property "Footprint" "Footprints:Pin0.9mm" (at 58.42 158.75 0)
|
||||||
(effects (font (size 1.27 1.27)) hide)
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
)
|
)
|
||||||
(property "Datasheet" "~" (at 58.42 158.75 0)
|
(property "Datasheet" "~" (at 58.42 158.75 0)
|
||||||
@ -1882,7 +1886,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid b18f82fd-f3fc-422d-a7c1-308eb0813f20))
|
(pin "1" (uuid b18f82fd-f3fc-422d-a7c1-308eb0813f20))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "E3") (unit 1)
|
(reference "E3") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1912,7 +1916,7 @@
|
|||||||
(pin "5" (uuid 7b86855e-0c05-4a3d-a3f5-05f79f993f8f))
|
(pin "5" (uuid 7b86855e-0c05-4a3d-a3f5-05f79f993f8f))
|
||||||
(pin "6" (uuid f0f0b53c-8105-4d93-a827-f841d53391a6))
|
(pin "6" (uuid f0f0b53c-8105-4d93-a827-f841d53391a6))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "S12") (unit 1)
|
(reference "S12") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1937,7 +1941,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 9a5ff29f-d4e9-4c4b-925d-3b4b5cdebf63))
|
(pin "1" (uuid 9a5ff29f-d4e9-4c4b-925d-3b4b5cdebf63))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR018") (unit 1)
|
(reference "#PWR018") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1954,7 +1958,7 @@
|
|||||||
(property "Value" "Conn_01x01_Pin" (at 64.77 150.495 0)
|
(property "Value" "Conn_01x01_Pin" (at 64.77 150.495 0)
|
||||||
(effects (font (size 1.27 1.27)) hide)
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
)
|
)
|
||||||
(property "Footprint" "Connector_Pin:Pin_D0.9mm_L10.0mm_W2.4mm_FlatFork" (at 66.04 151.13 0)
|
(property "Footprint" "Footprints:Pin0.9mm" (at 66.04 151.13 0)
|
||||||
(effects (font (size 1.27 1.27)) hide)
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
)
|
)
|
||||||
(property "Datasheet" "~" (at 66.04 151.13 0)
|
(property "Datasheet" "~" (at 66.04 151.13 0)
|
||||||
@ -1962,7 +1966,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid b3fee799-844f-4251-896c-71dcf5167701))
|
(pin "1" (uuid b3fee799-844f-4251-896c-71dcf5167701))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "E6") (unit 1)
|
(reference "E6") (unit 1)
|
||||||
)
|
)
|
||||||
@ -1990,7 +1994,7 @@
|
|||||||
(pin "S" (uuid f59b4c8f-5ced-4eb8-9de6-17f561765e71))
|
(pin "S" (uuid f59b4c8f-5ced-4eb8-9de6-17f561765e71))
|
||||||
(pin "T" (uuid a2bf1d99-3b19-4d13-856a-f2f7646f8841))
|
(pin "T" (uuid a2bf1d99-3b19-4d13-856a-f2f7646f8841))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "J1") (unit 1)
|
(reference "J1") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2020,7 +2024,7 @@
|
|||||||
(pin "5" (uuid 10c7c1ef-d2db-4706-a5bf-f5a673f49916))
|
(pin "5" (uuid 10c7c1ef-d2db-4706-a5bf-f5a673f49916))
|
||||||
(pin "6" (uuid e59e3398-cb12-4b55-b51c-0c0a6464a386))
|
(pin "6" (uuid e59e3398-cb12-4b55-b51c-0c0a6464a386))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "S1") (unit 1)
|
(reference "S1") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2045,7 +2049,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 4f156734-6523-4866-83c7-3620b0bb557f))
|
(pin "1" (uuid 4f156734-6523-4866-83c7-3620b0bb557f))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR045") (unit 1)
|
(reference "#PWR045") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2075,7 +2079,7 @@
|
|||||||
(pin "5" (uuid ec9d623e-81bc-4386-97b4-29de0a2a5c4d))
|
(pin "5" (uuid ec9d623e-81bc-4386-97b4-29de0a2a5c4d))
|
||||||
(pin "6" (uuid a8bc9c31-b2ea-4e1b-bbac-2a74641bd0d2))
|
(pin "6" (uuid a8bc9c31-b2ea-4e1b-bbac-2a74641bd0d2))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "S9") (unit 1)
|
(reference "S9") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2100,7 +2104,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 20187bf1-85d8-4c2f-8ed4-c936b2e38753))
|
(pin "1" (uuid 20187bf1-85d8-4c2f-8ed4-c936b2e38753))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR030") (unit 1)
|
(reference "#PWR030") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2125,7 +2129,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 2527b9be-ed38-476d-aa88-c0b5f0c267e8))
|
(pin "1" (uuid 2527b9be-ed38-476d-aa88-c0b5f0c267e8))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR019") (unit 1)
|
(reference "#PWR019") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2133,24 +2137,24 @@
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
(symbol (lib_id "power:GND") (at 218.44 114.3 90) (unit 1)
|
(symbol (lib_id "power:GND") (at 222.25 106.68 90) (unit 1)
|
||||||
(in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
|
(in_bom yes) (on_board yes) (dnp no) (fields_autoplaced)
|
||||||
(uuid 8b86b0c7-7e8c-4d4b-b184-8a287140ad04)
|
(uuid 8b86b0c7-7e8c-4d4b-b184-8a287140ad04)
|
||||||
(property "Reference" "#PWR048" (at 224.79 114.3 0)
|
(property "Reference" "#PWR048" (at 228.6 106.68 0)
|
||||||
(effects (font (size 1.27 1.27)) hide)
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
)
|
)
|
||||||
(property "Value" "GND" (at 222.25 114.935 90)
|
(property "Value" "GND" (at 226.06 107.315 90)
|
||||||
(effects (font (size 1.27 1.27)) (justify right))
|
(effects (font (size 1.27 1.27)) (justify right))
|
||||||
)
|
)
|
||||||
(property "Footprint" "" (at 218.44 114.3 0)
|
(property "Footprint" "" (at 222.25 106.68 0)
|
||||||
(effects (font (size 1.27 1.27)) hide)
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
)
|
)
|
||||||
(property "Datasheet" "" (at 218.44 114.3 0)
|
(property "Datasheet" "" (at 222.25 106.68 0)
|
||||||
(effects (font (size 1.27 1.27)) hide)
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
)
|
)
|
||||||
(pin "1" (uuid 10aea5bc-cebd-4591-9e01-07d4bc8e21bc))
|
(pin "1" (uuid 10aea5bc-cebd-4591-9e01-07d4bc8e21bc))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR048") (unit 1)
|
(reference "#PWR048") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2180,7 +2184,7 @@
|
|||||||
(pin "5" (uuid aed754dc-2983-4af5-86aa-e5f563e46a6d))
|
(pin "5" (uuid aed754dc-2983-4af5-86aa-e5f563e46a6d))
|
||||||
(pin "6" (uuid d73e750f-547c-40ee-a119-edad178bbb23))
|
(pin "6" (uuid d73e750f-547c-40ee-a119-edad178bbb23))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "S7") (unit 1)
|
(reference "S7") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2205,7 +2209,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 8859f255-9dd4-4ffa-aef2-2dd34a306a46))
|
(pin "1" (uuid 8859f255-9dd4-4ffa-aef2-2dd34a306a46))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR040") (unit 1)
|
(reference "#PWR040") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2230,7 +2234,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 43589a1a-4e2d-4327-ad2a-f57134fcc2d9))
|
(pin "1" (uuid 43589a1a-4e2d-4327-ad2a-f57134fcc2d9))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR044") (unit 1)
|
(reference "#PWR044") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2255,7 +2259,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid a1db44cb-628e-47b0-84d2-d907212864d5))
|
(pin "1" (uuid a1db44cb-628e-47b0-84d2-d907212864d5))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR021") (unit 1)
|
(reference "#PWR021") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2285,7 +2289,7 @@
|
|||||||
(pin "5" (uuid 6a4f2232-9299-4e8f-9f1f-4d17bbf6f974))
|
(pin "5" (uuid 6a4f2232-9299-4e8f-9f1f-4d17bbf6f974))
|
||||||
(pin "6" (uuid f39e2e1c-014a-42c2-96ef-a835035b6e02))
|
(pin "6" (uuid f39e2e1c-014a-42c2-96ef-a835035b6e02))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "S11") (unit 1)
|
(reference "S11") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2310,7 +2314,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid a1796b56-e745-4770-8d52-1a0816eb478e))
|
(pin "1" (uuid a1796b56-e745-4770-8d52-1a0816eb478e))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR017") (unit 1)
|
(reference "#PWR017") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2340,7 +2344,7 @@
|
|||||||
(pin "5" (uuid 2658aad9-f55b-4586-b9ef-30dea23bf788))
|
(pin "5" (uuid 2658aad9-f55b-4586-b9ef-30dea23bf788))
|
||||||
(pin "6" (uuid 31eed18c-dd8d-4009-a153-2b5c9819e076))
|
(pin "6" (uuid 31eed18c-dd8d-4009-a153-2b5c9819e076))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "S15") (unit 1)
|
(reference "S15") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2365,7 +2369,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid c1258a76-2b59-48c3-aa22-cdd932e1fce6))
|
(pin "1" (uuid c1258a76-2b59-48c3-aa22-cdd932e1fce6))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR01") (unit 1)
|
(reference "#PWR01") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2390,7 +2394,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid dcc0d602-8c49-4089-b2e5-57839a936a10))
|
(pin "1" (uuid dcc0d602-8c49-4089-b2e5-57839a936a10))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR036") (unit 1)
|
(reference "#PWR036") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2415,7 +2419,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 1eb695c4-876c-42c9-ab0f-1de8011272cc))
|
(pin "1" (uuid 1eb695c4-876c-42c9-ab0f-1de8011272cc))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR020") (unit 1)
|
(reference "#PWR020") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2432,7 +2436,7 @@
|
|||||||
(property "Value" "Conn_01x01_Pin" (at 52.07 163.195 0)
|
(property "Value" "Conn_01x01_Pin" (at 52.07 163.195 0)
|
||||||
(effects (font (size 1.27 1.27)) hide)
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
)
|
)
|
||||||
(property "Footprint" "Connector_Pin:Pin_D0.9mm_L10.0mm_W2.4mm_FlatFork" (at 53.34 163.83 0)
|
(property "Footprint" "Footprints:Pin0.9mm" (at 53.34 163.83 0)
|
||||||
(effects (font (size 1.27 1.27)) hide)
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
)
|
)
|
||||||
(property "Datasheet" "~" (at 53.34 163.83 0)
|
(property "Datasheet" "~" (at 53.34 163.83 0)
|
||||||
@ -2440,7 +2444,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 47af8854-6300-4077-8bd8-fb163f3bd7cd))
|
(pin "1" (uuid 47af8854-6300-4077-8bd8-fb163f3bd7cd))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "E1") (unit 1)
|
(reference "E1") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2457,7 +2461,7 @@
|
|||||||
(property "Value" "Conn_01x01_Pin" (at 54.61 160.655 0)
|
(property "Value" "Conn_01x01_Pin" (at 54.61 160.655 0)
|
||||||
(effects (font (size 1.27 1.27)) hide)
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
)
|
)
|
||||||
(property "Footprint" "Connector_Pin:Pin_D0.9mm_L10.0mm_W2.4mm_FlatFork" (at 55.88 161.29 0)
|
(property "Footprint" "Footprints:Pin0.9mm" (at 55.88 161.29 0)
|
||||||
(effects (font (size 1.27 1.27)) hide)
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
)
|
)
|
||||||
(property "Datasheet" "~" (at 55.88 161.29 0)
|
(property "Datasheet" "~" (at 55.88 161.29 0)
|
||||||
@ -2465,7 +2469,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 60a5cada-c1fa-4496-88af-dcb14570929a))
|
(pin "1" (uuid 60a5cada-c1fa-4496-88af-dcb14570929a))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "E2") (unit 1)
|
(reference "E2") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2490,7 +2494,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid b9c793df-6f5d-4683-835a-756b0eb88036))
|
(pin "1" (uuid b9c793df-6f5d-4683-835a-756b0eb88036))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR028") (unit 1)
|
(reference "#PWR028") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2515,7 +2519,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 4c37fec4-740e-43c7-9155-08fd6d7569ca))
|
(pin "1" (uuid 4c37fec4-740e-43c7-9155-08fd6d7569ca))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR024") (unit 1)
|
(reference "#PWR024") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2545,7 +2549,7 @@
|
|||||||
(pin "5" (uuid 450444b9-1858-4603-a14a-e11a087490f5))
|
(pin "5" (uuid 450444b9-1858-4603-a14a-e11a087490f5))
|
||||||
(pin "6" (uuid aaf6d14c-b518-4dc9-948f-b6124fc99382))
|
(pin "6" (uuid aaf6d14c-b518-4dc9-948f-b6124fc99382))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "S5") (unit 1)
|
(reference "S5") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2570,7 +2574,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 4277efec-6634-485b-b269-c962b865916c))
|
(pin "1" (uuid 4277efec-6634-485b-b269-c962b865916c))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR023") (unit 1)
|
(reference "#PWR023") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2587,7 +2591,7 @@
|
|||||||
(property "Value" "Conn_01x01_Pin" (at 59.69 155.575 0)
|
(property "Value" "Conn_01x01_Pin" (at 59.69 155.575 0)
|
||||||
(effects (font (size 1.27 1.27)) hide)
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
)
|
)
|
||||||
(property "Footprint" "Connector_Pin:Pin_D0.9mm_L10.0mm_W2.4mm_FlatFork" (at 60.96 156.21 0)
|
(property "Footprint" "Footprints:Pin0.9mm" (at 60.96 156.21 0)
|
||||||
(effects (font (size 1.27 1.27)) hide)
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
)
|
)
|
||||||
(property "Datasheet" "~" (at 60.96 156.21 0)
|
(property "Datasheet" "~" (at 60.96 156.21 0)
|
||||||
@ -2595,7 +2599,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid abcc0953-9b9a-4782-9e15-90134e8472b2))
|
(pin "1" (uuid abcc0953-9b9a-4782-9e15-90134e8472b2))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "E4") (unit 1)
|
(reference "E4") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2625,7 +2629,7 @@
|
|||||||
(pin "5" (uuid 8ddff36e-73d8-40f7-b106-5ea211e406a5))
|
(pin "5" (uuid 8ddff36e-73d8-40f7-b106-5ea211e406a5))
|
||||||
(pin "6" (uuid 6be3f964-9d9c-4796-9c1a-4ee7e89d4e5b))
|
(pin "6" (uuid 6be3f964-9d9c-4796-9c1a-4ee7e89d4e5b))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "S3") (unit 1)
|
(reference "S3") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2650,7 +2654,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 4dd697cd-eccd-4268-bafd-2aa3f4b79939))
|
(pin "1" (uuid 4dd697cd-eccd-4268-bafd-2aa3f4b79939))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR032") (unit 1)
|
(reference "#PWR032") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2675,7 +2679,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 87364ab0-8e4d-4fc4-9def-599abd503938))
|
(pin "1" (uuid 87364ab0-8e4d-4fc4-9def-599abd503938))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR046") (unit 1)
|
(reference "#PWR046") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2700,7 +2704,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid f460d2a6-da50-4fa8-ae3e-d7b95ce53ab5))
|
(pin "1" (uuid f460d2a6-da50-4fa8-ae3e-d7b95ce53ab5))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR051") (unit 1)
|
(reference "#PWR051") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2730,7 +2734,7 @@
|
|||||||
(pin "5" (uuid 01649b87-2e95-4105-a359-b8df650350f4))
|
(pin "5" (uuid 01649b87-2e95-4105-a359-b8df650350f4))
|
||||||
(pin "6" (uuid 63a469e3-c153-4735-bee8-575d2ecd1b4b))
|
(pin "6" (uuid 63a469e3-c153-4735-bee8-575d2ecd1b4b))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "S2") (unit 1)
|
(reference "S2") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2755,7 +2759,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid ced3328e-00ab-4c52-ac2e-19d2ca747447))
|
(pin "1" (uuid ced3328e-00ab-4c52-ac2e-19d2ca747447))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR05") (unit 1)
|
(reference "#PWR05") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2780,7 +2784,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid b83c64a3-d529-4a91-abfb-c8c67869fe43))
|
(pin "1" (uuid b83c64a3-d529-4a91-abfb-c8c67869fe43))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR038") (unit 1)
|
(reference "#PWR038") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2805,7 +2809,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 6d017571-2263-4180-b140-689e39480222))
|
(pin "1" (uuid 6d017571-2263-4180-b140-689e39480222))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR02") (unit 1)
|
(reference "#PWR02") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2830,7 +2834,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 7a5f29be-f291-4866-8765-cf590f6aa416))
|
(pin "1" (uuid 7a5f29be-f291-4866-8765-cf590f6aa416))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR09") (unit 1)
|
(reference "#PWR09") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2885,7 +2889,7 @@
|
|||||||
(pin "8" (uuid b5889620-f22f-4bbf-97b1-d0bda3b456c3))
|
(pin "8" (uuid b5889620-f22f-4bbf-97b1-d0bda3b456c3))
|
||||||
(pin "9" (uuid 9bad2cc6-2420-4c19-a98d-da82392756f1))
|
(pin "9" (uuid 9bad2cc6-2420-4c19-a98d-da82392756f1))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "MCU1") (unit 1)
|
(reference "MCU1") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2915,7 +2919,7 @@
|
|||||||
(pin "5" (uuid 8e1d4c84-f365-4acf-a11f-5b6d46ecf3c4))
|
(pin "5" (uuid 8e1d4c84-f365-4acf-a11f-5b6d46ecf3c4))
|
||||||
(pin "6" (uuid aa8aa242-f916-41dc-acb7-ca13ec2740cd))
|
(pin "6" (uuid aa8aa242-f916-41dc-acb7-ca13ec2740cd))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "S8") (unit 1)
|
(reference "S8") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2945,7 +2949,7 @@
|
|||||||
(pin "5" (uuid b2146209-ff46-460c-9892-06cfe6b2d0ae))
|
(pin "5" (uuid b2146209-ff46-460c-9892-06cfe6b2d0ae))
|
||||||
(pin "6" (uuid 255a2142-4e14-44b9-9bff-d424580decfe))
|
(pin "6" (uuid 255a2142-4e14-44b9-9bff-d424580decfe))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "S10") (unit 1)
|
(reference "S10") (unit 1)
|
||||||
)
|
)
|
||||||
@ -2975,7 +2979,7 @@
|
|||||||
(pin "5" (uuid 54537fe0-72d3-46b9-9dc2-b62bc898c7d6))
|
(pin "5" (uuid 54537fe0-72d3-46b9-9dc2-b62bc898c7d6))
|
||||||
(pin "6" (uuid d8cbd17b-7bd7-4a56-bef2-0c760655e2fd))
|
(pin "6" (uuid d8cbd17b-7bd7-4a56-bef2-0c760655e2fd))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "S13") (unit 1)
|
(reference "S13") (unit 1)
|
||||||
)
|
)
|
||||||
@ -3000,7 +3004,7 @@
|
|||||||
)
|
)
|
||||||
(pin "1" (uuid 55eb5a06-de96-40f7-a9a2-cb9d79ba2cfe))
|
(pin "1" (uuid 55eb5a06-de96-40f7-a9a2-cb9d79ba2cfe))
|
||||||
(instances
|
(instances
|
||||||
(project "tangentbord1"
|
(project "tangentbord1-left"
|
||||||
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
(path "/9b6bc1bb-4d53-4a7c-b909-4e79c2146edb"
|
||||||
(reference "#PWR041") (unit 1)
|
(reference "#PWR041") (unit 1)
|
||||||
)
|
)
|
||||||
|
|||||||
4
schematic/pcb_right/fp-lib-table
Normal file
4
schematic/pcb_right/fp-lib-table
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
(fp_lib_table
|
||||||
|
(version 7)
|
||||||
|
(lib (name "Footprints")(type "KiCad")(uri "${KIPRJMOD}/../Footprints.pretty")(options "")(descr ""))
|
||||||
|
)
|
||||||
566
schematic/pcb_right/library.kicad_sym
Normal file
566
schematic/pcb_right/library.kicad_sym
Normal file
@ -0,0 +1,566 @@
|
|||||||
|
(kicad_symbol_lib (version 20220914) (generator kicad_symbol_editor)
|
||||||
|
(symbol "ITSYBITSY_RP2040" (in_bom yes) (on_board yes)
|
||||||
|
(property "Reference" "U1" (at 3.81 41.91 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(property "Value" "ITSYBITSY RP2040" (at 0 11.43 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(property "Footprint" "Footprints:ITSYBITSY BREAKOUT" (at 24.13 -20.32 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(property "Datasheet" "" (at 0 5.08 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(symbol "ITSYBITSY_RP2040_1_1"
|
||||||
|
(rectangle (start -17.78 39.37) (end 17.78 -16.51)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type background))
|
||||||
|
)
|
||||||
|
(pin input line (at -17.78 35.56 0) (length 2.54)
|
||||||
|
(name "RESET" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "0" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_out line (at -17.78 31.75 0) (length 2.54)
|
||||||
|
(name "3.3V" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "1" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 -2.54 0) (length 2.54)
|
||||||
|
(name "SCK" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "10" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 -6.35 0) (length 2.54)
|
||||||
|
(name "MOSI" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "11" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 -10.16 0) (length 2.54)
|
||||||
|
(name "MISO" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "12" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 -13.97 0) (length 2.54)
|
||||||
|
(name "D2" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "13" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -3.81 -16.51 90) (length 2.54)
|
||||||
|
(name "SWDIO" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "14" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 0 -16.51 90) (length 2.54)
|
||||||
|
(name "SWCLK" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "15" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 3.81 -16.51 90) (length 2.54)
|
||||||
|
(name "D3" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "16" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 -13.97 180) (length 2.54)
|
||||||
|
(name "RX" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "17" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 -10.16 180) (length 2.54)
|
||||||
|
(name "TX" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "18" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 -6.35 180) (length 2.54)
|
||||||
|
(name "SDA" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "19" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_out line (at -17.78 27.94 0) (length 2.54)
|
||||||
|
(name "3.3V" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "2" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 -2.54 180) (length 2.54)
|
||||||
|
(name "SCL" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "20" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 1.27 180) (length 2.54)
|
||||||
|
(name "D5" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "21" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 5.08 180) (length 2.54)
|
||||||
|
(name "D7" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "22" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 8.89 180) (length 2.54)
|
||||||
|
(name "D9" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "23" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 12.7 180) (length 2.54)
|
||||||
|
(name "D10" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "24" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 16.51 180) (length 2.54)
|
||||||
|
(name "D11" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "25" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 20.32 180) (length 2.54)
|
||||||
|
(name "D12" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "26" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 24.13 180) (length 2.54)
|
||||||
|
(name "D13" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "27" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at 17.78 27.94 180) (length 2.54)
|
||||||
|
(name "VBUS" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "28" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at 17.78 31.75 180) (length 2.54)
|
||||||
|
(name "GND" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "29" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_out line (at -17.78 24.13 0) (length 2.54)
|
||||||
|
(name "VHI" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "3" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at 17.78 35.56 180) (length 2.54)
|
||||||
|
(name "VBAT" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "30" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 20.32 0) (length 2.54)
|
||||||
|
(name "A0" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "4" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 16.51 0) (length 2.54)
|
||||||
|
(name "A1" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "5" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 12.7 0) (length 2.54)
|
||||||
|
(name "A2" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "6" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 8.89 0) (length 2.54)
|
||||||
|
(name "A3" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "7" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 5.08 0) (length 2.54)
|
||||||
|
(name "D24" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "8" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 1.27 0) (length 2.54)
|
||||||
|
(name "D25" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "9" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB" (in_bom yes) (on_board yes)
|
||||||
|
(property "Reference" "S4" (at 11.43 -1.27 0)
|
||||||
|
(effects (font (size 1.27 1.27)) (justify left))
|
||||||
|
)
|
||||||
|
(property "Value" "MX_SW_SOCKET_LED" (at 21.59 10.16 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(property "Footprint" "Footprints:MX_SW_SOCKET_LED" (at 26.67 7.62 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(property "Datasheet" "" (at 0 3.81 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB_0_1"
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -1.27 5.08)
|
||||||
|
(xy -0.8247 5.0799)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 1.27 5.08)
|
||||||
|
(xy 0.8247 5.0801)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -10.16 6.35)
|
||||||
|
(xy -10.16 8.89)
|
||||||
|
(xy -7.62 8.89)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -7.62 -10.16)
|
||||||
|
(xy -10.16 -10.16)
|
||||||
|
(xy -10.16 -7.62)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 7.62 8.89)
|
||||||
|
(xy 10.16 8.89)
|
||||||
|
(xy 10.16 6.35)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 10.16 -7.62)
|
||||||
|
(xy 10.16 -10.16)
|
||||||
|
(xy 7.62 -10.16)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB_1_1"
|
||||||
|
(rectangle (start -6.35 0) (end 6.35 -8.89)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type background))
|
||||||
|
)
|
||||||
|
(rectangle (start -3.81 7.62) (end 3.81 1.27)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type background))
|
||||||
|
)
|
||||||
|
(circle (center -0.6173 5.0896) (radius 0.1705)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -1.27 5.08)
|
||||||
|
(xy -1.27 7.62)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type dot))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -0.5184 5.2126)
|
||||||
|
(xy 0.4381 5.7713)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 1.27 7.62)
|
||||||
|
(xy 1.27 5.08)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type dot))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(circle (center 0.6471 5.08) (radius 0.1705)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(text "NEOPIXEL" (at 0 -1.27 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(text "SWITCH" (at 0 2.54 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -1.27 10.16 270) (length 2.54)
|
||||||
|
(name "L" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "1" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 1.27 10.16 270) (length 2.54)
|
||||||
|
(name "R" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "2" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at -2.54 -11.43 90) (length 2.54)
|
||||||
|
(name "G" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "3" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at 2.54 -11.43 90) (length 2.54)
|
||||||
|
(name "5V" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "4" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin input line (at 8.89 -3.81 180) (length 2.54)
|
||||||
|
(name "DIN" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "5" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin output line (at -8.89 -3.81 0) (length 2.54)
|
||||||
|
(name "DOUT" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "6" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB_1" (in_bom yes) (on_board yes)
|
||||||
|
(property "Reference" "S5" (at 11.43 -1.27 0)
|
||||||
|
(effects (font (size 1.27 1.27)) (justify left))
|
||||||
|
)
|
||||||
|
(property "Value" "MX_SW_SOCKET_LED" (at 20.32 7.62 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(property "Footprint" "Footprints:MX_SW_SOCKET_LED" (at 25.4 5.08 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(property "Datasheet" "" (at 0 3.81 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB_1_0_1"
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -1.27 5.08)
|
||||||
|
(xy -0.8247 5.0799)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 1.27 5.08)
|
||||||
|
(xy 0.8247 5.0801)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -10.16 6.35)
|
||||||
|
(xy -10.16 8.89)
|
||||||
|
(xy -7.62 8.89)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -7.62 -10.16)
|
||||||
|
(xy -10.16 -10.16)
|
||||||
|
(xy -10.16 -7.62)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 7.62 8.89)
|
||||||
|
(xy 10.16 8.89)
|
||||||
|
(xy 10.16 6.35)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 10.16 -7.62)
|
||||||
|
(xy 10.16 -10.16)
|
||||||
|
(xy 7.62 -10.16)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB_1_1_1"
|
||||||
|
(rectangle (start -6.35 0) (end 6.35 -8.89)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type background))
|
||||||
|
)
|
||||||
|
(rectangle (start -3.81 7.62) (end 3.81 1.27)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type background))
|
||||||
|
)
|
||||||
|
(circle (center -0.6173 5.0896) (radius 0.1705)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -1.27 5.08)
|
||||||
|
(xy -1.27 7.62)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type dot))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -0.5184 5.2126)
|
||||||
|
(xy 0.4381 5.7713)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 1.27 7.62)
|
||||||
|
(xy 1.27 5.08)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type dot))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(circle (center 0.6471 5.08) (radius 0.1705)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(text "NEOPIXEL" (at 0 -1.27 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(text "SWITCH" (at 0 2.54 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -1.27 10.16 270) (length 2.54)
|
||||||
|
(name "L" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "1" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 1.27 10.16 270) (length 2.54)
|
||||||
|
(name "R" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "2" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at -2.54 -11.43 90) (length 2.54)
|
||||||
|
(name "G" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "3" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at 2.54 -11.43 90) (length 2.54)
|
||||||
|
(name "5V" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "4" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin input line (at 8.89 -3.81 180) (length 2.54)
|
||||||
|
(name "DIN" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "5" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin output line (at -8.89 -3.81 0) (length 2.54)
|
||||||
|
(name "DOUT" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "6" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB_REV" (in_bom yes) (on_board yes)
|
||||||
|
(property "Reference" "S6" (at 12.7 -3.2259 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(property "Value" "MX_SW_SOCKET_RGB_REV" (at 22.86 8.89 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(property "Footprint" "Footprints:MX_SW_SOCKET_LED_REV" (at 27.94 6.35 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(property "Datasheet" "" (at 0 3.81 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB_REV_0_1"
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -1.27 5.08)
|
||||||
|
(xy -0.8247 5.0799)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 1.27 5.08)
|
||||||
|
(xy 0.8247 5.0801)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -10.16 6.35)
|
||||||
|
(xy -10.16 8.89)
|
||||||
|
(xy -7.62 8.89)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -7.62 -10.16)
|
||||||
|
(xy -10.16 -10.16)
|
||||||
|
(xy -10.16 -7.62)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 7.62 8.89)
|
||||||
|
(xy 10.16 8.89)
|
||||||
|
(xy 10.16 6.35)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 10.16 -7.62)
|
||||||
|
(xy 10.16 -10.16)
|
||||||
|
(xy 7.62 -10.16)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB_REV_1_1"
|
||||||
|
(rectangle (start -6.35 0) (end 6.35 -8.89)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type background))
|
||||||
|
)
|
||||||
|
(rectangle (start -3.81 7.62) (end 3.81 1.27)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type background))
|
||||||
|
)
|
||||||
|
(circle (center -0.6173 5.0896) (radius 0.1705)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -1.27 5.08)
|
||||||
|
(xy -1.27 7.62)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type dot))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -0.5184 5.2126)
|
||||||
|
(xy 0.4381 5.7713)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 1.27 7.62)
|
||||||
|
(xy 1.27 5.08)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type dot))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(circle (center 0.6471 5.08) (radius 0.1705)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(text "NEOPIXEL" (at 0 -1.27 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(text "SWITCH" (at 0 2.54 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -1.27 10.16 270) (length 2.54)
|
||||||
|
(name "L" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "1" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 1.27 10.16 270) (length 2.54)
|
||||||
|
(name "R" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "2" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at -2.54 -11.43 90) (length 2.54)
|
||||||
|
(name "G" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "3" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at 2.54 -11.43 90) (length 2.54)
|
||||||
|
(name "5V" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "4" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin input line (at -8.89 -3.81 0) (length 2.54)
|
||||||
|
(name "DIN" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "5" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin output line (at 8.89 -3.81 180) (length 2.54)
|
||||||
|
(name "DOUT" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "6" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
4
schematic/pcb_right/sym-lib-table
Normal file
4
schematic/pcb_right/sym-lib-table
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
(sym_lib_table
|
||||||
|
(version 7)
|
||||||
|
(lib (name "library")(type "KiCad")(uri "${KIPRJMOD}/library.kicad_sym")(options "")(descr ""))
|
||||||
|
)
|
||||||
13994
schematic/pcb_right/tangentbord1-right.kicad_pcb
Normal file
13994
schematic/pcb_right/tangentbord1-right.kicad_pcb
Normal file
File diff suppressed because it is too large
Load Diff
500
schematic/pcb_right/tangentbord1-right.kicad_pro
Normal file
500
schematic/pcb_right/tangentbord1-right.kicad_pro
Normal file
@ -0,0 +1,500 @@
|
|||||||
|
{
|
||||||
|
"board": {
|
||||||
|
"3dviewports": [],
|
||||||
|
"design_settings": {
|
||||||
|
"defaults": {
|
||||||
|
"board_outline_line_width": 0.09999999999999999,
|
||||||
|
"copper_line_width": 0.19999999999999998,
|
||||||
|
"copper_text_italic": false,
|
||||||
|
"copper_text_size_h": 1.5,
|
||||||
|
"copper_text_size_v": 1.5,
|
||||||
|
"copper_text_thickness": 0.3,
|
||||||
|
"copper_text_upright": false,
|
||||||
|
"courtyard_line_width": 0.049999999999999996,
|
||||||
|
"dimension_precision": 4,
|
||||||
|
"dimension_units": 3,
|
||||||
|
"dimensions": {
|
||||||
|
"arrow_length": 1270000,
|
||||||
|
"extension_offset": 500000,
|
||||||
|
"keep_text_aligned": true,
|
||||||
|
"suppress_zeroes": false,
|
||||||
|
"text_position": 0,
|
||||||
|
"units_format": 1
|
||||||
|
},
|
||||||
|
"fab_line_width": 0.09999999999999999,
|
||||||
|
"fab_text_italic": false,
|
||||||
|
"fab_text_size_h": 1.0,
|
||||||
|
"fab_text_size_v": 1.0,
|
||||||
|
"fab_text_thickness": 0.15,
|
||||||
|
"fab_text_upright": false,
|
||||||
|
"other_line_width": 0.15,
|
||||||
|
"other_text_italic": false,
|
||||||
|
"other_text_size_h": 1.0,
|
||||||
|
"other_text_size_v": 1.0,
|
||||||
|
"other_text_thickness": 0.15,
|
||||||
|
"other_text_upright": false,
|
||||||
|
"pads": {
|
||||||
|
"drill": 1.0,
|
||||||
|
"height": 1.875,
|
||||||
|
"width": 1.875
|
||||||
|
},
|
||||||
|
"silk_line_width": 0.15,
|
||||||
|
"silk_text_italic": false,
|
||||||
|
"silk_text_size_h": 1.0,
|
||||||
|
"silk_text_size_v": 1.0,
|
||||||
|
"silk_text_thickness": 0.15,
|
||||||
|
"silk_text_upright": false,
|
||||||
|
"zones": {
|
||||||
|
"min_clearance": 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"diff_pair_dimensions": [
|
||||||
|
{
|
||||||
|
"gap": 0.0,
|
||||||
|
"via_gap": 0.0,
|
||||||
|
"width": 0.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"drc_exclusions": [],
|
||||||
|
"meta": {
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"rule_severities": {
|
||||||
|
"annular_width": "error",
|
||||||
|
"clearance": "error",
|
||||||
|
"connection_width": "warning",
|
||||||
|
"copper_edge_clearance": "error",
|
||||||
|
"copper_sliver": "warning",
|
||||||
|
"courtyards_overlap": "error",
|
||||||
|
"diff_pair_gap_out_of_range": "error",
|
||||||
|
"diff_pair_uncoupled_length_too_long": "error",
|
||||||
|
"drill_out_of_range": "error",
|
||||||
|
"duplicate_footprints": "warning",
|
||||||
|
"extra_footprint": "warning",
|
||||||
|
"footprint": "error",
|
||||||
|
"footprint_type_mismatch": "ignore",
|
||||||
|
"hole_clearance": "error",
|
||||||
|
"hole_near_hole": "error",
|
||||||
|
"invalid_outline": "error",
|
||||||
|
"isolated_copper": "warning",
|
||||||
|
"item_on_disabled_layer": "error",
|
||||||
|
"items_not_allowed": "error",
|
||||||
|
"length_out_of_range": "error",
|
||||||
|
"lib_footprint_issues": "warning",
|
||||||
|
"lib_footprint_mismatch": "warning",
|
||||||
|
"malformed_courtyard": "error",
|
||||||
|
"microvia_drill_out_of_range": "error",
|
||||||
|
"missing_courtyard": "ignore",
|
||||||
|
"missing_footprint": "warning",
|
||||||
|
"net_conflict": "warning",
|
||||||
|
"npth_inside_courtyard": "ignore",
|
||||||
|
"padstack": "warning",
|
||||||
|
"pth_inside_courtyard": "ignore",
|
||||||
|
"shorting_items": "error",
|
||||||
|
"silk_edge_clearance": "warning",
|
||||||
|
"silk_over_copper": "warning",
|
||||||
|
"silk_overlap": "warning",
|
||||||
|
"skew_out_of_range": "error",
|
||||||
|
"solder_mask_bridge": "error",
|
||||||
|
"starved_thermal": "error",
|
||||||
|
"text_height": "warning",
|
||||||
|
"text_thickness": "warning",
|
||||||
|
"through_hole_pad_without_hole": "error",
|
||||||
|
"too_many_vias": "error",
|
||||||
|
"track_dangling": "warning",
|
||||||
|
"track_width": "error",
|
||||||
|
"tracks_crossing": "error",
|
||||||
|
"unconnected_items": "error",
|
||||||
|
"unresolved_variable": "error",
|
||||||
|
"via_dangling": "warning",
|
||||||
|
"zones_intersect": "error"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"max_error": 0.005,
|
||||||
|
"min_clearance": 0.127,
|
||||||
|
"min_connection": 0.0,
|
||||||
|
"min_copper_edge_clearance": 0.19999999999999998,
|
||||||
|
"min_hole_clearance": 0.25,
|
||||||
|
"min_hole_to_hole": 0.25,
|
||||||
|
"min_microvia_diameter": 0.19999999999999998,
|
||||||
|
"min_microvia_drill": 0.09999999999999999,
|
||||||
|
"min_resolved_spokes": 2,
|
||||||
|
"min_silk_clearance": 0.0,
|
||||||
|
"min_text_height": 0.7999999999999999,
|
||||||
|
"min_text_thickness": 0.08,
|
||||||
|
"min_through_hole_diameter": 0.3,
|
||||||
|
"min_track_width": 0.0,
|
||||||
|
"min_via_annular_width": 0.09999999999999999,
|
||||||
|
"min_via_diameter": 0.5,
|
||||||
|
"solder_mask_clearance": 0.0,
|
||||||
|
"solder_mask_min_width": 0.0,
|
||||||
|
"solder_mask_to_copper_clearance": 0.0,
|
||||||
|
"use_height_for_length_calcs": true
|
||||||
|
},
|
||||||
|
"teardrop_options": [
|
||||||
|
{
|
||||||
|
"td_allow_use_two_tracks": true,
|
||||||
|
"td_curve_segcount": 5,
|
||||||
|
"td_on_pad_in_zone": false,
|
||||||
|
"td_onpadsmd": true,
|
||||||
|
"td_onroundshapesonly": false,
|
||||||
|
"td_ontrackend": false,
|
||||||
|
"td_onviapad": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"teardrop_parameters": [
|
||||||
|
{
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_target_name": "td_round_shape",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_target_name": "td_rect_shape",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_target_name": "td_track_end",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"track_widths": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"via_dimensions": [
|
||||||
|
{
|
||||||
|
"diameter": 0.0,
|
||||||
|
"drill": 0.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"zones_allow_external_fillets": false
|
||||||
|
},
|
||||||
|
"layer_presets": [],
|
||||||
|
"viewports": []
|
||||||
|
},
|
||||||
|
"boards": [],
|
||||||
|
"cvpcb": {
|
||||||
|
"equivalence_files": []
|
||||||
|
},
|
||||||
|
"erc": {
|
||||||
|
"erc_exclusions": [],
|
||||||
|
"meta": {
|
||||||
|
"version": 0
|
||||||
|
},
|
||||||
|
"pin_map": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"rule_severities": {
|
||||||
|
"bus_definition_conflict": "error",
|
||||||
|
"bus_entry_needed": "error",
|
||||||
|
"bus_to_bus_conflict": "error",
|
||||||
|
"bus_to_net_conflict": "error",
|
||||||
|
"conflicting_netclasses": "error",
|
||||||
|
"different_unit_footprint": "error",
|
||||||
|
"different_unit_net": "error",
|
||||||
|
"duplicate_reference": "error",
|
||||||
|
"duplicate_sheet_names": "error",
|
||||||
|
"endpoint_off_grid": "warning",
|
||||||
|
"extra_units": "error",
|
||||||
|
"global_label_dangling": "warning",
|
||||||
|
"hier_label_mismatch": "error",
|
||||||
|
"label_dangling": "error",
|
||||||
|
"lib_symbol_issues": "warning",
|
||||||
|
"missing_bidi_pin": "warning",
|
||||||
|
"missing_input_pin": "warning",
|
||||||
|
"missing_power_pin": "error",
|
||||||
|
"missing_unit": "warning",
|
||||||
|
"multiple_net_names": "warning",
|
||||||
|
"net_not_bus_member": "warning",
|
||||||
|
"no_connect_connected": "warning",
|
||||||
|
"no_connect_dangling": "warning",
|
||||||
|
"pin_not_connected": "error",
|
||||||
|
"pin_not_driven": "error",
|
||||||
|
"pin_to_pin": "warning",
|
||||||
|
"power_pin_not_driven": "error",
|
||||||
|
"similar_labels": "warning",
|
||||||
|
"simulation_model_issue": "error",
|
||||||
|
"unannotated": "error",
|
||||||
|
"unit_value_mismatch": "error",
|
||||||
|
"unresolved_variable": "error",
|
||||||
|
"wire_dangling": "error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libraries": {
|
||||||
|
"pinned_footprint_libs": [],
|
||||||
|
"pinned_symbol_libs": []
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"filename": "tangentbord1.kicad_pro",
|
||||||
|
"version": 1
|
||||||
|
},
|
||||||
|
"net_settings": {
|
||||||
|
"classes": [
|
||||||
|
{
|
||||||
|
"bus_width": 12,
|
||||||
|
"clearance": 0.2,
|
||||||
|
"diff_pair_gap": 0.25,
|
||||||
|
"diff_pair_via_gap": 0.25,
|
||||||
|
"diff_pair_width": 0.2,
|
||||||
|
"line_style": 0,
|
||||||
|
"microvia_diameter": 0.3,
|
||||||
|
"microvia_drill": 0.1,
|
||||||
|
"name": "Default",
|
||||||
|
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"track_width": 0.25,
|
||||||
|
"via_diameter": 0.8,
|
||||||
|
"via_drill": 0.4,
|
||||||
|
"wire_width": 6
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"net_colors": null,
|
||||||
|
"netclass_assignments": null,
|
||||||
|
"netclass_patterns": []
|
||||||
|
},
|
||||||
|
"pcbnew": {
|
||||||
|
"last_paths": {
|
||||||
|
"gencad": "",
|
||||||
|
"idf": "",
|
||||||
|
"netlist": "",
|
||||||
|
"specctra_dsn": "",
|
||||||
|
"step": "",
|
||||||
|
"vrml": ""
|
||||||
|
},
|
||||||
|
"page_layout_descr_file": ""
|
||||||
|
},
|
||||||
|
"schematic": {
|
||||||
|
"annotate_start_num": 0,
|
||||||
|
"drawing": {
|
||||||
|
"dashed_lines_dash_length_ratio": 12.0,
|
||||||
|
"dashed_lines_gap_length_ratio": 3.0,
|
||||||
|
"default_line_thickness": 6.0,
|
||||||
|
"default_text_size": 50.0,
|
||||||
|
"field_names": [],
|
||||||
|
"intersheets_ref_own_page": false,
|
||||||
|
"intersheets_ref_prefix": "",
|
||||||
|
"intersheets_ref_short": false,
|
||||||
|
"intersheets_ref_show": false,
|
||||||
|
"intersheets_ref_suffix": "",
|
||||||
|
"junction_size_choice": 3,
|
||||||
|
"label_size_ratio": 0.375,
|
||||||
|
"pin_symbol_size": 25.0,
|
||||||
|
"text_offset_ratio": 0.15
|
||||||
|
},
|
||||||
|
"legacy_lib_dir": "",
|
||||||
|
"legacy_lib_list": [],
|
||||||
|
"meta": {
|
||||||
|
"version": 1
|
||||||
|
},
|
||||||
|
"net_format_name": "",
|
||||||
|
"ngspice": {
|
||||||
|
"fix_include_paths": true,
|
||||||
|
"fix_passive_vals": false,
|
||||||
|
"meta": {
|
||||||
|
"version": 0
|
||||||
|
},
|
||||||
|
"model_mode": 0,
|
||||||
|
"workbook_filename": ""
|
||||||
|
},
|
||||||
|
"page_layout_descr_file": "",
|
||||||
|
"plot_directory": "",
|
||||||
|
"spice_adjust_passive_values": false,
|
||||||
|
"spice_current_sheet_as_root": false,
|
||||||
|
"spice_external_command": "spice \"%I\"",
|
||||||
|
"spice_model_current_sheet_as_root": true,
|
||||||
|
"spice_save_all_currents": false,
|
||||||
|
"spice_save_all_voltages": false,
|
||||||
|
"subpart_first_id": 65,
|
||||||
|
"subpart_id_separator": 0
|
||||||
|
},
|
||||||
|
"sheets": [
|
||||||
|
[
|
||||||
|
"9b6bc1bb-4d53-4a7c-b909-4e79c2146edb",
|
||||||
|
""
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"text_variables": {}
|
||||||
|
}
|
||||||
3022
schematic/pcb_right/tangentbord1-right.kicad_sch
Normal file
3022
schematic/pcb_right/tangentbord1-right.kicad_sch
Normal file
File diff suppressed because it is too large
Load Diff
4
schematic/pcb_thumb/fp-lib-table
Normal file
4
schematic/pcb_thumb/fp-lib-table
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
(fp_lib_table
|
||||||
|
(version 7)
|
||||||
|
(lib (name "Footprints")(type "KiCad")(uri "${KIPRJMOD}/../Footprints.pretty")(options "")(descr ""))
|
||||||
|
)
|
||||||
566
schematic/pcb_thumb/library.kicad_sym
Normal file
566
schematic/pcb_thumb/library.kicad_sym
Normal file
@ -0,0 +1,566 @@
|
|||||||
|
(kicad_symbol_lib (version 20220914) (generator kicad_symbol_editor)
|
||||||
|
(symbol "ITSYBITSY_RP2040" (in_bom yes) (on_board yes)
|
||||||
|
(property "Reference" "U1" (at 3.81 41.91 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(property "Value" "ITSYBITSY RP2040" (at 0 11.43 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(property "Footprint" "Footprints:ITSYBITSY BREAKOUT" (at 24.13 -20.32 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(property "Datasheet" "" (at 0 5.08 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(symbol "ITSYBITSY_RP2040_1_1"
|
||||||
|
(rectangle (start -17.78 39.37) (end 17.78 -16.51)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type background))
|
||||||
|
)
|
||||||
|
(pin input line (at -17.78 35.56 0) (length 2.54)
|
||||||
|
(name "RESET" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "0" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_out line (at -17.78 31.75 0) (length 2.54)
|
||||||
|
(name "3.3V" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "1" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 -2.54 0) (length 2.54)
|
||||||
|
(name "SCK" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "10" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 -6.35 0) (length 2.54)
|
||||||
|
(name "MOSI" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "11" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 -10.16 0) (length 2.54)
|
||||||
|
(name "MISO" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "12" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 -13.97 0) (length 2.54)
|
||||||
|
(name "D2" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "13" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -3.81 -16.51 90) (length 2.54)
|
||||||
|
(name "SWDIO" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "14" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 0 -16.51 90) (length 2.54)
|
||||||
|
(name "SWCLK" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "15" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 3.81 -16.51 90) (length 2.54)
|
||||||
|
(name "D3" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "16" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 -13.97 180) (length 2.54)
|
||||||
|
(name "RX" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "17" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 -10.16 180) (length 2.54)
|
||||||
|
(name "TX" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "18" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 -6.35 180) (length 2.54)
|
||||||
|
(name "SDA" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "19" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_out line (at -17.78 27.94 0) (length 2.54)
|
||||||
|
(name "3.3V" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "2" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 -2.54 180) (length 2.54)
|
||||||
|
(name "SCL" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "20" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 1.27 180) (length 2.54)
|
||||||
|
(name "D5" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "21" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 5.08 180) (length 2.54)
|
||||||
|
(name "D7" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "22" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 8.89 180) (length 2.54)
|
||||||
|
(name "D9" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "23" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 12.7 180) (length 2.54)
|
||||||
|
(name "D10" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "24" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 16.51 180) (length 2.54)
|
||||||
|
(name "D11" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "25" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 20.32 180) (length 2.54)
|
||||||
|
(name "D12" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "26" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 17.78 24.13 180) (length 2.54)
|
||||||
|
(name "D13" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "27" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at 17.78 27.94 180) (length 2.54)
|
||||||
|
(name "VBUS" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "28" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at 17.78 31.75 180) (length 2.54)
|
||||||
|
(name "GND" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "29" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_out line (at -17.78 24.13 0) (length 2.54)
|
||||||
|
(name "VHI" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "3" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at 17.78 35.56 180) (length 2.54)
|
||||||
|
(name "VBAT" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "30" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 20.32 0) (length 2.54)
|
||||||
|
(name "A0" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "4" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 16.51 0) (length 2.54)
|
||||||
|
(name "A1" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "5" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 12.7 0) (length 2.54)
|
||||||
|
(name "A2" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "6" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 8.89 0) (length 2.54)
|
||||||
|
(name "A3" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "7" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 5.08 0) (length 2.54)
|
||||||
|
(name "D24" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "8" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -17.78 1.27 0) (length 2.54)
|
||||||
|
(name "D25" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "9" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB" (in_bom yes) (on_board yes)
|
||||||
|
(property "Reference" "S4" (at 11.43 -1.27 0)
|
||||||
|
(effects (font (size 1.27 1.27)) (justify left))
|
||||||
|
)
|
||||||
|
(property "Value" "MX_SW_SOCKET_LED" (at 21.59 10.16 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(property "Footprint" "Footprints:MX_SW_SOCKET_LED" (at 26.67 7.62 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(property "Datasheet" "" (at 0 3.81 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB_0_1"
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -1.27 5.08)
|
||||||
|
(xy -0.8247 5.0799)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 1.27 5.08)
|
||||||
|
(xy 0.8247 5.0801)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -10.16 6.35)
|
||||||
|
(xy -10.16 8.89)
|
||||||
|
(xy -7.62 8.89)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -7.62 -10.16)
|
||||||
|
(xy -10.16 -10.16)
|
||||||
|
(xy -10.16 -7.62)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 7.62 8.89)
|
||||||
|
(xy 10.16 8.89)
|
||||||
|
(xy 10.16 6.35)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 10.16 -7.62)
|
||||||
|
(xy 10.16 -10.16)
|
||||||
|
(xy 7.62 -10.16)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB_1_1"
|
||||||
|
(rectangle (start -6.35 0) (end 6.35 -8.89)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type background))
|
||||||
|
)
|
||||||
|
(rectangle (start -3.81 7.62) (end 3.81 1.27)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type background))
|
||||||
|
)
|
||||||
|
(circle (center -0.6173 5.0896) (radius 0.1705)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -1.27 5.08)
|
||||||
|
(xy -1.27 7.62)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type dot))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -0.5184 5.2126)
|
||||||
|
(xy 0.4381 5.7713)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 1.27 7.62)
|
||||||
|
(xy 1.27 5.08)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type dot))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(circle (center 0.6471 5.08) (radius 0.1705)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(text "NEOPIXEL" (at 0 -1.27 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(text "SWITCH" (at 0 2.54 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -1.27 10.16 270) (length 2.54)
|
||||||
|
(name "L" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "1" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 1.27 10.16 270) (length 2.54)
|
||||||
|
(name "R" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "2" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at -2.54 -11.43 90) (length 2.54)
|
||||||
|
(name "G" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "3" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at 2.54 -11.43 90) (length 2.54)
|
||||||
|
(name "5V" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "4" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin input line (at 8.89 -3.81 180) (length 2.54)
|
||||||
|
(name "DIN" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "5" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin output line (at -8.89 -3.81 0) (length 2.54)
|
||||||
|
(name "DOUT" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "6" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB_1" (in_bom yes) (on_board yes)
|
||||||
|
(property "Reference" "S5" (at 11.43 -1.27 0)
|
||||||
|
(effects (font (size 1.27 1.27)) (justify left))
|
||||||
|
)
|
||||||
|
(property "Value" "MX_SW_SOCKET_LED" (at 20.32 7.62 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(property "Footprint" "Footprints:MX_SW_SOCKET_LED" (at 25.4 5.08 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(property "Datasheet" "" (at 0 3.81 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB_1_0_1"
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -1.27 5.08)
|
||||||
|
(xy -0.8247 5.0799)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 1.27 5.08)
|
||||||
|
(xy 0.8247 5.0801)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -10.16 6.35)
|
||||||
|
(xy -10.16 8.89)
|
||||||
|
(xy -7.62 8.89)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -7.62 -10.16)
|
||||||
|
(xy -10.16 -10.16)
|
||||||
|
(xy -10.16 -7.62)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 7.62 8.89)
|
||||||
|
(xy 10.16 8.89)
|
||||||
|
(xy 10.16 6.35)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 10.16 -7.62)
|
||||||
|
(xy 10.16 -10.16)
|
||||||
|
(xy 7.62 -10.16)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB_1_1_1"
|
||||||
|
(rectangle (start -6.35 0) (end 6.35 -8.89)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type background))
|
||||||
|
)
|
||||||
|
(rectangle (start -3.81 7.62) (end 3.81 1.27)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type background))
|
||||||
|
)
|
||||||
|
(circle (center -0.6173 5.0896) (radius 0.1705)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -1.27 5.08)
|
||||||
|
(xy -1.27 7.62)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type dot))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -0.5184 5.2126)
|
||||||
|
(xy 0.4381 5.7713)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 1.27 7.62)
|
||||||
|
(xy 1.27 5.08)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type dot))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(circle (center 0.6471 5.08) (radius 0.1705)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(text "NEOPIXEL" (at 0 -1.27 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(text "SWITCH" (at 0 2.54 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -1.27 10.16 270) (length 2.54)
|
||||||
|
(name "L" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "1" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 1.27 10.16 270) (length 2.54)
|
||||||
|
(name "R" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "2" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at -2.54 -11.43 90) (length 2.54)
|
||||||
|
(name "G" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "3" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at 2.54 -11.43 90) (length 2.54)
|
||||||
|
(name "5V" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "4" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin input line (at 8.89 -3.81 180) (length 2.54)
|
||||||
|
(name "DIN" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "5" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin output line (at -8.89 -3.81 0) (length 2.54)
|
||||||
|
(name "DOUT" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "6" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB_REV" (in_bom yes) (on_board yes)
|
||||||
|
(property "Reference" "S6" (at 12.7 -3.2259 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(property "Value" "MX_SW_SOCKET_RGB_REV" (at 22.86 8.89 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(property "Footprint" "Footprints:MX_SW_SOCKET_LED_REV" (at 27.94 6.35 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(property "Datasheet" "" (at 0 3.81 0)
|
||||||
|
(effects (font (size 1.27 1.27)) hide)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB_REV_0_1"
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -1.27 5.08)
|
||||||
|
(xy -0.8247 5.0799)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 1.27 5.08)
|
||||||
|
(xy 0.8247 5.0801)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -10.16 6.35)
|
||||||
|
(xy -10.16 8.89)
|
||||||
|
(xy -7.62 8.89)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -7.62 -10.16)
|
||||||
|
(xy -10.16 -10.16)
|
||||||
|
(xy -10.16 -7.62)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 7.62 8.89)
|
||||||
|
(xy 10.16 8.89)
|
||||||
|
(xy 10.16 6.35)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 10.16 -7.62)
|
||||||
|
(xy 10.16 -10.16)
|
||||||
|
(xy 7.62 -10.16)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
(symbol "MX_SW_SOCKET_RGB_REV_1_1"
|
||||||
|
(rectangle (start -6.35 0) (end 6.35 -8.89)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type background))
|
||||||
|
)
|
||||||
|
(rectangle (start -3.81 7.62) (end 3.81 1.27)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type background))
|
||||||
|
)
|
||||||
|
(circle (center -0.6173 5.0896) (radius 0.1705)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -1.27 5.08)
|
||||||
|
(xy -1.27 7.62)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type dot))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy -0.5184 5.2126)
|
||||||
|
(xy 0.4381 5.7713)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(polyline
|
||||||
|
(pts
|
||||||
|
(xy 1.27 7.62)
|
||||||
|
(xy 1.27 5.08)
|
||||||
|
)
|
||||||
|
(stroke (width 0) (type dot))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(circle (center 0.6471 5.08) (radius 0.1705)
|
||||||
|
(stroke (width 0) (type default))
|
||||||
|
(fill (type none))
|
||||||
|
)
|
||||||
|
(text "NEOPIXEL" (at 0 -1.27 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(text "SWITCH" (at 0 2.54 0)
|
||||||
|
(effects (font (size 1.27 1.27)))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at -1.27 10.16 270) (length 2.54)
|
||||||
|
(name "L" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "1" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin bidirectional line (at 1.27 10.16 270) (length 2.54)
|
||||||
|
(name "R" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "2" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at -2.54 -11.43 90) (length 2.54)
|
||||||
|
(name "G" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "3" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin power_in line (at 2.54 -11.43 90) (length 2.54)
|
||||||
|
(name "5V" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "4" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin input line (at -8.89 -3.81 0) (length 2.54)
|
||||||
|
(name "DIN" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "5" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
(pin output line (at 8.89 -3.81 180) (length 2.54)
|
||||||
|
(name "DOUT" (effects (font (size 1.27 1.27))))
|
||||||
|
(number "6" (effects (font (size 1.27 1.27))))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
4
schematic/pcb_thumb/sym-lib-table
Normal file
4
schematic/pcb_thumb/sym-lib-table
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
(sym_lib_table
|
||||||
|
(version 7)
|
||||||
|
(lib (name "library")(type "KiCad")(uri "${KIPRJMOD}/library.kicad_sym")(options "")(descr ""))
|
||||||
|
)
|
||||||
2473
schematic/pcb_thumb/tangentbord1-thumb.kicad_pcb
Normal file
2473
schematic/pcb_thumb/tangentbord1-thumb.kicad_pcb
Normal file
File diff suppressed because it is too large
Load Diff
500
schematic/pcb_thumb/tangentbord1-thumb.kicad_pro
Normal file
500
schematic/pcb_thumb/tangentbord1-thumb.kicad_pro
Normal file
@ -0,0 +1,500 @@
|
|||||||
|
{
|
||||||
|
"board": {
|
||||||
|
"3dviewports": [],
|
||||||
|
"design_settings": {
|
||||||
|
"defaults": {
|
||||||
|
"board_outline_line_width": 0.09999999999999999,
|
||||||
|
"copper_line_width": 0.19999999999999998,
|
||||||
|
"copper_text_italic": false,
|
||||||
|
"copper_text_size_h": 1.5,
|
||||||
|
"copper_text_size_v": 1.5,
|
||||||
|
"copper_text_thickness": 0.3,
|
||||||
|
"copper_text_upright": false,
|
||||||
|
"courtyard_line_width": 0.049999999999999996,
|
||||||
|
"dimension_precision": 4,
|
||||||
|
"dimension_units": 3,
|
||||||
|
"dimensions": {
|
||||||
|
"arrow_length": 1270000,
|
||||||
|
"extension_offset": 500000,
|
||||||
|
"keep_text_aligned": true,
|
||||||
|
"suppress_zeroes": false,
|
||||||
|
"text_position": 0,
|
||||||
|
"units_format": 1
|
||||||
|
},
|
||||||
|
"fab_line_width": 0.09999999999999999,
|
||||||
|
"fab_text_italic": false,
|
||||||
|
"fab_text_size_h": 1.0,
|
||||||
|
"fab_text_size_v": 1.0,
|
||||||
|
"fab_text_thickness": 0.15,
|
||||||
|
"fab_text_upright": false,
|
||||||
|
"other_line_width": 0.15,
|
||||||
|
"other_text_italic": false,
|
||||||
|
"other_text_size_h": 1.0,
|
||||||
|
"other_text_size_v": 1.0,
|
||||||
|
"other_text_thickness": 0.15,
|
||||||
|
"other_text_upright": false,
|
||||||
|
"pads": {
|
||||||
|
"drill": 1.0,
|
||||||
|
"height": 1.875,
|
||||||
|
"width": 1.875
|
||||||
|
},
|
||||||
|
"silk_line_width": 0.15,
|
||||||
|
"silk_text_italic": false,
|
||||||
|
"silk_text_size_h": 1.0,
|
||||||
|
"silk_text_size_v": 1.0,
|
||||||
|
"silk_text_thickness": 0.15,
|
||||||
|
"silk_text_upright": false,
|
||||||
|
"zones": {
|
||||||
|
"min_clearance": 0.5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"diff_pair_dimensions": [
|
||||||
|
{
|
||||||
|
"gap": 0.0,
|
||||||
|
"via_gap": 0.0,
|
||||||
|
"width": 0.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"drc_exclusions": [],
|
||||||
|
"meta": {
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"rule_severities": {
|
||||||
|
"annular_width": "error",
|
||||||
|
"clearance": "error",
|
||||||
|
"connection_width": "warning",
|
||||||
|
"copper_edge_clearance": "error",
|
||||||
|
"copper_sliver": "warning",
|
||||||
|
"courtyards_overlap": "error",
|
||||||
|
"diff_pair_gap_out_of_range": "error",
|
||||||
|
"diff_pair_uncoupled_length_too_long": "error",
|
||||||
|
"drill_out_of_range": "error",
|
||||||
|
"duplicate_footprints": "warning",
|
||||||
|
"extra_footprint": "warning",
|
||||||
|
"footprint": "error",
|
||||||
|
"footprint_type_mismatch": "ignore",
|
||||||
|
"hole_clearance": "error",
|
||||||
|
"hole_near_hole": "error",
|
||||||
|
"invalid_outline": "error",
|
||||||
|
"isolated_copper": "warning",
|
||||||
|
"item_on_disabled_layer": "error",
|
||||||
|
"items_not_allowed": "error",
|
||||||
|
"length_out_of_range": "error",
|
||||||
|
"lib_footprint_issues": "warning",
|
||||||
|
"lib_footprint_mismatch": "warning",
|
||||||
|
"malformed_courtyard": "error",
|
||||||
|
"microvia_drill_out_of_range": "error",
|
||||||
|
"missing_courtyard": "ignore",
|
||||||
|
"missing_footprint": "warning",
|
||||||
|
"net_conflict": "warning",
|
||||||
|
"npth_inside_courtyard": "ignore",
|
||||||
|
"padstack": "warning",
|
||||||
|
"pth_inside_courtyard": "ignore",
|
||||||
|
"shorting_items": "error",
|
||||||
|
"silk_edge_clearance": "warning",
|
||||||
|
"silk_over_copper": "warning",
|
||||||
|
"silk_overlap": "warning",
|
||||||
|
"skew_out_of_range": "error",
|
||||||
|
"solder_mask_bridge": "error",
|
||||||
|
"starved_thermal": "error",
|
||||||
|
"text_height": "warning",
|
||||||
|
"text_thickness": "warning",
|
||||||
|
"through_hole_pad_without_hole": "error",
|
||||||
|
"too_many_vias": "error",
|
||||||
|
"track_dangling": "warning",
|
||||||
|
"track_width": "error",
|
||||||
|
"tracks_crossing": "error",
|
||||||
|
"unconnected_items": "error",
|
||||||
|
"unresolved_variable": "error",
|
||||||
|
"via_dangling": "warning",
|
||||||
|
"zones_intersect": "error"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"max_error": 0.005,
|
||||||
|
"min_clearance": 0.127,
|
||||||
|
"min_connection": 0.0,
|
||||||
|
"min_copper_edge_clearance": 0.19999999999999998,
|
||||||
|
"min_hole_clearance": 0.25,
|
||||||
|
"min_hole_to_hole": 0.25,
|
||||||
|
"min_microvia_diameter": 0.19999999999999998,
|
||||||
|
"min_microvia_drill": 0.09999999999999999,
|
||||||
|
"min_resolved_spokes": 2,
|
||||||
|
"min_silk_clearance": 0.0,
|
||||||
|
"min_text_height": 0.7999999999999999,
|
||||||
|
"min_text_thickness": 0.08,
|
||||||
|
"min_through_hole_diameter": 0.3,
|
||||||
|
"min_track_width": 0.0,
|
||||||
|
"min_via_annular_width": 0.09999999999999999,
|
||||||
|
"min_via_diameter": 0.5,
|
||||||
|
"solder_mask_clearance": 0.0,
|
||||||
|
"solder_mask_min_width": 0.0,
|
||||||
|
"solder_mask_to_copper_clearance": 0.0,
|
||||||
|
"use_height_for_length_calcs": true
|
||||||
|
},
|
||||||
|
"teardrop_options": [
|
||||||
|
{
|
||||||
|
"td_allow_use_two_tracks": true,
|
||||||
|
"td_curve_segcount": 5,
|
||||||
|
"td_on_pad_in_zone": false,
|
||||||
|
"td_onpadsmd": true,
|
||||||
|
"td_onroundshapesonly": false,
|
||||||
|
"td_ontrackend": false,
|
||||||
|
"td_onviapad": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"teardrop_parameters": [
|
||||||
|
{
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_target_name": "td_round_shape",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_target_name": "td_rect_shape",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"td_curve_segcount": 0,
|
||||||
|
"td_height_ratio": 1.0,
|
||||||
|
"td_length_ratio": 0.5,
|
||||||
|
"td_maxheight": 2.0,
|
||||||
|
"td_maxlen": 1.0,
|
||||||
|
"td_target_name": "td_track_end",
|
||||||
|
"td_width_to_size_filter_ratio": 0.9
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"track_widths": [
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"via_dimensions": [
|
||||||
|
{
|
||||||
|
"diameter": 0.0,
|
||||||
|
"drill": 0.0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"zones_allow_external_fillets": false
|
||||||
|
},
|
||||||
|
"layer_presets": [],
|
||||||
|
"viewports": []
|
||||||
|
},
|
||||||
|
"boards": [],
|
||||||
|
"cvpcb": {
|
||||||
|
"equivalence_files": []
|
||||||
|
},
|
||||||
|
"erc": {
|
||||||
|
"erc_exclusions": [],
|
||||||
|
"meta": {
|
||||||
|
"version": 0
|
||||||
|
},
|
||||||
|
"pin_map": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"rule_severities": {
|
||||||
|
"bus_definition_conflict": "error",
|
||||||
|
"bus_entry_needed": "error",
|
||||||
|
"bus_to_bus_conflict": "error",
|
||||||
|
"bus_to_net_conflict": "error",
|
||||||
|
"conflicting_netclasses": "error",
|
||||||
|
"different_unit_footprint": "error",
|
||||||
|
"different_unit_net": "error",
|
||||||
|
"duplicate_reference": "error",
|
||||||
|
"duplicate_sheet_names": "error",
|
||||||
|
"endpoint_off_grid": "warning",
|
||||||
|
"extra_units": "error",
|
||||||
|
"global_label_dangling": "warning",
|
||||||
|
"hier_label_mismatch": "error",
|
||||||
|
"label_dangling": "error",
|
||||||
|
"lib_symbol_issues": "warning",
|
||||||
|
"missing_bidi_pin": "warning",
|
||||||
|
"missing_input_pin": "warning",
|
||||||
|
"missing_power_pin": "error",
|
||||||
|
"missing_unit": "warning",
|
||||||
|
"multiple_net_names": "warning",
|
||||||
|
"net_not_bus_member": "warning",
|
||||||
|
"no_connect_connected": "warning",
|
||||||
|
"no_connect_dangling": "warning",
|
||||||
|
"pin_not_connected": "error",
|
||||||
|
"pin_not_driven": "error",
|
||||||
|
"pin_to_pin": "warning",
|
||||||
|
"power_pin_not_driven": "error",
|
||||||
|
"similar_labels": "warning",
|
||||||
|
"simulation_model_issue": "error",
|
||||||
|
"unannotated": "error",
|
||||||
|
"unit_value_mismatch": "error",
|
||||||
|
"unresolved_variable": "error",
|
||||||
|
"wire_dangling": "error"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"libraries": {
|
||||||
|
"pinned_footprint_libs": [],
|
||||||
|
"pinned_symbol_libs": []
|
||||||
|
},
|
||||||
|
"meta": {
|
||||||
|
"filename": "tangentbord1.kicad_pro",
|
||||||
|
"version": 1
|
||||||
|
},
|
||||||
|
"net_settings": {
|
||||||
|
"classes": [
|
||||||
|
{
|
||||||
|
"bus_width": 12,
|
||||||
|
"clearance": 0.2,
|
||||||
|
"diff_pair_gap": 0.25,
|
||||||
|
"diff_pair_via_gap": 0.25,
|
||||||
|
"diff_pair_width": 0.2,
|
||||||
|
"line_style": 0,
|
||||||
|
"microvia_diameter": 0.3,
|
||||||
|
"microvia_drill": 0.1,
|
||||||
|
"name": "Default",
|
||||||
|
"pcb_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"schematic_color": "rgba(0, 0, 0, 0.000)",
|
||||||
|
"track_width": 0.25,
|
||||||
|
"via_diameter": 0.8,
|
||||||
|
"via_drill": 0.4,
|
||||||
|
"wire_width": 6
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"meta": {
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"net_colors": null,
|
||||||
|
"netclass_assignments": null,
|
||||||
|
"netclass_patterns": []
|
||||||
|
},
|
||||||
|
"pcbnew": {
|
||||||
|
"last_paths": {
|
||||||
|
"gencad": "",
|
||||||
|
"idf": "",
|
||||||
|
"netlist": "",
|
||||||
|
"specctra_dsn": "",
|
||||||
|
"step": "",
|
||||||
|
"vrml": ""
|
||||||
|
},
|
||||||
|
"page_layout_descr_file": ""
|
||||||
|
},
|
||||||
|
"schematic": {
|
||||||
|
"annotate_start_num": 0,
|
||||||
|
"drawing": {
|
||||||
|
"dashed_lines_dash_length_ratio": 12.0,
|
||||||
|
"dashed_lines_gap_length_ratio": 3.0,
|
||||||
|
"default_line_thickness": 6.0,
|
||||||
|
"default_text_size": 50.0,
|
||||||
|
"field_names": [],
|
||||||
|
"intersheets_ref_own_page": false,
|
||||||
|
"intersheets_ref_prefix": "",
|
||||||
|
"intersheets_ref_short": false,
|
||||||
|
"intersheets_ref_show": false,
|
||||||
|
"intersheets_ref_suffix": "",
|
||||||
|
"junction_size_choice": 3,
|
||||||
|
"label_size_ratio": 0.375,
|
||||||
|
"pin_symbol_size": 25.0,
|
||||||
|
"text_offset_ratio": 0.15
|
||||||
|
},
|
||||||
|
"legacy_lib_dir": "",
|
||||||
|
"legacy_lib_list": [],
|
||||||
|
"meta": {
|
||||||
|
"version": 1
|
||||||
|
},
|
||||||
|
"net_format_name": "",
|
||||||
|
"ngspice": {
|
||||||
|
"fix_include_paths": true,
|
||||||
|
"fix_passive_vals": false,
|
||||||
|
"meta": {
|
||||||
|
"version": 0
|
||||||
|
},
|
||||||
|
"model_mode": 0,
|
||||||
|
"workbook_filename": ""
|
||||||
|
},
|
||||||
|
"page_layout_descr_file": "",
|
||||||
|
"plot_directory": "",
|
||||||
|
"spice_adjust_passive_values": false,
|
||||||
|
"spice_current_sheet_as_root": false,
|
||||||
|
"spice_external_command": "spice \"%I\"",
|
||||||
|
"spice_model_current_sheet_as_root": true,
|
||||||
|
"spice_save_all_currents": false,
|
||||||
|
"spice_save_all_voltages": false,
|
||||||
|
"subpart_first_id": 65,
|
||||||
|
"subpart_id_separator": 0
|
||||||
|
},
|
||||||
|
"sheets": [
|
||||||
|
[
|
||||||
|
"9b6bc1bb-4d53-4a7c-b909-4e79c2146edb",
|
||||||
|
""
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"text_variables": {}
|
||||||
|
}
|
||||||
1110
schematic/pcb_thumb/tangentbord1-thumb.kicad_sch
Normal file
1110
schematic/pcb_thumb/tangentbord1-thumb.kicad_sch
Normal file
File diff suppressed because it is too large
Load Diff
37
serial-logs
Executable file
37
serial-logs
Executable file
@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env nu
|
||||||
|
|
||||||
|
let level_colors = {
|
||||||
|
TRACE: (ansi m)
|
||||||
|
DEBUG: (ansi c)
|
||||||
|
INFO: (ansi g)
|
||||||
|
WARN: (ansi y)
|
||||||
|
ERROR: (ansi rb)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Attach to serial logs of a running firmware
|
||||||
|
def main [
|
||||||
|
--serial (-s): string # The serial device to get logs from.
|
||||||
|
] {
|
||||||
|
let serial = if $serial == null {
|
||||||
|
"/dev/serial/by-id/usb-Tux_Tangentbord1_42069-if00"
|
||||||
|
} else {
|
||||||
|
$serial
|
||||||
|
}
|
||||||
|
|
||||||
|
print -n ("waiting for serial log " + $serial)
|
||||||
|
|
||||||
|
while not ($serial | path exists) {
|
||||||
|
print -n "."
|
||||||
|
sleep 500ms
|
||||||
|
}
|
||||||
|
echo
|
||||||
|
|
||||||
|
cat $serial |
|
||||||
|
parse -r '\[(?<timestamp>[0-9\.]*)\] \((?<level>\w*)\) (?<message>.*)' |
|
||||||
|
each { |it|
|
||||||
|
print -n "[" (ansi d) $it.timestamp (ansi reset) "] "
|
||||||
|
print -n "(" ($level_colors | get $it.level) $it.level (ansi reset) ") "
|
||||||
|
print $it.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
1
src/bin/.gitignore
vendored
1
src/bin/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
*.pc
|
|
||||||
@ -1,88 +0,0 @@
|
|||||||
//! Firmware for Tangentbord1, left half.
|
|
||||||
|
|
||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
#![feature(type_alias_impl_trait)]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
extern crate cortex_m_rt;
|
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use embassy_executor::Spawner;
|
|
||||||
use embassy_rp::gpio::{Level, Output, Pin};
|
|
||||||
use embassy_time::{Duration, Timer};
|
|
||||||
use tangentbord1::board::Board;
|
|
||||||
use tangentbord1::keyboard::KeyboardConfig;
|
|
||||||
use tangentbord1::util::{stall, wheel};
|
|
||||||
use tangentbord1::ws2812::{Rgb, Ws2812};
|
|
||||||
use tangentbord1::{allocator, usb};
|
|
||||||
use tgnt::layer::Layer;
|
|
||||||
|
|
||||||
#[embassy_executor::main]
|
|
||||||
async fn main(_spawner: Spawner) {
|
|
||||||
allocator::init();
|
|
||||||
|
|
||||||
let p = embassy_rp::init(Default::default());
|
|
||||||
let board = Board::from(p);
|
|
||||||
|
|
||||||
let _led = Output::new(board.d13, Level::High);
|
|
||||||
let _neopixel_power = Output::new(board.neopixel_power, Level::High);
|
|
||||||
|
|
||||||
let mut neopixel = Ws2812::new(board.PIO0, board.DMA_CH0, board.neopixel.degrade());
|
|
||||||
let mut neopixels_d5 = Ws2812::new(board.PIO1, board.DMA_CH1, board.d5.degrade());
|
|
||||||
|
|
||||||
neopixel.write(&[Rgb::new(0xFF, 0x00, 0x00)]).await;
|
|
||||||
usb::setup_logger_and_keyboard(board.USB).await;
|
|
||||||
neopixel.write(&[Rgb::new(0x00, 0x00, 0xFF)]).await;
|
|
||||||
|
|
||||||
//Timer::after(Duration::from_millis(3000)).await;
|
|
||||||
|
|
||||||
let layers = include_bytes!("layers-left.pc");
|
|
||||||
let Ok(layers): Result<Vec<Layer>, _> = postcard::from_bytes(layers) else {
|
|
||||||
log::error!("Failed to deserialize layer config");
|
|
||||||
stall().await
|
|
||||||
};
|
|
||||||
|
|
||||||
let keyboard = KeyboardConfig {
|
|
||||||
layers,
|
|
||||||
pins: [
|
|
||||||
// row 1
|
|
||||||
board.d24.degrade(),
|
|
||||||
board.a3.degrade(),
|
|
||||||
board.a2.degrade(),
|
|
||||||
board.a1.degrade(),
|
|
||||||
board.a0.degrade(),
|
|
||||||
// row 2
|
|
||||||
board.d25.degrade(),
|
|
||||||
board.sck.degrade(),
|
|
||||||
board.mosi.degrade(),
|
|
||||||
board.miso.degrade(),
|
|
||||||
board.d2.degrade(),
|
|
||||||
// row 3
|
|
||||||
board.d12.degrade(),
|
|
||||||
board.d11.degrade(),
|
|
||||||
board.d10.degrade(),
|
|
||||||
board.d9.degrade(),
|
|
||||||
board.d3.degrade(),
|
|
||||||
// thumbpad
|
|
||||||
board.d7.degrade(),
|
|
||||||
board.scl.degrade(),
|
|
||||||
board.sda.degrade(),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
keyboard.create().await;
|
|
||||||
|
|
||||||
for w in 0usize.. {
|
|
||||||
neopixel.write(&[wheel(w as u8)]).await;
|
|
||||||
neopixels_d5
|
|
||||||
.write(&[
|
|
||||||
wheel((w + 50) as u8),
|
|
||||||
wheel((w + 100) as u8),
|
|
||||||
wheel((w + 150) as u8),
|
|
||||||
wheel((w + 200) as u8),
|
|
||||||
])
|
|
||||||
.await;
|
|
||||||
Timer::after(Duration::from_millis(10)).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,89 +0,0 @@
|
|||||||
//! Firmware for Tangentbord1, right half.
|
|
||||||
|
|
||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
#![feature(type_alias_impl_trait)]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
extern crate cortex_m_rt;
|
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use embassy_executor::Spawner;
|
|
||||||
use embassy_rp::gpio::{Level, Output, Pin};
|
|
||||||
use embassy_time::{Duration, Timer};
|
|
||||||
use tangentbord1::board::Board;
|
|
||||||
use tangentbord1::keyboard::KeyboardConfig;
|
|
||||||
use tangentbord1::util::{stall, wheel};
|
|
||||||
use tangentbord1::ws2812::{Rgb, Ws2812};
|
|
||||||
use tangentbord1::{allocator, usb};
|
|
||||||
use tgnt::layer::Layer;
|
|
||||||
|
|
||||||
#[embassy_executor::main]
|
|
||||||
async fn main(_spawner: Spawner) {
|
|
||||||
allocator::init();
|
|
||||||
|
|
||||||
let p = embassy_rp::init(Default::default());
|
|
||||||
let board = Board::from(p);
|
|
||||||
|
|
||||||
let _led = Output::new(board.d13, Level::High);
|
|
||||||
let _neopixel_power = Output::new(board.neopixel_power, Level::High);
|
|
||||||
|
|
||||||
let mut neopixel = Ws2812::new(board.PIO0, board.DMA_CH0, board.neopixel.degrade());
|
|
||||||
let mut neopixels_d5 = Ws2812::new(board.PIO1, board.DMA_CH1, board.d5.degrade());
|
|
||||||
|
|
||||||
neopixel.write(&[Rgb::new(0xFF, 0x00, 0x00)]).await;
|
|
||||||
usb::setup_logger_and_keyboard(board.USB).await;
|
|
||||||
neopixel.write(&[Rgb::new(0x00, 0x00, 0xFF)]).await;
|
|
||||||
|
|
||||||
//Timer::after(Duration::from_millis(3000)).await;
|
|
||||||
|
|
||||||
let layers = include_bytes!("layers-right.pc");
|
|
||||||
let Ok(layers): Result<Vec<Layer>, _> = postcard::from_bytes(layers) else {
|
|
||||||
log::error!("Failed to deserialize layer config");
|
|
||||||
stall().await
|
|
||||||
};
|
|
||||||
|
|
||||||
let keyboard = KeyboardConfig {
|
|
||||||
layers,
|
|
||||||
pins: [
|
|
||||||
// TODO: reconfigure these for right PCB
|
|
||||||
// row 1
|
|
||||||
board.d24.degrade(),
|
|
||||||
board.a3.degrade(),
|
|
||||||
board.a2.degrade(),
|
|
||||||
board.a1.degrade(),
|
|
||||||
board.a0.degrade(),
|
|
||||||
// row 2
|
|
||||||
board.d25.degrade(),
|
|
||||||
board.sck.degrade(),
|
|
||||||
board.mosi.degrade(),
|
|
||||||
board.miso.degrade(),
|
|
||||||
board.d2.degrade(),
|
|
||||||
// row 3
|
|
||||||
board.d12.degrade(),
|
|
||||||
board.d11.degrade(),
|
|
||||||
board.d10.degrade(),
|
|
||||||
board.d9.degrade(),
|
|
||||||
board.d3.degrade(),
|
|
||||||
// thumbpad
|
|
||||||
board.d7.degrade(),
|
|
||||||
board.scl.degrade(),
|
|
||||||
board.sda.degrade(),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
keyboard.create().await;
|
|
||||||
|
|
||||||
for w in 0usize.. {
|
|
||||||
neopixel.write(&[wheel(w as u8)]).await;
|
|
||||||
neopixels_d5
|
|
||||||
.write(&[
|
|
||||||
wheel((w + 50) as u8),
|
|
||||||
wheel((w + 100) as u8),
|
|
||||||
wheel((w + 150) as u8),
|
|
||||||
wheel((w + 200) as u8),
|
|
||||||
])
|
|
||||||
.await;
|
|
||||||
Timer::after(Duration::from_millis(10)).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
191
src/keyboard.rs
191
src/keyboard.rs
@ -1,191 +0,0 @@
|
|||||||
use core::sync::atomic::{AtomicU16, Ordering};
|
|
||||||
|
|
||||||
use alloc::{boxed::Box, vec::Vec};
|
|
||||||
use embassy_executor::Spawner;
|
|
||||||
use embassy_rp::gpio::{AnyPin, Input, Pin, Pull};
|
|
||||||
use embassy_time::{Duration, Timer};
|
|
||||||
use futures::{select_biased, FutureExt};
|
|
||||||
use log::{debug, error, info, warn};
|
|
||||||
use tgnt::{button::Button, layer::Layer};
|
|
||||||
|
|
||||||
use crate::usb::keyboard::KB_REPORT;
|
|
||||||
|
|
||||||
static CURRENT_LAYER: AtomicU16 = AtomicU16::new(0);
|
|
||||||
|
|
||||||
pub struct KeyboardConfig {
|
|
||||||
pub pins: [AnyPin; SWITCH_COUNT],
|
|
||||||
pub layers: Vec<Layer>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl KeyboardConfig {
|
|
||||||
pub async fn create(self) {
|
|
||||||
let spawner = Spawner::for_current_executor().await;
|
|
||||||
|
|
||||||
if self.layers.is_empty() {
|
|
||||||
error!("no layers defined");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"setting up keyboard layout with {} layer(s)",
|
|
||||||
self.layers.len()
|
|
||||||
);
|
|
||||||
|
|
||||||
let layers = Box::leak(self.layers.into_boxed_slice());
|
|
||||||
for (i, layer) in layers.iter().enumerate() {
|
|
||||||
if layer.buttons.len() != SWITCH_COUNT {
|
|
||||||
warn!(
|
|
||||||
"layer {i} defines {} buttons, but there are {SWITCH_COUNT} switches",
|
|
||||||
layer.buttons.len(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i, pin) in self.pins.into_iter().enumerate() {
|
|
||||||
if spawner.spawn(switch_task(i, pin, layers)).is_err() {
|
|
||||||
error!("failed to spawn switch task, pool_size mismatch?");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const MOD_TAP_TIME: Duration = Duration::from_millis(100);
|
|
||||||
const SWITCH_COUNT: usize = 18;
|
|
||||||
|
|
||||||
/// Task for monitoring a single switch pin, and handling button presses.
|
|
||||||
#[embassy_executor::task(pool_size = 18)]
|
|
||||||
async fn switch_task(switch_num: usize, pin: AnyPin, layers: &'static [Layer]) -> ! {
|
|
||||||
let _pin_nr = pin.pin();
|
|
||||||
let mut pin = Input::new(pin, Pull::Up);
|
|
||||||
loop {
|
|
||||||
// pins are pull-up, so when the switch is pressed they are brought low.
|
|
||||||
pin.wait_for_low().await;
|
|
||||||
|
|
||||||
// TODO: do we need debouncing?
|
|
||||||
|
|
||||||
// get current layer
|
|
||||||
let mut current_layer = CURRENT_LAYER.load(Ordering::Relaxed);
|
|
||||||
let layer_count = layers.len() as u16;
|
|
||||||
if current_layer >= layer_count {
|
|
||||||
error!("current layer was out of bounds for some reason ({current_layer})");
|
|
||||||
current_layer = 0;
|
|
||||||
}
|
|
||||||
let Some(Layer { buttons }) = layers.get(usize::from(current_layer)) else {
|
|
||||||
error!("current layer was out of bounds for some reason ({current_layer})");
|
|
||||||
CURRENT_LAYER.store(0, Ordering::Relaxed);
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
// and current button
|
|
||||||
let Some(button) = buttons.get(switch_num) else {
|
|
||||||
warn!("no button defined for switch {switch_num}");
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!("switch {switch_num} button {button:?} pressed");
|
|
||||||
|
|
||||||
let wait_for_release = async {
|
|
||||||
pin.wait_for_high().await;
|
|
||||||
debug!("switch {switch_num} button {button:?} released");
|
|
||||||
};
|
|
||||||
|
|
||||||
match button {
|
|
||||||
&Button::Key(key) => {
|
|
||||||
KB_REPORT.lock().await.press_key(key);
|
|
||||||
wait_for_release.await;
|
|
||||||
KB_REPORT.lock().await.release_key(key);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
&Button::Mod(modifier) => {
|
|
||||||
KB_REPORT.lock().await.press_modifier(modifier);
|
|
||||||
wait_for_release.await;
|
|
||||||
KB_REPORT.lock().await.release_modifier(modifier);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
&Button::ModTap { keycode, modifier } => {
|
|
||||||
select_biased! {
|
|
||||||
_ = Timer::after(MOD_TAP_TIME).fuse() => {
|
|
||||||
KB_REPORT.lock().await.press_modifier(modifier);
|
|
||||||
pin.wait_for_high().await;
|
|
||||||
KB_REPORT.lock().await.release_modifier(modifier);
|
|
||||||
debug!("switch {switch_num} button {button:?} released");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
_ = wait_for_release.fuse() => {
|
|
||||||
KB_REPORT.lock().await.press_key(keycode);
|
|
||||||
Timer::after(Duration::from_millis(10)).await;
|
|
||||||
KB_REPORT.lock().await.release_key(keycode);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Button::NextLayer => {
|
|
||||||
let next_layer = (current_layer + 1) % layer_count;
|
|
||||||
CURRENT_LAYER.store(next_layer, Ordering::Relaxed);
|
|
||||||
debug!("switched to layer {next_layer}");
|
|
||||||
}
|
|
||||||
Button::PrevLayer => {
|
|
||||||
let prev_layer = current_layer.checked_sub(1).unwrap_or(layer_count - 1);
|
|
||||||
CURRENT_LAYER.store(prev_layer, Ordering::Relaxed);
|
|
||||||
debug!("switched to layer {prev_layer}");
|
|
||||||
}
|
|
||||||
Button::None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
wait_for_release.await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Random functions for testing
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub mod test {
|
|
||||||
use tgnt::{button::Button, keys::Key};
|
|
||||||
|
|
||||||
pub fn letter_to_key(c: char) -> Button {
|
|
||||||
if !c.is_ascii() {
|
|
||||||
return Button::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let c = c.to_ascii_uppercase();
|
|
||||||
|
|
||||||
let key = match c {
|
|
||||||
'A' => Key::A,
|
|
||||||
'B' => Key::B,
|
|
||||||
'C' => Key::C,
|
|
||||||
'D' => Key::D,
|
|
||||||
'E' => Key::E,
|
|
||||||
'F' => Key::F,
|
|
||||||
'G' => Key::G,
|
|
||||||
'H' => Key::H,
|
|
||||||
'I' => Key::I,
|
|
||||||
'J' => Key::J,
|
|
||||||
'K' => Key::K,
|
|
||||||
'L' => Key::L,
|
|
||||||
'M' => Key::M,
|
|
||||||
'N' => Key::N,
|
|
||||||
'O' => Key::O,
|
|
||||||
'P' => Key::P,
|
|
||||||
'Q' => Key::Q,
|
|
||||||
'R' => Key::R,
|
|
||||||
'S' => Key::S,
|
|
||||||
'T' => Key::T,
|
|
||||||
'U' => Key::U,
|
|
||||||
'V' => Key::V,
|
|
||||||
'W' => Key::W,
|
|
||||||
'X' => Key::X,
|
|
||||||
'Y' => Key::Y,
|
|
||||||
'Z' => Key::Z,
|
|
||||||
' ' => Key::Space,
|
|
||||||
'\n' => Key::Return,
|
|
||||||
_ => {
|
|
||||||
log::info!("char {c:?} -> None");
|
|
||||||
return Button::None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
log::info!("char {c:?} -> {key:?}");
|
|
||||||
|
|
||||||
Button::Key(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
13
src/lib.rs
13
src/lib.rs
@ -1,13 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![feature(type_alias_impl_trait)]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
pub mod allocator;
|
|
||||||
pub mod board;
|
|
||||||
pub mod keyboard;
|
|
||||||
pub mod neopixel;
|
|
||||||
pub mod panic_handler;
|
|
||||||
pub mod usb;
|
|
||||||
pub mod util;
|
|
||||||
pub mod ws2812;
|
|
||||||
@ -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
|
|
||||||
122
src/neopixel.rs
122
src/neopixel.rs
@ -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
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
use core::panic::PanicInfo;
|
|
||||||
|
|
||||||
use embassy_executor::Executor;
|
|
||||||
use embassy_rp::gpio::{AnyPin, Level, Output, Pin};
|
|
||||||
use embassy_time::{Duration, Timer};
|
|
||||||
use static_cell::StaticCell;
|
|
||||||
|
|
||||||
#[panic_handler]
|
|
||||||
fn panic_blink(_info: &PanicInfo) -> ! {
|
|
||||||
static EXECUTOR: StaticCell<Executor> = StaticCell::new();
|
|
||||||
|
|
||||||
// SAFETY: we panicked, so no other code will be running.
|
|
||||||
let p = unsafe { embassy_rp::Peripherals::steal() };
|
|
||||||
|
|
||||||
EXECUTOR.init(Executor::new()).run(|spawner| {
|
|
||||||
spawner.spawn(blink(p.PIN_11.degrade())).ok();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::task]
|
|
||||||
async fn blink(led: AnyPin) {
|
|
||||||
let mut led = Output::new(led, Level::High);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
Timer::after(Duration::from_secs(1)).await;
|
|
||||||
led.toggle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
73
src/usb.rs
73
src/usb.rs
@ -1,73 +0,0 @@
|
|||||||
use embassy_executor::Spawner;
|
|
||||||
use embassy_rp::{interrupt, peripherals::USB, usb::Driver};
|
|
||||||
use embassy_usb::{Builder, Config, UsbDevice};
|
|
||||||
use static_cell::StaticCell;
|
|
||||||
|
|
||||||
pub mod keyboard;
|
|
||||||
pub mod logger;
|
|
||||||
|
|
||||||
pub const MAX_PACKET_SIZE: u8 = 64;
|
|
||||||
|
|
||||||
struct State {
|
|
||||||
device_descriptor: [u8; 256],
|
|
||||||
config_descriptor: [u8; 256],
|
|
||||||
bos_descriptor: [u8; 256],
|
|
||||||
control_buf: [u8; 64],
|
|
||||||
}
|
|
||||||
|
|
||||||
static STATE: StaticCell<State> = StaticCell::new();
|
|
||||||
|
|
||||||
pub async fn setup_logger_and_keyboard(usb: USB) {
|
|
||||||
let mut builder = builder(usb);
|
|
||||||
logger::setup(&mut builder).await;
|
|
||||||
|
|
||||||
log::error!("log_level: error");
|
|
||||||
log::warn!("log_level: warn");
|
|
||||||
log::info!("log_level: info");
|
|
||||||
log::debug!("log_level: debug");
|
|
||||||
log::trace!("log_level: trace");
|
|
||||||
|
|
||||||
keyboard::setup(&mut builder).await;
|
|
||||||
let usb = builder.build();
|
|
||||||
Spawner::for_current_executor().await.must_spawn(run(usb));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn builder(usb: USB) -> Builder<'static, Driver<'static, USB>> {
|
|
||||||
let state = STATE.init(State {
|
|
||||||
device_descriptor: [0; 256],
|
|
||||||
config_descriptor: [0; 256],
|
|
||||||
bos_descriptor: [0; 256],
|
|
||||||
control_buf: [0; 64],
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create embassy-usb Config
|
|
||||||
let mut config = Config::new(0xb00b, 0x1355);
|
|
||||||
config.manufacturer = Some("Tux");
|
|
||||||
config.product = Some("Tangentbord1");
|
|
||||||
config.serial_number = Some("42069");
|
|
||||||
config.max_power = 100;
|
|
||||||
config.max_packet_size_0 = MAX_PACKET_SIZE;
|
|
||||||
|
|
||||||
// Required for windows compatiblity.
|
|
||||||
// https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
|
|
||||||
config.device_class = 0xEF;
|
|
||||||
config.device_sub_class = 0x02;
|
|
||||||
config.device_protocol = 0x01;
|
|
||||||
config.composite_with_iads = true;
|
|
||||||
|
|
||||||
let driver = Driver::new(usb, interrupt::take!(USBCTRL_IRQ));
|
|
||||||
|
|
||||||
Builder::new(
|
|
||||||
driver,
|
|
||||||
config,
|
|
||||||
&mut state.device_descriptor,
|
|
||||||
&mut state.config_descriptor,
|
|
||||||
&mut state.bos_descriptor,
|
|
||||||
&mut state.control_buf,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::task]
|
|
||||||
pub async fn run(mut device: UsbDevice<'static, Driver<'static, USB>>) {
|
|
||||||
device.run().await
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user