Skip to content

Instantly share code, notes, and snippets.

@wware
Last active February 12, 2025 18:29
Show Gist options
  • Save wware/962b7ca1fb18e3f0996e9cadae75d033 to your computer and use it in GitHub Desktop.
Save wware/962b7ca1fb18e3f0996e9cadae75d033 to your computer and use it in GitHub Desktop.

Prompt:

Alt text description

The inverters in the upper left are a square wave oscillator (around 100 kHz), used to sense capacitive connectivity between X wires and Y wires. The comparators are TLV3404. Inputs A,B,C,D,E,F and outputs G,H,I are connected to an Adafruit Grand Central M4 controller board. Inputs D,E,F select one of eight options in the 4051 analog multiplexer, just prior to the 3-bit flash ADC that generates G,H,I.

Show me Rust code that would run on the Grand Central board to perform a keyboard scan.

Response:

I'll help you create Rust code for scanning this capacitive keyboard matrix. From the schematic, we're dealing with:

  1. A scanning system using X/Y wires for capacitive sensing
  2. Row/column selection using multiplexers (74HC138 visible)
  3. A 3-bit flash ADC output (G,H,I) that indicates key pressure
  4. Selection lines (D,E,F) for the 4051 multiplexer
  5. Row select lines (A,B,C)

Here's a Rust implementation that would run on the Grand Central M4.

This implementation provides:

  1. A KeyboardScanner struct that manages the GPIO pins and scanning logic
  2. Methods to select rows and columns using the multiplexer control pins
  3. Pressure reading from the 3-bit flash ADC
  4. Debouncing logic to prevent false triggers
  5. Event generation for key press/release with pressure values

The code assumes you're using the atsamd-hal crate for hardware access and defmt for debugging output. You'll need to add these dependencies to your Cargo.toml:

[dependencies]
atsamd-hal = "0.14"
cortex-m = "0.7"
defmt = "0.3"

The pin assignments (PA02-PA10) are examples - you'll need to adjust them to match your actual wiring. You may also want to adjust the pressure threshold and debounce delay based on your specific hardware characteristics.

Would you like me to help you with any specific aspects of the implementation, such as adjusting the sensitivity threshold or adding MIDI output functionality?

Prompt:

Tell me about how to set up a Rust cross-compiler on an Ubuntu Linux system.

Response:

I'll walk you through setting up a Rust cross-compilation environment on Ubuntu Linux.

  1. First, install Rust if you haven't already:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
  1. Install essential build tools:
sudo apt update
sudo apt install build-essential git cmake
  1. Install your target toolchain. For example, for ARM Cortex-M4 (like your Grand Central M4):
rustup target add thumbv7em-none-eabihf
  1. Install cross-compilation tools for ARM:
sudo apt install gcc-arm-none-eabi binutils-arm-none-eabi libnewlib-arm-none-eabi
  1. Create or modify .cargo/config.toml in your project directory to specify the linker:
[target.thumbv7em-none-eabihf]
rustflags = [
  "-C", "link-arg=-Tlink.x",
]
runner = "probe-run --chip ATSAMD51P20A"
  1. For debugging support, install these tools:
# cargo install probe-run     NOPE
# cargo install cargo-embed   NOPE
sudo apt install gdb-multiarch openocd
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/probe-rs/probe-rs/releases/latest/download/probe-rs-tools-installer.sh | sh
  1. For development, you'll likely want these components:
cargo install cargo-generate
cargo install flip-link

To build your project:

cargo build --target thumbv7em-none-eabihf

Common issues and solutions:

  • If you get linker errors, make sure you have the correct target-specific dependencies in your Cargo.toml:
[dependencies]
cortex-m = "0.7"
cortex-m-rt = "0.7"
panic-halt = "0.2"
  • For Adafruit boards specifically, you might want to add:
[dependencies]
atsamd-hal = "0.14"

Would you like me to explain any specific part of this setup in more detail?

use atsamd_hal::{
gpio::{Pin, Output, Input, Floating},
pac,
prelude::*,
};
use cortex_m::asm::delay;
const ROWS: usize = 8;
const COLS: usize = 8;
const DEBOUNCE_DELAY: u32 = 5; // milliseconds
struct KeyboardScanner {
// Row select pins (A,B,C)
row_a: Pin<Output>,
row_b: Pin<Output>,
row_c: Pin<Output>,
// Column select pins (D,E,F)
col_d: Pin<Output>,
col_e: Pin<Output>,
col_f: Pin<Output>,
// ADC input pins (G,H,I)
adc_g: Pin<Input<Floating>>,
adc_h: Pin<Input<Floating>>,
adc_i: Pin<Input<Floating>>,
// Current key states
key_states: [[bool; COLS]; ROWS],
// Previous scan's key states for debouncing
prev_states: [[bool; COLS]; ROWS],
}
impl KeyboardScanner {
pub fn new(
row_a: Pin<Output>,
row_b: Pin<Output>,
row_c: Pin<Output>,
col_d: Pin<Output>,
col_e: Pin<Output>,
col_f: Pin<Output>,
adc_g: Pin<Input<Floating>>,
adc_h: Pin<Input<Floating>>,
adc_i: Pin<Input<Floating>>,
) -> Self {
Self {
row_a, row_b, row_c,
col_d, col_e, col_f,
adc_g, adc_h, adc_i,
key_states: [[false; COLS]; ROWS],
prev_states: [[false; COLS]; ROWS],
}
}
fn select_row(&mut self, row: u8) {
// Set A,B,C pins to select row
self.row_a.set_state((row & 0b001) != 0);
self.row_b.set_state((row & 0b010) != 0);
self.row_c.set_state((row & 0b100) != 0);
}
fn select_column(&mut self, col: u8) {
// Set D,E,F pins to select column on 4051
self.col_d.set_state((col & 0b001) != 0);
self.col_e.set_state((col & 0b010) != 0);
self.col_f.set_state((col & 0b100) != 0);
}
fn read_pressure(&self) -> u8 {
// Read 3-bit ADC value from G,H,I
let mut value = 0;
if self.adc_g.is_high() { value |= 0b001; }
if self.adc_h.is_high() { value |= 0b010; }
if self.adc_i.is_high() { value |= 0b100; }
value
}
pub fn scan(&mut self) -> Option<KeyEvent> {
// Save previous state
self.prev_states = self.key_states;
// Scan entire matrix
for row in 0..ROWS {
self.select_row(row as u8);
for col in 0..COLS {
self.select_column(col as u8);
// Give circuit time to settle
delay(10);
// Read pressure value
let pressure = self.read_pressure();
// Consider key pressed if pressure exceeds threshold
self.key_states[row][col] = pressure > 2;
// If state changed and passes debounce, return event
if self.key_states[row][col] != self.prev_states[row][col] {
delay(DEBOUNCE_DELAY * 1000); // Convert to microseconds
// Re-read to confirm
let new_pressure = self.read_pressure();
let new_state = new_pressure > 2;
if new_state == self.key_states[row][col] {
return Some(KeyEvent {
row,
col,
pressed: new_state,
pressure: new_pressure,
});
}
}
}
}
None
}
}
#[derive(Debug)]
pub struct KeyEvent {
row: usize,
col: usize,
pressed: bool,
pressure: u8,
}
// Example usage in main
#[entry]
fn main() -> ! {
let mut peripherals = pac::Peripherals::take().unwrap();
let mut clocks = GenericClockController::with_external_32kosc(
peripherals.GCLK,
&mut peripherals.MCLK,
&mut peripherals.OSC32KCTRL,
&mut peripherals.OSCCTRL,
&mut peripherals.NVMCTRL,
);
let pins = hal::Pins::new(peripherals.PORT);
let mut scanner = KeyboardScanner::new(
pins.pa02.into_push_pull_output(), // A
pins.pa03.into_push_pull_output(), // B
pins.pa04.into_push_pull_output(), // C
pins.pa05.into_push_pull_output(), // D
pins.pa06.into_push_pull_output(), // E
pins.pa07.into_push_pull_output(), // F
pins.pa08.into_floating_input(), // G
pins.pa09.into_floating_input(), // H
pins.pa10.into_floating_input(), // I
);
loop {
if let Some(event) = scanner.scan() {
// Handle key event
// Example: Print to debug console
defmt::println!("Key event: {:?}", event);
}
}
}
[package]
name = "scanner"
version = "0.1.0"
edition = "2021"
[dependencies]
cortex-m = "0.7"
cortex-m-rt = "0.7"
panic-halt = "0.2"
atsamd-hal = { version = "0.14", features = ["samd51g", "unproven"] }
[lib]
name = "scanner"
path = "src/lib.rs"
[profile.release]
opt-level = "s" # Optimize for size
lto = true # Link time optimization
#![no_std]
#![no_main]
use panic_halt as _; // Panic handler
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment