Compare commits
No commits in common. "2bbabaff5ced7a2e4b3b211a994ffc1cdf1cdc63" and "448b8e232c3c39979fd01dad1457d24525f9e328" have entirely different histories.
2bbabaff5c
...
448b8e232c
@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
|
|
||||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
runner = "elf2uf2-rs --deploy --serial"
|
runner = "elf2uf2-rs -d"
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
|
target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
|
||||||
|
22
Cargo.lock
generated
22
Cargo.lock
generated
@ -788,7 +788,6 @@ dependencies = [
|
|||||||
"embassy-time",
|
"embassy-time",
|
||||||
"embassy-usb",
|
"embassy-usb",
|
||||||
"embassy-usb-logger",
|
"embassy-usb-logger",
|
||||||
"embedded-hal 0.2.7",
|
|
||||||
"embedded-hal 1.0.0",
|
"embedded-hal 1.0.0",
|
||||||
"embedded-hal-async",
|
"embedded-hal-async",
|
||||||
"embedded-hal-bus",
|
"embedded-hal-bus",
|
||||||
@ -799,10 +798,8 @@ dependencies = [
|
|||||||
"futures",
|
"futures",
|
||||||
"heapless 0.8.0",
|
"heapless 0.8.0",
|
||||||
"log",
|
"log",
|
||||||
"mcp23017",
|
|
||||||
"panic-probe",
|
"panic-probe",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"shared-bus",
|
|
||||||
"smart-leds",
|
"smart-leds",
|
||||||
"static_cell",
|
"static_cell",
|
||||||
"usbd-hid 0.7.0",
|
"usbd-hid 0.7.0",
|
||||||
@ -987,15 +984,6 @@ version = "0.4.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mcp23017"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2c32fd6627e73f1cfa95c00ddcdcb5a6a6ddbd10b308d08588a502c018b6e12c"
|
|
||||||
dependencies = [
|
|
||||||
"embedded-hal 0.2.7",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.2"
|
version = "2.7.2"
|
||||||
@ -1385,16 +1373,6 @@ dependencies = [
|
|||||||
"syn 2.0.58",
|
"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]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "0.3.11"
|
version = "0.3.11"
|
||||||
|
@ -33,7 +33,6 @@ heapless = "0.8"
|
|||||||
usbd-hid = "0.7.0"
|
usbd-hid = "0.7.0"
|
||||||
|
|
||||||
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
|
embedded-hal-1 = { package = "embedded-hal", version = "1.0" }
|
||||||
embedded-hal-02 = { package = "embedded-hal", version = "0.2.7" }
|
|
||||||
embedded-hal-async = "1.0"
|
embedded-hal-async = "1.0"
|
||||||
embedded-hal-bus = { version = "0.1", features = ["async"] }
|
embedded-hal-bus = { version = "0.1", features = ["async"] }
|
||||||
embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
|
embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
|
||||||
@ -42,8 +41,5 @@ static_cell = "2"
|
|||||||
portable-atomic = { version = "1.5", features = ["critical-section"] }
|
portable-atomic = { version = "1.5", features = ["critical-section"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
||||||
mcp23017 = { version = "1.0.0" }
|
|
||||||
shared-bus = "0.3.1"
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = 2
|
debug = 2
|
||||||
|
33
README.md
33
README.md
@ -10,37 +10,4 @@ This project only attempts to expose the keyboard as a MIDI device.
|
|||||||
- Install `elf2uf2-rs`.
|
- Install `elf2uf2-rs`.
|
||||||
- `cargo run --bin --release geode-piano`
|
- `cargo run --bin --release geode-piano`
|
||||||
|
|
||||||
## materials
|
|
||||||
|
|
||||||
- 1 Raspberry Pi Pico (preferably with pre-soldered headers)
|
|
||||||
- 2 MCP23017 I/O extender chips, DIP format
|
|
||||||
- 2 pull-up resistors for I²C (1-10kΩ), these are optional but recommended
|
|
||||||
- 1 USB to Micro-USB cable with data transfer
|
|
||||||
- Many jumper cables
|
|
||||||
- Breadboard
|
|
||||||
|
|
||||||
## wiring
|
|
||||||
|
|
||||||
### rails
|
|
||||||
|
|
||||||
- Pin 3 -> GND rail
|
|
||||||
- Pin 36 (3V3OUT) -> power (positive) rail
|
|
||||||
|
|
||||||
### i2c
|
|
||||||
|
|
||||||
Let's call the closest MCP23017 chip to the Pico MCP A, and the further one MCP B.
|
|
||||||
|
|
||||||
- GP16 -> MCP A SDA
|
|
||||||
- GP17 -> MCP A SCL
|
|
||||||
- Pull-up resistor from GP16 to power rail
|
|
||||||
- Pull-up resistor from GP17 to power rail
|
|
||||||
|
|
||||||
For both MCP23017s:
|
|
||||||
|
|
||||||
- MCP RESET -> power rail
|
|
||||||
- MCP A0, A1, A2 -> GND rail for 0, power rail for 1
|
|
||||||
- MCP A should be 0x20 (GND, GND, GND), MCP B 0x27 (3V3, 3V3, 3V3)
|
|
||||||
- MCP VDD -> power rail
|
|
||||||
- MCP VSS -> GND rail
|
|
||||||
|
|
||||||
If you are missing dependencies, consult [Alex Wilson's guide](https://www.alexdwilson.dev/learning-in-public/how-to-program-a-raspberry-pi-pico) on Rust Pico development.
|
If you are missing dependencies, consult [Alex Wilson's guide](https://www.alexdwilson.dev/learning-in-public/how-to-program-a-raspberry-pi-pico) on Rust Pico development.
|
||||||
|
@ -33,7 +33,7 @@ DEALINGS IN THE SOFTWARE.
|
|||||||
use embassy_futures::join::join;
|
use embassy_futures::join::join;
|
||||||
use embassy_rp::{peripherals::USB, usb::Driver};
|
use embassy_rp::{peripherals::USB, usb::Driver};
|
||||||
|
|
||||||
use crate::midi::midi_session;
|
use crate::geode_midi::midi_session;
|
||||||
use embassy_usb::class::cdc_acm::CdcAcmClass;
|
use embassy_usb::class::cdc_acm::CdcAcmClass;
|
||||||
use embassy_usb::class::cdc_acm::State;
|
use embassy_usb::class::cdc_acm::State;
|
||||||
use embassy_usb::class::midi::MidiClass;
|
use embassy_usb::class::midi::MidiClass;
|
||||||
@ -81,7 +81,7 @@ pub async fn usb_task(
|
|||||||
// Create classes on the builder.
|
// Create classes on the builder.
|
||||||
let mut midi_class = MidiClass::new(&mut builder, 1, 1, 64);
|
let mut midi_class = MidiClass::new(&mut builder, 1, 1, 64);
|
||||||
let logger_class = CdcAcmClass::new(&mut builder, &mut logger_state, 64);
|
let logger_class = CdcAcmClass::new(&mut builder, &mut logger_state, 64);
|
||||||
let log_fut = embassy_usb_logger::with_class!(1024, log::LevelFilter::Trace, logger_class);
|
let log_fut = embassy_usb_logger::with_class!(1024, log::LevelFilter::Info, logger_class);
|
||||||
|
|
||||||
// The `MidiClass` can be split into `Sender` and `Receiver`, to be used in separate tasks.
|
// The `MidiClass` can be split into `Sender` and `Receiver`, to be used in separate tasks.
|
||||||
// let (sender, receiver) = class.split();
|
// let (sender, receiver) = class.split();
|
121
src/main.rs
121
src/main.rs
@ -1,42 +1,26 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![deny(rust_2018_idioms)]
|
|
||||||
|
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_rp::bind_interrupts;
|
use embassy_rp::bind_interrupts;
|
||||||
use embassy_rp::gpio;
|
use embassy_rp::gpio;
|
||||||
use embassy_rp::i2c;
|
use embassy_rp::gpio::AnyPin;
|
||||||
|
use embassy_rp::gpio::Input;
|
||||||
|
use embassy_rp::gpio::Pull;
|
||||||
use embassy_rp::peripherals::USB;
|
use embassy_rp::peripherals::USB;
|
||||||
use embassy_rp::usb::{Driver, InterruptHandler};
|
use embassy_rp::usb::{Driver, InterruptHandler};
|
||||||
use embassy_time::Timer;
|
use embassy_time::Timer;
|
||||||
|
use geode_usb::usb_task;
|
||||||
use gpio::{Level, Output};
|
use gpio::{Level, Output};
|
||||||
use usb::usb_task;
|
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
mod midi;
|
mod geode_midi;
|
||||||
mod pins;
|
mod geode_usb;
|
||||||
mod usb;
|
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
bind_interrupts!(struct Irqs {
|
||||||
USBCTRL_IRQ => InterruptHandler<USB>;
|
USBCTRL_IRQ => InterruptHandler<USB>;
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Unwrap, but log before panic
|
|
||||||
///
|
|
||||||
/// Waits a bit to give time for the logger to flush before halting.
|
|
||||||
/// This exists because I do not own a debug probe 😎
|
|
||||||
async fn unwrap<T, E: core::fmt::Debug>(res: Result<T, E>) -> T {
|
|
||||||
match res {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => {
|
|
||||||
log::error!("[FATAL] {:?}", e);
|
|
||||||
log::error!("HALTING DUE TO PANIC.");
|
|
||||||
Timer::after_millis(10).await;
|
|
||||||
panic!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn blink_task(pin: embassy_rp::gpio::AnyPin) {
|
async fn blink_task(pin: embassy_rp::gpio::AnyPin) {
|
||||||
let mut led = Output::new(pin, Level::Low);
|
let mut led = Output::new(pin, Level::Low);
|
||||||
@ -46,15 +30,62 @@ async fn blink_task(pin: embassy_rp::gpio::AnyPin) {
|
|||||||
Timer::after_millis(100).await;
|
Timer::after_millis(100).await;
|
||||||
|
|
||||||
led.set_low();
|
led.set_low();
|
||||||
Timer::after_millis(900).await;
|
Timer::after_secs(5).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
enum Note {
|
||||||
async fn read_task(mut pin_driver: pins::TransparentPins) {
|
C,
|
||||||
|
Pedal,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task(pool_size = 2)]
|
||||||
|
async fn button(pin: AnyPin, note: Note) {
|
||||||
|
let mut button = Input::new(pin, Pull::Up);
|
||||||
|
let chan = geode_midi::MidiChannel::new(0);
|
||||||
loop {
|
loop {
|
||||||
log::warn!("{:b}", unwrap(pin_driver.read_all()).await);
|
let mut counter = 10;
|
||||||
Timer::after_millis(1000).await;
|
button.wait_for_falling_edge().await;
|
||||||
|
loop {
|
||||||
|
Timer::after_millis(5).await;
|
||||||
|
if button.is_low() {
|
||||||
|
counter -= 1;
|
||||||
|
} else {
|
||||||
|
counter = 10;
|
||||||
|
}
|
||||||
|
if counter <= 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match note {
|
||||||
|
Note::C => chan.note_on(72, 64).await,
|
||||||
|
Note::Pedal => {
|
||||||
|
chan.controller(geode_midi::Controller::SustainPedal, 64)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::info!("button press");
|
||||||
|
counter = 10;
|
||||||
|
button.wait_for_rising_edge().await;
|
||||||
|
loop {
|
||||||
|
Timer::after_millis(5).await;
|
||||||
|
if button.is_high() {
|
||||||
|
counter -= 1;
|
||||||
|
} else {
|
||||||
|
counter = 10;
|
||||||
|
}
|
||||||
|
if counter <= 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match note {
|
||||||
|
Note::C => chan.note_off(72, 0).await,
|
||||||
|
Note::Pedal => {
|
||||||
|
chan.controller(geode_midi::Controller::SustainPedal, 0)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log::info!("button release");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,37 +94,11 @@ async fn main(_spawner: Spawner) {
|
|||||||
let p = embassy_rp::init(Default::default());
|
let p = embassy_rp::init(Default::default());
|
||||||
|
|
||||||
let driver = Driver::new(p.USB, Irqs);
|
let driver = Driver::new(p.USB, Irqs);
|
||||||
_spawner.spawn(usb_task(driver)).unwrap();
|
|
||||||
|
|
||||||
_spawner.spawn(blink_task(p.PIN_25.into())).unwrap();
|
_spawner.spawn(blink_task(p.PIN_25.into())).unwrap();
|
||||||
|
_spawner.spawn(button(p.PIN_16.into(), Note::C)).unwrap();
|
||||||
Timer::after_secs(2).await;
|
_spawner
|
||||||
|
.spawn(button(p.PIN_17.into(), Note::Pedal))
|
||||||
log::info!("main: init i2c");
|
.unwrap();
|
||||||
let sda = p.PIN_16;
|
_spawner.spawn(usb_task(driver)).unwrap();
|
||||||
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 mut pin_driver = pins::TransparentPins::new(i2c, [0x20, 0x27], []);
|
|
||||||
|
|
||||||
log::info!("main: setting pins as input");
|
|
||||||
for i in 0..pins::N_EXTENDED_PINS {
|
|
||||||
log::debug!("main: setting pin {} as input, pull up", i);
|
|
||||||
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
|
|
||||||
// unwrap(pin_driver.set_output(7)).await;
|
|
||||||
// unwrap(pin_driver.set_output(8 + 7)).await;
|
|
||||||
// unwrap(pin_driver.set_output(16 + 7)).await;
|
|
||||||
// unwrap(pin_driver.set_output(16 + 8 + 7)).await;
|
|
||||||
|
|
||||||
log::debug!("main: starting read task");
|
|
||||||
_spawner.spawn(read_task(pin_driver)).unwrap();
|
|
||||||
}
|
}
|
||||||
|
183
src/pins.rs
183
src/pins.rs
@ -1,183 +0,0 @@
|
|||||||
/*
|
|
||||||
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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
//! Manage I²C and provide a transparent pin interface for both onboard and MCP23017 pins.
|
|
||||||
|
|
||||||
use embassy_rp::{
|
|
||||||
gpio::{AnyPin, Flex, Pull},
|
|
||||||
i2c::{self, Blocking},
|
|
||||||
peripherals::I2C0,
|
|
||||||
};
|
|
||||||
|
|
||||||
use mcp23017;
|
|
||||||
use mcp23017::MCP23017;
|
|
||||||
|
|
||||||
/// Number of pins driven by each MCP23017 pin extender.
|
|
||||||
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.
|
|
||||||
pub const N_REGULAR_PINS: usize = 0;
|
|
||||||
/// Number of total extended pins
|
|
||||||
pub const N_EXTENDED_PINS: usize = PINS_PER_EXTENDER * N_PIN_EXTENDERS;
|
|
||||||
|
|
||||||
type I2cPeripheral = i2c::I2c<'static, I2C0, Blocking>;
|
|
||||||
type I2cBus = shared_bus::BusManagerSimple<I2cPeripheral>;
|
|
||||||
|
|
||||||
/// GPIO extender pin
|
|
||||||
struct ExtendedPin {
|
|
||||||
/// Index of extender being used
|
|
||||||
ext_id: usize,
|
|
||||||
/// Pin number in the extender's addressing scheme
|
|
||||||
loc_pin: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum TransparentPin {
|
|
||||||
/// On-board GPIO (this is an index into `TransparentPins::pins` not the Pico numbering)
|
|
||||||
Onboard(usize),
|
|
||||||
/// Extender pin
|
|
||||||
Extended(ExtendedPin),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
InvalidPin(u8),
|
|
||||||
I2cError(i2c::Error),
|
|
||||||
ExtenderError,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<i2c::Error> for Error {
|
|
||||||
fn from(err: i2c::Error) -> Error {
|
|
||||||
Error::I2cError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E> From<mcp23017::Error<E>> for Error {
|
|
||||||
fn from(_err: mcp23017::Error<E>) -> 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(&mut self, pin: u8) -> Result<TransparentPin, Error> {
|
|
||||||
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 as usize) / PINS_PER_EXTENDER;
|
|
||||||
let loc_pin = pin % (PINS_PER_EXTENDER as u8);
|
|
||||||
Ok(TransparentPin::Extended(ExtendedPin { ext_id, loc_pin }))
|
|
||||||
} else {
|
|
||||||
Ok(TransparentPin::Onboard(pin as usize - N_EXTENDED_PINS))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new(
|
|
||||||
i2c: i2c::I2c<'static, I2C0, Blocking>,
|
|
||||||
addrs: [u8; N_PIN_EXTENDERS],
|
|
||||||
pins: [AnyPin; N_REGULAR_PINS],
|
|
||||||
) -> Self {
|
|
||||||
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.
|
|
||||||
///
|
|
||||||
/// 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<u64, Error> {
|
|
||||||
log::trace!("read_all: called");
|
|
||||||
let mut ret: u64 = 0;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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)?;
|
|
||||||
match pin {
|
|
||||||
TransparentPin::Onboard(p) => {
|
|
||||||
self.pins[p].set_pull(pull);
|
|
||||||
}
|
|
||||||
TransparentPin::Extended(p) => {
|
|
||||||
let pull_on: bool = match pull {
|
|
||||||
Pull::None => false,
|
|
||||||
Pull::Up => true,
|
|
||||||
// Extended pins don't seem to support pull-down
|
|
||||||
Pull::Down => unimplemented!("MCP23017 does not support pull-down."),
|
|
||||||
};
|
|
||||||
extender!(self, p.ext_id)?.pull_up(p.loc_pin, pull_on)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
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) => {
|
|
||||||
extender!(self, p.ext_id)?.pin_mode(p.loc_pin, mcp23017::PinMode::INPUT)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
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) => {
|
|
||||||
extender!(self, p.ext_id)?.pin_mode(p.loc_pin, mcp23017::PinMode::OUTPUT)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user