diff --git a/Cargo.lock b/Cargo.lock index f52398e..e6f976c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -802,6 +802,7 @@ dependencies = [ "mcp23017", "panic-probe", "portable-atomic", + "shared-bus", "smart-leds", "static_cell", "usbd-hid 0.7.0", @@ -1384,6 +1385,16 @@ dependencies = [ "syn 2.0.58", ] +[[package]] +name = "shared-bus" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6b8d3f0e34309c22ca4a9a27d24fa493e31573485f3493802b75b9d706756a6" +dependencies = [ + "embedded-hal 0.2.7", + "nb 1.1.0", +] + [[package]] name = "siphasher" version = "0.3.11" diff --git a/Cargo.toml b/Cargo.toml index 07e97b3..5d4c800 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ portable-atomic = { version = "1.5", features = ["critical-section"] } log = "0.4" mcp23017 = { version = "1.0.0" } +shared-bus = "0.3.1" [profile.release] debug = 2 diff --git a/src/main.rs b/src/main.rs index c9edde6..f694081 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ #![no_std] #![no_main] +#![deny(rust_2018_idioms)] use embassy_executor::Spawner; use embassy_rp::bind_interrupts; @@ -78,13 +79,13 @@ 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(i2c, [0x20], []); + let mut pin_driver = pins::TransparentPins::new(i2c, [0x20, 0x27], []); log::info!("main: setting pins as input"); - for i in 0..16 { + for i in 0..pins::N_EXTENDED_PINS { log::debug!("main: setting pin {} as input, pull up", i); - unwrap(pin_driver.set_input(i)).await; - unwrap(pin_driver.set_pull(i, gpio::Pull::Up)).await; + unwrap(pin_driver.set_input(i as u8)).await; + unwrap(pin_driver.set_pull(i as u8, gpio::Pull::Up)).await; } // these pins are faulty as inputs diff --git a/src/pins.rs b/src/pins.rs index bd52605..03def41 100644 --- a/src/pins.rs +++ b/src/pins.rs @@ -18,39 +18,31 @@ //! Manage I²C and provide a transparent pin interface for both onboard and MCP23017 pins. -extern crate embedded_hal_02; use embassy_rp::{ gpio::{AnyPin, Flex, Pull}, i2c::{self, Blocking}, peripherals::I2C0, }; -extern crate mcp23017; +use mcp23017; use mcp23017::MCP23017; /// Number of pins driven by each MCP23017 pin extender. -const PINS_PER_EXTENDER: usize = 16; -/// Number of MCP23017 chips used. This can not be changed without changing code. -const N_PIN_EXTENDERS: usize = 1; +pub const PINS_PER_EXTENDER: usize = 16; +/// Number of MCP23017 chips used. +pub const N_PIN_EXTENDERS: usize = 2; /// Number of pins driven directly by the board. -const N_REGULAR_PINS: usize = 0; +pub const N_REGULAR_PINS: usize = 0; /// Number of total extended pins -const N_EXTENDED_PINS: usize = PINS_PER_EXTENDER * N_PIN_EXTENDERS; +pub const N_EXTENDED_PINS: usize = PINS_PER_EXTENDER * N_PIN_EXTENDERS; -/// "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. -/// ext0 is 0-15, ext1 is 16-31, regular pins are 32-63. -pub struct TransparentPins { - ext0: MCP23017>, - //ext1: MCP23017>, - pins: [Flex<'static, AnyPin>; N_REGULAR_PINS], -} +type I2cPeripheral = i2c::I2c<'static, I2C0, Blocking>; +type I2cBus = shared_bus::BusManagerSimple; /// GPIO extender pin struct ExtendedPin { - /// ID (not address) of the extender being used - ext_id: u8, + /// Index of extender being used + ext_id: usize, /// Pin number in the extender's addressing scheme loc_pin: u8, } @@ -62,39 +54,81 @@ enum TransparentPin { Extended(ExtendedPin), } +#[derive(Debug)] +pub enum Error { + InvalidPin(u8), + I2cError(i2c::Error), + ExtenderError, +} + +impl From for Error { + fn from(err: i2c::Error) -> Error { + Error::I2cError(err) + } +} + +impl From> for Error { + fn from(_err: mcp23017::Error) -> Error { + Error::ExtenderError + } +} + +/// "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. +/// `ext[0]` is 0-15, `ext[1]` is 16-31, regular pins are 32-63. +pub struct TransparentPins { + addrs: [u8; N_PIN_EXTENDERS], + pins: [Flex<'static, AnyPin>; N_REGULAR_PINS], + i2c_bus: I2cBus, +} + +/// Create a new short-lived MCP23017 struct. +/// +/// This is needed because our bus proxy uses references to the bus, +/// and having long-lived references angers the borrow-checker +macro_rules! extender { + ($self:ident,$ext_id:expr) => { + MCP23017::new($self.i2c_bus.acquire_i2c(), $self.addrs[$ext_id]) + }; +} + impl TransparentPins { - fn get_pin(pin: u8) -> TransparentPin { + fn get_pin(&mut self, pin: u8) -> Result { + if pin as usize >= N_EXTENDED_PINS + N_REGULAR_PINS { + return Err(Error::InvalidPin(pin)); + } if pin < (N_EXTENDED_PINS as u8) { - let ext_id = pin / (PINS_PER_EXTENDER as u8); + let ext_id = (pin as usize) / PINS_PER_EXTENDER; let loc_pin = pin % (PINS_PER_EXTENDER as u8); - if ext_id >= N_PIN_EXTENDERS as u8 { - panic!("invalid pin") - } - TransparentPin::Extended(ExtendedPin { ext_id, loc_pin }) + Ok(TransparentPin::Extended(ExtendedPin { ext_id, loc_pin })) } else { - TransparentPin::Onboard(pin as usize - N_EXTENDED_PINS) + Ok(TransparentPin::Onboard(pin as usize - N_EXTENDED_PINS)) } } pub fn new( - i2c0: i2c::I2c<'static, I2C0, Blocking>, - //i2c1: i2c::I2c<'static, I2C1, Blocking>, + i2c: i2c::I2c<'static, I2C0, Blocking>, addrs: [u8; N_PIN_EXTENDERS], pins: [AnyPin; N_REGULAR_PINS], ) -> Self { - let pin_init = pins.map(|x| Flex::new(x)); - return TransparentPins { - ext0: MCP23017::new(i2c0, addrs[0]).unwrap(), - pins: pin_init, - }; + TransparentPins { + addrs, + pins: pins.map(|x| Flex::new(x)), + i2c_bus: shared_bus::BusManagerSimple::new(i2c), + } } /// Read all pins into a single 64-bit value. - pub fn read_all(&mut self) -> Result { + /// + /// For a given extender's range, port B is in the lower byte and port A in the upper byte. + pub fn read_all(&mut self) -> Result { log::trace!("read_all: called"); let mut ret: u64 = 0; - // remember here port b is in the lower byte and port a in the upper byte - ret |= self.ext0.read_gpioab()? as u64; + for i in 0..N_PIN_EXTENDERS { + let mut ext = extender!(self, i)?; + ret |= (ext.read_gpioab()? as u64) << (i * PINS_PER_EXTENDER); + } for pin in 0..N_REGULAR_PINS { log::trace!("pin read: {}", pin); ret |= (self.pins[pin].is_high() as u64) << (N_EXTENDED_PINS + pin); @@ -106,8 +140,8 @@ 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<(), i2c::Error> { - let pin = TransparentPins::get_pin(pin); + pub fn set_pull(&mut self, pin: u8, pull: Pull) -> Result<(), Error> { + let pin = self.get_pin(pin)?; match pin { TransparentPin::Onboard(p) => { self.pins[p].set_pull(pull); @@ -119,41 +153,29 @@ impl TransparentPins { // Extended pins don't seem to support pull-down Pull::Down => unimplemented!("MCP23017 does not support pull-down."), }; - match p.ext_id { - 0 => self.ext0.pull_up(p.loc_pin, pull_on)?, - //1 => self.ext1.pull_up(p.loc_pin, pull_on)?, - _ => panic!("invalid pin"), - } + extender!(self, p.ext_id)?.pull_up(p.loc_pin, pull_on)? } } Ok(()) } - pub fn set_input(&mut self, pin: u8) -> Result<(), i2c::Error> { - let pin = TransparentPins::get_pin(pin); + pub fn set_input(&mut self, pin: u8) -> Result<(), Error> { + let pin = self.get_pin(pin)?; match pin { TransparentPin::Onboard(p) => self.pins[p].set_as_input(), TransparentPin::Extended(p) => { - match p.ext_id { - 0 => self.ext0.pin_mode(p.loc_pin, mcp23017::PinMode::INPUT)?, - //1 => self.ext1.pin_mode(p.loc_pin, mcp23017::PinMode::INPUT).unwrap(), - _ => panic!("invalid pin"), - } + extender!(self, p.ext_id)?.pin_mode(p.loc_pin, mcp23017::PinMode::INPUT)? } } Ok(()) } - pub fn set_output(&mut self, pin: u8) -> Result<(), i2c::Error> { - let pin = TransparentPins::get_pin(pin); + pub fn set_output(&mut self, pin: u8) -> Result<(), Error> { + let pin = self.get_pin(pin)?; match pin { TransparentPin::Onboard(p) => self.pins[p].set_as_output(), TransparentPin::Extended(p) => { - match p.ext_id { - 0 => self.ext0.pin_mode(p.loc_pin, mcp23017::PinMode::OUTPUT)?, - //1 => self.ext1.pin_mode(p.loc_pin, mcp23017::PinMode::OUTPUT).unwrap(), - _ => panic!("invalid pin"), - } + extender!(self, p.ext_id)?.pin_mode(p.loc_pin, mcp23017::PinMode::OUTPUT)? } } Ok(())