Skip to content

Instantly share code, notes, and snippets.

@wware
Last active February 12, 2025 18:29
Show Gist options
  • Select an option

  • Save wware/962b7ca1fb18e3f0996e9cadae75d033 to your computer and use it in GitHub Desktop.

Select an option

Save wware/962b7ca1fb18e3f0996e9cadae75d033 to your computer and use it in GitHub Desktop.

Revisions

  1. wware revised this gist Feb 12, 2025. 1 changed file with 1 addition and 3 deletions.
    4 changes: 1 addition & 3 deletions 0README.md
    Original file line number Diff line number Diff line change
    @@ -15,9 +15,7 @@ I'll help you create Rust code for scanning this capacitive keyboard matrix. Fro
    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:

    [Capacitive Keyboard Scanner](https://gist.github.com/wware/962b7ca1fb18e3f0996e9cadae75d033/raw/e104601a15b8b6a7a7fc12a402473f5e1d2c1f39/scan.rs)
    Here's a [Rust implementation](#file-scan-rs) that would run on the Grand Central M4.

    This implementation provides:

  2. wware revised this gist Feb 12, 2025. 2 changed files with 37 additions and 0 deletions.
    18 changes: 18 additions & 0 deletions scanner_Cargo.toml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,18 @@
    [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
    19 changes: 19 additions & 0 deletions scanner_src_lib.rs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    #![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);
    }
    }
  3. wware revised this gist Feb 12, 2025. 1 changed file with 3 additions and 2 deletions.
    5 changes: 3 additions & 2 deletions 0README.md
    Original file line number Diff line number Diff line change
    @@ -84,9 +84,10 @@ runner = "probe-run --chip ATSAMD51P20A"
    6. For debugging support, install these tools:

    ```bash
    cargo install probe-run
    cargo install cargo-embed
    # 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
    ```

    7. For development, you'll likely want these components:
  4. wware revised this gist Feb 12, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion 0README.md
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@

    ![Alt text description](https://gist.github.com/wware/962b7ca1fb18e3f0996e9cadae75d033/raw/e104601a15b8b6a7a7fc12a402473f5e1d2c1f39/diagram.jpg)

    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.
    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](https://www.adafruit.com/product/4064) 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.

  5. wware revised this gist Feb 12, 2025. 1 changed file with 82 additions and 0 deletions.
    82 changes: 82 additions & 0 deletions 0README.md
    Original file line number Diff line number Diff line change
    @@ -40,3 +40,85 @@ The pin assignments (PA02-PA10) are examples - you'll need to adjust them to mat

    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:

    ```bash
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    ```

    2. Install essential build tools:

    ```bash
    sudo apt update
    sudo apt install build-essential git cmake
    ```

    3. Install your target toolchain. For example, for ARM Cortex-M4 (like your Grand Central M4):

    ```bash
    rustup target add thumbv7em-none-eabihf
    ```

    4. Install cross-compilation tools for ARM:

    ```bash
    sudo apt install gcc-arm-none-eabi binutils-arm-none-eabi libnewlib-arm-none-eabi
    ```

    5. Create or modify `.cargo/config.toml` in your project directory to specify the linker:

    ```toml
    [target.thumbv7em-none-eabihf]
    rustflags = [
    "-C", "link-arg=-Tlink.x",
    ]
    runner = "probe-run --chip ATSAMD51P20A"
    ```

    6. For debugging support, install these tools:

    ```bash
    cargo install probe-run
    cargo install cargo-embed
    sudo apt install gdb-multiarch openocd
    ```

    7. For development, you'll likely want these components:

    ```bash
    cargo install cargo-generate
    cargo install flip-link
    ```

    To build your project:

    ```bash
    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`:

    ```toml
    [dependencies]
    cortex-m = "0.7"
    cortex-m-rt = "0.7"
    panic-halt = "0.2"
    ```

    * For Adafruit boards specifically, you might want to add:

    ```toml
    [dependencies]
    atsamd-hal = "0.14"
    ```

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

  6. wware revised this gist Feb 12, 2025. 1 changed file with 4 additions and 3 deletions.
    7 changes: 4 additions & 3 deletions 0README.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,7 @@
    ## Prompt:

    ![Alt text description](https://gist.github.com/wware/962b7ca1fb18e3f0996e9cadae75d033/raw/e104601a15b8b6a7a7fc12a402473f5e1d2c1f39/diagram.jpg)

    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.
    @@ -14,9 +17,7 @@ I'll help you create Rust code for scanning this capacitive keyboard matrix. Fro

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

    Capacitive Keyboard Scanner

    Click to open code
    [Capacitive Keyboard Scanner](https://gist.github.com/wware/962b7ca1fb18e3f0996e9cadae75d033/raw/e104601a15b8b6a7a7fc12a402473f5e1d2c1f39/scan.rs)

    This implementation provides:

  7. wware revised this gist Feb 12, 2025. 3 changed files with 204 additions and 1 deletion.
    42 changes: 41 additions & 1 deletion 0README.md
    Original file line number Diff line number Diff line change
    @@ -1 +1,41 @@
    # Quux
    ## Prompt:
    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:

    Capacitive Keyboard Scanner

    Click to open code

    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`:

    ```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?

    Binary file added diagram.jpg
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
    163 changes: 163 additions & 0 deletions scan.rs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,163 @@
    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);
    }
    }
    }
  8. wware created this gist Feb 12, 2025.
    1 change: 1 addition & 0 deletions 0README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    # Quux