pin_scanner: added

This commit is contained in:
dogeystamp 2024-04-17 19:03:47 -04:00
parent e50f5051fc
commit 38e36b7bd5
Signed by: dogeystamp
GPG Key ID: 7225FE3592EFFA38
2 changed files with 142 additions and 1 deletions

141
src/bin/pin_scanner.rs Normal file
View File

@ -0,0 +1,141 @@
/*
geode-piano
Copyright (C) 2024 dogeystamp <dogeystamp@disroot.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//! Scanner utility to detect which pins are directly connected.
//! This can be useful to reverse-engineer a key-matrix.
#![no_std]
#![no_main]
#![deny(rust_2018_idioms)]
use embassy_executor::Spawner;
use embassy_rp::bind_interrupts;
use embassy_rp::gpio;
use embassy_rp::i2c;
use embassy_rp::peripherals::USB;
use embassy_rp::usb::{Driver, InterruptHandler};
use embassy_time::Timer;
use geode_piano::usb::usb_task;
use geode_piano::{blinky, pin_array, pins, unwrap};
/// Represents a connection between two pins as detected by the scanner.
#[derive(Clone, Copy)]
struct Connection {
/// Active low pin number
gnd_pin: u8,
/// Pull-up input pin number
input_pin: u8,
}
#[embassy_executor::task]
async fn scanner_task(mut pin_driver: pins::TransparentPins) {
log::info!("scanner_task: setting pins as input");
for i in 0..pin_driver.n_total_pins {
unwrap(pin_driver.set_input(i as u8)).await;
unwrap(pin_driver.set_pull(i as u8, gpio::Pull::Up)).await;
}
loop {
const MAX_CONNECTIONS: usize = 10;
let mut n_connections = 0;
let mut connections: [Option<Connection>; MAX_CONNECTIONS] = [None; MAX_CONNECTIONS];
// for all outputs, use active low
// (only one pin will be output at a time)
unwrap(pin_driver.write_all(0)).await;
log::info!("");
log::info!("---");
log::info!("STARTING SCAN...");
for gnd_pin in 0..pin_driver.n_total_pins {
let gnd_pin = gnd_pin as u8;
unwrap(pin_driver.set_output(gnd_pin)).await;
let input = unwrap(pin_driver.read_all()).await;
unwrap(pin_driver.set_input(gnd_pin)).await;
// this represents the pins that are different from expected
let mask = input ^ (((1 << pin_driver.n_total_pins) - 1) ^ (1 << gnd_pin));
for input_pin in 0..pin_driver.n_total_pins {
let input_pin = input_pin as u8;
if ((1 << input_pin) & mask) != 0 {
if n_connections < MAX_CONNECTIONS {
connections[n_connections] = Some(Connection { gnd_pin, input_pin });
n_connections += 1;
}
}
}
// this should avoid overexerting the components
// in total it will take 0.4 seconds per scan
Timer::after_millis(10).await;
}
log::info!("SCAN RESULTS");
for i in 0..n_connections {
match connections[i] {
None => {}
Some(con) => {
log::warn!("GND {:0>2} -> INPUT {:0>2}", con.gnd_pin, con.input_pin);
}
}
}
if n_connections < MAX_CONNECTIONS {
log::info!("{n_connections} connections found.");
} else {
log::warn!("more than maximum ({MAX_CONNECTIONS}) connections found. list has been truncated. this might mean you used pins GPA7 or GPB7 on the MCP23017, which are unsafe as inputs, and therefore set as outputs.");
}
Timer::after_millis(3000).await;
}
}
bind_interrupts!(struct Irqs {
USBCTRL_IRQ => InterruptHandler<USB>;
});
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_rp::init(Default::default());
let driver = Driver::new(p.USB, Irqs);
unwrap(_spawner.spawn(usb_task(driver, log::LevelFilter::Debug))).await;
unwrap(_spawner.spawn(blinky::blink_task(p.PIN_25.into()))).await;
Timer::after_secs(2).await;
log::info!("main: init i2c");
let sda = p.PIN_16;
let scl = p.PIN_17;
let mut i2c_config = i2c::Config::default();
let freq = 100_000;
i2c_config.frequency = freq;
let i2c = i2c::I2c::new_blocking(p.I2C0, scl, sda, i2c_config);
log::info!("main: starting transparent pin driver");
let pin_driver = unwrap(pins::TransparentPins::new(
i2c,
[0x20, 0x27],
pin_array!(
p.PIN_15, p.PIN_14, p.PIN_13, p.PIN_12, p.PIN_11, p.PIN_10, p.PIN_9, p.PIN_18,
p.PIN_19, p.PIN_20, p.PIN_21, p.PIN_22
),
true,
))
.await;
log::info!("main: starting scanner task");
_spawner.spawn(scanner_task(pin_driver)).unwrap();
}

View File

@ -187,7 +187,7 @@ impl TransparentPins {
ret.n_total_pins = ret.usable_extended_pins + N_REGULAR_PINS;
}
ret.disable_unsafe_pins = true;
log::debug!("TransparentPins: usable_pins {}", ret.n_total_pins)
log::debug!("TransparentPins: {} usable pins", ret.n_total_pins)
}
Ok(ret)
}