feat: TransparentPins can now disable unsafe pins

This commit is contained in:
dogeystamp 2024-04-16 19:03:54 -04:00
parent 0489e7c8f8
commit e50f5051fc
Signed by: dogeystamp
GPG Key ID: 7225FE3592EFFA38
3 changed files with 125 additions and 31 deletions

View File

@ -19,7 +19,7 @@ use geode_piano::{blinky, pin_array, pins, unwrap};
#[embassy_executor::task]
async fn read_task(mut pin_driver: pins::TransparentPins) {
loop {
log::warn!("{:b}", unwrap(pin_driver.read_all()).await);
log::warn!("{:036b}", unwrap(pin_driver.read_all()).await);
Timer::after_millis(1000).await;
}
}
@ -33,7 +33,7 @@ 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(usb_task(driver, log::LevelFilter::Info))).await;
unwrap(_spawner.spawn(blinky::blink_task(p.PIN_25.into()))).await;
Timer::after_secs(2).await;
@ -48,20 +48,25 @@ async fn main(_spawner: Spawner) {
let i2c = i2c::I2c::new_blocking(p.I2C0, scl, sda, i2c_config);
log::info!("main: starting transparent pin driver");
let mut pin_driver = pins::TransparentPins::new(
let mut 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_18, p.PIN_19),
);
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: setting pins as input");
for i in 0..(pins::N_EXTENDED_PINS + pins::N_REGULAR_PINS) {
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;
}
log::debug!("main: setting pin 0 as output, active low");
unwrap(pin_driver.set_output(0)).await;
unwrap(pin_driver.write_all((1 << 40 - 1) & 0)).await;
unwrap(pin_driver.write_all(0)).await;
log::debug!("main: starting read task");
_spawner.spawn(read_task(pin_driver)).unwrap();

View File

@ -20,7 +20,7 @@ pub async fn unwrap<T, E: core::fmt::Debug>(res: Result<T, E>) -> T {
Err(e) => {
log::error!("[FATAL] {:?}", e);
log::error!("HALTING DUE TO PANIC.");
Timer::after_millis(10).await;
Timer::after_secs(1).await;
panic!();
}
}

View File

@ -28,13 +28,19 @@ use mcp23017;
use mcp23017::MCP23017;
/// Number of pins driven by each MCP23017 pin extender.
pub const PINS_PER_EXTENDER: usize = 16;
const PINS_PER_EXTENDER: usize = 16;
/// Number of MCP23017 chips used.
pub const N_PIN_EXTENDERS: usize = 2;
const N_PIN_EXTENDERS: usize = 2;
/// Number of pins driven directly by the board.
pub const N_REGULAR_PINS: usize = 8;
const N_REGULAR_PINS: usize = 12;
/// Number of total extended pins
pub const N_EXTENDED_PINS: usize = PINS_PER_EXTENDER * N_PIN_EXTENDERS;
const N_EXTENDED_PINS: usize = PINS_PER_EXTENDER * N_PIN_EXTENDERS;
/// Number of unsafe pins per extender (GPA7, GPB7)
const UNSAFE_PER_EXTENDER: usize = 2;
/// Single extender address offset of PORTA
const PORT_A: u8 = 0;
/// Single extender address offset of PORTB
const PORT_B: u8 = 8;
type I2cPeripheral = i2c::I2c<'static, I2C0, Blocking>;
type I2cBus = shared_bus::BusManagerSimple<I2cPeripheral>;
@ -75,13 +81,26 @@ impl<E> From<mcp23017::Error<E>> for Error {
/// "Transparent pins" to consistently interface with a GPIO extender + onboard GPIO ports.
///
/// This interface uses a single addressing scheme for all the pins it manages.
/// Extender A is 0-15, Extender B is 16-31, and so on, then all the onboard pins.
/// Port A is in the lower byte and port B is in the upper byte of each extender range.
/// This interface uses a single addressing scheme for all the pins it manages. Extender A is 0-15,
/// Extender B is 16-31, and so on, then all the onboard pins. Port A is in the lower byte and port
/// B is in the upper byte of each extender range. This addressing scheme may be changed with some
/// options. The exact pins each address refers to are not supposed to be important.
///
/// The MCP23017 is known to have two defective pins, GPA7 and GPB7. These can not be set as inputs
/// without risks of weird behaviour. To disable these pins, you may set `disable_unsafe_pins` in
/// the constructor. This will set them to output pins, and then remove them from the transparent
/// pins addressing scheme.
pub struct TransparentPins {
addrs: [u8; N_PIN_EXTENDERS],
pins: [Flex<'static, AnyPin>; N_REGULAR_PINS],
i2c_bus: I2cBus,
disable_unsafe_pins: bool,
/// Number of total usable pins. Transparent pins all have an address from `0..n_total_pins`.
pub n_total_pins: usize,
/// Usable pins per extender. Depends on `disable_unsafe_pins`.
usable_pins_per_extender: usize,
/// Usable pin count on all extenders. Depends on `disable_unsafe_pins`.
usable_extended_pins: usize,
}
/// Helper to define the onboard pins in TransparentPins
@ -103,6 +122,34 @@ macro_rules! extender {
}
impl TransparentPins {
/// Transform addresses into a transparent pin number, taking into account pins that aren't being used.
fn addr_to_pin(&self, addr: u8) -> u8 {
if self.disable_unsafe_pins {
if addr as usize >= (self.usable_pins_per_extender * N_PIN_EXTENDERS) {
return addr + (UNSAFE_PER_EXTENDER as u8) * (N_PIN_EXTENDERS as u8);
}
// extender index
let div = addr as usize / self.usable_pins_per_extender;
// offset within extender
let m = addr as usize % self.usable_pins_per_extender;
// difference between `m` and the MCP23017 pin number within this extender
let mut offset = 0;
if m >= PORT_A as usize + 7 {
// these pins are offset by one because GPA7 is missing
offset += 1
}
// GPB7 doesn't need an offset because it is the last pin anyways
// the div-mod above takes care of that
(div * PINS_PER_EXTENDER + m + offset) as u8
} else {
addr
}
}
/// Get a pin by its pin number.
///
/// This is NOT by the transparent address.
fn get_pin(&mut self, pin: u8) -> Result<TransparentPin, Error> {
if pin as usize >= N_EXTENDED_PINS + N_REGULAR_PINS {
return Err(Error::InvalidPin(pin));
@ -120,11 +167,51 @@ impl TransparentPins {
i2c: i2c::I2c<'static, I2C0, Blocking>,
addrs: [u8; N_PIN_EXTENDERS],
pins: [AnyPin; N_REGULAR_PINS],
) -> Self {
TransparentPins {
disable_unsafe_pins: bool,
) -> Result<Self, Error> {
let mut ret = TransparentPins {
addrs,
pins: pins.map(|x| Flex::new(x)),
i2c_bus: shared_bus::BusManagerSimple::new(i2c),
disable_unsafe_pins: false,
usable_pins_per_extender: PINS_PER_EXTENDER,
usable_extended_pins: N_EXTENDED_PINS,
n_total_pins: N_EXTENDED_PINS + N_REGULAR_PINS,
};
if disable_unsafe_pins {
for i in 0..N_PIN_EXTENDERS {
ret.set_output((i as u8) * (PINS_PER_EXTENDER as u8) + PORT_A + 7)?;
ret.set_output((i as u8) * (PINS_PER_EXTENDER as u8) + PORT_B + 7)?;
ret.usable_pins_per_extender = PINS_PER_EXTENDER - UNSAFE_PER_EXTENDER;
ret.usable_extended_pins = N_PIN_EXTENDERS * ret.usable_pins_per_extender;
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)
}
Ok(ret)
}
/// Convert the raw pin input for an extender to just usable pins
fn raw_to_usable(&self, val: u16) -> u16 {
if self.disable_unsafe_pins {
// read api is wonky (https://github.com/lucazulian/mcp23017/issues/8)
// ports are flipped from what it should be
let port_a = (val & (0xff00)) >> 8;
let port_b = val & (0x00ff);
log::trace!("raw_to_usable: raw {val:016b} a {port_a:08b} b {port_b:08b}");
(port_a & 0x7f) | ((port_b & 0x7f) << 7)
} else {
val
}
}
// Convert the usable pin mask to raw pin output
fn usable_to_raw(&self, val: u16) -> u16 {
if self.disable_unsafe_pins {
(val & 0x00ff) | ((val & 0xff00) << 1)
} else {
val
}
}
@ -133,11 +220,12 @@ impl TransparentPins {
log::trace!("write_all: called with val {}", val);
for i in 0..N_PIN_EXTENDERS {
// value for this extender
let ext_val = (val >> (i * PINS_PER_EXTENDER)) & ((1 << PINS_PER_EXTENDER) - 1);
extender!(self, i)?.write_gpioab(ext_val as u16)?;
let ext_val = (val >> (i * self.usable_pins_per_extender))
& ((1 << self.usable_pins_per_extender) - 1);
extender!(self, i)?.write_gpioab(self.usable_to_raw(ext_val as u16))?;
}
for pin in 0..N_REGULAR_PINS {
self.pins[pin].set_level(match (val >> N_EXTENDED_PINS >> pin) & 1 {
self.pins[pin].set_level(match (val >> self.usable_extended_pins >> pin) & 1 {
0 => embassy_rp::gpio::Level::Low,
1 => embassy_rp::gpio::Level::High,
_ => panic!("Invalid level"),
@ -153,13 +241,11 @@ impl TransparentPins {
let mut ret: u64 = 0;
for i in 0..N_PIN_EXTENDERS {
let mut ext = extender!(self, i)?;
let read_val = ext.read_gpioab()? as u64;
// api is wonky (https://github.com/lucazulian/mcp23017/issues/8)
let flipped_val = ((read_val & 0x00ff) << 8) | ((read_val & 0xff00) >> 8);
ret |= flipped_val << (i * PINS_PER_EXTENDER);
let read_val = ext.read_gpioab()?;
ret |= (self.raw_to_usable(read_val) as u64) << (i * self.usable_pins_per_extender);
}
for pin in 0..N_REGULAR_PINS {
ret |= (self.pins[pin].is_high() as u64) << (N_EXTENDED_PINS + pin);
ret |= (self.pins[pin].is_high() as u64) << (self.usable_extended_pins + pin);
}
Ok(ret)
@ -168,8 +254,9 @@ impl TransparentPins {
/// Set the pull on an individual pin (0-index).
///
/// Note: MCP23017 pins do not support pull-down.
pub fn set_pull(&mut self, pin: u8, pull: Pull) -> Result<(), Error> {
let pin = self.get_pin(pin)?;
pub fn set_pull(&mut self, addr: u8, pull: Pull) -> Result<(), Error> {
let pin_n = self.addr_to_pin(addr);
let pin = self.get_pin(pin_n)?;
match pin {
TransparentPin::Onboard(p) => {
self.pins[p].set_pull(pull);
@ -188,8 +275,9 @@ impl TransparentPins {
}
/// Sets a pin as an input.
pub fn set_input(&mut self, pin: u8) -> Result<(), Error> {
let pin = self.get_pin(pin)?;
pub fn set_input(&mut self, addr: u8) -> Result<(), Error> {
let pin_n = self.addr_to_pin(addr);
let pin = self.get_pin(pin_n)?;
match pin {
TransparentPin::Onboard(p) => self.pins[p].set_as_input(),
TransparentPin::Extended(p) => {
@ -200,8 +288,9 @@ impl TransparentPins {
}
/// Sets a pin as an output.
pub fn set_output(&mut self, pin: u8) -> Result<(), Error> {
let pin = self.get_pin(pin)?;
pub fn set_output(&mut self, addr: u8) -> Result<(), Error> {
let pin_n = self.addr_to_pin(addr);
let pin = self.get_pin(pin_n)?;
match pin {
TransparentPin::Onboard(p) => self.pins[p].set_as_output(),
TransparentPin::Extended(p) => {