diff --git a/rp2040-hal/CHANGELOG.md b/rp2040-hal/CHANGELOG.md index 69f9afaa2..54abbaad8 100644 --- a/rp2040-hal/CHANGELOG.md +++ b/rp2040-hal/CHANGELOG.md @@ -10,11 +10,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Re-enabled implementations of traits from embedded-hal-nb 1.0.0-alpha.1 - @jannic +- Added `AdcPin` wrapper to disable digital function for ADC operations - @ithinuel +- Added `Sealed` supertrait to `PIOExt` - @ithinuel +- Added pins to `Spi` to fix inconsistencies in gpio bounds in peripheral (i2c, uart, spi) - @ithinuel +- Added `sio::Sio::read_bank0() -> u32` to provide single instruction multiple io read. ### Changed - pwm::Slice::has_overflown() returns the raw interrupt flag, without masking/forcing. - @jannic +- Merge DynPin and Pin into Pin. The type class used in Pin now have a runtime variant allowing for + the creation of uniform array of pins (eg: `[Pin]`). - @ithinuel +- Fix miss defined ValidPinMode bound allowing any Bank0 pin to be Xip and any Qspi pin to be any + other function (except for clock). - @ithinuel +- Use `let _ =` to ignore result rather than `.ok();` as this gives a false sense the result is + checked. - @ithinuel - Use an enum for core identification. - @ithinuel +- Reduce code repetition in i2c modules. - @ithinuel ## [0.8.0] - 2023-02-16 diff --git a/rp2040-hal/examples/adc.rs b/rp2040-hal/examples/adc.rs index bdb773f1f..96e66b82f 100644 --- a/rp2040-hal/examples/adc.rs +++ b/rp2040-hal/examples/adc.rs @@ -88,8 +88,8 @@ fn main() -> ! { // UART TX (characters sent from pico) on pin 1 (GPIO0) and RX (on pin 2 (GPIO1) let uart_pins = ( - pins.gpio0.into_mode::(), - pins.gpio1.into_mode::(), + pins.gpio0.into_function::(), + pins.gpio1.into_function::(), ); // Create a UART driver @@ -110,7 +110,7 @@ fn main() -> ! { let mut temperature_sensor = adc.enable_temp_sensor(); // Configure GPIO26 as an ADC input - let mut adc_pin_0 = pins.gpio26.into_floating_input(); + let mut adc_pin_0 = hal::adc::AdcPin::new(pins.gpio26); loop { // Read the raw ADC counts from the temperature sensor channel. let temp_sens_adc_counts: u16 = adc.read(&mut temperature_sensor).unwrap(); diff --git a/rp2040-hal/examples/dht11.rs b/rp2040-hal/examples/dht11.rs index e6c205f7c..d506b99d1 100644 --- a/rp2040-hal/examples/dht11.rs +++ b/rp2040-hal/examples/dht11.rs @@ -24,9 +24,7 @@ use rp2040_hal as hal; use hal::pac; // Some traits we need -use embedded_hal::digital::v2::InputPin; use embedded_hal::digital::v2::OutputPin; -use hal::gpio::dynpin::DynPin; use hal::Clock; /// The linker will place this boot block at the start of our program image. We @@ -43,48 +41,6 @@ const XTAL_FREQ_HZ: u32 = 12_000_000u32; use dht_sensor::{dht11, DhtReading}; -/// A wrapper for DynPin, implementing both InputPin and OutputPin, to simulate -/// an open-drain pin as needed by the wire protocol the DHT11 sensor speaks. -/// https://how2electronics.com/interfacing-dht11-temperature-humidity-sensor-with-raspberry-pi-pico/ -struct InOutPin { - inner: DynPin, -} - -impl InOutPin { - fn new(inner: DynPin) -> Self { - Self { inner } - } -} - -impl InputPin for InOutPin { - type Error = rp2040_hal::gpio::Error; - fn is_high(&self) -> Result::Error> { - self.inner.is_high() - } - fn is_low(&self) -> Result::Error> { - self.inner.is_low() - } -} - -impl OutputPin for InOutPin { - type Error = rp2040_hal::gpio::Error; - fn set_low(&mut self) -> Result<(), ::Error> { - // To actively pull the pin low, it must also be configured as a (readable) output pin - self.inner.into_readable_output(); - // In theory, we should set the pin to low first, to make sure we never actively - // pull it up. But if we try it on the input pin, we get Err(Gpio(InvalidPinType)). - self.inner.set_low()?; - Ok(()) - } - fn set_high(&mut self) -> Result<(), ::Error> { - // To set the open-drain pin to high, just disable the output driver by changing the - // pin to input mode with pull-up. That way, the DHT11 can still pull the data line down - // to send its response. - self.inner.into_pull_up_input(); - Ok(()) - } -} - /// Entry point to our bare-metal application. /// /// The `#[rp2040_hal::entry]` macro ensures the Cortex-M start-up code calls this function @@ -128,8 +84,8 @@ fn main() -> ! { let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz()); // Use GPIO 28 as an InOutPin - let mut pin = InOutPin::new(pins.gpio28.into()); - pin.set_high().ok(); + let mut pin = hal::gpio::InOutPin::new(pins.gpio28); + let _ = pin.set_high(); // Perform a sensor reading let _measurement = dht11::Reading::read(&mut delay, &mut pin); diff --git a/rp2040-hal/examples/gpio_irq_example.rs b/rp2040-hal/examples/gpio_irq_example.rs index 35ccc0072..0eaed59ac 100644 --- a/rp2040-hal/examples/gpio_irq_example.rs +++ b/rp2040-hal/examples/gpio_irq_example.rs @@ -63,11 +63,11 @@ const XTAL_FREQ_HZ: u32 = 12_000_000u32; // We'll create some type aliases using `type` to help with that /// This pin will be our output - it will drive an LED if you run this on a Pico -type LedPin = gpio::Pin; +type LedPin = gpio::Pin; /// This pin will be our interrupt source. /// It will trigger an interrupt if pulled to ground (via a switch or jumper wire) -type ButtonPin = gpio::Pin; +type ButtonPin = gpio::Pin; /// Since we're always accessing these pins together we'll store them in a tuple. /// Giving this tuple a type alias means we won't need to use () when putting them @@ -121,10 +121,10 @@ fn main() -> ! { // Configure GPIO 25 as an output to drive our LED. // we can use into_mode() instead of into_pull_up_input() // since the variable we're pushing it into has that type - let led = pins.gpio25.into_mode(); + let led = pins.gpio25.into(); // Set up the GPIO pin that will be our input - let in_pin = pins.gpio26.into_mode(); + let in_pin = pins.gpio26.into(); // Trigger on the 'falling edge' of the input pin. // This will happen as the button is being pressed diff --git a/rp2040-hal/examples/i2c.rs b/rp2040-hal/examples/i2c.rs index 1eb720438..790ab3bf2 100644 --- a/rp2040-hal/examples/i2c.rs +++ b/rp2040-hal/examples/i2c.rs @@ -75,8 +75,8 @@ fn main() -> ! { ); // Configure two pins as being I²C, not GPIO - let sda_pin = pins.gpio18.into_mode::(); - let scl_pin = pins.gpio19.into_mode::(); + let sda_pin = pins.gpio18.into_function::(); + let scl_pin = pins.gpio19.into_function::(); // let not_an_scl_pin = pins.gpio20.into_mode::(); // Create the I²C drive, using the two pre-configured pins. This will fail diff --git a/rp2040-hal/examples/pio_blink.rs b/rp2040-hal/examples/pio_blink.rs index 1338ccc4c..18e69220b 100644 --- a/rp2040-hal/examples/pio_blink.rs +++ b/rp2040-hal/examples/pio_blink.rs @@ -38,9 +38,9 @@ fn main() -> ! { ); // configure LED pin for Pio0. - let _led: Pin<_, FunctionPio0> = pins.gpio25.into_mode(); + let led: Pin<_, FunctionPio0, _> = pins.gpio25.into_function(); // PIN id for use inside of PIO - let led_pin_id = 25; + let led_pin_id = led.id().num; // Define some simple PIO program. const MAX_DELAY: u8 = 31; diff --git a/rp2040-hal/examples/pio_dma.rs b/rp2040-hal/examples/pio_dma.rs index 7850a9a87..78e37cd69 100644 --- a/rp2040-hal/examples/pio_dma.rs +++ b/rp2040-hal/examples/pio_dma.rs @@ -35,9 +35,9 @@ fn main() -> ! { ); // configure LED pin for Pio0. - let _led: Pin<_, FunctionPio0> = pins.gpio25.into_mode(); + let led: Pin<_, FunctionPio0, _> = pins.gpio25.into_function(); // PIN id for use inside of PIO - let led_pin_id = 25; + let led_pin_id = led.id().num; // HELLO WORLD in morse code: // .... . .-.. .-.. --- / .-- --- .-. .-.. -.. diff --git a/rp2040-hal/examples/pio_proc_blink.rs b/rp2040-hal/examples/pio_proc_blink.rs index 6f9da4623..7f5c9d245 100644 --- a/rp2040-hal/examples/pio_proc_blink.rs +++ b/rp2040-hal/examples/pio_proc_blink.rs @@ -36,9 +36,9 @@ fn main() -> ! { ); // configure LED pin for Pio0. - let _led: Pin<_, FunctionPio0> = pins.gpio25.into_mode(); + let led: Pin<_, FunctionPio0, _> = pins.gpio25.into_function(); // PIN id for use inside of PIO - let led_pin_id = 25; + let led_pin_id = led.id().num; // Define some simple PIO program. let program = pio_proc::pio_asm!( diff --git a/rp2040-hal/examples/pio_side_set.rs b/rp2040-hal/examples/pio_side_set.rs index 25539f2b9..89f01b940 100644 --- a/rp2040-hal/examples/pio_side_set.rs +++ b/rp2040-hal/examples/pio_side_set.rs @@ -38,7 +38,7 @@ fn main() -> ! { ); // configure LED pin for Pio0. - let led: Pin<_, FunctionPio0> = pins.gpio25.into_mode(); + let led: Pin<_, FunctionPio0, _> = pins.gpio25.into_function(); // PIN id for use inside of PIO let led_pin_id = led.id().num; diff --git a/rp2040-hal/examples/pio_synchronized.rs b/rp2040-hal/examples/pio_synchronized.rs index 8258905a9..9759162b5 100644 --- a/rp2040-hal/examples/pio_synchronized.rs +++ b/rp2040-hal/examples/pio_synchronized.rs @@ -38,12 +38,12 @@ fn main() -> ! { ); // configure pins for Pio0. - let _: Pin<_, FunctionPio0> = pins.gpio0.into_mode(); - let _: Pin<_, FunctionPio0> = pins.gpio1.into_mode(); + let gp0: Pin<_, FunctionPio0, _> = pins.gpio0.into_function(); + let gp1: Pin<_, FunctionPio0, _> = pins.gpio1.into_function(); // PIN id for use inside of PIO - let pin0 = 0; - let pin1 = 1; + let pin0 = gp0.id().num; + let pin1 = gp1.id().num; // Define some simple PIO program. let program = pio_proc::pio_asm!( diff --git a/rp2040-hal/examples/pwm_irq_input.rs b/rp2040-hal/examples/pwm_irq_input.rs index d4a51937d..291214912 100644 --- a/rp2040-hal/examples/pwm_irq_input.rs +++ b/rp2040-hal/examples/pwm_irq_input.rs @@ -63,10 +63,10 @@ const XTAL_FREQ_HZ: u32 = 12_000_000u32; /// We'll create some type aliases using `type` to help with that /// This pin will be our output - it will drive an LED if you run this on a Pico -type LedPin = gpio::Pin; +type LedPin = gpio::Pin, gpio::PullNone>; /// This pin will be our input for a 50 Hz servo PWM signal -type InputPwmPin = gpio::Pin; +type InputPwmPin = gpio::Pin; /// This will be our PWM Slice - it will interpret the PWM signal from the pin type PwmSlice = pwm::Slice; @@ -134,8 +134,8 @@ fn main() -> ! { pwm.enable(); // Connect to GPI O1 as the input to channel B on PWM0 + let input_pin = pins.gpio1.into(); let channel = &mut pwm.channel_b; - let input_pin = channel.input_from(pins.gpio1); channel.enable(); // Enable an interrupt whenever GPI O1 goes from high to low (the end of a pulse) @@ -144,7 +144,7 @@ fn main() -> ! { // Configure GPIO 25 as an output to drive our LED. // we can use into_mode() instead of into_pull_up_input() // since the variable we're pushing it into has that type - let led = pins.gpio25.into_mode(); + let led = pins.gpio25.into(); // Give away our pins by moving them into the `GLOBAL_PINS` variable. // We won't need to access them in the main thread again @@ -193,14 +193,14 @@ fn IO_IRQ_BANK0() { // if the PWM signal indicates low, turn off the LED if pulse_width_us < LOW_US { // set_low can't fail, but the embedded-hal traits always allow for it - // we can discard the Result by transforming it to an Option - led.set_low().ok(); + // we can discard the Result + let _ = led.set_low(); } // if the PWM signal indicates low, turn on the LED else if pulse_width_us > HIGH_US { // set_high can't fail, but the embedded-hal traits always allow for it - // we can discard the Result by transforming it to an Option - led.set_high().ok(); + // we can discard the Result + let _ = led.set_high(); } // If the PWM signal was in the dead-zone between LOW and HIGH, don't change the LED's diff --git a/rp2040-hal/examples/rom_funcs.rs b/rp2040-hal/examples/rom_funcs.rs index 281025df7..fe81839ca 100644 --- a/rp2040-hal/examples/rom_funcs.rs +++ b/rp2040-hal/examples/rom_funcs.rs @@ -86,9 +86,9 @@ fn main() -> ! { let uart_pins = ( // UART TX (characters sent from RP2040) on pin 1 (GPIO0) - pins.gpio0.into_mode::(), + pins.gpio0.into_function::(), // UART RX (characters received by RP2040) on pin 2 (GPIO1) - pins.gpio1.into_mode::(), + pins.gpio1.into_function::(), ); let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS) .enable( diff --git a/rp2040-hal/examples/spi.rs b/rp2040-hal/examples/spi.rs index c4dca4162..dae0c486d 100644 --- a/rp2040-hal/examples/spi.rs +++ b/rp2040-hal/examples/spi.rs @@ -80,10 +80,10 @@ fn main() -> ! { ); // These are implicitly used by the spi driver if they are in the correct mode - let _spi_sclk = pins.gpio6.into_mode::(); - let _spi_mosi = pins.gpio7.into_mode::(); - let _spi_miso = pins.gpio4.into_mode::(); - let spi = hal::Spi::<_, _, 8>::new(pac.SPI0); + let spi_mosi = pins.gpio7.into_function::(); + let spi_miso = pins.gpio4.into_function::(); + let spi_sclk = pins.gpio6.into_function::(); + let spi = hal::spi::Spi::<_, _, _, 8>::new(pac.SPI0, (spi_mosi, spi_miso, spi_sclk)); // Exchange the uninitialised SPI driver for an initialised one let mut spi = spi.init( diff --git a/rp2040-hal/examples/spi_dma.rs b/rp2040-hal/examples/spi_dma.rs index 51d434c03..469de52a7 100644 --- a/rp2040-hal/examples/spi_dma.rs +++ b/rp2040-hal/examples/spi_dma.rs @@ -58,10 +58,10 @@ fn main() -> ! { ); // These are implicitly used by the spi driver if they are in the correct mode - let _spi_sclk = pins.gpio6.into_mode::(); - let _spi_mosi = pins.gpio7.into_mode::(); - let _spi_miso = pins.gpio4.into_mode::(); - let spi = hal::spi::Spi::<_, _, 8>::new(pac.SPI0); + let spi_mosi = pins.gpio7.into_function::(); + let spi_miso = pins.gpio4.into_function::(); + let spi_sclk = pins.gpio6.into_function::(); + let spi = hal::spi::Spi::<_, _, _, 8>::new(pac.SPI0, (spi_mosi, spi_miso, spi_sclk)); // Exchange the uninitialised SPI driver for an initialised one let spi = spi.init( diff --git a/rp2040-hal/examples/uart.rs b/rp2040-hal/examples/uart.rs index 071df17cb..178979eae 100644 --- a/rp2040-hal/examples/uart.rs +++ b/rp2040-hal/examples/uart.rs @@ -86,9 +86,9 @@ fn main() -> ! { let uart_pins = ( // UART TX (characters sent from RP2040) on pin 1 (GPIO0) - pins.gpio0.into_mode(), + pins.gpio0.into_function(), // UART RX (characters received by RP2040) on pin 2 (GPIO1) - pins.gpio1.into_mode(), + pins.gpio1.into_function(), ); let mut uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS) .enable( diff --git a/rp2040-hal/examples/uart_dma.rs b/rp2040-hal/examples/uart_dma.rs index 84d5900fb..35dc770a1 100644 --- a/rp2040-hal/examples/uart_dma.rs +++ b/rp2040-hal/examples/uart_dma.rs @@ -86,9 +86,9 @@ fn main() -> ! { let uart_pins = ( // UART TX (characters sent from RP2040) on pin 1 (GPIO0) - pins.gpio0.into_mode(), + pins.gpio0.into_function(), // UART RX (characters received by RP2040) on pin 2 (GPIO1) - pins.gpio1.into_mode(), + pins.gpio1.into_function(), ); let uart = hal::uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS) .enable( diff --git a/rp2040-hal/examples/vector_table.rs b/rp2040-hal/examples/vector_table.rs index ccc0d4519..856926718 100644 --- a/rp2040-hal/examples/vector_table.rs +++ b/rp2040-hal/examples/vector_table.rs @@ -33,7 +33,7 @@ static mut RAM_VTABLE: VectorTable = VectorTable::new(); // Give our LED and Alarm a type alias to make it easier to refer to them type LedAndAlarm = ( - hal::gpio::Pin, + hal::gpio::Pin, hal::timer::Alarm0, ); diff --git a/rp2040-hal/src/adc.rs b/rp2040-hal/src/adc.rs index 1e2827580..3c5cb364d 100644 --- a/rp2040-hal/src/adc.rs +++ b/rp2040-hal/src/adc.rs @@ -7,14 +7,14 @@ //! Capture ADC reading from a pin //! ```no_run //! use embedded_hal::adc::OneShot; -//! use rp2040_hal::{adc::Adc, gpio::Pins, pac, Sio}; +//! use rp2040_hal::{adc::Adc, adc::AdcPin, gpio::Pins, pac, Sio}; //! let mut peripherals = pac::Peripherals::take().unwrap(); //! let sio = Sio::new(peripherals.SIO); //! let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS); //! // Enable adc //! let mut adc = Adc::new(peripherals.ADC, &mut peripherals.RESETS); //! // Configure one of the pins as an ADC input -//! let mut adc_pin_0 = pins.gpio26.into_floating_input(); +//! let mut adc_pin_0 = AdcPin::new(pins.gpio26.into_floating_input()); //! // Read the ADC counts from the ADC channel //! let pin_adc_counts: u16 = adc.read(&mut adc_pin_0).unwrap(); //! ``` @@ -37,20 +37,58 @@ //! See [examples/adc.rs](https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal/examples/adc.rs) and //! [pimoroni_pico_explorer_showcase.rs](https://github.com/rp-rs/rp-hal-boards/tree/main/boards/pimoroni-pico-explorer/examples/pimoroni_pico_explorer_showcase.rs) for more complete examples +use core::convert::Infallible; + use hal::adc::{Channel, OneShot}; use pac::{ADC, RESETS}; use crate::{ - gpio::Pin, gpio::{ bank0::{Gpio26, Gpio27, Gpio28, Gpio29}, - FloatingInput, + AnyPin, Function, OutputEnableOverride, Pin, PullType, DynPinId, ValidFunction, }, resets::SubsystemReset, }; const TEMPERATURE_SENSOR_CHANNEL: u8 = 4; +/// A pin locked in use with the ADC. +pub struct AdcPin

+where + P: AnyPin, +{ + pin: P, + output_disable: bool, + input_enable: bool, +} + +impl

AdcPin

+where + P: AnyPin, +{ + /// Captures the pin to be used with an ADC and disables its digital circuitery. + pub fn new(pin: P) -> Self + { + let mut p = pin.into(); + let (od, ie) = (p.get_output_disable(), p.get_input_enable()); + p.set_output_enable_override(OutputEnableOverride::Disable); + p.set_input_enable(false); + Self { + pin: P::from(p), + output_disable: od, + input_enable: ie, + } + } + + /// Release the pin and restore its digital circuitery's state. + pub fn release(self) -> P { + let mut p = self.pin.into(); + p.set_output_disable(self.output_disable); + p.set_input_enable(self.input_enable); + P::from(p) + } +} + /// Adc pub struct Adc { device: ADC, @@ -92,11 +130,30 @@ impl Adc { pub fn disable_temp_sensor(&mut self, _: TempSense) { self.device.cs.modify(|_, w| w.ts_en().clear_bit()); } + + fn read(&mut self, chan: u8) -> u16 { + while !self.device.cs.read().ready().bit_is_set() { + cortex_m::asm::nop(); + } + + self.device + .cs + .modify(|_, w| unsafe { w.ainsel().bits(chan).start_once().set_bit() }); + + while !self.device.cs.read().ready().bit_is_set() { + cortex_m::asm::nop(); + } + + self.device.result.read().result().bits() + } } macro_rules! channel { ($pin:ident, $channel:expr) => { - impl Channel for Pin<$pin, FloatingInput> { + impl Channel for AdcPin> + where + $pin: crate::gpio::ValidFunction, + { type ID = u8; // ADC channels are identified numerically fn channel() -> u8 { @@ -110,6 +167,12 @@ channel!(Gpio26, 0); channel!(Gpio27, 1); channel!(Gpio28, 2); channel!(Gpio29, 3); +impl Channel for AdcPin> +where DynPinId: crate::gpio::ValidFunction +{ + type ID = (); // ADC channels are identified at run time + fn channel() {} +} /// Internal temperature sensor type pub struct TempSense { @@ -124,32 +187,49 @@ impl Channel for TempSense { } } -impl OneShot for Adc +// Implementation for TempSense and type-checked pins +impl OneShot for Adc where WORD: From, - PIN: Channel, + SRC: Channel, { - type Error = (); - - fn read(&mut self, _pin: &mut PIN) -> nb::Result { - let chan = PIN::channel(); + type Error = Infallible; + fn read(&mut self, _pin: &mut SRC) -> nb::Result { + let chan = SRC::channel(); if chan == TEMPERATURE_SENSOR_CHANNEL { self.device.cs.modify(|_, w| w.ts_en().set_bit()) } - while !self.device.cs.read().ready().bit_is_set() { - cortex_m::asm::nop(); - } - - self.device - .cs - .modify(|_, w| unsafe { w.ainsel().bits(chan).start_once().set_bit() }); + Ok(self.read(chan).into()) + } +} - while !self.device.cs.read().ready().bit_is_set() { - cortex_m::asm::nop(); - } +/// The pin was invalid for the requested operation +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct InvalidPinError; - Ok(self.device.result.read().result().bits().into()) +// Implementation for dyn-pins +impl OneShot>> for Adc +where + WORD: From, + F: Function, + M: PullType, + DynPinId: ValidFunction, + AdcPin>: Channel, +{ + type Error = InvalidPinError; + + fn read(&mut self, _pin: &mut AdcPin>) -> nb::Result { + use crate::gpio::DynBankId; + let pin_id = _pin.pin.id(); + let chan = if (26..=29).contains(&pin_id.num) && pin_id.bank == DynBankId::Bank0 { + pin_id.num - 26 + } else { + return Err(nb::Error::Other(InvalidPinError)); + }; + + Ok(self.read(chan).into()) } } diff --git a/rp2040-hal/src/clocks/clock_sources.rs b/rp2040-hal/src/clocks/clock_sources.rs index d4661310b..c399f5766 100644 --- a/rp2040-hal/src/clocks/clock_sources.rs +++ b/rp2040-hal/src/clocks/clock_sources.rs @@ -4,7 +4,7 @@ use super::*; use crate::{ gpio::{ bank0::{Gpio20, Gpio22}, - FunctionClock, Pin, + FunctionClock, Pin, PullNone, PullType, }, pll::{Locked, PhaseLockedLoop}, rosc::{Enabled, RingOscillator}, @@ -77,16 +77,16 @@ impl ClockSource for RingOscillator { } // GPIN0 -pub(crate) type GPin0 = Pin; -impl ClockSource for GPin0 { +pub(crate) type GPin0 = Pin; +impl ClockSource for GPin0 { fn get_freq(&self) -> HertzU32 { todo!() } } // GPIN1 -pub(crate) type GPin1 = Pin; -impl ClockSource for Pin { +pub(crate) type GPin1 = Pin; +impl ClockSource for Pin { fn get_freq(&self) -> HertzU32 { todo!() } diff --git a/rp2040-hal/src/critical_section_impl.rs b/rp2040-hal/src/critical_section_impl.rs index 6673f9e94..a121059c3 100644 --- a/rp2040-hal/src/critical_section_impl.rs +++ b/rp2040-hal/src/critical_section_impl.rs @@ -36,7 +36,7 @@ impl RpSpinlockCs { // Store the initial interrupt state and current core id in stack variables let interrupts_active = cortex_m::register::primask::read().is_active(); // We reserved 0 as our `LOCK_UNOWNED` value, so add 1 to core_id so we get 1 for core0, 2 for core1. - let core = crate::Sio::core() + 1_u8; + let core = crate::Sio::core() as u8 + 1_u8; // Do we already own the spinlock? if LOCK_OWNER.load(Ordering::Acquire) == core { // We already own the lock, so we must have called acquire within a critical_section. diff --git a/rp2040-hal/src/dma/single_channel.rs b/rp2040-hal/src/dma/single_channel.rs index 896594279..57125ed14 100644 --- a/rp2040-hal/src/dma/single_channel.rs +++ b/rp2040-hal/src/dma/single_channel.rs @@ -116,8 +116,6 @@ impl SingleChannel for (Channel, Chan } } -impl Sealed for (Channel, Channel) {} - pub(crate) trait ChannelConfig { fn config( &mut self, diff --git a/rp2040-hal/src/gpio/dynpin.rs b/rp2040-hal/src/gpio/dynpin.rs deleted file mode 100644 index 15735303a..000000000 --- a/rp2040-hal/src/gpio/dynpin.rs +++ /dev/null @@ -1,643 +0,0 @@ -//! # Type-erased, value-level module for GPIO pins -//! -//! Based heavily on `atsamd-hal`. -//! -//! Although the type-level API is generally preferred, it is not suitable in -//! all cases. Because each pin is represented by a distinct type, it is not -//! possible to store multiple pins in a homogeneous data structure. The -//! value-level API solves this problem by erasing the type information and -//! tracking the pin at run-time. -//! -//! Value-level pins are represented by the [`DynPin`] type. [`DynPin`] has two -//! fields, `id` and `mode` with types [`DynPinId`] and [`DynPinMode`] -//! respectively. The implementation of these types closely mirrors the -//! type-level API. -//! -//! Instances of [`DynPin`] cannot be created directly. Rather, they must be -//! created from their type-level equivalents using [`From`]/[`Into`]. -//! -//! ```no_run -//! // Move a pin out of the Pins struct and convert to a DynPin -//! # use rp2040_hal::{pac, gpio::{DynPin, bank0::Gpio12, Pins}, Sio}; -//! # let mut peripherals = pac::Peripherals::take().unwrap(); -//! # let sio = Sio::new(peripherals.SIO); -//! # let pins = Pins::new(peripherals.IO_BANK0,peripherals.PADS_BANK0,sio.gpio_bank0, &mut peripherals.RESETS); -//! # use rp2040_hal::gpio::DYN_FLOATING_INPUT; -//! let gpio12: DynPin = pins.gpio12.into(); -//! ``` -//! -//! Conversions between pin modes use a value-level version of the type-level -//! API. -//! -//! ```no_run -//! # use rp2040_hal::{pac, gpio::{DynPin, Pins}, Sio}; -//! # let mut peripherals = pac::Peripherals::take().unwrap(); -//! # let sio = Sio::new(peripherals.SIO); -//! # let pins = Pins::new(peripherals.IO_BANK0,peripherals.PADS_BANK0,sio.gpio_bank0, &mut peripherals.RESETS); -//! # use rp2040_hal::gpio::DYN_FLOATING_INPUT; -//! # let mut gpio12: DynPin = pins.gpio12.into(); -//! // Use one of the literal function names -//! gpio12.into_floating_input(); -//! // Use a method and a DynPinMode variant -//! gpio12.try_into_mode(DYN_FLOATING_INPUT).unwrap(); -//! ``` -//! -//! Because the pin state cannot be tracked at compile-time, many [`DynPin`] -//! operations become fallible. Run-time checks are inserted to ensure that -//! users don't try to, for example, set the output level of an input pin. -//! -//! Users may try to convert value-level pins back to their type-level -//! equivalents. However, this option is fallible, because the compiler cannot -//! guarantee the pin has the correct ID or is in the correct mode at -//! compile-time. Use [`TryFrom`](core::convert::TryFrom)/ -//! [`TryInto`](core::convert::TryInto) for this conversion. -//! -//! ```no_run -//! # use core::convert::TryInto; -//! # use rp2040_hal::{pac, gpio::{DynPin, bank0::Gpio12, Pin, Pins, FloatingInput}, Sio}; -//! # let mut peripherals = pac::Peripherals::take().unwrap(); -//! # let sio = Sio::new(peripherals.SIO); -//! # let pins = Pins::new(peripherals.IO_BANK0,peripherals.PADS_BANK0,sio.gpio_bank0, &mut peripherals.RESETS); -//! // Convert to a `DynPin` -//! let mut gpio12: DynPin = pins.gpio12.into(); -//! // Change pin mode -//! gpio12.into_floating_input(); -//! // Convert back to a `Pin` -//! let gpio12: Pin = gpio12.try_into().unwrap(); -//! ``` -//! -//! # Embedded HAL traits -//! -//! This module implements all of the embedded HAL GPIO traits for [`DynPin`]. -//! However, whereas the type-level API uses -//! `Error = core::convert::Infallible`, the value-level API can return a real -//! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the -//! operation, the trait functions will return -//! [`InvalidPinType`](Error::InvalidPinType). -use super::pin::{Pin, PinId, PinMode, ValidPinMode}; -use super::reg::RegisterInterface; -use super::{Interrupt, InterruptOverride}; -use core::convert::TryFrom; - -#[cfg(feature = "eh1_0_alpha")] -use eh1_0_alpha::digital as eh1; -use hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; - -//============================================================================== -// DynPinMode configurations -//============================================================================== - -/// Value-level `enum` for disabled configurations -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] -pub enum DynDisabled { - Floating, - PullDown, - PullUp, - BusKeep, -} - -/// Value-level `enum` for input configurations -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] -pub enum DynInput { - Floating, - PullDown, - PullUp, - BusKeep, -} - -/// Value-level `enum` for output configurations -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] -pub enum DynOutput { - PushPull, - Readable, -} - -/// Value-level `enum` for output configurations -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] -pub enum DynFunction { - Spi, - Xip, - Uart, - I2C, - Pwm, - Pio0, - Pio1, - Clock, - UsbAux, -} - -//============================================================================== -// DynPinMode -//============================================================================== - -/// Value-level `enum` representing pin modes -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] -pub enum DynPinMode { - Disabled(DynDisabled), - Input(DynInput), - Output(DynOutput), - Function(DynFunction), -} - -impl DynPinMode { - #[inline] - fn valid_for(&self, id: DynPinId) -> bool { - use DynFunction::*; - use DynGroup::*; - use DynPinMode::*; - match self { - Disabled(_) => true, - Input(_) => true, - Output(_) => true, - Function(alt) => match id.group { - Bank0 => match alt { - Spi | Uart | I2C | Pwm | Pio0 | Pio1 | UsbAux => true, - Clock if id.num >= 20 && id.num <= 25 => true, - _ => false, - }, - #[allow(clippy::match_like_matches_macro)] - Qspi => match alt { - Xip => true, - _ => false, - }, - }, - } - } -} - -/// Value-level variant of [`DynPinMode`] for floating disabled mode -pub const DYN_FLOATING_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::Floating); -/// Value-level variant of [`DynPinMode`] for pull-down disabled mode -pub const DYN_PULL_DOWN_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::PullDown); -/// Value-level variant of [`DynPinMode`] for pull-up disabled mode -pub const DYN_PULL_UP_DISABLED: DynPinMode = DynPinMode::Disabled(DynDisabled::PullUp); - -/// Value-level variant of [`DynPinMode`] for floating input mode -pub const DYN_FLOATING_INPUT: DynPinMode = DynPinMode::Input(DynInput::Floating); -/// Value-level variant of [`DynPinMode`] for pull-down input mode -pub const DYN_PULL_DOWN_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullDown); -/// Value-level variant of [`DynPinMode`] for pull-up input mode -pub const DYN_PULL_UP_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullUp); - -/// Value-level variant of [`DynPinMode`] for push-pull output mode -pub const DYN_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::PushPull); -/// Value-level variant of [`DynPinMode`] for readable push-pull output mode -pub const DYN_READABLE_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::Readable); - -macro_rules! dyn_function { - ( $($Func:ident),+ ) => { - crate::paste::paste! { - $( - #[ - doc = "Value-level variant of [`DynPinMode`] for alternate " - "peripheral function " $Func - ] - pub const []: DynPinMode = - DynPinMode::Function(DynFunction::$Func); - )+ - } - }; -} - -dyn_function!(Spi, Xip, Uart, I2C, Pwm, Pio0, Pio1, Clock, UsbAux); - -//============================================================================== -// DynGroup & DynPinId -//============================================================================== - -/// Value-level `enum` for pin groups -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] -pub enum DynGroup { - Bank0, - Qspi, -} - -/// Value-level `struct` representing pin IDs -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[allow(missing_docs)] -pub struct DynPinId { - pub group: DynGroup, - pub num: u8, -} - -//============================================================================== -// DynRegisters -//============================================================================== - -/// Provide a safe register interface for [`DynPin`]s -/// -/// This `struct` takes ownership of a [`DynPinId`] and provides an API to -/// access the corresponding registers. -struct DynRegisters { - id: DynPinId, -} - -// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`] -// guarantees that each pin is a singleton, so this implementation is safe. -unsafe impl RegisterInterface for DynRegisters { - #[inline] - fn id(&self) -> DynPinId { - self.id - } -} - -impl DynRegisters { - /// Create a new instance of [`DynRegisters`] - /// - /// # Safety - /// - /// Users must never create two simultaneous instances of this `struct` with - /// the same [`DynPinId`] - #[inline] - unsafe fn new(id: DynPinId) -> Self { - DynRegisters { id } - } -} - -//============================================================================== -// Error -//============================================================================== - -/// GPIO error type -/// -/// [`DynPin`]s are not tracked and verified at compile-time, so run-time -/// operations are fallible. This `enum` represents the corresponding errors. -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Error { - /// The pin did not have the correct ID or mode for the requested operation - InvalidPinType, - /// The pin does not support the requeted mode - InvalidPinMode, -} - -//============================================================================== -// DynPin -//============================================================================== - -/// A value-level pin, parameterized by [`DynPinId`] and [`DynPinMode`] -/// -/// This type acts as a type-erased version of [`Pin`]. Every pin is represented -/// by the same type, and pins are tracked and distinguished at run-time. -pub struct DynPin { - regs: DynRegisters, - mode: DynPinMode, -} - -impl DynPin { - /// Create a new [`DynPin`] - /// - /// # Safety - /// - /// Each [`DynPin`] must be a singleton. For a given [`DynPinId`], there - /// must be at most one corresponding [`DynPin`] in existence at any given - /// time. Violating this requirement is `unsafe`. - #[inline] - unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self { - DynPin { - regs: DynRegisters::new(id), - mode, - } - } - - /// Return a copy of the pin ID - #[inline] - pub fn id(&self) -> DynPinId { - self.regs.id - } - - /// Return a copy of the pin mode - #[inline] - pub fn mode(&self) -> DynPinMode { - self.mode - } - - /// Convert the pin to the requested [`DynPinMode`] - #[inline] - pub fn try_into_mode(&mut self, mode: DynPinMode) -> Result<(), Error> { - // FIXME: check valid modes - // Only modify registers if we are actually changing pin mode - if mode.valid_for(self.regs.id) { - if mode != self.mode { - self.regs.do_change_mode(mode); - self.mode = mode; - } - Ok(()) - } else { - Err(Error::InvalidPinMode) - } - } - - /// Clear interrupt. - #[inline] - pub fn clear_interrupt(&self, interrupt: Interrupt) { - self.regs.clear_interrupt(interrupt) - } - - /// Interrupt status. - #[inline] - pub fn interrupt_status(&self, interrupt: Interrupt) -> bool { - self.regs.interrupt_status(interrupt) - } - - /// Is interrupt enabled. - #[inline] - pub fn is_interrupt_enabled(&self, interrupt: Interrupt) -> bool { - self.regs.is_interrupt_enabled(interrupt) - } - - /// Enable or disable interrupt - #[inline] - pub fn set_interrupt_enabled(&self, interrupt: Interrupt, enabled: bool) { - self.regs.set_interrupt_enabled(interrupt, enabled) - } - - /// Is interrupt forced. - #[inline] - pub fn is_interrupt_forced(&self, interrupt: Interrupt) -> bool { - self.regs.is_interrupt_forced(interrupt) - } - - /// Force or release interrupt. - #[inline] - pub fn set_interrupt_forced(&self, interrupt: Interrupt, forced: bool) { - self.regs.set_interrupt_forced(interrupt, forced); - } - - /// Set the interrupt override. - #[inline] - pub fn set_interrupt_override(&mut self, override_value: InterruptOverride) { - self.regs.set_interrupt_override(override_value); - } - - /// Disable the pin and set it to float - #[inline] - #[allow(clippy::wrong_self_convention)] // matches pin api - pub fn into_floating_disabled(&mut self) { - self.try_into_mode(DYN_FLOATING_DISABLED).unwrap(); // always valid - } - - /// Disable the pin and set it to pull down - #[inline] - #[allow(clippy::wrong_self_convention)] // matches pin api - pub fn into_pull_down_disabled(&mut self) { - self.try_into_mode(DYN_PULL_DOWN_DISABLED).unwrap(); // always valid - } - - /// Disable the pin and set it to pull up - #[inline] - #[allow(clippy::wrong_self_convention)] // matches pin api - pub fn into_pull_up_disabled(&mut self) { - self.try_into_mode(DYN_PULL_UP_DISABLED).unwrap(); // always valid - } - - /// Configure the pin to operate as a floating input - #[inline] - #[allow(clippy::wrong_self_convention)] // matches pin api - pub fn into_floating_input(&mut self) { - self.try_into_mode(DYN_FLOATING_INPUT).unwrap(); // always valid - } - - /// Configure the pin to operate as a pulled down input - #[inline] - #[allow(clippy::wrong_self_convention)] // matches pin api - pub fn into_pull_down_input(&mut self) { - self.try_into_mode(DYN_PULL_DOWN_INPUT).unwrap(); // always valid - } - - /// Configure the pin to operate as a pulled up input - #[inline] - #[allow(clippy::wrong_self_convention)] // matches pin api - pub fn into_pull_up_input(&mut self) { - self.try_into_mode(DYN_PULL_UP_INPUT).unwrap(); // always valid - } - - /// Configure the pin to operate as a push-pull output - #[inline] - #[allow(clippy::wrong_self_convention)] // matches pin api - pub fn into_push_pull_output(&mut self) { - self.try_into_mode(DYN_PUSH_PULL_OUTPUT).unwrap(); // always valid - } - - /// Configure the pin to operate as a readable push pull output - #[inline] - #[allow(clippy::wrong_self_convention)] // matches pin api - pub fn into_readable_output(&mut self) { - self.try_into_mode(DYN_READABLE_OUTPUT).unwrap(); // always valid - } - - #[inline] - fn _read(&self) -> Result { - match self.mode { - DynPinMode::Input(_) | DYN_READABLE_OUTPUT => Ok(self.regs.read_pin()), - _ => Err(Error::InvalidPinType), - } - } - #[inline] - fn _write(&mut self, bit: bool) -> Result<(), Error> { - match self.mode { - DynPinMode::Output(_) => { - self.regs.write_pin(bit); - Ok(()) - } - _ => Err(Error::InvalidPinType), - } - } - #[inline] - fn _toggle(&mut self) -> Result<(), Error> { - match self.mode { - DynPinMode::Output(_) => { - self.regs.toggle_pin(); - Ok(()) - } - _ => Err(Error::InvalidPinType), - } - } - #[inline] - fn _read_out(&self) -> Result { - match self.mode { - DynPinMode::Output(_) => Ok(self.regs.read_out_pin()), - _ => Err(Error::InvalidPinType), - } - } - #[inline] - #[allow(clippy::bool_comparison)] // more explicit this way - fn _is_low(&self) -> Result { - Ok(self._read()? == false) - } - #[inline] - #[allow(clippy::bool_comparison)] // more explicit this way - fn _is_high(&self) -> Result { - Ok(self._read()? == true) - } - #[inline] - fn _set_low(&mut self) -> Result<(), Error> { - self._write(false) - } - #[inline] - fn _set_high(&mut self) -> Result<(), Error> { - self._write(true) - } - #[inline] - #[allow(clippy::bool_comparison)] // more explicit this way - fn _is_set_low(&self) -> Result { - Ok(self._read_out()? == false) - } - #[inline] - #[allow(clippy::bool_comparison)] // more explicit this way - fn _is_set_high(&self) -> Result { - Ok(self._read_out()? == true) - } -} - -//============================================================================== -// Convert between Pin and DynPin -//============================================================================== - -impl From> for DynPin -where - I: PinId, - M: PinMode + ValidPinMode, -{ - /// Erase the type-level information in a [`Pin`] and return a value-level - /// [`DynPin`] - #[inline] - fn from(_pin: Pin) -> Self { - // The `Pin` is consumed, so it is safe to replace it with the - // corresponding `DynPin` - unsafe { DynPin::new(I::DYN, M::DYN) } - } -} - -impl TryFrom for Pin -where - I: PinId, - M: PinMode + ValidPinMode, -{ - type Error = Error; - - /// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`] - /// - /// There is no way for the compiler to know if the conversion will be - /// successful at compile-time. We must verify the conversion at run-time - /// or refuse to perform it. - #[inline] - fn try_from(pin: DynPin) -> Result { - if pin.regs.id == I::DYN && pin.mode == M::DYN { - // The `DynPin` is consumed, so it is safe to replace it with the - // corresponding `Pin` - Ok(unsafe { Self::new() }) - } else { - Err(Error::InvalidPinType) - } - } -} - -//============================================================================== -// Embedded HAL traits -//============================================================================== - -impl OutputPin for DynPin { - type Error = Error; - #[inline] - fn set_high(&mut self) -> Result<(), Self::Error> { - self._set_high() - } - #[inline] - fn set_low(&mut self) -> Result<(), Self::Error> { - self._set_low() - } -} - -impl InputPin for DynPin { - type Error = Error; - #[inline] - fn is_high(&self) -> Result { - self._is_high() - } - #[inline] - fn is_low(&self) -> Result { - self._is_low() - } -} - -impl ToggleableOutputPin for DynPin { - type Error = Error; - #[inline] - fn toggle(&mut self) -> Result<(), Self::Error> { - self._toggle() - } -} - -impl StatefulOutputPin for DynPin { - #[inline] - fn is_set_high(&self) -> Result { - self._is_set_high() - } - #[inline] - fn is_set_low(&self) -> Result { - self._is_set_low() - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::ErrorType for DynPin { - type Error = Error; -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::OutputPin for DynPin { - #[inline] - fn set_high(&mut self) -> Result<(), Self::Error> { - self._set_high() - } - #[inline] - fn set_low(&mut self) -> Result<(), Self::Error> { - self._set_low() - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::InputPin for DynPin { - #[inline] - fn is_high(&self) -> Result { - self._is_high() - } - #[inline] - fn is_low(&self) -> Result { - self._is_low() - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::ToggleableOutputPin for DynPin { - #[inline] - fn toggle(&mut self) -> Result<(), Self::Error> { - self._toggle() - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::StatefulOutputPin for DynPin { - #[inline] - fn is_set_high(&self) -> Result { - self._is_set_high() - } - #[inline] - fn is_set_low(&self) -> Result { - self._is_set_low() - } -} diff --git a/rp2040-hal/src/gpio/func.rs b/rp2040-hal/src/gpio/func.rs new file mode 100644 index 000000000..aa20c2ab8 --- /dev/null +++ b/rp2040-hal/src/gpio/func.rs @@ -0,0 +1,183 @@ +use core::marker::PhantomData; + +use paste::paste; + +use super::pin::DynBankId; + +pub(crate) mod func_sealed { + use super::DynFunction; + + pub trait Function { + fn from(f: DynFunction) -> Self; + fn as_dyn(&self) -> DynFunction; + } + pub trait TypeLevelFunction {} +} + +/// Type-level `enum` for pin function +pub trait Function: func_sealed::Function {} + +/// Value-level `enum` for pin function. +#[allow(missing_docs)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DynFunction { + Xip, + Spi, + Uart, + I2c, + Pwm, + Sio(DynSioConfig), + Pio0, + Pio1, + Clock, + Usb, + Null, +} + +#[allow(missing_docs)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DynSioConfig { + Input, + Output, +} + +impl Function for DynFunction {} +impl func_sealed::Function for DynFunction { + fn from(f: DynFunction) -> Self { + f + } + + fn as_dyn(&self) -> DynFunction { + *self + } +} + +macro_rules! pin_func { + ($($fn:ident $(as $alias:ident)?),*) => { + $(paste! { + /// Type-level `variant` for pin function + pub struct [](pub(super) ()); + impl Function for [] {} + impl func_sealed::TypeLevelFunction for [] {} + impl func_sealed::Function for [] { + fn from(_f: DynFunction) -> Self { + Self(()) + } + fn as_dyn(&self) -> DynFunction { + DynFunction::[<$fn>] + } + } + $( + #[doc = "Alias to `Function" $fn "`."] + pub type [] = []; + )? + })* + }; +} +pin_func!(Xip, Spi, Uart, I2c as I2C, Pwm, Pio0, Pio1, Clock, Usb, Null); + +// ============================= +// SIO sub-types + +/// Type-level `variant` for pin function +pub struct FunctionSio(PhantomData); +impl Function for FunctionSio {} +impl func_sealed::TypeLevelFunction for FunctionSio {} +impl func_sealed::Function for FunctionSio { + fn from(_f: DynFunction) -> Self { + FunctionSio(PhantomData) + } + fn as_dyn(&self) -> DynFunction { + DynFunction::Sio(C::DYN) + } +} +/// Alias to [`FunctionSio`] +pub type FunctionSioInput = FunctionSio; +/// Alias to [`FunctionSio`] +pub type FunctionSioOutput = FunctionSio; + +/// Type-level `enum` for SIO configuration. +pub trait SioConfig { + #[allow(missing_docs)] + const DYN: DynSioConfig; +} + +/// Type-level `variant` for SIO configuration +pub enum SioInput {} +impl SioConfig for SioInput { + #[allow(missing_docs)] + const DYN: DynSioConfig = DynSioConfig::Input; +} +/// Type-level `variant` for SIO configuration +pub enum SioOutput {} +impl SioConfig for SioOutput { + #[allow(missing_docs)] + const DYN: DynSioConfig = DynSioConfig::Output; +} + +// ============================= +// Pin to function mapping + +/// Error type for invalid function conversion. +pub struct InvalidFunction; + +/// Marker of valid pin -> function combination. +/// +/// Read as `F is a valid function implemented for the pin F` +pub trait ValidFunction: super::pin::PinId {} + +impl DynFunction { + pub(crate) fn is_valid(&self, id: &P) -> bool { + use DynBankId::*; + use DynFunction::*; + + let dyn_pin = id.as_dyn(); + match (self, dyn_pin.bank, dyn_pin.num) { + (Xip, Bank0, _) => false, + (Clock, _, 0..=19 | 26..=29) => false, + (_, Bank0, 0..=29) => true, + + (Xip | Sio(_), Qspi, 0..=5) => true, + (_, Qspi, 0..=5) => false, + + _ => unreachable!(), + } + } +} +macro_rules! pin_valid_func { + ($bank:ident as $prefix:ident, [$head:ident $(, $func:ident)*], [$($name:tt),+]) => { + pin_valid_func!($bank as $prefix, [$($func),*], [$($name),+]); + paste::paste!{$( + impl ValidFunction<[]> for super::pin::[<$bank:lower>]::[<$prefix $name>] {} + )+} + }; + ($bank:ident as $prefix:ident, [], [$($name:tt),+]) => {}; +} +pin_valid_func!( + bank0 as Gpio, + [Spi, Uart, I2c, Pwm, Pio0, Pio1, Usb, Null], + [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29 + ] +); +pin_valid_func!(bank0 as Gpio, [Clock], [20, 21, 22, 23, 24, 25]); +pin_valid_func!(qspi as Qspi, [Xip, Null], [Sclk, Sd0, Sd1, Sd2, Sd3, Ss]); + +macro_rules! pin_valid_func_sio { + ($bank:ident as $prefix:ident, [$($name:tt),+]) => { + paste::paste!{$( + impl ValidFunction> for super::pin::[<$bank:lower>]::[<$prefix $name>] {} + )+} + }; +} +pin_valid_func_sio!( + bank0 as Gpio, + [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29 + ] +); +pin_valid_func_sio!(qspi as Qspi, [Sclk, Sd0, Sd1, Sd2, Sd3, Ss]); diff --git a/rp2040-hal/src/gpio/mod.rs b/rp2040-hal/src/gpio/mod.rs index 2e0041d81..dc033f538 100644 --- a/rp2040-hal/src/gpio/mod.rs +++ b/rp2040-hal/src/gpio/mod.rs @@ -32,17 +32,33 @@ //! ``` //! See [examples/gpio_in_out.rs](https://github.com/rp-rs/rp-hal/tree/main/rp2040-hal/examples/gpio_in_out.rs) for a more practical example -// Based heavily on and in some places copied from `atsamd-hal` gpio::v2 -pub mod pin; -pub use pin::*; +// Design Notes: +// +// - The user must not be able to instantiate by themselves nor obtain an instance of the Type-level +// structure. +// - non-typestated features (overides, irq configuration, pads' output disable, pad's input +// enable, drive strength, schmitt, slew rate, sio's in sync bypass) are considered somewhat +// advanced usage of the pin (relative to reading/writing a gpio) and it is the responsibility of +// the user to make sure these are in a correct state when converting and passing the pin around. -pub mod dynpin; -pub use dynpin::*; +pub use embedded_hal::digital::v2::PinState; -mod reg; +use crate::{ + atomic_register_access::{write_bitmask_clear, write_bitmask_set}, + sio::Sio, + typelevel::{self, Sealed}, +}; + +mod func; +pub(crate) mod pin; +mod pull; + +pub use func::*; +pub use pin::{DynBankId, DynPinId, PinId}; +pub use pull::*; -#[derive(Clone, Copy, Eq, PartialEq, Debug)] /// The amount of current that a pin can drive when used as an output +#[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum OutputDriveStrength { /// 2 mA TwoMilliAmps, @@ -54,8 +70,8 @@ pub enum OutputDriveStrength { TwelveMilliAmps, } -#[derive(Clone, Copy, Eq, PartialEq, Debug)] /// The slew rate of a pin when used as an output +#[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum OutputSlewRate { /// Slew slow Slow, @@ -63,8 +79,8 @@ pub enum OutputSlewRate { Fast, } -#[derive(Clone, Copy, Eq, PartialEq, Debug)] /// Interrupt kind +#[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum Interrupt { /// While low LevelLow, @@ -75,9 +91,19 @@ pub enum Interrupt { /// On rising edge EdgeHigh, } +impl Interrupt { + fn mask(&self) -> u32 { + match self { + Interrupt::LevelLow => 0b0001, + Interrupt::LevelHigh => 0b0010, + Interrupt::EdgeLow => 0b0100, + Interrupt::EdgeHigh => 0b1000, + } + } +} -#[derive(Clone, Copy, Eq, PartialEq, Debug)] /// Interrupt override state. +#[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum InterruptOverride { /// Don't invert the interrupt. DontInvert = 0, @@ -89,8 +115,8 @@ pub enum InterruptOverride { AlwaysHigh = 3, } -#[derive(Clone, Copy, Eq, PartialEq, Debug)] /// Input override state. +#[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum InputOverride { /// Don't invert the peripheral input. DontInvert = 0, @@ -127,3 +153,1224 @@ pub enum OutputOverride { /// Drive output high. AlwaysHigh = 3, } + +/// Pin with type-state validation of function and pull mode. +pub struct Pin { + id: I, + function: F, + pull_type: M, +} + +/// Create a new pin instance. +/// +/// # Safety +/// The unicity of the pin is not verified. User must make sure no other instance of that specific +/// pin exists at the same time. +pub unsafe fn new_pin(id: DynPinId) -> Pin { + use pin::pin_sealed::PinIdOps; + use rp2040_pac::io_bank0::gpio::gpio_ctrl::FUNCSEL_A; + + let funcsel = id + .io_ctrl() + .read() + .funcsel() + .variant() + .expect("Invalid funcsel read from register."); + let function = match funcsel { + FUNCSEL_A::JTAG => DynFunction::Xip, + FUNCSEL_A::SPI => DynFunction::Spi, + FUNCSEL_A::UART => DynFunction::Uart, + FUNCSEL_A::I2C => DynFunction::I2c, + FUNCSEL_A::PWM => DynFunction::Pwm, + FUNCSEL_A::SIO => { + let mask = id.mask(); + let cfg = if id.sio_oe().read().bits() & mask == mask { + DynSioConfig::Output + } else { + DynSioConfig::Input + }; + DynFunction::Sio(cfg) + } + FUNCSEL_A::PIO0 => DynFunction::Pio0, + FUNCSEL_A::PIO1 => DynFunction::Pio1, + FUNCSEL_A::CLOCK => DynFunction::Clock, + FUNCSEL_A::USB => DynFunction::Usb, + FUNCSEL_A::NULL => DynFunction::Null, + }; + let pad = id.pad_ctrl().read(); + let pull_type = match (pad.pue().bit_is_set(), pad.pde().bit_is_set()) { + (true, true) => DynPullType::Both, + (true, false) => DynPullType::Up, + (false, true) => DynPullType::Down, + (false, false) => DynPullType::None, + }; + + Pin { + id, + function, + pull_type, + } +} + +impl Pin { + /// Pin ID. + pub fn id(&self) -> DynPinId { + self.id.as_dyn() + } + + /// # Safety + /// This method does not check if the pin is actually configured as the target function or pull + /// mode. This may lead to inconcistencies between the type-state and the actual state of the + /// pin's configuration. + pub unsafe fn into_unchecked(self) -> Pin { + Pin { + id: self.id, + function: F2::from(self.function.as_dyn()), + pull_type: M2::from(self.pull_type.as_dyn()), + } + } + + /// Convert the pin from one state to the other. + pub fn into(self) -> Pin + where + F2: func::Function, + M2: PullType, + I: func::ValidFunction, + { + self.into_function().into_pull_mode() + } + + /// Convert the pin function. + #[deprecated( + note = "Misleading name `mode` when it changes the `function`. Please use `into_function` instead.", + since = "0.9.0" + )] + pub fn into_mode(self) -> Pin + where + F2: func::Function, + I: func::ValidFunction, + { + self.into_function() + } + /// Convert the pin function. + pub fn into_function(self) -> Pin + where + F2: func::Function, + I: func::ValidFunction, + { + // Thanks to type-level validation, we know F2 is valid for I + let prev_function = self.function.as_dyn(); + let function = F2::from(prev_function); + let new_function = function.as_dyn(); + + if prev_function != new_function { + pin::set_function(&self.id, new_function); + } + + Pin { + function, + id: self.id, + pull_type: self.pull_type, + } + } + /// Convert the pin pull type. + pub fn into_pull_mode(self) -> Pin { + let prev_pull_type = self.pull_type.as_dyn(); + let pull_type = M2::from(prev_pull_type); + let new_pull_type = pull_type.as_dyn(); + + if prev_pull_type != new_pull_type { + pin::set_pull_type(&self.id, new_pull_type); + } + + Pin { + pull_type, + id: self.id, + function: self.function, + } + } + /// Erase the Pin ID type check. + pub fn into_dyn_pin(self) -> Pin { + Pin { + id: self.id.as_dyn(), + function: self.function, + pull_type: self.pull_type, + } + } + + /// Get the pin's pull type. + pub fn pull_type(&self) -> DynPullType { + self.pull_type.as_dyn() + } + + // ========================================== + // Typical pin conversions. + + /// Disable the pin and set it to float + #[inline] + pub fn into_floating_disabled(self) -> Pin + where + I: ValidFunction, + { + self.into() + } + + /// Disable the pin and set it to pull down + #[inline] + pub fn into_pull_down_disabled(self) -> Pin + where + I: ValidFunction, + { + self.into() + } + + /// Disable the pin and set it to pull up + #[inline] + pub fn into_pull_up_disabled(self) -> Pin + where + I: ValidFunction, + { + self.into() + } + + /// Configure the pin to operate as a floating input + #[inline] + pub fn into_floating_input(self) -> Pin, PullNone> + where + I: ValidFunction>, + { + self.into_function().into_pull_mode() + } + /// Configure the pin to operate as a pulled down input + #[inline] + pub fn into_pull_down_input(self) -> Pin, PullDown> + where + I: ValidFunction>, + { + self.into_function().into_pull_mode() + } + /// Configure the pin to operate as a pulled up input + #[inline] + pub fn into_pull_up_input(self) -> Pin, PullUp> + where + I: ValidFunction>, + { + self.into_function().into_pull_mode() + } + /// Configure the pin to operate as a bus keep input + #[inline] + pub fn into_bus_keep_input(self) -> Pin, PullBoth> + where + I: ValidFunction>, + { + self.into_function().into_pull_mode() + } + + /// Configure the pin to operate as a push-pull output. + /// + /// If you want to specify the initial pin state, use [`Pin::into_push_pull_output_in_state`]. + #[inline] + pub fn into_push_pull_output(self) -> Pin, M> + where + I: ValidFunction>, + { + self.into_function() + } + /// Configure the pin to operate as a push-pull output, specifying an initial + /// state which is applied immediately. + #[inline] + pub fn into_push_pull_output_in_state( + mut self, + state: PinState, + ) -> Pin, M> + where + I: ValidFunction>, + { + match state { + PinState::High => self._set_high(), + PinState::Low => self._set_low(), + } + self.into_push_pull_output() + } + + /// Configure the pin to operate as a readable push pull output. + /// + /// If you want to specify the initial pin state, use [`Pin::into_readable_output_in_state`]. + #[inline] + #[deprecated(note = "All gpio are readable, use `.into_push_pull_output()` instead.")] + pub fn into_readable_output(self) -> Pin, M> + where + I: ValidFunction>, + { + self.into_function() + } + /// Configure the pin to operate as a readable push pull output, specifying an initial + /// state which is applied immediately. + #[inline] + #[deprecated(note = "All gpio are readable, use `.into_push_pull_output_in_state()` instead.")] + pub fn into_readable_output_in_state(self, state: PinState) -> Pin, M> + where + I: ValidFunction>, + { + self.into_push_pull_output_in_state(state) + } + + // ========================================== + // methods available for all pins. + + // =================================== + // Pad related methods + + /// Get the current drive strength of the pin. + #[inline] + pub fn get_drive_strength(&self) -> OutputDriveStrength { + use pac::pads_bank0::gpio::DRIVE_A; + match self.id.pad_ctrl().read().drive().variant() { + DRIVE_A::_2MA => OutputDriveStrength::TwoMilliAmps, + DRIVE_A::_4MA => OutputDriveStrength::FourMilliAmps, + DRIVE_A::_8MA => OutputDriveStrength::EightMilliAmps, + DRIVE_A::_12MA => OutputDriveStrength::TwelveMilliAmps, + } + } + + /// Set the drive strength for the pin. + #[inline] + pub fn set_drive_strength(&mut self, strength: OutputDriveStrength) { + use pac::pads_bank0::gpio::DRIVE_A; + let variant = match strength { + OutputDriveStrength::TwoMilliAmps => DRIVE_A::_2MA, + OutputDriveStrength::FourMilliAmps => DRIVE_A::_4MA, + OutputDriveStrength::EightMilliAmps => DRIVE_A::_8MA, + OutputDriveStrength::TwelveMilliAmps => DRIVE_A::_12MA, + }; + self.id.pad_ctrl().modify(|_, w| w.drive().variant(variant)) + } + + /// Get the slew rate for the pin. + #[inline] + pub fn get_slew_rate(&self) -> OutputSlewRate { + if self.id.pad_ctrl().read().slewfast().bit_is_set() { + OutputSlewRate::Fast + } else { + OutputSlewRate::Slow + } + } + + /// Set the slew rate for the pin. + #[inline] + pub fn set_slew_rate(&mut self, rate: OutputSlewRate) { + self.id + .pad_ctrl() + .modify(|_, w| w.slewfast().bit(OutputSlewRate::Fast == rate)); + } + + /// Get wether the schmitt trigger (hysteresis) is enabled. + #[inline] + pub fn get_schimtt_enabled(&self) -> bool { + self.id.pad_ctrl().read().schmitt().bit_is_set() + } + + /// Enable/Disable the schmitt trigger. + #[inline] + pub fn set_schmitt_enabled(&self, enable: bool) { + self.id.pad_ctrl().modify(|_, w| w.schmitt().bit(enable)); + } + + /// Get the state of the digital output circuitery of the pad. + #[inline] + pub fn get_output_disable(&mut self) -> bool { + self.id.pad_ctrl().read().od().bit_is_set() + } + + /// Set the digital output circuitery of the pad. + #[inline] + pub fn set_output_disable(&mut self, disable: bool) { + self.id.pad_ctrl().modify(|_, w| w.od().bit(disable)); + } + + /// Get the state of the digital input circuitery of the pad. + #[inline] + pub fn get_input_enable(&mut self) -> bool { + self.id.pad_ctrl().read().ie().bit_is_set() + } + + /// Set the digital input circuitery of the pad. + #[inline] + pub fn set_input_enable(&mut self, enable: bool) { + self.id.pad_ctrl().modify(|_, w| w.ie().bit(enable)); + } + + // =================================== + // IO related methods + + /// Set the input override. + #[inline] + pub fn set_input_override(&mut self, override_value: InputOverride) { + use pac::io_bank0::gpio::gpio_ctrl::INOVER_A; + let variant = match override_value { + InputOverride::DontInvert => INOVER_A::NORMAL, + InputOverride::Invert => INOVER_A::INVERT, + InputOverride::AlwaysLow => INOVER_A::LOW, + InputOverride::AlwaysHigh => INOVER_A::HIGH, + }; + self.id.io_ctrl().modify(|_, w| w.inover().variant(variant)); + } + + /// Set the output enable override. + #[inline] + pub fn set_output_enable_override(&mut self, override_value: OutputEnableOverride) { + use pac::io_bank0::gpio::gpio_ctrl::OEOVER_A; + let variant = match override_value { + OutputEnableOverride::DontInvert => OEOVER_A::NORMAL, + OutputEnableOverride::Invert => OEOVER_A::INVERT, + OutputEnableOverride::Disable => OEOVER_A::DISABLE, + OutputEnableOverride::Enable => OEOVER_A::ENABLE, + }; + self.id.io_ctrl().modify(|_, w| w.oeover().variant(variant)); + } + + /// Set the output override. + #[inline] + pub fn set_output_override(&mut self, override_value: OutputOverride) { + use pac::io_bank0::gpio::gpio_ctrl::OUTOVER_A; + let variant = match override_value { + OutputOverride::DontInvert => OUTOVER_A::NORMAL, + OutputOverride::Invert => OUTOVER_A::INVERT, + OutputOverride::AlwaysLow => OUTOVER_A::LOW, + OutputOverride::AlwaysHigh => OUTOVER_A::HIGH, + }; + self.id + .io_ctrl() + .modify(|_, w| w.outover().variant(variant)); + } + + /// Set the interrupt override. + #[inline] + pub fn set_interrupt_override(&mut self, override_value: InterruptOverride) { + use pac::io_bank0::gpio::gpio_ctrl::IRQOVER_A; + let variant = match override_value { + InterruptOverride::DontInvert => IRQOVER_A::NORMAL, + InterruptOverride::Invert => IRQOVER_A::INVERT, + InterruptOverride::AlwaysLow => IRQOVER_A::LOW, + InterruptOverride::AlwaysHigh => IRQOVER_A::HIGH, + }; + self.id + .io_ctrl() + .modify(|_, w| w.irqover().variant(variant)); + } + + // =================================== + // SIO related methods + + #[inline] + #[allow(clippy::bool_comparison)] // more explicit this way + pub(crate) fn _is_low(&self) -> bool { + let mask = self.id.mask(); + self.id.sio_in().read().bits() & mask == 0 + } + + #[inline] + #[allow(clippy::bool_comparison)] // more explicit this way + pub(crate) fn _is_high(&self) -> bool { + !self._is_low() + } + + #[inline] + pub(crate) fn _set_low(&mut self) { + let mask = self.id.mask(); + self.id + .sio_out_clr() + .write(|w| unsafe { w.gpio_out_clr().bits(mask) }); + } + + #[inline] + pub(crate) fn _set_high(&mut self) { + let mask = self.id.mask(); + self.id + .sio_out_set() + .write(|w| unsafe { w.gpio_out_set().bits(mask) }); + } + + #[inline] + pub(crate) fn _toggle(&mut self) { + let mask = self.id.mask(); + self.id + .sio_out_xor() + .write(|w| unsafe { w.gpio_out_xor().bits(mask) }); + } + + #[inline] + pub(crate) fn _is_set_low(&self) -> bool { + let mask = self.id.mask(); + self.id.sio_out().read().bits() & mask == 0 + } + + #[inline] + pub(crate) fn _is_set_high(&self) -> bool { + !self._is_set_low() + } + + // =================================== + // Interrupt related methods + + /// Clear interrupt. + #[inline] + pub fn clear_interrupt(&mut self, interrupt: Interrupt) { + let (reg, offset) = self.id.intr(); + let mask = interrupt.mask(); + reg.write(|w| unsafe { w.bits(mask << offset) }); + } + + /// Interrupt status. + #[inline] + pub fn interrupt_status(&self, interrupt: Interrupt) -> bool { + let (reg, offset) = self.id.proc_ints(Sio::core()); + let mask = interrupt.mask(); + (reg.read().bits() >> offset) & mask == mask + } + + /// Is interrupt enabled. + #[inline] + pub fn is_interrupt_enabled(&self, interrupt: Interrupt) -> bool { + let (reg, offset) = self.id.proc_inte(Sio::core()); + let mask = interrupt.mask(); + (reg.read().bits() >> offset) & mask == mask + } + + /// Enable or disable interrupt. + #[inline] + pub fn set_interrupt_enabled(&self, interrupt: Interrupt, enabled: bool) { + let (reg, offset) = self.id.proc_inte(Sio::core()); + let mask = interrupt.mask(); + unsafe { + if enabled { + write_bitmask_set(reg.as_ptr(), mask << offset); + } else { + write_bitmask_clear(reg.as_ptr(), mask << offset); + } + } + } + + /// Is interrupt forced. + #[inline] + pub fn is_interrupt_forced(&self, interrupt: Interrupt) -> bool { + let (reg, offset) = self.id.proc_intf(Sio::core()); + let mask = interrupt.mask(); + (reg.read().bits() >> offset) & mask == mask + } + + /// Force or release interrupt. + #[inline] + pub fn set_interrupt_forced(&self, interrupt: Interrupt, forced: bool) { + let (reg, offset) = self.id.proc_intf(Sio::core()); + let mask = interrupt.mask(); + unsafe { + if forced { + write_bitmask_set(reg.as_ptr(), mask << offset); + } else { + write_bitmask_clear(reg.as_ptr(), mask << offset); + } + } + } + + /// Dormant wake status. + #[inline] + pub fn dormant_wake_status(&self, interrupt: Interrupt) -> bool { + let (reg, offset) = self.id.dormant_wake_ints(); + let mask = interrupt.mask(); + (reg.read().bits() >> offset) & mask == mask + } + + /// Is dormant wake enabled. + #[inline] + pub fn is_dormant_wake_enabled(&self, interrupt: Interrupt) -> bool { + let (reg, offset) = self.id.dormant_wake_inte(); + let mask = interrupt.mask(); + (reg.read().bits() >> offset) & mask == mask + } + + /// Enable or disable dormant wake. + #[inline] + pub fn set_dormant_wake_enabled(&self, interrupt: Interrupt, enabled: bool) { + let (reg, offset) = self.id.dormant_wake_inte(); + let mask = interrupt.mask(); + unsafe { + if enabled { + write_bitmask_set(reg.as_ptr(), mask << offset); + } else { + write_bitmask_clear(reg.as_ptr(), mask << offset); + } + } + } + + /// Is dormant wake forced. + #[inline] + pub fn is_dormant_wake_forced(&self, interrupt: Interrupt) -> bool { + let (reg, offset) = self.id.dormant_wake_intf(); + let mask = interrupt.mask(); + (reg.read().bits() >> offset) & mask == mask + } + + /// Force dormant wake. + #[inline] + pub fn set_dormant_wake_forced(&mut self, interrupt: Interrupt, forced: bool) { + let (reg, offset) = self.id.dormant_wake_intf(); + let mask = interrupt.mask(); + unsafe { + if forced { + write_bitmask_set(reg.as_ptr(), mask << offset); + } else { + write_bitmask_clear(reg.as_ptr(), mask << offset); + } + } + } +} +impl Pin { + /// Try to return to a type-checked pin id. + /// + /// This method may fail if the target pin id differs from the current dynamic one. + pub fn try_into_pin(self) -> Result, Self> { + if P::ID == self.id { + Ok(Pin { + id: P::new(), + function: self.function, + pull_type: self.pull_type, + }) + } else { + Err(self) + } + } +} +impl Pin { + /// Try to set the pin's function. + /// + /// This method may fail if the requested function is not supported by the pin, eg `FunctionXiP` + /// on a gpio from `Bank0`. + pub fn try_set_function(&mut self, function: DynFunction) -> Result<(), func::InvalidFunction> { + use func::func_sealed::Function; + if !function.is_valid(&self.id) { + return Err(func::InvalidFunction); + } else if function != self.function.as_dyn() { + pin::set_function(&self.id, function); + self.function = function; + } + Ok(()) + } + /// Gets the pin's function. + pub fn function(&self) -> DynFunction { + use func::func_sealed::Function; + self.function.as_dyn() + } +} + +impl Pin { + /// Set the pin's pull type. + pub fn set_pull_type(&mut self, pull_type: DynPullType) { + if pull_type != self.pull_type { + pin::set_pull_type(&self.id, pull_type); + self.pull_type = pull_type; + } + } +} + +//============================================================================== +// Embedded-HAL +//============================================================================== + +/// GPIO error type. +pub type Error = core::convert::Infallible; + +impl embedded_hal::digital::v2::OutputPin for Pin, M> +where + I: PinId, + M: PullType, +{ + type Error = Error; + + fn set_low(&mut self) -> Result<(), Self::Error> { + self._set_low(); + Ok(()) + } + + fn set_high(&mut self) -> Result<(), Self::Error> { + self._set_high(); + Ok(()) + } +} + +impl embedded_hal::digital::v2::InputPin for Pin, M> +where + I: PinId, + M: PullType, +{ + type Error = Error; + + fn is_high(&self) -> Result { + Ok(self._is_high()) + } + + fn is_low(&self) -> Result { + Ok(self._is_low()) + } +} + +impl embedded_hal::digital::v2::StatefulOutputPin for Pin, M> +where + I: PinId, + M: PullType, +{ + fn is_set_high(&self) -> Result { + Ok(self._is_set_high()) + } + + fn is_set_low(&self) -> Result { + Ok(self._is_set_low()) + } +} + +impl embedded_hal::digital::v2::ToggleableOutputPin for Pin, M> +where + I: PinId, + M: PullType, +{ + type Error = Error; + + fn toggle(&mut self) -> Result<(), Self::Error> { + self._toggle(); + Ok(()) + } +} +impl embedded_hal::digital::v2::InputPin for Pin, M> +where + I: PinId, + M: PullType, +{ + type Error = Error; + + fn is_high(&self) -> Result { + Ok(self._is_high()) + } + + fn is_low(&self) -> Result { + Ok(self._is_low()) + } +} + +//============================================================================== +// Pins +//============================================================================== + +/// Default type state of a pin after reset of the pads, io and sio. +pub trait DefaultTypeState: crate::typelevel::Sealed { + /// Default function. + type Function: Function; + /// Default pull type. + type PullType: PullType; +} + +macro_rules! gpio { + ( $bank:ident:$prefix:ident, [ $(($id:expr, $pull_mode:ident, $func:ident)),* ] ) => { + paste::paste!{ + #[doc = "Pin bank " [<$bank>] ] + pub mod [<$bank:snake>] { + use crate::sio::[]; + use pac::{[],[]}; + use super::{Pin, pin, pull, func}; + $(pub use super::pin::[<$bank:lower>]::[<$prefix $id>];)* + + $( + impl super::DefaultTypeState for [<$prefix $id>] { + type Function = super::[]; + type PullType = super::[]; + } + )* + gpio!(struct: $bank $prefix $([<$prefix $id>], $id, $func, $pull_mode),*); + + impl Pins { + /// Take ownership of the PAC peripherals and SIO slice and split it into discrete [`Pin`]s + pub fn new(io : [], pads: [], sio: [], reset : &mut pac::RESETS) -> Self { + use crate::resets::SubsystemReset; + pads.reset_bring_down(reset); + io.reset_bring_down(reset); + + { + use $crate::gpio::pin::DynBankId; + // SAFETY: this function owns the whole bank that will be affected. + let sio = unsafe { &*pac::SIO::PTR }; + if DynBankId::$bank == DynBankId::Bank0 { + sio.gpio_oe.reset(); + sio.gpio_out.reset(); + } else { + sio.gpio_hi_oe.reset(); + sio.gpio_hi_out.reset(); + } + } + + io.reset_bring_up(reset); + pads.reset_bring_up(reset); + gpio!(members: io, pads, sio, $(([<$prefix $id>], $func, $pull_mode)),+) + } + } + } + } + }; + (struct: $bank:ident $prefix:ident $($PXi:ident, $id:expr, $func:ident, $pull_mode:ident),*) => { + paste::paste!{ + /// Collection of all the individual [`Pin`]s + pub struct Pins { + _io: [], + _pads: [], + _sio: [], + $( + #[doc = "Pin " [<$PXi>] ] + pub [<$PXi:snake>]: Pin]::[<$prefix $id>] , func::[], pull::[]>, + )* + } + } + }; + (members: $io:ident, $pads:ident, $sio:ident, $(($PXi:ident, $func:ident, $pull_mode:ident)),+) => { + paste::paste!{ + Self { + _io: $io, + _pads: $pads, + _sio: $sio, + $( + [<$PXi:snake>]: Pin { + id: [<$PXi>] (()), + function: func::[] (()), + pull_type: pull::[] (()) + }, + )+ + } + } + }; +} + +gpio!( + Bank0: Gpio, + [ + (0, Down, Null), + (1, Down, Null), + (2, Down, Null), + (3, Down, Null), + (4, Down, Null), + (5, Down, Null), + (6, Down, Null), + (7, Down, Null), + (8, Down, Null), + (9, Down, Null), + (10, Down, Null), + (11, Down, Null), + (12, Down, Null), + (13, Down, Null), + (14, Down, Null), + (15, Down, Null), + (16, Down, Null), + (17, Down, Null), + (18, Down, Null), + (19, Down, Null), + (20, Down, Null), + (21, Down, Null), + (22, Down, Null), + (23, Down, Null), + (24, Down, Null), + (25, Down, Null), + (26, Down, Null), + (27, Down, Null), + (28, Down, Null), + (29, Down, Null) + ] +); + +gpio!( + Qspi: Qspi, + [ + (Sclk, Down, Null), + (Ss, Up, Null), + (Sd0, None, Null), + (Sd1, None, Null), + (Sd2, None, Null), + (Sd3, None, Null) + ] +); + +pub use bank0::Pins; + +//============================================================================== +// AnyPin +//============================================================================== + +/// Type class for [`Pin`] types +/// +/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for +/// [`Pin`] types. See the `AnyKind` documentation for more details on the +/// pattern. +/// +/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern +/// [type class]: crate::typelevel#type-classes +pub trait AnyPin: Sealed +where + Self: typelevel::Sealed, + Self: typelevel::Is>, + ::Id: ValidFunction<::Function>, +{ + /// [`PinId`] of the corresponding [`Pin`] + type Id: PinId; + /// [`func::Function`] of the corresponding [`Pin`] + type Function: func::Function; + /// [`PullType`] of the corresponding [`Pin`] + type Mode: PullType; +} + +impl Sealed for Pin +where + I: PinId + func::ValidFunction, + F: func::Function, + M: PullType, +{ +} + +impl AnyPin for Pin +where + I: func::ValidFunction, + F: func::Function, + M: PullType, +{ + type Id = I; + type Function = F; + type Mode = M; +} + +/// Type alias to recover the specific [`Pin`] type from an implementation of +/// [`AnyPin`] +/// +/// See the [`AnyKind`] documentation for more details on the pattern. +/// +/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern +pub type SpecificPin

= Pin<

::Id,

::Function,

::Mode>; + +//============================================================================== +// bsp_pins helper macro +//============================================================================== + +/// Helper macro to give meaningful names to GPIO pins +/// +/// The normal [`Pins`] struct names each [`Pin`] according to its [`PinId`]. +/// However, BSP authors would prefer to name each [`Pin`] according to its +/// function. This macro defines a new `Pins` struct with custom field names +/// for each [`Pin`], and it defines type aliases and constants to make it +/// easier to work with the [`Pin`]s and [`DynPin`](super::DynPin)s. +/// +/// When specifying pin aliases, be sure to use a [`PinMode`]. See +/// [here](self#types) for a list of the available [`PinMode`] type aliases. +/// +/// # Example +/// Calling the macro like this: +/// ```rust +/// use rp2040_hal::bsp_pins; +/// bsp_pins! { +/// #[cfg(feature = "gpio")] +/// Gpio0 { +/// /// Doc gpio0 +/// name: gpio0, +/// aliases: { FunctionPio0, PullNone: PioPin } +/// }, +/// Gpio1 { +/// name: led, +/// aliases: { FunctionPwm, PullDown: LedPwm } +/// }, +/// } +/// ``` +/// +/// Is roughly equivalent to the following source code (excluding the docs strings below): +/// ``` +/// use ::rp2040_hal as hal; +/// use hal::gpio; +/// pub struct Pins { +/// /// Doc gpio0 +/// #[cfg(feature = "gpio")] +/// pub gpio0: gpio::Pin< +/// gpio::bank0::Gpio0, +/// ::Function, +/// ::PullType, +/// >, +/// pub led: gpio::Pin< +/// gpio::bank0::Gpio1, +/// ::Function, +/// ::PullType, +/// >, +/// } +/// impl Pins { +/// #[inline] +/// pub fn new( +/// io: hal::pac::IO_BANK0, +/// pads: hal::pac::PADS_BANK0, +/// sio: hal::sio::SioGpioBank0, +/// reset: &mut hal::pac::RESETS, +/// ) -> Self { +/// let mut pins = gpio::Pins::new(io, pads, sio, reset); +/// Self { +/// #[cfg(feature = "gpio")] +/// gpio0: pins.gpio0, +/// led: pins.gpio1, +/// } +/// } +/// } +/// pub type PioPin = gpio::Pin; +/// pub type LedPwm = gpio::Pin; +/// ``` +#[macro_export] +macro_rules! bsp_pins { + ( + $( + $( #[$id_cfg:meta] )* + $Id:ident { + $( #[$name_doc:meta] )* + name: $name:ident $(,)? + $( + aliases: { + $( + $( #[$alias_cfg:meta] )* + $Function:ty, $PullType:ident: $Alias:ident + ),+ + } + )? + } $(,)? + )+ + ) => { + $crate::paste::paste! { + + /// BSP replacement for the HAL + /// [`Pins`](rp2040_hal::gpio::Pins) type + /// + /// This type is intended to provide more meaningful names for the + /// given pins. + /// + /// To enable specific functions of the pins you can use the + /// [rp2040_hal::gpio::pin::Pin::into_mode] function with + /// one of: + /// - [rp2040_hal::gpio::FunctionI2C] + /// - [rp2040_hal::gpio::FunctionPwm] + /// - [rp2040_hal::gpio::FunctionSpi] + /// - [rp2040_hal::gpio::FunctionXip] + /// - [rp2040_hal::gpio::FunctionPio0] + /// - [rp2040_hal::gpio::FunctionPio1] + /// - [rp2040_hal::gpio::FunctionUart] + /// + /// like this: + ///```no_run + /// use rp2040_hal::{pac, gpio::{bank0::Gpio12, Pin, Pins, PushPullOutput}, sio::Sio}; + /// + /// let mut peripherals = pac::Peripherals::take().unwrap(); + /// let sio = Sio::new(peripherals.SIO); + /// let pins = Pins::new(peripherals.IO_BANK0,peripherals.PADS_BANK0,sio.gpio_bank0, &mut peripherals.RESETS); + /// + /// let _spi_sclk = pins.gpio2.into_function::(); + /// let _spi_mosi = pins.gpio3.into_function::(); + /// let _spi_miso = pins.gpio4.into_function::(); + ///``` + /// + /// **See also [rp2040_hal::gpio] for more in depth information about this**! + pub struct Pins { + $( + $( #[$id_cfg] )* + $( #[$name_doc] )* + pub $name: $crate::gpio::Pin< + $crate::gpio::bank0::$Id, + <$crate::gpio::bank0::$Id as $crate::gpio::DefaultTypeState>::Function, + <$crate::gpio::bank0::$Id as $crate::gpio::DefaultTypeState>::PullType, + >, + )+ + } + + impl Pins { + /// Take ownership of the PAC [`PORT`] and split it into + /// discrete [`Pin`]s. + /// + /// This struct serves as a replacement for the HAL [`Pins`] + /// struct. It is intended to provide more meaningful names for + /// each [`Pin`] in a BSP. Any [`Pin`] not defined by the BSP is + /// dropped. + /// + /// [`Pin`](rp2040_hal::gpio::Pin) + /// [`Pins`](rp2040_hal::gpio::Pins) + #[inline] + pub fn new(io : $crate::pac::IO_BANK0, pads: $crate::pac::PADS_BANK0, sio: $crate::sio::SioGpioBank0, reset : &mut $crate::pac::RESETS) -> Self { + let mut pins = $crate::gpio::Pins::new(io,pads,sio,reset); + Self { + $( + $( #[$id_cfg] )* + $name: pins.[<$Id:lower>], + )+ + } + } + } + $( + $( #[$id_cfg] )* + $crate::bsp_pins!(@aliases, $( $( $( #[$alias_cfg] )* $Id $Function $PullType $Alias )+ )? ); + )+ + } + }; + ( @aliases, $( $( $( #[$attr:meta] )* $Id:ident $Function:ident $PullType:ident $Alias:ident )+ )? ) => { + $crate::paste::paste! { + $( + $( + $( #[$attr] )* + /// Alias for a configured [`Pin`](rp2040_hal::gpio::Pin) + pub type $Alias = $crate::gpio::Pin< + $crate::gpio::bank0::$Id, + $crate::gpio::$Function, + $crate::gpio::$PullType + >; + )+ + )? + } + }; +} + +//============================================================================== +// InOutPin +//============================================================================== + +/// A wrapper for DynPin, implementing both InputPin and OutputPin, to simulate +/// an open-drain pin as needed by the wire protocol the DHT11 sensor speaks. +/// https://how2electronics.com/interfacing-dht11-temperature-humidity-sensor-with-raspberry-pi-pico/ +pub struct InOutPin { + inner: Pin, +} + +impl InOutPin { + /// Create a new wrapper + pub fn new(inner: T) -> InOutPin + where + T::Id: ValidFunction, + { + let mut inner = inner.into(); + inner.set_output_enable_override(OutputEnableOverride::Disable); + + // into Pin<_, FunctionSioOutput, _> + let inner = inner.into_push_pull_output_in_state(PinState::Low); + + Self { + inner: inner.into(), + } + } + + /// Releases the pin reverting to its previous function. + pub fn release(self) -> T { + let mut inner = self.inner.into(); + inner.set_output_enable_override(OutputEnableOverride::DontInvert); + T::from(inner) + } +} + +impl embedded_hal::digital::v2::InputPin for InOutPin { + type Error = Error; + fn is_high(&self) -> Result { + self.inner.is_high() + } + fn is_low(&self) -> Result { + self.inner.is_low() + } +} + +impl embedded_hal::digital::v2::OutputPin for InOutPin { + type Error = Error; + fn set_low(&mut self) -> Result<(), Error> { + // The pin is already set to output low but this is inhibited by the override. + self.inner + .set_output_enable_override(OutputEnableOverride::Enable); + Ok(()) + } + fn set_high(&mut self) -> Result<(), Error> { + // To set the open-drain pin to high, just disable the output driver by configuring the + // output override. That way, the DHT11 can still pull the data line down to send its response. + self.inner + .set_output_enable_override(OutputEnableOverride::Disable); + Ok(()) + } +} + +#[cfg(feature = "eh1_0_alpha")] +mod eh1 { + use eh1_0_alpha::digital::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin, ErrorType}; + + use super::{ FunctionSio, Pin, SioInput, SioOutput, PinId, PullType, Error, SioConfig}; + + impl ErrorType for Pin, M> + where + I: PinId, + M: PullType, + S: SioConfig + { + type Error = Error; + } + + impl OutputPin for Pin, M> + where + I: PinId, + M: PullType, + { + fn set_low(&mut self) -> Result<(),Self::Error> { + self._set_low(); + Ok(()) + } + + fn set_high(&mut self) -> Result<(),Self::Error> { + self._set_high(); + Ok(()) + } + } + + impl InputPin for Pin, M> + where + I: PinId, + M: PullType, + { + fn is_high(&self) -> Result { + Ok(self._is_high()) + } + + fn is_low(&self) -> Result { + Ok(self._is_low()) + } + } + + impl StatefulOutputPin for Pin, M> + where + I: PinId, + M: PullType, + { + fn is_set_high(&self) -> Result { + Ok(self._is_set_high()) + } + + fn is_set_low(&self) -> Result { + Ok(self._is_set_low()) + } + } + + impl ToggleableOutputPin for Pin, M> + where + I: PinId, + M: PullType, + { + fn toggle(&mut self) -> Result<(),Self::Error> { + self._toggle(); + Ok(()) + } + } + impl InputPin for Pin, M> + where + I: PinId, + M: PullType, + { + fn is_high(&self) -> Result { + Ok(self._is_high()) + } + + fn is_low(&self) -> Result { + Ok(self._is_low()) + } + } +} diff --git a/rp2040-hal/src/gpio/pin.rs b/rp2040-hal/src/gpio/pin.rs index ce00d9029..4f8042f3d 100644 --- a/rp2040-hal/src/gpio/pin.rs +++ b/rp2040-hal/src/gpio/pin.rs @@ -1,1275 +1,150 @@ -//! # Type-level module for GPIO pins +//! ## Note 1 //! -//! Based heavily on `atsamd-hal`. +//! QSPI registers are ordered differently on pads, io & SIO: +//! - PADS: sclk, sd0, sd1, sd2, sd3, ss +//! - IO bank: sclk, ss, sd0, sd1, sd2, sd3 +//! - SIO: sclk, ss, sd0, sd1, sd2, sd3 //! -//! This module provides a type-level API for GPIO pins. It uses the type system -//! to track the state of pins at compile-time. To do so, it uses traits to -//! represent [type classes] and types as instances of those type classes. For -//! example, the trait [`InputConfig`] acts as a [type-level enum] of the -//! available input configurations, and the types [`Floating`], [`PullDown`], -//! [`PullUp`] and [`BusKeep`] are its type-level variants. +//! This HAL will use the order shared by IO bank & SIO. The main reason for that being the bit +//! shift operation used in SIO and interrupt related registers. //! -//! When applied as a trait bound, a type-level enum restricts type parameters -//! to the corresponding variants. All of the traits in this module are closed, -//! using the `Sealed` trait pattern, so the type-level instances found in this -//! module are the only possible variants. +//! ## Note 2 //! -//! Type-level [`Pin`]s are parameterized by two type-level enums, [`PinId`] and -//! [`PinMode`]. +//! The SWD and SWCLK pin only appear on the pad control and cannot be used as gpio. +//! They are therefore absent from this implementation. //! -//! A `PinId` identifies a pin by it's group (BANK0 or QSPI) and pin number. Each -//! `PinId` instance is named according to its datasheet identifier, e.g. -//! [`Gpio0`](`bank0::Gpio0`). +//! ## Note 3 //! -//! A `PinMode` represents the various pin modes. The available `PinMode` -//! variants are [`Disabled`], [`Input`], [`Output`] and -//! [`Function`], each with its own corresponding configurations. +//! Dues to limitations in svd2rust and svdtools (and their shared dependencies) it is not possible +//! to fully express the relations between the gpio registers in the different banks on the RP2040 +//! at the PAC level. //! -//! It is not possible for users to create new instances of a [`Pin`]. Singleton -//! instances of each pin are made available to users through the [`Pins`] -//! struct. +//! These limitations are respectively: +//! - Inability to derive register with different reset values +//! - Inability to derive from path including clusters and/or arrays //! -//! To create the [`Pins`] struct, users must supply the PAC -//! [`IO_BANK0`](crate::pac::IO_BANK0) and [`PAD_BANK0`](crate::pac::PADS_BANK0) peripherals as well as the [SIO partition](crate::sio). -//! The [`Pins`] struct takes -//! ownership of the peripherals and provides the corresponding pins. Each [`Pin`] -//! within the [`Pins`] struct can be moved out and used individually. -//! -//! -//! ```no_run -//! # use rp2040_hal::{pac, gpio::Pins, sio::Sio}; -//! let mut peripherals = pac::Peripherals::take().unwrap(); -//! let sio = Sio::new(peripherals.SIO); -//! let pins = Pins::new(peripherals.IO_BANK0,peripherals.PADS_BANK0,sio.gpio_bank0, &mut peripherals.RESETS); -//! ``` -//! -//! Pins can be converted between modes using several different methods. -//! -//! ```no_run -//! # use rp2040_hal::{pac, gpio::{bank0::Gpio12, Pin, Pins, FloatingInput}, sio::Sio}; -//! # let mut peripherals = pac::Peripherals::take().unwrap(); -//! # let sio = Sio::new(peripherals.SIO); -//! # let pins = Pins::new(peripherals.IO_BANK0,peripherals.PADS_BANK0,sio.gpio_bank0, &mut peripherals.RESETS); -//! // Use one of the literal function names -//! let gpio12 = pins.gpio12.into_floating_input(); -//! // Use a generic method and one of the `PinMode` variant types -//! let gpio12 = gpio12.into_mode::(); -//! // Specify the target type and use `.into_mode()` -//! let gpio12: Pin = gpio12.into_mode(); -//! ``` -//! -//! # Embedded HAL traits -//! -//! This module implements all of the embedded HAL GPIO traits for each [`Pin`] -//! in the corresponding [`PinMode`]s, namely: [`InputPin`], [`OutputPin`], -//! [`ToggleableOutputPin`] and [`StatefulOutputPin`]. -//! -//! For example, you can control the logic level of an `OutputPin` like so -//! -//! ```no_run -//! use rp2040_hal::{pac, gpio::{bank0::Gpio12, Pin, Pins, PushPullOutput}, sio::Sio}; -//! use embedded_hal::digital::v2::OutputPin; -//! -//! let mut peripherals = pac::Peripherals::take().unwrap(); -//! let sio = Sio::new(peripherals.SIO); -//! let pins = Pins::new(peripherals.IO_BANK0,peripherals.PADS_BANK0,sio.gpio_bank0, &mut peripherals.RESETS); -//! -//! let mut pin12: Pin = pins.gpio12.into_mode(); -//! pin12.set_high(); -//! ``` -//! -//! # Type-level features -//! -//! This module also provides additional, type-level tools to work with GPIO -//! pins. -//! -//! The [`OptionalPinId`] and [`OptionalPin`] traits use the [`OptionalKind`] -//! pattern to act as type-level versions of [`Option`] for `PinId` and `Pin` -//! respectively. And the [`AnyPin`] trait defines an [`AnyKind`] type class -//! for all `Pin` types. -//! -//! [type classes]: crate::typelevel#type-classes -//! [type-level enum]: crate::typelevel#type-level-enum -//! [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -//! [`AnyKind`]: crate::typelevel#anykind-trait-pattern -use super::dynpin::{DynDisabled, DynInput, DynOutput, DynPinId, DynPinMode}; -use super::{ - InputOverride, Interrupt, InterruptOverride, OutputDriveStrength, OutputEnableOverride, - OutputOverride, OutputSlewRate, -}; -use crate::gpio::reg::RegisterInterface; -use crate::typelevel::{Is, NoneT, Sealed}; -use core::convert::Infallible; -use core::marker::PhantomData; - -use crate::gpio::dynpin::DynFunction; -#[cfg(feature = "eh1_0_alpha")] -use eh1_0_alpha::digital as eh1; -pub use embedded_hal::digital::v2::PinState; -use hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin}; - -/// Type-level marker for tracking which pin modes are valid for which pins -pub trait ValidPinMode: Sealed + PinMode {} - -//============================================================================== -// Disabled configurations -//============================================================================== - -/// Type-level `enum` for disabled configurations -pub trait DisabledConfig: Sealed { - /// Corresponding [`DynDisabled`](super::DynDisabled) - const DYN: DynDisabled; -} - -/// Type-level variant of both [`DisabledConfig`] and [`InputConfig`] -pub enum Floating {} -/// Type-level variant of both [`DisabledConfig`] and [`InputConfig`] -pub enum PullDown {} -/// Type-level variant of both [`DisabledConfig`] and [`InputConfig`] -pub enum PullUp {} -/// Type-level variant of both [`DisabledConfig`] and [`InputConfig`] -pub enum BusKeep {} - -impl Sealed for Floating {} -impl Sealed for PullDown {} -impl Sealed for PullUp {} -impl Sealed for BusKeep {} - -impl DisabledConfig for Floating { - const DYN: DynDisabled = DynDisabled::Floating; -} -impl DisabledConfig for PullDown { - const DYN: DynDisabled = DynDisabled::PullDown; -} -impl DisabledConfig for PullUp { - const DYN: DynDisabled = DynDisabled::PullUp; -} -impl DisabledConfig for BusKeep { - const DYN: DynDisabled = DynDisabled::BusKeep; -} - -/// Type-level variant of [`PinMode`] for disabled modes -/// -/// Type `C` is one of four configurations: [`Floating`], [`PullDown`], -/// [`PullUp`] or [`BusKeep`] -pub struct Disabled { - cfg: PhantomData, -} - -impl Sealed for Disabled {} - -/// Type-level variant of [`PinMode`] for floating disabled mode -pub type FloatingDisabled = Disabled; - -/// Type-level variant of [`PinMode`] for pull-down disabled mode -pub type PullDownDisabled = Disabled; - -/// Type-level variant of [`PinMode`] for pull-up disabled mode -pub type PullUpDisabled = Disabled; - -/// Type-level variant of [`PinMode`] for bus keep disabled mode -pub type BusKeepDisabled = Disabled; - -impl ValidPinMode for Disabled {} - -//============================================================================== -// Input configurations -//============================================================================== - -/// Type-level `enum` for input configurations -pub trait InputConfig: Sealed { - /// Corresponding [`DynInput`](super::DynInput) - const DYN: DynInput; -} - -impl InputConfig for Floating { - const DYN: DynInput = DynInput::Floating; -} -impl InputConfig for PullDown { - const DYN: DynInput = DynInput::PullDown; -} -impl InputConfig for PullUp { - const DYN: DynInput = DynInput::PullUp; -} -impl InputConfig for BusKeep { - const DYN: DynInput = DynInput::BusKeep; -} - -/// Type-level variant of [`PinMode`] for input modes -/// -/// Type `C` is one of four input configurations: [`Floating`], [`PullDown`], -/// [`PullUp`] or [`BusKeep`] -pub struct Input { - cfg: PhantomData, -} - -impl Sealed for Input {} - -/// Type-level variant of [`PinMode`] for floating input mode -pub type FloatingInput = Input; - -/// Type-level variant of [`PinMode`] for pull-down input mode -pub type PullDownInput = Input; - -/// Type-level variant of [`PinMode`] for pull-up input mode -pub type PullUpInput = Input; - -/// Type-level variant of [`PinMode`] for bus keep input mode -pub type BusKeepInput = Input; - -impl ValidPinMode for Input {} - -//============================================================================== -// Output configurations -//============================================================================== - -/// Type-level `enum` for output configurations -pub trait OutputConfig: Sealed { - /// Corresponding [`DynOutput`](super::DynOutput) - const DYN: DynOutput; -} - -/// Type-level variant of [`OutputConfig`] for a push-pull configuration -pub enum PushPull {} -/// Type-level variant of [`OutputConfig`] for a readable push-pull -/// configuration -pub enum Readable {} - -impl Sealed for PushPull {} -impl Sealed for Readable {} - -impl OutputConfig for PushPull { - const DYN: DynOutput = DynOutput::PushPull; -} -impl OutputConfig for Readable { - const DYN: DynOutput = DynOutput::Readable; -} - -/// Type-level variant of [`PinMode`] for output modes -/// -/// Type `C` is one of two output configurations: [`PushPull`] or [`Readable`] -pub struct Output { - cfg: PhantomData, -} - -impl Sealed for Output {} - -/// Type-level variant of [`PinMode`] for push-pull output mode -pub type PushPullOutput = Output; - -/// Type-level variant of [`PinMode`] for readable push-pull output mode -pub type ReadableOutput = Output; - -impl ValidPinMode for Output {} - -// - -/// Type-level variant of [`PinMode`] for alternate peripheral functions -/// -/// Type `C` is an [`FunctionConfig`] -pub struct Function { - cfg: PhantomData, -} - -impl Sealed for Function {} - -/// Type-level enum for alternate peripheral function configurations -pub trait FunctionConfig: Sealed { - /// Corresponding [`DynFunction`](super::DynFunction) - const DYN: DynFunction; -} - -macro_rules! function { - ( - $( - $Func:ident - ),+ - ) => { - $crate::paste::paste! { - $( - #[ - doc = "Type-level variant of [`FunctionConfig`] for \ - alternate peripheral function " $Func - ] - pub enum $Func {} - impl Sealed for $Func {} - impl FunctionConfig for $Func { - const DYN: DynFunction = DynFunction::$Func; - } - #[ - doc = "Type-level variant of [`PinMode`] for alternate \ - peripheral function [`" $Func "`]" - ] - pub type [] = Function<$Func>; - )+ - } - }; -} - -function!(Spi, Xip, Uart, I2C, Pwm, Clock, UsbAux); - -impl Sealed for pac::PIO0 {} -impl FunctionConfig for pac::PIO0 { - const DYN: DynFunction = DynFunction::Pio0; -} -/// Type-level variant of [`PinMode`] for alternate peripheral function `pac::PIO0` -pub type FunctionPio0 = Function; - -impl Sealed for pac::PIO1 {} -impl FunctionConfig for pac::PIO1 { - const DYN: DynFunction = DynFunction::Pio1; -} -/// Type-level variant of [`PinMode`] for alternate peripheral function `pac::PIO1` -pub type FunctionPio1 = Function; - -//============================================================================== -// Pin modes -//============================================================================== - -/// Type-level `enum` representing pin modes -pub trait PinMode: Sealed + Sized { - /// Corresponding [`DynPinMode`](super::DynPinMode) - const DYN: DynPinMode; -} - -impl PinMode for Disabled { - const DYN: DynPinMode = DynPinMode::Disabled(C::DYN); -} - -impl PinMode for Input { - const DYN: DynPinMode = DynPinMode::Input(C::DYN); -} - -impl PinMode for Output { - const DYN: DynPinMode = DynPinMode::Output(C::DYN); -} - -impl PinMode for Function { - const DYN: DynPinMode = DynPinMode::Function(C::DYN); -} - -//============================================================================== -// Pin IDs -//============================================================================== - -/// Type-level `enum` for pin IDs -pub trait PinId: Sealed { - /// Corresponding [`DynPinId`](super::DynPinId) - const DYN: DynPinId; - /// [`PinMode`] at reset - type Reset; -} - -macro_rules! pin_id { - ($Group:ident, $Id:ident, $NUM:literal, $reset : ident) => { - #[doc = "Pin ID representing pin "] - pub enum $Id {} - impl Sealed for $Id {} - impl PinId for $Id { - type Reset = $reset; - const DYN: DynPinId = DynPinId { - group: DynGroup::$Group, - num: $NUM, - }; - } - }; -} - -//============================================================================== -// OptionalPinId -//============================================================================== - -/// Type-level equivalent of `Option` -/// -/// See the [`OptionalKind`] documentation for more details on the pattern. -/// -/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -pub trait OptionalPinId: Sealed {} - -impl OptionalPinId for NoneT {} - -impl OptionalPinId for I {} - -/// Type-level equivalent of `Some(PinId)` -/// -/// See the [`OptionalKind`] documentation for more details on the pattern. -/// -/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -pub trait SomePinId: OptionalPinId + PinId + Sealed {} - -impl SomePinId for I {} - -//============================================================================== -// Registers -//============================================================================== - -/// Provide a safe register interface for [`Pin`]s -/// -/// This `struct` takes ownership of a [`PinId`] and provides an API to -/// access the corresponding registers. -struct Registers { - id: PhantomData, -} - -// [`Registers`] takes ownership of the [`PinId`], and [`Pin`] guarantees that -// each pin is a singleton, so this implementation is safe. -unsafe impl RegisterInterface for Registers { - #[inline] - fn id(&self) -> DynPinId { - I::DYN - } -} - -impl Registers { - /// Create a new instance of [`Registers`] - /// - /// # Safety - /// - /// Users must never create two simultaneous instances of this `struct` with - /// the same [`PinId`] - #[inline] - unsafe fn new() -> Self { - Registers { id: PhantomData } - } - - /// Provide a type-level equivalent for the - /// [`RegisterInterface::change_mode`] method. - #[inline] - fn change_mode>(&mut self) { - RegisterInterface::do_change_mode(self, M::DYN); - } -} - -//============================================================================== -// Pin -//============================================================================== - -/// A type-level GPIO pin, parameterized by [`PinId`] and [`PinMode`] types -pub struct Pin -where - I: PinId, - M: ValidPinMode, -{ - regs: Registers, - mode: PhantomData, -} - -impl Pin -where - I: PinId, - M: ValidPinMode, -{ - /// Create a new [`Pin`] - /// - /// # Safety - /// - /// Each [`Pin`] must be a singleton. For a given [`PinId`], there must be - /// at most one corresponding [`Pin`] in existence at any given time. - /// Violating this requirement is `unsafe`. - #[inline] - pub(crate) unsafe fn new() -> Pin { - Pin { - regs: Registers::new(), - mode: PhantomData, - } - } - - /// Return the [`DynPinId`] corresponding to this pin. - /// - /// To get the numeric pin number, access the num field - /// directly: - /// - /// ```no_run - /// # use rp2040_hal::gpio::{Pin, PinId, PinMode, ValidPinMode}; - /// # fn get_id> (pin: Pin) -> u8 { - /// pin.id().num - /// # } - /// ```` - #[inline] - pub fn id(&self) -> DynPinId { - I::DYN - } - - /// Convert the pin to the requested [`PinMode`] - #[inline] - pub fn into_mode>(mut self) -> Pin { - if N::DYN != M::DYN { - self.regs.change_mode::(); - } - // Safe because we drop the existing Pin - unsafe { Pin::new() } - } - - /// Disable the pin and set it to float - #[inline] - pub fn into_floating_disabled(self) -> Pin { - self.into_mode() - } - - /// Disable the pin and set it to pull down - #[inline] - pub fn into_pull_down_disabled(self) -> Pin { - self.into_mode() - } - - /// Disable the pin and set it to pull up - #[inline] - pub fn into_pull_up_disabled(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a floating input - #[inline] - pub fn into_floating_input(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a pulled down input - #[inline] - pub fn into_pull_down_input(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a pulled up input - #[inline] - pub fn into_pull_up_input(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a bus keep input - #[inline] - pub fn into_bus_keep_input(self) -> Pin { - self.into_mode() - } +//! This modules bridges that gap by adding a trait definition per register type and implementing it +//! for each of the relevant registers. - /// Configure the pin to operate as a push-pull output. - /// - /// If you want to specify the initial pin state, use [`Pin::into_push_pull_output_in_state`]. - #[inline] - pub fn into_push_pull_output(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a push-pull output, specifying an initial - /// state which is applied immediately. - #[inline] - pub fn into_push_pull_output_in_state(mut self, state: PinState) -> Pin { - match state { - PinState::High => self._set_high(), - PinState::Low => self._set_low(), - } - self.into_mode() - } - - /// Configure the pin to operate as a readable push pull output. - /// - /// If you want to specify the initial pin state, use [`Pin::into_readable_output_in_state`]. - #[inline] - pub fn into_readable_output(self) -> Pin { - self.into_mode() - } - - /// Configure the pin to operate as a readable push pull output, specifying an initial - /// state which is applied immediately. - #[inline] - pub fn into_readable_output_in_state(mut self, state: PinState) -> Pin { - match state { - PinState::High => self._set_high(), - PinState::Low => self._set_low(), - } - self.into_mode() - } - - /// Read the current drive strength of the pin. - #[inline] - pub fn get_drive_strength(&self) -> OutputDriveStrength { - self.regs.read_drive_strength() - } - - /// Set the drive strength for the pin. - #[inline] - pub fn set_drive_strength(&mut self, strength: OutputDriveStrength) { - self.regs.write_drive_strength(strength); - } - - /// Get the slew rate for the pin. - #[inline] - pub fn get_slew_rate(&self) -> OutputSlewRate { - self.regs.read_slew_rate() - } - - /// Set the slew rate for the pin. - #[inline] - pub fn set_slew_rate(&mut self, rate: OutputSlewRate) { - self.regs.write_slew_rate(rate) - } - - /// Clear interrupt. - #[inline] - pub fn clear_interrupt(&mut self, interrupt: Interrupt) { - self.regs.clear_interrupt(interrupt); - } - - /// Interrupt status. - #[inline] - pub fn interrupt_status(&self, interrupt: Interrupt) -> bool { - self.regs.interrupt_status(interrupt) - } - - /// Is interrupt enabled. - #[inline] - pub fn is_interrupt_enabled(&self, interrupt: Interrupt) -> bool { - self.regs.is_interrupt_enabled(interrupt) - } - - /// Enable or disable interrupt. - #[inline] - pub fn set_interrupt_enabled(&self, interrupt: Interrupt, enabled: bool) { - self.regs.set_interrupt_enabled(interrupt, enabled); - } - - /// Is interrupt forced. - #[inline] - pub fn is_interrupt_forced(&self, interrupt: Interrupt) -> bool { - self.regs.is_interrupt_forced(interrupt) - } +use super::{DynFunction, DynPullType}; - /// Force or release interrupt. - #[inline] - pub fn set_interrupt_forced(&self, interrupt: Interrupt, forced: bool) { - self.regs.set_interrupt_forced(interrupt, forced); - } - - /// Set the interrupt override. - #[inline] - pub fn set_interrupt_override(&mut self, override_value: InterruptOverride) { - self.regs.set_interrupt_override(override_value); - } - - /// Set the input override. - #[inline] - pub fn set_input_override(&mut self, override_value: InputOverride) { - self.regs.set_input_override(override_value); - } - - /// Set the output enable override. - #[inline] - pub fn set_output_enable_override(&mut self, override_value: OutputEnableOverride) { - self.regs.set_output_enable_override(override_value); - } - - /// Set the output override. - #[inline] - pub fn set_output_override(&mut self, override_value: OutputOverride) { - self.regs.set_output_override(override_value); - } +pub(crate) mod pin_sealed; - #[inline] - #[allow(clippy::bool_comparison)] // more explicit this way - pub(crate) fn _is_low(&self) -> bool { - self.regs.read_pin() == false - } - - #[inline] - #[allow(clippy::bool_comparison)] // more explicit this way - pub(crate) fn _is_high(&self) -> bool { - self.regs.read_pin() == true - } - - #[inline] - pub(crate) fn _set_low(&mut self) { - self.regs.write_pin(false); - } - - #[inline] - pub(crate) fn _set_high(&mut self) { - self.regs.write_pin(true); - } - - #[inline] - pub(crate) fn _toggle(&mut self) { - self.regs.toggle_pin(); - } - - #[inline] - #[allow(clippy::bool_comparison)] // more explicit this way - pub(crate) fn _is_set_low(&self) -> bool { - self.regs.read_out_pin() == false - } - - #[inline] - #[allow(clippy::bool_comparison)] // more explicit this way - pub(crate) fn _is_set_high(&self) -> bool { - self.regs.read_out_pin() == true - } -} - -//============================================================================== -// AnyPin -//============================================================================== - -/// Type class for [`Pin`] types -/// -/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for -/// [`Pin`] types. See the `AnyKind` documentation for more details on the -/// pattern. -/// -/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern -/// [type class]: crate::typelevel#type-classes -pub trait AnyPin -where - Self: Sealed, - Self: Is>, - ::Mode: ValidPinMode<::Id>, -{ - /// [`PinId`] of the corresponding [`Pin`] - type Id: PinId; - /// [`PinMode`] of the corresponding [`Pin`] - type Mode: PinMode; -} - -impl Sealed for Pin -where - I: PinId, - M: ValidPinMode, -{ +/// Type-Level `enum` for the pin Id (pin number + bank). +pub trait PinId: pin_sealed::PinIdOps { + /// This pin as a `DynPinId`. + fn as_dyn(&self) -> DynPinId; } -impl AnyPin for Pin -where - I: PinId, - M: ValidPinMode, -{ - type Id = I; - type Mode = M; +/// Value-level `enum` for the pin's bank. +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum DynBankId { + /// GPIO Pins bank + Bank0, + /// QSPI Pins bank + Qspi, } -/// Type alias to recover the specific [`Pin`] type from an implementation of -/// [`AnyPin`] -/// -/// See the [`AnyKind`] documentation for more details on the pattern. -/// -/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern -pub type SpecificPin

= Pin<

::Id,

::Mode>; - -//============================================================================== -// Optional pins -//============================================================================== - -/// Type-level equivalent of `Option` -/// -/// See the [`OptionalKind`] documentation for more details on the pattern. -/// -/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -pub trait OptionalPin: Sealed { +/// Value-level representation for the pin (bank + id). +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct DynPinId { #[allow(missing_docs)] - type Id: OptionalPinId; + pub bank: DynBankId, #[allow(missing_docs)] - const IS_NONE: bool; -} - -impl OptionalPin for NoneT { - type Id = NoneT; - const IS_NONE: bool = true; -} - -impl OptionalPin for P { - type Id = P::Id; - /// Value-level translation of the Type-level equivalent of [`Option::is_none`]. - const IS_NONE: bool = false; -} - -/// Type-level equivalent of `Some(PinId)` -/// -/// See the [`OptionalKind`] documentation for more details on the pattern. -/// -/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern -pub trait SomePin: OptionalPin + Sealed { - /// Value-level translation of the Type-level equivalent of [`Option::is_some`]. - const IS_SOME: bool; -} -impl> SomePin for P { - const IS_SOME: bool = !P::IS_NONE; + pub num: u8, } - -//============================================================================== -// Embedded HAL traits -//============================================================================== - -impl OutputPin for Pin> -where - I: PinId, - C: OutputConfig, -{ - type Error = Infallible; - #[inline] - fn set_high(&mut self) -> Result<(), Self::Error> { - self._set_high(); - Ok(()) - } - #[inline] - fn set_low(&mut self) -> Result<(), Self::Error> { - self._set_low(); - Ok(()) - } -} - -impl InputPin for Pin -where - I: PinId, -{ - type Error = Infallible; - #[inline] - fn is_high(&self) -> Result { - Ok(self._is_high()) - } - #[inline] - fn is_low(&self) -> Result { - Ok(self._is_low()) +impl PinId for DynPinId { + fn as_dyn(&self) -> DynPinId { + *self } } -impl InputPin for Pin> -where - I: PinId, - C: InputConfig, -{ - type Error = Infallible; - #[inline] - fn is_high(&self) -> Result { - Ok(self._is_high()) - } - #[inline] - fn is_low(&self) -> Result { - Ok(self._is_low()) - } -} - -impl ToggleableOutputPin for Pin> -where - I: PinId, - C: OutputConfig, -{ - type Error = Infallible; - #[inline] - fn toggle(&mut self) -> Result<(), Self::Error> { - self._toggle(); - Ok(()) - } -} - -impl StatefulOutputPin for Pin> -where - I: PinId, - C: OutputConfig, -{ - #[inline] - fn is_set_high(&self) -> Result { - Ok(self._is_set_high()) - } - #[inline] - fn is_set_low(&self) -> Result { - Ok(self._is_set_low()) - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::ErrorType for Pin> -where - I: PinId, - C: OutputConfig, -{ - type Error = Infallible; -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::OutputPin for Pin> -where - I: PinId, - C: OutputConfig, -{ - #[inline] - fn set_high(&mut self) -> Result<(), Self::Error> { - self._set_high(); - Ok(()) - } - #[inline] - fn set_low(&mut self) -> Result<(), Self::Error> { - self._set_low(); - Ok(()) - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::InputPin for Pin -where - I: PinId, -{ - #[inline] - fn is_high(&self) -> Result { - Ok(self._is_high()) - } - #[inline] - fn is_low(&self) -> Result { - Ok(self._is_low()) - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::ErrorType for Pin> -where - I: PinId, - C: InputConfig, -{ - type Error = Infallible; -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::InputPin for Pin> -where - I: PinId, - C: InputConfig, -{ - #[inline] - fn is_high(&self) -> Result { - Ok(self._is_high()) - } - #[inline] - fn is_low(&self) -> Result { - Ok(self._is_low()) - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::ToggleableOutputPin for Pin> -where - I: PinId, - C: OutputConfig, -{ - #[inline] - fn toggle(&mut self) -> Result<(), Self::Error> { - self._toggle(); - Ok(()) - } -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::StatefulOutputPin for Pin> -where - I: PinId, - C: OutputConfig, -{ - #[inline] - fn is_set_high(&self) -> Result { - Ok(self._is_set_high()) - } - #[inline] - fn is_set_low(&self) -> Result { - Ok(self._is_set_low()) - } -} - -//============================================================================== -// Pin definitions -//============================================================================== - -macro_rules! gpio { - ($Group:ident, [ $($Func:ident),+ ], [ - $($PXi:ident: ($i:expr, $is:expr, $reset:ident $(, [ $($PinFunc:ident),+ ])? )),+ - ]) => { - $crate::paste::paste! { - #[doc = "GPIO Pins for " $Group] - pub mod [<$Group:lower>] { - use crate::sio::[]; - use pac::{[],[]}; - - /// Bank0 bank pin id - pub trait BankPinId {} - use crate::typelevel::Sealed; - use crate::gpio::dynpin::{DynGroup,DynPinId}; - - // FIXME: Somehow just import what we need - #[allow(unused_imports)] - use super::{PullDownDisabled,PullUpDisabled,FloatingDisabled,BusKeepDisabled}; - - use super::{Pin,PinId}; - use crate::resets::SubsystemReset; - - $( - pin_id!($Group, $PXi, $i, $reset); - impl BankPinId for $PXi {} - - $( $(impl super::ValidPinMode<$PXi> for super::Function {})+ )* - )+ - - /// Collection of all the individual [`Pin`]s - pub struct Pins { - _io: [], - _pads: [], - _sio: [], - $( - #[doc = "Pin " $PXi] - pub [<$PXi:lower>] : Pin<$PXi,<$PXi as PinId>::Reset>, - )+ - } - - impl Pins { - /// Take ownership of the PAC peripherals and SIO slice and split it into discrete [`Pin`]s - pub fn new(io : [], pads: [], sio: [], reset : &mut pac::RESETS) -> Self { - pads.reset_bring_down(reset); - io.reset_bring_down(reset); - - io.reset_bring_up(reset); - pads.reset_bring_up(reset); - unsafe { - Self { - _io: io, - _pads: pads, - _sio: sio, - $( - [<$PXi:lower>]: Pin::new(), - )+ - } +macro_rules! pin_ids { + ($bank:ident: $($id:expr;$name:ident),*) => { + pin_ids!($bank as $bank: $($id;$name),*); + }; + ($bank:ident as $prefix:ident: $($id:tt),*) => { + pin_ids!($bank as $prefix: $($id;$id),*); + }; + ($bank:ident as $prefix:ident: $($id:expr;$name:tt),*) => { + paste::paste!{ + $( + #[doc = "Type level variant for the pin `" $name "` in bank `" $prefix "`."] + pub struct [<$prefix $name>] (pub(crate) ()); + impl crate::typelevel::Sealed for [<$prefix $name>] {} + impl PinId for [<$prefix $name>] { + fn as_dyn(&self) -> DynPinId { + DynPinId { + bank: DynBankId::$bank, + num: $id } } } - - $( impl super::ValidPinMode for super::[] {} )+ - } + impl pin_sealed::TypeLevelPinId for [<$prefix $name>] { + const ID: DynPinId = DynPinId { + bank: DynBankId::$bank, + num: $id + }; + + fn new() -> Self { + Self(()) + } + } + )* } - } + }; } - -gpio!( - Bank0, [ Spi, Uart, I2C, Pwm, Pio0, Pio1, UsbAux ], [ - Gpio0: (0, "0", PullDownDisabled), - Gpio1: (1, "1", PullDownDisabled), - Gpio2: (2, "2", PullDownDisabled), - Gpio3: (3, "3", PullDownDisabled), - Gpio4: (4, "4", PullDownDisabled), - Gpio5: (5, "5", PullDownDisabled), - Gpio6: (6, "6", PullDownDisabled), - Gpio7: (7, "7", PullDownDisabled), - Gpio8: (8, "8", PullDownDisabled), - Gpio9: (9, "9", PullDownDisabled), - Gpio10: (10, "10", PullDownDisabled), - Gpio11: (11, "11", PullDownDisabled), - Gpio12: (12, "12", PullDownDisabled), - Gpio13: (13, "13", PullDownDisabled), - Gpio14: (14, "14", PullDownDisabled), - Gpio15: (15, "15", PullDownDisabled), - Gpio16: (16, "16", PullDownDisabled), - Gpio17: (17, "17", PullDownDisabled), - Gpio18: (18, "18", PullDownDisabled), - Gpio19: (19, "19", PullDownDisabled), - Gpio20: (20, "20", PullDownDisabled, [Clock]), - Gpio21: (21, "21", PullDownDisabled, [Clock]), - Gpio22: (22, "22", PullDownDisabled, [Clock]), - Gpio23: (23, "23", PullDownDisabled, [Clock]), - Gpio24: (24, "24", PullDownDisabled, [Clock]), - Gpio25: (25, "25", PullDownDisabled, [Clock]), - Gpio26: (26, "26", PullDownDisabled), - Gpio27: (27, "27", PullDownDisabled), - Gpio28: (28, "28", PullDownDisabled), - Gpio29: (29, "29", PullDownDisabled) - ] -); - -pub use bank0::Pins; // this is probably the default everyone is going to want - -gpio!( - Qspi, [ Xip ], [ - Sck: (0, "sck", PullDownDisabled), - Cs: (1, "cs", PullUpDisabled), - Sd0: (2, "sd0", FloatingDisabled), - Sd1: (3, "sd1", FloatingDisabled), - Sd2: (4, "sd2", FloatingDisabled), - Sd3: (5, "sd3", FloatingDisabled) - ] -); - -//============================================================================== -// bsp_pins -//============================================================================== - -/// Helper macro to give meaningful names to GPIO pins -/// -/// The normal [`Pins`] struct names each [`Pin`] according to its [`PinId`]. -/// However, BSP authors would prefer to name each [`Pin`] according to its -/// function. This macro defines a new `Pins` struct with custom field names -/// for each [`Pin`], and it defines type aliases and constants to make it -/// easier to work with the [`Pin`]s and [`DynPin`](super::DynPin)s. -/// -/// When specifying pin aliases, be sure to use a [`PinMode`]. See -/// [here](self#types) for a list of the available [`PinMode`] type aliases. -/// -/// # Example -/// Calling the macro like this: -/// ```rust -/// use rp2040_hal::bsp_pins; -/// bsp_pins! { -/// #[cfg(feature = "gpio")] -/// Gpio0 { -/// /// Doc gpio0 -/// name: gpio0, -/// aliases: { FunctionPio0: PioPin } -/// }, -/// Gpio1 { -/// name: led, -/// aliases: { FunctionPwm: LedPwm } -/// }, -/// } -/// ``` -/// -/// Is roughly equivalent to the following source code (excluding the docs strings below): -/// ``` -/// use ::rp2040_hal as hal; -/// use hal::gpio; -/// pub struct Pins { -/// #[cfg(feature = "gpio")] -/// /// Doc gpio0 -/// pub gpio0: gpio::Pin::Reset>, -/// pub led: gpio::Pin::Reset>, -/// } -/// impl Pins { -/// #[inline] -/// pub fn new( -/// io: hal::pac::IO_BANK0, -/// pads: hal::pac::PADS_BANK0, -/// sio: hal::sio::SioGpioBank0, -/// reset: &mut hal::pac::RESETS, -/// ) -> Self { -/// let mut pins = gpio::Pins::new(io, pads, sio, reset); -/// Self { -/// #[cfg(feature = "gpio")] -/// gpio0: pins.gpio0, -/// led: pins.gpio1, -/// } -/// } -/// } -/// pub type PioPin = gpio::Pin; -/// pub const PIO_PIN_ID: gpio::DynPinId = ::DYN; -/// pub const PIO_PIN_MODE: gpio::DynPinMode = ::DYN; -/// pub type LedPwm = gpio::Pin; -/// pub const LED_PWM_ID: gpio::DynPinId = ::DYN; -/// pub const LED_PWM_MODE: gpio::DynPinMode = ::DYN; -/// ``` -#[macro_export] -macro_rules! bsp_pins { - ( - $( - $( #[$id_cfg:meta] )* - $Id:ident { - $( #[$name_doc:meta] )* - name: $name:ident $(,)? - $( - aliases: { - $( - $( #[$alias_cfg:meta] )* - $Mode:ident: $Alias:ident - ),+ - } - )? - } $(,)? - )+ - ) => { - $crate::paste::paste! { - - /// BSP replacement for the HAL - /// [`Pins`](rp2040_hal::gpio::Pins) type - /// - /// This type is intended to provide more meaningful names for the - /// given pins. - /// - /// To enable specific functions of the pins you can use the - /// [rp2040_hal::gpio::pin::Pin::into_mode] function with - /// one of: - /// - [rp2040_hal::gpio::pin::FunctionI2C] - /// - [rp2040_hal::gpio::pin::FunctionPwm] - /// - [rp2040_hal::gpio::pin::FunctionSpi] - /// - [rp2040_hal::gpio::pin::FunctionXip] - /// - [rp2040_hal::gpio::pin::FunctionPio0] - /// - [rp2040_hal::gpio::pin::FunctionPio1] - /// - [rp2040_hal::gpio::pin::FunctionUart] - /// - /// like this: - ///```no_run - /// use rp2040_hal::{pac, gpio::{bank0::Gpio12, Pin, Pins, PushPullOutput}, sio::Sio}; - /// - /// let mut peripherals = pac::Peripherals::take().unwrap(); - /// let sio = Sio::new(peripherals.SIO); - /// let pins = Pins::new(peripherals.IO_BANK0,peripherals.PADS_BANK0,sio.gpio_bank0, &mut peripherals.RESETS); - /// - /// let _spi_sclk = pins.gpio2.into_mode::(); - /// let _spi_mosi = pins.gpio3.into_mode::(); - /// let _spi_miso = pins.gpio4.into_mode::(); - ///``` - /// - /// **See also [rp2040_hal::gpio::pin] for more in depth information - /// about this**! - pub struct Pins { - $( - $( #[$id_cfg] )* - $( #[$name_doc] )* - pub $name: $crate::gpio::Pin< - $crate::gpio::bank0::$Id, - <$crate::gpio::bank0::$Id as $crate::gpio::PinId>::Reset - >, - )+ - } - - impl Pins { - /// Take ownership of the PAC [`PORT`] and split it into - /// discrete [`Pin`]s. - /// - /// This struct serves as a replacement for the HAL [`Pins`] - /// struct. It is intended to provide more meaningful names for - /// each [`Pin`] in a BSP. Any [`Pin`] not defined by the BSP is - /// dropped. - /// - /// [`Pin`](rp2040_hal::gpio::Pin) - /// [`Pins`](rp2040_hal::gpio::Pins) - #[inline] - pub fn new(io : $crate::pac::IO_BANK0, pads: $crate::pac::PADS_BANK0, sio: $crate::sio::SioGpioBank0, reset : &mut $crate::pac::RESETS) -> Self { - let mut pins = $crate::gpio::Pins::new(io,pads,sio,reset); - Self { - $( - $( #[$id_cfg] )* - $name: pins.[<$Id:lower>], - )+ - } +/// Bank of all the GPIOs. +pub mod bank0 { + use super::{pin_sealed, DynBankId, DynPinId, PinId}; + pin_ids!(Bank0 as Gpio: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29); +} +/// Bank of the QSPI related pins. +pub mod qspi { + use super::{pin_sealed, DynBankId, DynPinId, PinId}; + pin_ids!(Qspi: 0;Sclk, 1;Ss, 2;Sd0, 3;Sd1, 4;Sd2, 5;Sd3); +} + +pub(crate) fn set_function(pin: &P, function: DynFunction) { + use pac::io_bank0::gpio::gpio_ctrl::FUNCSEL_A; + let funcsel = match function { + DynFunction::Xip => FUNCSEL_A::JTAG, + DynFunction::Spi => FUNCSEL_A::SPI, + DynFunction::Uart => FUNCSEL_A::UART, + DynFunction::I2c => FUNCSEL_A::I2C, + DynFunction::Pwm => FUNCSEL_A::PWM, + DynFunction::Sio(sio) => { + let mask = pin.mask(); + match sio { + crate::gpio::DynSioConfig::Input => { + pin.sio_oe_clr().write(|w| unsafe { w.bits(mask) }); + } + crate::gpio::DynSioConfig::Output => { + pin.sio_oe_set().write(|w| unsafe { w.bits(mask) }); } } - $( - $( #[$id_cfg] )* - $crate::bsp_pins!(@aliases, $( $( $( #[$alias_cfg] )* $Id $Mode $Alias )+ )? ); - )+ + + FUNCSEL_A::SIO } + DynFunction::Pio0 => FUNCSEL_A::PIO0, + DynFunction::Pio1 => FUNCSEL_A::PIO1, + DynFunction::Clock => FUNCSEL_A::CLOCK, + DynFunction::Usb => FUNCSEL_A::USB, + DynFunction::Null => FUNCSEL_A::NULL, }; - ( @aliases, $( $( $( #[$attr:meta] )* $Id:ident $Mode:ident $Alias:ident )+ )? ) => { - $crate::paste::paste! { - $( - $( - $( #[$attr] )* - /// Alias for a configured [`Pin`](rp2040_hal::gpio::Pin) - pub type $Alias = $crate::gpio::Pin< - $crate::gpio::bank0::$Id, - $crate::gpio::$Mode - >; - $( #[$attr] )* - #[doc = "[DynPinId](rp2040_hal::gpio::DynPinId) "] - #[doc = "for the `" $Alias "` alias."] - pub const [<$Alias:snake:upper _ID>]: $crate::gpio::DynPinId = - <$crate::gpio::bank0::$Id as $crate::gpio::PinId>::DYN; - - $( #[$attr] )* - #[doc = "[DynPinMode](rp2040_hal::gpio::DynPinMode) "] - #[doc = "for the `" $Alias "` alias."] - pub const [<$Alias:snake:upper _MODE>]: $crate::gpio::DynPinMode = - <$crate::gpio::$Mode as $crate::gpio::PinMode>::DYN; - )+ - )? - } + pin.io_ctrl().modify(|_, w| w.funcsel().variant(funcsel)); +} +pub(crate) fn set_pull_type(pin: &P, pull_type: DynPullType) { + let (pue, pde) = match pull_type { + DynPullType::None => (false, false), + DynPullType::Up => (true, false), + DynPullType::Down => (false, true), + DynPullType::Both => (true, true), }; + + pin.pad_ctrl() + .modify(|_, w| w.pue().bit(pue).pde().bit(pde)); } diff --git a/rp2040-hal/src/gpio/pin/pin_sealed.rs b/rp2040-hal/src/gpio/pin/pin_sealed.rs new file mode 100644 index 000000000..bee520d32 --- /dev/null +++ b/rp2040-hal/src/gpio/pin/pin_sealed.rs @@ -0,0 +1,192 @@ +use crate::sio::CoreId; + +use super::{DynBankId, DynPinId}; + +pub trait TypeLevelPinId: super::PinId { + const ID: DynPinId; + + fn new() -> Self; +} + +pub trait PinIdOps { + fn mask(&self) -> u32; + fn io_status(&self) -> &pac::io_bank0::gpio::GPIO_STATUS; + fn io_ctrl(&self) -> &pac::io_bank0::gpio::GPIO_CTRL; + fn pad_ctrl(&self) -> &pac::pads_bank0::GPIO; + + fn sio_in(&self) -> &pac::sio::GPIO_IN; + fn sio_out(&self) -> &pac::sio::GPIO_OUT; + fn sio_out_set(&self) -> &pac::sio::GPIO_OUT_SET; + fn sio_out_clr(&self) -> &pac::sio::GPIO_OUT_CLR; + fn sio_out_xor(&self) -> &pac::sio::GPIO_OUT_XOR; + fn sio_oe(&self) -> &pac::sio::GPIO_OE; + fn sio_oe_set(&self) -> &pac::sio::GPIO_OE_SET; + fn sio_oe_clr(&self) -> &pac::sio::GPIO_OE_CLR; + fn sio_oe_xor(&self) -> &pac::sio::GPIO_OE_XOR; + + fn intr(&self) -> (&pac::io_bank0::INTR, usize); + fn proc_ints(&self, proc: CoreId) -> (&pac::io_bank0::PROC0_INTS, usize); + fn proc_inte(&self, proc: CoreId) -> (&pac::io_bank0::PROC0_INTE, usize); + fn proc_intf(&self, proc: CoreId) -> (&pac::io_bank0::PROC0_INTF, usize); + fn dormant_wake_ints(&self) -> (&pac::io_bank0::DORMANT_WAKE_INTS, usize); + fn dormant_wake_inte(&self) -> (&pac::io_bank0::DORMANT_WAKE_INTE, usize); + fn dormant_wake_intf(&self) -> (&pac::io_bank0::DORMANT_WAKE_INTF, usize); +} + +macro_rules! accessor_fns { + (sio $reg:ident) => { + paste::paste! { + fn [](&self) -> &pac::sio::[] { + let pin = self.as_dyn(); + unsafe { + let sio = &*pac::SIO::PTR; + match pin.bank { + DynBankId::Bank0 => &sio.[], + DynBankId::Qspi => core::mem::transmute(&sio.[]), + } + } + } + } + }; + (io $reg:ident) => { + paste::paste! { + fn [](&self) -> &pac::io_bank0::gpio::[] { + let pin = self.as_dyn(); + match pin.bank { + DynBankId::Bank0 => { + let gpio = unsafe { &*pac::IO_BANK0::PTR }; + &gpio.gpio[usize::from(pin.num)].[] + } + DynBankId::Qspi => unsafe { + let qspi = &*pac::IO_QSPI::PTR; + match pin.num { + 0 => core::mem::transmute(&qspi.gpio_qspisclk.[]), + 1 => core::mem::transmute(&qspi.gpio_qspiss.[]), + 2 => core::mem::transmute(&qspi.gpio_qspisd0.[]), + 3 => core::mem::transmute(&qspi.gpio_qspisd1.[]), + 4 => core::mem::transmute(&qspi.gpio_qspisd2.[]), + 5 => core::mem::transmute(&qspi.gpio_qspisd3.[]), + _ => unreachable!("Invalid QSPI bank pin number."), + } + }, + } + } + } + }; + (int $reg:ident) => { + paste::paste! { + fn [](&self, proc: CoreId) -> (&pac::io_bank0::[], usize) { + let pin = self.as_dyn(); + let (index, offset) = (pin.num / 8, pin.num % 8 * 4); + unsafe { + let reg = match pin.bank { + DynBankId::Bank0 => { + let bank = &*pac::IO_BANK0::PTR; + match proc { + CoreId::Core0 => &bank.[][usize::from(index)], + CoreId::Core1 => core::mem::transmute(&bank.[][usize::from(index)]), + } + } + DynBankId::Qspi => { + let bank = &*pac::IO_QSPI::PTR; + match proc { + CoreId::Core0 => core::mem::transmute(&bank.[]), + CoreId::Core1 => core::mem::transmute(&bank.[]), + } + } + }; + (reg, usize::from(offset)) + } + } + } + }; + (dormant $reg:ident) => { + paste::paste! { + fn [< dormant_wake_ $reg:lower>](&self) -> (&pac::io_bank0::[< DORMANT_WAKE_ $reg:upper >], usize) { + let pin = self.as_dyn(); + let (index, offset) = (pin.num / 8, pin.num % 8 * 4); + unsafe { + let reg = match pin.bank { + DynBankId::Bank0 => { + let bank = &*pac::IO_BANK0::PTR; + &bank.[< dormant_wake_ $reg:lower>][usize::from(index)] + } + DynBankId::Qspi => { + let bank = &*pac::IO_QSPI::PTR; + core::mem::transmute(&bank.[< dormant_wake_ $reg:lower>]) + } + }; + (reg, usize::from(offset)) + } + } + } + }; +} +impl PinIdOps for T +where + T: super::PinId, +{ + fn mask(&self) -> u32 { + 1 << self.as_dyn().num + } + fn pad_ctrl(&self) -> &pac::pads_bank0::GPIO { + let pin = self.as_dyn(); + match pin.bank { + DynBankId::Bank0 => { + let gpio = unsafe { &*pac::PADS_BANK0::PTR }; + &gpio.gpio[usize::from(pin.num)] + } + DynBankId::Qspi => unsafe { + let qspi = &*pac::PADS_QSPI::PTR; + match pin.num { + 0 => core::mem::transmute(&qspi.gpio_qspi_sclk), + 1 => core::mem::transmute(&qspi.gpio_qspi_ss), + 2 => core::mem::transmute(&qspi.gpio_qspi_sd0), + 3 => core::mem::transmute(&qspi.gpio_qspi_sd1), + 4 => core::mem::transmute(&qspi.gpio_qspi_sd2), + 5 => core::mem::transmute(&qspi.gpio_qspi_sd3), + _ => unreachable!("Invalid QSPI bank pin number."), + } + }, + } + } + accessor_fns!(io ctrl); + accessor_fns!(io status); + + accessor_fns!(sio in); + accessor_fns!(sio out); + accessor_fns!(sio out_set); + accessor_fns!(sio out_clr); + accessor_fns!(sio out_xor); + accessor_fns!(sio oe); + accessor_fns!(sio oe_set); + accessor_fns!(sio oe_clr); + accessor_fns!(sio oe_xor); + + fn intr(&self) -> (&pac::io_bank0::INTR, usize) { + let pin = self.as_dyn(); + let (index, offset) = (pin.num / 8, pin.num % 8 * 4); + unsafe { + let reg = match pin.bank { + DynBankId::Bank0 => { + let bank = &*pac::IO_BANK0::PTR; + &bank.intr[usize::from(index)] + } + DynBankId::Qspi => { + let bank = &*pac::IO_QSPI::PTR; + core::mem::transmute(&bank.intr) + } + }; + + (reg, usize::from(offset)) + } + } + + accessor_fns!(int ints); + accessor_fns!(int inte); + accessor_fns!(int intf); + + accessor_fns!(dormant ints); + accessor_fns!(dormant inte); + accessor_fns!(dormant intf); +} diff --git a/rp2040-hal/src/gpio/pull.rs b/rp2040-hal/src/gpio/pull.rs new file mode 100644 index 000000000..c4fd417e9 --- /dev/null +++ b/rp2040-hal/src/gpio/pull.rs @@ -0,0 +1,57 @@ +use paste::paste; + +pub(crate) mod pull_sealed { + use super::DynPullType; + + pub trait PullType { + fn from(pm: DynPullType) -> Self; + fn as_dyn(&self) -> DynPullType; + } +} +/// Type-level enum for pull resitor types. +pub trait PullType: pull_sealed::PullType {} + +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +/// Value level enum for pull resistor types. +pub enum DynPullType { + #[allow(missing_docs)] + None, + #[allow(missing_docs)] + Up, + #[allow(missing_docs)] + Down, + /// This enables both pull resistor. This setup is described as bus-keep in the RP2040 + /// datasheet. + Both, +} + +impl PullType for DynPullType {} +impl pull_sealed::PullType for DynPullType { + fn from(pm: DynPullType) -> Self { + pm + } + + fn as_dyn(&self) -> DynPullType { + *self + } +} + +macro_rules! pin_mode { + ($($mode:ident),*) => { + $(paste! { + /// Type-level variant of [`PullType`]. + pub struct [](pub(super) ()); + impl PullType for [] {} + impl pull_sealed::PullType for [] { + fn from(_pm: DynPullType) -> Self { + Self(()) + } + fn as_dyn(&self) -> DynPullType { + DynPullType::[<$mode>] + } + } + })* + }; +} +pin_mode!(None, Up, Down, Both); diff --git a/rp2040-hal/src/gpio/reg.rs b/rp2040-hal/src/gpio/reg.rs deleted file mode 100644 index 6e2350e6f..000000000 --- a/rp2040-hal/src/gpio/reg.rs +++ /dev/null @@ -1,530 +0,0 @@ -// Based heavily on and in some places copied from `atsamd-hal` gpio::v2 -use super::dynpin::{DynGroup, DynPinId}; -use super::{ - InputOverride, Interrupt, InterruptOverride, OutputDriveStrength, OutputEnableOverride, - OutputOverride, OutputSlewRate, -}; -use crate::atomic_register_access::{write_bitmask_clear, write_bitmask_set}; -use crate::gpio::dynpin::{DynDisabled, DynFunction, DynInput, DynOutput, DynPinMode}; -use crate::pac; - -//============================================================================== -// ModeFields -//============================================================================== - -/// Collect all fields needed to set the [`PinMode`](super::PinMode) -#[derive(Default)] -struct ModeFields { - inen: bool, - pue: bool, - pde: bool, - - sio_outen: bool, - funcsel: u8, -} - -const SIO_FUNCSEL: u8 = 5; -const NULL_FUNCSEL: u8 = 0x1f; - -impl From for ModeFields { - #[inline] - fn from(mode: DynPinMode) -> Self { - use DynPinMode::*; - let mut fields = Self::default(); - match mode { - Disabled(config) => { - use DynDisabled::*; - fields.funcsel = NULL_FUNCSEL; - fields.sio_outen = false; - fields.inen = false; - - match config { - Floating => (), - PullDown => { - fields.pde = true; - } - PullUp => { - fields.pue = true; - } - BusKeep => { - fields.pde = true; - fields.pue = true; - } - } - } - Input(config) => { - use DynInput::*; - fields.funcsel = SIO_FUNCSEL; - fields.sio_outen = false; - fields.inen = true; - - match config { - Floating => (), - PullDown => { - fields.pde = true; - } - PullUp => { - fields.pue = true; - } - BusKeep => { - fields.pde = true; - fields.pue = true; - } - } - } - Output(config) => { - use DynOutput::*; - fields.funcsel = SIO_FUNCSEL; - fields.sio_outen = true; - match config { - PushPull => { - fields.inen = false; - } - Readable => { - fields.inen = true; - } - } - } - Function(func) => { - use DynFunction::*; - fields.funcsel = match func { - Xip => 0, - Spi => 1, - Uart => 2, - I2C => 3, - Pwm => 4, - // SIO is 5, but isn't an alternate function but is instead for using the pin as GPIO - Pio0 => 6, - Pio1 => 7, - Clock => 8, - UsbAux => 9, - }; - fields.inen = true; - if func == I2C { - fields.pue = true; - } - } - }; - - fields - } -} - -/// # Safety -/// -/// Users should only implement the [`id`] function. No default function -/// implementations should be overridden. The implementing type must also have -/// "control" over the corresponding pin ID, i.e. it must guarantee that each -/// pin ID is a singleton -pub(super) unsafe trait RegisterInterface { - /// Provide a [`DynPinId`] identifying the set of registers controlled by - /// this type. - fn id(&self) -> DynPinId; - - #[inline] - fn mask_32(&self) -> u32 { - 1 << self.id().num - } - - /// Read the logic level of an input put - #[inline] - fn read_pin(&self) -> bool { - let mask = self.mask_32(); - (match self.id().group { - DynGroup::Bank0 => unsafe { &(*pac::SIO::ptr()) }.gpio_in.read().bits(), - DynGroup::Qspi => unsafe { &(*pac::SIO::ptr()) }.gpio_hi_in.read().bits(), - }) & mask - != 0 - } - - /// Write the logic level of an output pin - #[inline] - fn write_pin(&mut self, bit: bool) { - let mask = self.mask_32(); - // This ordering to try and make the group match inline if bit can't be - unsafe { - match self.id().group { - DynGroup::Bank0 => { - if bit { - (*pac::SIO::ptr()).gpio_out_set.write(|w| w.bits(mask)); - } else { - (*pac::SIO::ptr()).gpio_out_clr.write(|w| w.bits(mask)); - } - } - DynGroup::Qspi => { - if bit { - (*pac::SIO::ptr()).gpio_hi_out_set.write(|w| w.bits(mask)); - } else { - (*pac::SIO::ptr()).gpio_hi_out_clr.write(|w| w.bits(mask)); - } - } - }; - } - } - - /// Toggle the logic level of an output pin - #[inline] - fn toggle_pin(&mut self) { - let mask = self.mask_32(); - match self.id().group { - DynGroup::Bank0 => unsafe { (*pac::SIO::ptr()).gpio_out_xor.write(|w| w.bits(mask)) }, - DynGroup::Qspi => unsafe { (*pac::SIO::ptr()).gpio_hi_out_xor.write(|w| w.bits(mask)) }, - } - } - - /// Read back the logic level of an output pin - #[inline] - fn read_out_pin(&self) -> bool { - let mask = self.mask_32(); - (match self.id().group { - DynGroup::Bank0 => unsafe { &(*pac::SIO::ptr()) }.gpio_out.read().bits(), - DynGroup::Qspi => unsafe { &(*pac::SIO::ptr()) }.gpio_hi_out.read().bits(), - }) & mask - != 0 - } - - #[inline] - fn read_drive_strength(&self) -> OutputDriveStrength { - use OutputDriveStrength::*; - let num = self.id().num as usize; - let strength = match self.id().group { - DynGroup::Bank0 => unsafe { &(*pac::PADS_BANK0::ptr()) }.gpio[num] - .read() - .drive() - .bits(), - DynGroup::Qspi => qspi_read_drive(num), - }; - match strength { - 0x0 => TwoMilliAmps, - 0x1 => FourMilliAmps, - 0x2 => EightMilliAmps, - 0x3 => TwelveMilliAmps, - _ => unreachable!("invalid drive strength"), - } - } - - #[inline] - fn write_drive_strength(&self, strength: OutputDriveStrength) { - use OutputDriveStrength::*; - let num = self.id().num as usize; - let strength = match strength { - TwoMilliAmps => 0x0, - FourMilliAmps => 0x1, - EightMilliAmps => 0x2, - TwelveMilliAmps => 0x3, - }; - - match self.id().group { - DynGroup::Bank0 => unsafe { &(*pac::PADS_BANK0::ptr()) }.gpio[num] - .modify(|_, w| w.drive().bits(strength)), - DynGroup::Qspi => qspi_write_drive(num, strength), - }; - } - - #[inline] - fn read_slew_rate(&self) -> OutputSlewRate { - let num = self.id().num as usize; - let slew_fast = match self.id().group { - DynGroup::Bank0 => unsafe { &(*pac::PADS_BANK0::ptr()) }.gpio[num] - .read() - .slewfast() - .bit_is_set(), - DynGroup::Qspi => qspi_read_slew(num), - }; - if slew_fast { - OutputSlewRate::Fast - } else { - OutputSlewRate::Slow - } - } - - #[inline] - fn write_slew_rate(&self, rate: OutputSlewRate) { - let num = self.id().num as usize; - let slewfast = match rate { - OutputSlewRate::Fast => true, - OutputSlewRate::Slow => false, - }; - - match self.id().group { - DynGroup::Bank0 => unsafe { &(*pac::PADS_BANK0::ptr()) }.gpio[num] - .modify(|_, w| w.slewfast().bit(slewfast)), - DynGroup::Qspi => qspi_write_slew(num, slewfast), - }; - } - - // We have to duplicate code, maybe a fix in the HAL layer can prevent this - #[inline] - fn do_change_mode(&self, mode: DynPinMode) { - let num = self.id().num as usize; - match self.id().group { - DynGroup::Bank0 => gpio_change_mode(num, mode), - DynGroup::Qspi => qspi_change_mode(num, mode), - } - } - - /// Clear interrupt. - #[inline] - fn clear_interrupt(&self, interrupt: Interrupt) { - let num = self.id().num as usize; - unsafe { - let io = &(*pac::IO_BANK0::ptr()); - // There are four bits for each GPIO pin (one for each enumerator - // in the `Interrupt` enum). There are therefore eight pins per - // 32-bit register, and four registers in total. - let bit_in_reg = num % 8 * 4 + interrupt as usize; - io.intr[num >> 3].write(|w| w.bits(1 << bit_in_reg)); - } - } - - /// Interrupt status. - #[inline] - fn interrupt_status(&self, interrupt: Interrupt) -> bool { - let num = self.id().num as usize; - unsafe { - let io = &(*pac::IO_BANK0::ptr()); - let cpuid = *(pac::SIO::ptr() as *const u32); - // There are four bits for each GPIO pin (one for each enumerator - // in the `Interrupt` enum). There are therefore eight pins per - // 32-bit register, and four registers per CPU. - let bit_in_reg = ((num % 8) * 4) + (interrupt as usize); - if cpuid == 0 { - (io.proc0_ints[num >> 3].read().bits() & (1 << bit_in_reg)) != 0 - } else { - (io.proc1_ints[num >> 3].read().bits() & (1 << bit_in_reg)) != 0 - } - } - } - - /// Is interrupt enabled. - #[inline] - fn is_interrupt_enabled(&self, interrupt: Interrupt) -> bool { - let num = self.id().num as usize; - unsafe { - let io = &(*pac::IO_BANK0::ptr()); - let cpuid = *(pac::SIO::ptr() as *const u32); - // There are four bits for each GPIO pin (one for each enumerator - // in the `Interrupt` enum). There are therefore eight pins per - // 32-bit register, and four registers per CPU. - let bit_in_reg = num % 8 * 4 + interrupt as usize; - if cpuid == 0 { - (io.proc0_inte[num >> 3].read().bits() & (1 << bit_in_reg)) != 0 - } else { - (io.proc1_inte[num >> 3].read().bits() & (1 << bit_in_reg)) != 0 - } - } - } - - /// Enable or disable interrupt. - #[inline] - fn set_interrupt_enabled(&self, interrupt: Interrupt, enabled: bool) { - let num = self.id().num as usize; - unsafe { - let cpuid = *(pac::SIO::ptr() as *const u32); - let io = &(*pac::IO_BANK0::ptr()); - // There are four bits for each GPIO pin (one for each enumerator - // in the `Interrupt` enum). There are therefore eight pins per - // 32-bit register, and four registers per CPU. - let reg = if cpuid == 0 { - io.proc0_inte[num >> 3].as_ptr() - } else { - io.proc1_inte[num >> 3].as_ptr() - }; - let bit_in_reg = num % 8 * 4 + interrupt as usize; - if enabled { - write_bitmask_set(reg, 1 << bit_in_reg); - } else { - write_bitmask_clear(reg, 1 << bit_in_reg); - } - } - } - - /// Is interrupt forced. - #[inline] - fn is_interrupt_forced(&self, interrupt: Interrupt) -> bool { - let num = self.id().num as usize; - unsafe { - let cpuid = *(pac::SIO::ptr() as *const u32); - let io = &(*pac::IO_BANK0::ptr()); - // There are four bits for each GPIO pin (one for each enumerator - // in the `Interrupt` enum). There are therefore eight pins per - // 32-bit register, and four registers per CPU. - let bit_in_reg = num % 8 * 4 + interrupt as usize; - if cpuid == 0 { - (io.proc0_intf[num >> 3].read().bits() & (1 << bit_in_reg)) != 0 - } else { - (io.proc1_intf[num >> 3].read().bits() & (1 << bit_in_reg)) != 0 - } - } - } - - /// Force or release interrupt. - #[inline] - fn set_interrupt_forced(&self, interrupt: Interrupt, forced: bool) { - let num = self.id().num as usize; - unsafe { - let cpuid = *(pac::SIO::ptr() as *const u32); - let io = &(*pac::IO_BANK0::ptr()); - // There are four bits for each GPIO pin (one for each enumerator - // in the `Interrupt` enum). There are therefore eight pins per - // 32-bit register, and four registers per CPU. - let reg = if cpuid == 0 { - io.proc0_intf[num >> 3].as_ptr() - } else { - io.proc1_intf[num >> 3].as_ptr() - }; - let bit_in_reg = num % 8 * 4 + interrupt as usize; - if forced { - write_bitmask_set(reg, 1 << bit_in_reg); - } else { - write_bitmask_clear(reg, 1 << bit_in_reg); - } - } - } - - /// Set the interrupt override. - #[inline] - fn set_interrupt_override(&self, override_value: InterruptOverride) { - let num = self.id().num as usize; - unsafe { &(*pac::IO_BANK0::ptr()) }.gpio[num] - .gpio_ctrl - .modify(|_, w| w.irqover().bits(override_value as u8)); - } - - /// Set the input override. - #[inline] - fn set_input_override(&self, override_value: InputOverride) { - let num = self.id().num as usize; - unsafe { &(*pac::IO_BANK0::ptr()) }.gpio[num] - .gpio_ctrl - .modify(|_, w| w.inover().bits(override_value as u8)); - } - - /// Set the output enable override. - #[inline] - fn set_output_enable_override(&self, override_value: OutputEnableOverride) { - let num = self.id().num as usize; - unsafe { &(*pac::IO_BANK0::ptr()) }.gpio[num] - .gpio_ctrl - .modify(|_, w| w.oeover().bits(override_value as u8)); - } - - /// Set the output override. - #[inline] - fn set_output_override(&self, override_value: OutputOverride) { - let num = self.id().num as usize; - unsafe { &(*pac::IO_BANK0::ptr()) }.gpio[num] - .gpio_ctrl - .modify(|_, w| w.outover().bits(override_value as u8)); - } -} - -#[inline] -fn gpio_change_mode(num: usize, mode: DynPinMode) { - let fields: ModeFields = mode.into(); - let io = unsafe { &(*pac::IO_BANK0::ptr()).gpio[num] }; - let pads = unsafe { &(*pac::PADS_BANK0::ptr()).gpio[num] }; - - pads.write(|w| { - w.pue().bit(fields.pue); - w.pde().bit(fields.pde); - w.ie().bit(fields.inen); - w.od().bit(false) // the SIO oe bit will handle this instead - }); - - io.gpio_ctrl - .write(|w| unsafe { w.funcsel().bits(fields.funcsel) }); - - if fields.funcsel == SIO_FUNCSEL { - if fields.sio_outen { - unsafe { - (*pac::SIO::ptr()).gpio_oe_set.write(|w| w.bits(1 << num)); - } - } else { - unsafe { - (*pac::SIO::ptr()).gpio_oe_clr.write(|w| w.bits(1 << num)); - } - } - } else { - unsafe { - (*pac::SIO::ptr()).gpio_oe_clr.write(|w| w.bits(1 << num)); - } - } -} - -// TODO: This is really nasty, but there's no single type for the QSPI pins -// I'm not sure if a svd change is even possible to fix this, as these do have -// different reset values -macro_rules! qspi_bits { - ( $( ($num : expr, $suffix : ident) ),+ ) => { - $crate::paste::paste! { - #[inline] - fn qspi_read_drive(num: usize) -> u8 { - match num { - $($num => unsafe { &(*pac::PADS_QSPI::ptr()).[] }.read().drive().bits(), )+ - _ => unreachable!("invalid ID for QSPI pin") - } - } - - #[inline] - fn qspi_write_drive(num: usize, val : u8) { - match num { - $($num => unsafe { &(*pac::PADS_QSPI::ptr()).[] }.modify(|_,w| w.drive().bits(val) ), )+ - _ => unreachable!("invalid ID for QSPI pin") - } - } - - #[inline] - fn qspi_read_slew(num: usize) -> bool { - match num { - $($num => unsafe { &(*pac::PADS_QSPI::ptr()).[] }.read().slewfast().bit_is_set(), )+ - _ => unreachable!("invalid ID for QSPI pin") - } - } - - #[inline] - fn qspi_write_slew(num: usize, slewfast : bool) { - match num { - $($num => unsafe { &(*pac::PADS_QSPI::ptr()).[] }.modify(|_,w| w.slewfast().bit(slewfast) ), )+ - _ => unreachable!("invalid ID for QSPI pin") - } - } - - #[inline] - fn qspi_change_mode(num: usize, mode: DynPinMode) { - let fields : ModeFields = mode.into(); - - match num { - $($num => { - let io = unsafe { &(*pac::IO_QSPI::ptr()).[] }; - let pads = unsafe { &(*pac::PADS_QSPI::ptr()).[] }; - - pads.write(|w| { - w.pue().bit(fields.pue); - w.pde().bit(fields.pde); - w.ie().bit(fields.inen); - w.od().bit(false) - }); - - io.gpio_ctrl.write(|w| unsafe { w.funcsel().bits(fields.funcsel) } ); - }, )+ - _ => unreachable!("invalid ID for QSPI pin") - } - - - // outen is only on SIO - if fields.funcsel == SIO_FUNCSEL { - if fields.sio_outen { - unsafe { (*pac::SIO::ptr()).gpio_hi_oe_set.write(|w| w.bits(1 << num)); } - } else { - unsafe { (*pac::SIO::ptr()).gpio_hi_oe_clr.write(|w| w.bits(1 << num)); } - } - } else { - unsafe { (*pac::SIO::ptr()).gpio_hi_oe_clr.write(|w| w.bits(1 << num)); } - } - } - } - } -} - -qspi_bits!((0, sclk), (1, ss), (2, sd0), (3, sd1), (4, sd2), (5, sd3)); diff --git a/rp2040-hal/src/i2c.rs b/rp2040-hal/src/i2c.rs index 76b32e4b2..c82606e1a 100644 --- a/rp2040-hal/src/i2c.rs +++ b/rp2040-hal/src/i2c.rs @@ -46,12 +46,7 @@ use core::{marker::PhantomData, ops::Deref}; use crate::{ - gpio::pin::bank0::{ - Gpio0, Gpio1, Gpio10, Gpio11, Gpio12, Gpio13, Gpio14, Gpio15, Gpio16, Gpio17, Gpio18, - Gpio19, Gpio2, Gpio20, Gpio21, Gpio22, Gpio23, Gpio24, Gpio25, Gpio26, Gpio27, Gpio28, - Gpio29, Gpio3, Gpio4, Gpio5, Gpio6, Gpio7, Gpio8, Gpio9, - }, - gpio::pin::{AnyPin, FunctionI2C}, + gpio::{bank0::*, AnyPin, FunctionI2C}, resets::SubsystemReset, typelevel::Sealed, }; @@ -141,55 +136,32 @@ impl eh1_0_alpha::i2c::Error for Error { } /// SCL pin -pub trait SclPin: Sealed {} +pub trait ValidSclPin: Sealed {} /// SDA pin -pub trait SdaPin: Sealed {} - -impl SdaPin for Gpio0 {} -impl SclPin for Gpio1 {} - -impl SdaPin for Gpio2 {} -impl SclPin for Gpio3 {} - -impl SdaPin for Gpio4 {} -impl SclPin for Gpio5 {} - -impl SdaPin for Gpio6 {} -impl SclPin for Gpio7 {} - -impl SdaPin for Gpio8 {} -impl SclPin for Gpio9 {} - -impl SdaPin for Gpio10 {} -impl SclPin for Gpio11 {} - -impl SdaPin for Gpio12 {} -impl SclPin for Gpio13 {} - -impl SdaPin for Gpio14 {} -impl SclPin for Gpio15 {} - -impl SdaPin for Gpio16 {} -impl SclPin for Gpio17 {} +pub trait ValidSdaPin: Sealed {} -impl SdaPin for Gpio18 {} -impl SclPin for Gpio19 {} - -impl SdaPin for Gpio20 {} -impl SclPin for Gpio21 {} - -impl SdaPin for Gpio22 {} -impl SclPin for Gpio23 {} - -impl SdaPin for Gpio24 {} -impl SclPin for Gpio25 {} - -impl SdaPin for Gpio26 {} -impl SclPin for Gpio27 {} - -impl SdaPin for Gpio28 {} -impl SclPin for Gpio29 {} +macro_rules! valid_pins { + ($($i2c:ident: { + sda: [$($sda:ident),*], + scl: [$($scl:ident),*] + }),*) => { + $( + $(impl ValidSdaPin<$i2c> for $sda {})* + $(impl ValidSclPin<$i2c> for $scl {})* + )* + }; +} +valid_pins!{ + I2C0: { + sda: [Gpio0, Gpio4, Gpio8, Gpio12, Gpio16, Gpio20, Gpio24, Gpio28], + scl: [Gpio1, Gpio5, Gpio9, Gpio13, Gpio17, Gpio21, Gpio25, Gpio29] + }, + I2C1: { + sda: [Gpio2, Gpio6, Gpio10, Gpio14, Gpio18, Gpio22, Gpio26], + scl: [Gpio3, Gpio7, Gpio11, Gpio15, Gpio19, Gpio23, Gpio27] + } +} /// Operational mode of the I2C peripheral. pub trait I2CMode: Sealed { @@ -279,8 +251,8 @@ macro_rules! hal { $( impl I2C<$I2CX, (Sda, Scl)> where - Sda: AnyPin, - Scl: AnyPin, + Sda: AnyPin, + Scl: AnyPin, { /// Configures the I2C peripheral to work in master mode pub fn $i2cX( @@ -292,8 +264,8 @@ macro_rules! hal { system_clock: SystemF) -> Self where F: Into, - Sda::Id: SdaPin<$I2CX>, - Scl::Id: SclPin<$I2CX>, + Sda::Id: ValidSdaPin<$I2CX>, + Scl::Id: ValidSclPin<$I2CX>, SystemF: Into, { Self::new_controller(i2c, sda_pin, scl_pin, freq.into(), resets, system_clock.into()) diff --git a/rp2040-hal/src/i2c/controller.rs b/rp2040-hal/src/i2c/controller.rs index 13524455b..4409b9e7b 100644 --- a/rp2040-hal/src/i2c/controller.rs +++ b/rp2040-hal/src/i2c/controller.rs @@ -1,7 +1,7 @@ use core::{marker::PhantomData, ops::Deref}; use crate::{ - gpio::{pin::FunctionI2C, AnyPin}, + gpio::{AnyPin, FunctionI2C}, resets::SubsystemReset, }; use fugit::HertzU32; @@ -11,15 +11,15 @@ use pac::{i2c0::RegisterBlock as Block, RESETS}; #[cfg(feature = "eh1_0_alpha")] use eh1_0_alpha::i2c as eh1; -use super::{i2c_reserved_addr, Controller, Error, SclPin, SdaPin, I2C}; +use super::{i2c_reserved_addr, Controller, Error, ValidSclPin, ValidSdaPin, I2C}; impl I2C where T: SubsystemReset + Deref, - Sda: AnyPin, - Scl: AnyPin, - Sda::Id: SdaPin, - Scl::Id: SclPin, + Sda: AnyPin, + Scl: AnyPin, + Sda::Id: ValidSdaPin, + Scl::Id: ValidSclPin, { /// Configures the I2C peripheral to work in controller mode pub fn new_controller( @@ -327,10 +327,10 @@ impl, PINS> eh1::I2c for I2C { Read::read(self, addr, buffer) } - fn transaction<'a>( + fn transaction( &mut self, address: u8, - operations: &mut [eh1::Operation<'a>], + operations: &mut [eh1::Operation<'_>], ) -> Result<(), Self::Error> { let addr: u16 = address.into(); self.setup(addr); diff --git a/rp2040-hal/src/i2c/peripheral.rs b/rp2040-hal/src/i2c/peripheral.rs index ff3011824..5ee3ac731 100644 --- a/rp2040-hal/src/i2c/peripheral.rs +++ b/rp2040-hal/src/i2c/peripheral.rs @@ -1,12 +1,12 @@ use core::{marker::PhantomData, ops::Deref}; use crate::{ - gpio::{pin::FunctionI2C, AnyPin}, + gpio::{AnyPin, FunctionI2C}, resets::SubsystemReset, }; use pac::{i2c0::RegisterBlock as I2CBlock, RESETS}; -use super::{Peripheral, SclPin, SdaPin, I2C}; +use super::{Peripheral, ValidSclPin, ValidSdaPin, I2C}; /// I2C bus events #[derive(Debug, PartialEq, Eq)] @@ -40,10 +40,10 @@ pub struct I2CPeripheralEventIterator { impl I2C where T: SubsystemReset + Deref, - Sda: AnyPin, - Scl: AnyPin, - Sda::Id: SdaPin, - Scl::Id: SclPin, + Sda: AnyPin, + Scl: AnyPin, + Sda::Id: ValidSdaPin, + Scl::Id: ValidSclPin, { /// Configures the I2C peripheral to work in peripheral mode /// diff --git a/rp2040-hal/src/pio.rs b/rp2040-hal/src/pio.rs index 8e59a29ea..07c71c623 100644 --- a/rp2040-hal/src/pio.rs +++ b/rp2040-hal/src/pio.rs @@ -1,21 +1,27 @@ //! Programmable IO (PIO) //! See [Chapter 3 of the datasheet](https://rptl.io/rp2040-datasheet#section_pio) for more details. +use core::ops::Deref; + use crate::{ atomic_register_access::{write_bitmask_clear, write_bitmask_set}, dma::{EndlessReadTarget, EndlessWriteTarget, ReadTarget, WriteTarget}, + gpio::{Function, FunctionPio0, FunctionPio1}, resets::SubsystemReset, typelevel::Sealed, }; use pio::{Instruction, InstructionOperands, Program, SideSet, Wrap}; -use rp2040_pac::dma::ch::ch_ctrl_trig::TREQ_SEL_A; -use rp2040_pac::{PIO0, PIO1}; +use rp2040_pac::{dma::ch::ch_ctrl_trig::TREQ_SEL_A, pio0::RegisterBlock, PIO0, PIO1}; const PIO_INSTRUCTION_COUNT: usize = 32; +impl crate::typelevel::Sealed for PIO0 {} +impl crate::typelevel::Sealed for PIO1 {} + /// PIO Instance -pub trait PIOExt: - core::ops::Deref + SubsystemReset + Sized + Send + Sealed -{ +pub trait PIOExt: Deref + SubsystemReset + Sized + Send + Sealed { + /// Associated Pin Function. + type PinFunction: Function; + /// Create a new PIO wrapper and split the state machines into individual objects. #[allow(clippy::type_complexity)] // Required for symmetry with PIO::free(). fn split( @@ -68,11 +74,13 @@ pub trait PIOExt: } impl PIOExt for PIO0 { + type PinFunction = FunctionPio0; fn id() -> usize { 0 } } impl PIOExt for PIO1 { + type PinFunction = FunctionPio1; fn id() -> usize { 1 } @@ -446,8 +454,6 @@ impl ValidStateMachine for (P, SM) { } } -impl Sealed for (P, SM) {} - /// Pin State in the PIO /// /// Note the GPIO is able to override/invert that. @@ -473,7 +479,7 @@ pub enum PinDir { /// PIO State Machine (uninitialized, without a program). #[derive(Debug)] pub struct UninitStateMachine { - block: *const rp2040_pac::pio0::RegisterBlock, + block: *const RegisterBlock, sm: *const rp2040_pac::pio0::SM, _phantom: core::marker::PhantomData, } @@ -535,7 +541,7 @@ impl UninitStateMachine { &*self.sm } - unsafe fn pio(&self) -> &rp2040_pac::pio0::RegisterBlock { + unsafe fn pio(&self) -> &RegisterBlock { &*self.block } } @@ -1272,7 +1278,7 @@ impl StateMachine { /// PIO RX FIFO handle. pub struct Rx { - block: *const rp2040_pac::pio0::RegisterBlock, + block: *const RegisterBlock, _phantom: core::marker::PhantomData, } @@ -1410,7 +1416,7 @@ impl EndlessReadTarget for Rx {} /// PIO TX FIFO handle. pub struct Tx { - block: *const rp2040_pac::pio0::RegisterBlock, + block: *const RegisterBlock, _phantom: core::marker::PhantomData, } @@ -1603,7 +1609,7 @@ impl EndlessWriteTarget for Tx {} /// PIO Interrupt controller. #[derive(Debug)] pub struct Interrupt<'a, P: PIOExt, const IRQ: usize> { - block: *const rp2040_pac::pio0::RegisterBlock, + block: *const RegisterBlock, _phantom: core::marker::PhantomData<&'a P>, } @@ -1769,7 +1775,7 @@ impl<'a, P: PIOExt, const IRQ: usize> Interrupt<'a, P, IRQ> { ) } - unsafe fn block(&self) -> &rp2040_pac::pio0::RegisterBlock { + unsafe fn block(&self) -> &RegisterBlock { &*self.block } diff --git a/rp2040-hal/src/pwm/mod.rs b/rp2040-hal/src/pwm/mod.rs index 6faf294ca..5ae7d5da3 100644 --- a/rp2040-hal/src/pwm/mod.rs +++ b/rp2040-hal/src/pwm/mod.rs @@ -79,7 +79,7 @@ use core::marker::PhantomData; use crate::{ - gpio::{bank0::*, AnyPin, FunctionPwm, Pin, ValidPinMode}, + gpio::{bank0::*, AnyPin, FunctionPwm, Pin, ValidFunction}, resets::SubsystemReset, typelevel::{Is, Sealed}, }; @@ -539,9 +539,9 @@ pwm! { } /// Marker trait for valid input pins (Channel B only) -pub trait ValidPwmInputPin: Sealed {} +pub trait ValidPwmInputPin: ValidFunction + Sealed {} /// Marker trait for valid output pins -pub trait ValidPwmOutputPin: Sealed {} +pub trait ValidPwmOutputPin: ValidFunction + Sealed {} impl Slices { /// Free the pwm registers from the pwm hal struct while consuming it. @@ -690,12 +690,11 @@ impl PwmPin for Channel { impl Channel { /// Capture a gpio pin and use it as pwm output for channel A - pub fn output_to(&mut self, pin: P) -> Pin + pub fn output_to(&mut self, pin: P) -> Pin where P::Id: ValidPwmOutputPin, - P::Mode: ValidPinMode, { - pin.into().into_mode() + pin.into().into_function() } /// Invert channel output @@ -713,12 +712,11 @@ impl Channel { impl Channel { /// Capture a gpio pin and use it as pwm output for channel B - pub fn output_to(&mut self, pin: P) -> Pin + pub fn output_to(&mut self, pin: P) -> Pin where P::Id: ValidPwmOutputPin, - P::Mode: ValidPinMode, { - pin.into().into_mode() + pin.into().into_function() } /// Invert channel output @@ -739,30 +737,30 @@ where S::Mode: ValidSliceInputMode, { /// Capture a gpio pin and use it as pwm input for channel B - pub fn input_from(&mut self, pin: P) -> Pin + pub fn input_from(&mut self, pin: P) -> Pin where P::Id: ValidPwmInputPin, { - pin.into().into_mode() + pin.into().into_function() } } impl> Slice { /// Capture a gpio pin and use it as pwm output - pub fn output_to(&mut self, pin: P) -> Pin + pub fn output_to(&mut self, pin: P) -> Pin where P::Id: ValidPwmOutputPin, { - pin.into().into_mode() + pin.into().into_function() } } impl> Slice { /// Capture a gpio pin and use it as pwm input for channel B - pub fn input_from(&mut self, pin: P) -> Pin + pub fn input_from(&mut self, pin: P) -> Pin where P::Id: ValidPwmInputPin, { - pin.into().into_mode() + pin.into().into_function() } } diff --git a/rp2040-hal/src/sio.rs b/rp2040-hal/src/sio.rs index 3dbd59abd..ead75c858 100644 --- a/rp2040-hal/src/sio.rs +++ b/rp2040-hal/src/sio.rs @@ -99,6 +99,11 @@ impl Sio { } } + /// Reads the whole bank0 at once. + pub fn read_bank0() -> u32 { + unsafe { (*pac::SIO::PTR).gpio_in.read().bits() } + } + /// Returns whether we are running on Core 0 (`0`) or Core 1 (`1`). pub fn core() -> CoreId { // Safety: it is always safe to read this read-only register diff --git a/rp2040-hal/src/spi.rs b/rp2040-hal/src/spi.rs index f624fa589..10624410a 100644 --- a/rp2040-hal/src/spi.rs +++ b/rp2040-hal/src/spi.rs @@ -13,26 +13,27 @@ //! let sio = Sio::new(peripherals.SIO); //! let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS); //! -//! let _ = pins.gpio2.into_mode::(); -//! let _ = pins.gpio3.into_mode::(); +//! let sclk = pins.gpio2.into_mode::(); +//! let mosi = pins.gpio3.into_mode::(); //! -//! let spi = Spi::<_, _, 8>::new(peripherals.SPI0).init(&mut peripherals.RESETS, 125_000_000u32.Hz(), 16_000_000u32.Hz(), &MODE_0); +//! let spi = Spi::<_, _, _, 8>::new(peripherals.SPI0, (mosi, sclk)).init(&mut peripherals.RESETS, 125_000_000u32.Hz(), 16_000_000u32.Hz(), &MODE_0); //! ``` use crate::dma::{EndlessReadTarget, EndlessWriteTarget, ReadTarget, WriteTarget}; -use crate::resets::SubsystemReset; -use crate::typelevel::Sealed; use core::{convert::Infallible, marker::PhantomData, ops::Deref}; + #[cfg(feature = "eh1_0_alpha")] use eh1_0_alpha::spi as eh1; #[cfg(feature = "eh1_0_alpha")] use eh_nb_1_0_alpha::spi as eh1nb; -use embedded_hal::blocking::spi; -use embedded_hal::spi::{FullDuplex, Mode, Phase, Polarity}; -use fugit::HertzU32; -use fugit::RateExtU32; -use pac::dma::ch::ch_ctrl_trig::TREQ_SEL_A; -use pac::RESETS; +use embedded_hal::{blocking::spi, spi::{FullDuplex, Mode, Phase, Polarity}}; +use fugit::{HertzU32, RateExtU32}; +use pac::{dma::ch::ch_ctrl_trig::TREQ_SEL_A, RESETS}; + +use crate::{resets::SubsystemReset, typelevel::Sealed}; + +mod pins; +pub use pins::*; /// State of the SPI pub trait State: Sealed {} @@ -88,15 +89,17 @@ impl Sealed for u8 {} impl Sealed for u16 {} /// Spi -pub struct Spi { +pub struct Spi, const DS: u8> { device: D, + pins: P, state: PhantomData, } -impl Spi { - fn transition(self, _: To) -> Spi { +impl, const DS: u8> Spi { + fn transition(self, _: To) -> Spi { Spi { device: self.device, + pins: self.pins, state: PhantomData, } } @@ -155,11 +158,12 @@ impl Spi { } } -impl Spi { +impl, const DS: u8> Spi { /// Create new spi device - pub fn new(device: D) -> Spi { + pub fn new(device: D, pins: P) -> Spi { Spi { device, + pins, state: PhantomData, } } @@ -192,7 +196,7 @@ impl Spi { baudrate: B, mode: &Mode, slave: bool, - ) -> Spi { + ) -> Spi { self.device.reset_bring_down(resets); self.device.reset_bring_up(resets); @@ -217,12 +221,12 @@ impl Spi { peri_frequency: F, baudrate: B, mode: &Mode, - ) -> Spi { + ) -> Spi { self.init_spi(resets, peri_frequency, baudrate, mode, false) } /// Initialize the SPI in slave mode - pub fn init_slave(self, resets: &mut RESETS, mode: &Mode) -> Spi { + pub fn init_slave(self, resets: &mut RESETS, mode: &Mode) -> Spi { // Use dummy values for frequency and baudrate. // With both values 0, set_baudrate will set prescale == u8::MAX, which will break if debug assertions are enabled. // u8::MAX is outside the allowed range 2..=254 for CPSDVSR, which might interfere with proper operation in slave mode. @@ -230,7 +234,7 @@ impl Spi { } } -impl Spi { +impl, const DS: u8> Spi { fn is_writable(&self) -> bool { self.device.sspsr.read().tnf().bit_is_set() } @@ -244,7 +248,7 @@ impl Spi { } /// Disable the spi to reset its configuration - pub fn disable(self) -> Spi { + pub fn disable(self) -> Spi { self.device.sspcr1.modify(|_, w| w.sse().clear_bit()); self.transition(Disabled { __private: () }) @@ -255,7 +259,7 @@ macro_rules! impl_write { ($type:ident, [$($nr:expr),+]) => { $( - impl FullDuplex<$type> for Spi { + impl> FullDuplex<$type> for Spi { type Error = Infallible; fn read(&mut self) -> Result<$type, nb::Error> { @@ -280,17 +284,17 @@ macro_rules! impl_write { } } - impl spi::write::Default<$type> for Spi {} - impl spi::transfer::Default<$type> for Spi {} - impl spi::write_iter::Default<$type> for Spi {} + impl> spi::write::Default<$type> for Spi {} + impl> spi::transfer::Default<$type> for Spi {} + impl> spi::write_iter::Default<$type> for Spi {} #[cfg(feature = "eh1_0_alpha")] - impl eh1::ErrorType for Spi { + impl> eh1::ErrorType for Spi { type Error = Infallible; } #[cfg(feature = "eh1_0_alpha")] - impl eh1::SpiBusFlush for Spi { + impl> eh1::SpiBusFlush for Spi { fn flush(&mut self) -> Result<(), Self::Error> { while self.is_busy() {} Ok(()) @@ -298,7 +302,7 @@ macro_rules! impl_write { } #[cfg(feature = "eh1_0_alpha")] - impl eh1::SpiBusRead<$type> for Spi { + impl> eh1::SpiBusRead<$type> for Spi { fn read(&mut self, words: &mut [$type]) -> Result<(), Self::Error> { for word in words.iter_mut() { // write empty word @@ -316,7 +320,7 @@ macro_rules! impl_write { } #[cfg(feature = "eh1_0_alpha")] - impl eh1::SpiBusWrite<$type> for Spi { + impl> eh1::SpiBusWrite<$type> for Spi { fn write(&mut self, words: &[$type]) -> Result<(), Self::Error> { for word in words.iter() { // write one word @@ -334,7 +338,7 @@ macro_rules! impl_write { } #[cfg(feature = "eh1_0_alpha")] - impl eh1::SpiBus<$type> for Spi { + impl> eh1::SpiBus<$type> for Spi { fn transfer(&mut self, read: &mut [$type], write: &[$type]) -> Result<(), Self::Error>{ let len = read.len().max(write.len()); for i in 0..len { @@ -374,7 +378,7 @@ macro_rules! impl_write { } #[cfg(feature = "eh1_0_alpha")] - impl eh1nb::FullDuplex<$type> for Spi { + impl> eh1nb::FullDuplex<$type> for Spi { fn read(&mut self) -> Result<$type, nb::Error> { if !self.is_readable() { return Err(nb::Error::WouldBlock); @@ -397,7 +401,7 @@ macro_rules! impl_write { } } - impl ReadTarget for Spi { + impl> ReadTarget for Spi { type ReceivedWord = $type; fn rx_treq() -> Option { @@ -416,9 +420,9 @@ macro_rules! impl_write { } } - impl EndlessReadTarget for Spi {} + impl> EndlessReadTarget for Spi {} - impl WriteTarget for Spi { + impl> WriteTarget for Spi { type TransmittedWord = $type; fn tx_treq() -> Option { @@ -437,7 +441,7 @@ macro_rules! impl_write { } } - impl EndlessWriteTarget for Spi {} + impl> EndlessWriteTarget for Spi {} )+ }; diff --git a/rp2040-hal/src/spi/pins.rs b/rp2040-hal/src/spi/pins.rs new file mode 100644 index 000000000..dad0ec61f --- /dev/null +++ b/rp2040-hal/src/spi/pins.rs @@ -0,0 +1,129 @@ +use super::SpiDevice; +use crate::gpio::{AnyPin, FunctionSpi}; +use crate::typelevel::{OptionTSome, Sealed}; +use crate::{gpio::bank0::*, typelevel::OptionTNone}; +use pac::{SPI0, SPI1}; + +/// Indicates a valid Rx pin for SPI0 or SPI1 +pub trait ValidPinRx: Sealed {} +/// Indicates a valid Tx pin for SPI0 or SPI1 +pub trait ValidPinTx: Sealed {} +/// Indicates a valid SCLK pin for SPI0 or SPI1 +pub trait ValidPinSck: Sealed {} +/// Indicates a valid CS pin for SPI0 or SPI1 +pub trait ValidPinCs: Sealed {} + +macro_rules! impl_valid_spi { + ($($spi:ident: { + rx: [$($rx:ident),*], + cs: [$($cs:ident),*], + sck: [$($sck:ident),*], + tx: [$($tx:ident),*], + }),*) => { + $( + $(impl ValidPinRx<$spi> for $rx {})* + $(impl ValidPinTx<$spi> for $tx {})* + $(impl ValidPinSck<$spi> for $sck {})* + $(impl ValidPinCs<$spi> for $cs {})* + )* + }; +} + +impl_valid_spi!( + SPI0: { + rx: [Gpio0, Gpio4, Gpio16, Gpio20], + cs: [Gpio1, Gpio5, Gpio17, Gpio21], + sck: [Gpio2, Gpio6, Gpio18, Gpio22], + tx: [Gpio3, Gpio7, Gpio19, Gpio23], + }, + SPI1: { + rx: [Gpio8, Gpio12, Gpio24, Gpio28], + cs: [Gpio9, Gpio13, Gpio25, Gpio29], + sck: [Gpio10, Gpio14, Gpio26], + tx: [Gpio11, Gpio15, Gpio27], + } +); + +/// Indicates a valid optional Rx pin for SPI0 or SPI1 +pub trait ValidOptionRx: Sealed {} +/// Indicates a valid optional Tx pin for SPI0 or SPI1 +pub trait ValidOptionTx: Sealed {} +/// Indicates a valid optional SCLK pin for SPI0 or SPI1 +pub trait ValidOptionSck: Sealed {} +/// Indicates a valid optional CS pin for SPI0 or SPI1 +pub trait ValidOptionCs: Sealed {} +impl ValidOptionRx for OptionTNone {} +impl ValidOptionCs for OptionTNone {} +impl ValidOptionSck for OptionTNone {} +impl ValidOptionTx for OptionTNone {} + +impl ValidOptionRx for OptionTSome +where + U: SpiDevice, + T: AnyPin, + T::Id: ValidPinRx, +{ +} +impl ValidOptionCs for OptionTSome +where + U: SpiDevice, + T: AnyPin, + T::Id: ValidPinCs, +{ +} +impl ValidOptionSck for OptionTSome +where + U: SpiDevice, + T: AnyPin, + T::Id: ValidPinSck, +{ +} +impl ValidOptionTx for OptionTSome +where + U: SpiDevice, + T: AnyPin, + T::Id: ValidPinTx, +{ +} + +/// Declares a valid SPI pinout. +pub trait ValidSpiPinout: Sealed { + #[allow(missing_docs)] + type Rx: ValidOptionRx; + #[allow(missing_docs)] + type Cs: ValidOptionCs; + #[allow(missing_docs)] + type Sck: ValidOptionSck; + #[allow(missing_docs)] + type Tx: ValidOptionTx; +} + +impl ValidSpiPinout for (Tx, Sck) +where + Spi: SpiDevice, + Tx: AnyPin, + Sck: AnyPin, + Tx::Id: ValidPinTx, + Sck::Id: ValidPinSck, +{ + type Rx = OptionTNone; + type Cs = OptionTNone; + type Sck = OptionTSome; + type Tx = OptionTSome; +} + +impl ValidSpiPinout for (Tx, Rx, Sck) +where + Spi: SpiDevice, + Tx: AnyPin, + Sck: AnyPin, + Rx: AnyPin, + Tx::Id: ValidPinTx, + Sck::Id: ValidPinSck, + Rx::Id: ValidPinRx +{ + type Rx = OptionTSome; + type Cs = OptionTNone; + type Sck = OptionTSome; + type Tx = OptionTSome; +} diff --git a/rp2040-hal/src/typelevel.rs b/rp2040-hal/src/typelevel.rs index 3eae9d4fd..ddf17b4a8 100644 --- a/rp2040-hal/src/typelevel.rs +++ b/rp2040-hal/src/typelevel.rs @@ -604,7 +604,7 @@ //! pub fn elided(mut self) -> Self { //! let pin = self.pin.into(); //! let mut pin = pin.into_push_pull_output(); -//! pin.set_high().ok(); +//! let _ = pin.set_high(); //! let pin = pin.into_floating_input(); //! let _bit = pin.is_low().unwrap(); //! let pin = pin.into_mode(); @@ -614,7 +614,7 @@ //! pub fn expanded(mut self) -> Self { //! let pin: SpecificPin

= self.pin.into(); //! let mut pin: Pin = pin.into_push_pull_output(); -//! pin.set_high().ok(); +//! let _ = pin.set_high(); //! let pin: Pin = pin.into_floating_input(); //! let _bit = pin.is_low().unwrap(); //! let pin: SpecificPin

= pin.into_mode::(); @@ -643,6 +643,10 @@ use core::borrow::{Borrow, BorrowMut}; pub(crate) use private::Sealed; +impl Sealed for (A, B) {} +impl Sealed for (A, B, C) {} +impl Sealed for (A, B, C, D) {} + /// Type-level version of the [None] variant #[derive(Default)] pub struct NoneT; @@ -697,3 +701,27 @@ where { type Type = T; } + +// ===================== +// Type level option +// ===================== + +/// Type-level `enum` for Option. +pub trait OptionT: Sealed { + /// Is this Some or None ? + const IS_SOME: bool; +} + +/// Type-level variant for `OptionT` +pub struct OptionTNone; +impl Sealed for OptionTNone {} +impl OptionT for OptionTNone { + const IS_SOME: bool = false; +} + +/// Type-level variant for `OptionT` +pub struct OptionTSome(pub T); +impl Sealed for OptionTSome {} +impl OptionT for OptionTSome { + const IS_SOME: bool = true; +} diff --git a/rp2040-hal/src/uart/mod.rs b/rp2040-hal/src/uart/mod.rs index f9696a562..5b1f65223 100644 --- a/rp2040-hal/src/uart/mod.rs +++ b/rp2040-hal/src/uart/mod.rs @@ -19,8 +19,8 @@ //! //! // Set up UART on GP0 and GP1 (Pico pins 1 and 2) //! let pins = ( -//! pins.gpio0.into_mode(), -//! pins.gpio1.into_mode(), +//! pins.gpio0.into_function(), +//! pins.gpio1.into_function(), //! ); //! // Need to perform clock init before using UART or it will freeze. //! let uart = UartPeripheral::new(peripherals.UART0, pins, &mut peripherals.RESETS) @@ -38,11 +38,11 @@ mod reader; mod utils; mod writer; -pub use self::peripheral::UartPeripheral; -pub use self::pins::*; -pub use self::reader::{ReadError, ReadErrorType, Reader}; -pub use self::utils::*; -pub use self::writer::Writer; +pub use peripheral::UartPeripheral; +pub use pins::*; +pub use reader::{ReadError, ReadErrorType, Reader}; +pub use utils::*; +pub use writer::Writer; /// Common configurations for UART. #[deprecated(note = "Use UartConfig::new(...) instead.")] diff --git a/rp2040-hal/src/uart/peripheral.rs b/rp2040-hal/src/uart/peripheral.rs index ae2c990fe..94b151e51 100644 --- a/rp2040-hal/src/uart/peripheral.rs +++ b/rp2040-hal/src/uart/peripheral.rs @@ -4,8 +4,8 @@ //! UartPeripheral object that can both read and write. use super::*; -use crate::gpio::SomePin; use crate::pac::uart0::uartlcr_h::W as UART_LCR_H_Writer; +use crate::typelevel::OptionT; use core::convert::Infallible; use core::fmt; use embedded_hal::serial::{Read, Write}; diff --git a/rp2040-hal/src/uart/pins.rs b/rp2040-hal/src/uart/pins.rs index 201258e55..515f347b2 100644 --- a/rp2040-hal/src/uart/pins.rs +++ b/rp2040-hal/src/uart/pins.rs @@ -1,69 +1,133 @@ -use crate::gpio::{bank0, AnyPin, FunctionUart, OptionalPin}; +use crate::gpio::{bank0::*, AnyPin, FunctionUart}; use crate::pac::{UART0, UART1}; -use crate::typelevel::{NoneT, Sealed}; +use crate::typelevel::{OptionT, OptionTNone, OptionTSome, Sealed}; use super::UartDevice; +/// Indicates a valid TX pin for UART0 or UART1 +pub trait ValidPinTx: Sealed {} +/// Indicates a valid RX pin for UART0 or UART1 +pub trait ValidPinRx: Sealed {} +/// Indicates a valid CTS pin for UART0 or UART1 +pub trait ValidPinCts: Sealed {} +/// Indicates a valid RTS pin for UART0 or UART1 +pub trait ValidPinRts: Sealed {} + +macro_rules! impl_valid_uart { + ($($uart:ident: { + tx: [$($tx:ident),*], + rx: [$($rx:ident),*], + cts: [$($cts:ident),*], + rts: [$($rts:ident),*], + }),*) => { + $( + $(impl ValidPinTx<$uart> for $tx {})* + $(impl ValidPinRx<$uart> for $rx {})* + $(impl ValidPinCts<$uart> for $cts {})* + $(impl ValidPinRts<$uart> for $rts {})* + )* + }; +} + +impl_valid_uart!( + UART0: { + tx: [Gpio0, Gpio12, Gpio16, Gpio28], + rx: [Gpio1, Gpio13, Gpio17, Gpio29], + cts: [Gpio2, Gpio14, Gpio18], + rts: [Gpio3, Gpio15, Gpio19], + }, + UART1: { + tx: [Gpio4, Gpio8, Gpio20, Gpio24], + rx: [Gpio5, Gpio9, Gpio21, Gpio25], + cts: [Gpio6, Gpio10, Gpio22, Gpio26], + rts: [Gpio7, Gpio11, Gpio23, Gpio27], + } +); + +/// Indicates a valid optional Tx pin for UART0 or UART1 +pub trait ValidOptionTx: OptionT {} +/// Indicates a valid optional Rx pin for UART0 or UART1 +pub trait ValidOptionRx: OptionT {} +/// Indicates a valid optional Cts pin for UART0 or UART1 +pub trait ValidOptionCts: OptionT {} +/// Indicates a valid optional Rts pin for UART0 or UART1 +pub trait ValidOptionRts: OptionT {} +impl ValidOptionTx for OptionTNone {} +impl ValidOptionRx for OptionTNone {} +impl ValidOptionCts for OptionTNone {} +impl ValidOptionRts for OptionTNone {} + +impl ValidOptionTx for OptionTSome +where + U: UartDevice, + T: AnyPin, + T::Id: ValidPinTx, +{ +} +impl ValidOptionRx for OptionTSome +where + U: UartDevice, + T: AnyPin, + T::Id: ValidPinRx, +{ +} +impl ValidOptionCts for OptionTSome +where + U: UartDevice, + T: AnyPin, + T::Id: ValidPinCts, +{ +} +impl ValidOptionRts for OptionTSome +where + U: UartDevice, + T: AnyPin, + T::Id: ValidPinRts, +{ +} + /// Declares a valid UART pinout. -pub trait ValidUartPinout { +pub trait ValidUartPinout: Sealed { #[allow(missing_docs)] - type Rx: OptionalPin; + type Rx: ValidOptionRx; #[allow(missing_docs)] - type Tx: OptionalPin; + type Tx: ValidOptionTx; #[allow(missing_docs)] - type Cts: OptionalPin; + type Cts: ValidOptionCts; #[allow(missing_docs)] - type Rts: OptionalPin; + type Rts: ValidOptionRts; } -impl ValidUartPinout for Pins +impl ValidUartPinout for (Tx, Rx) where - UART: UartDevice, - TX: OptionalPin, - RX: OptionalPin, - CTS: OptionalPin, - RTS: OptionalPin, - TX::Id: ValidTxPin, - RX::Id: ValidRxPin, - CTS::Id: ValidCtsPin, - RTS::Id: ValidRtsPin, + Uart: UartDevice, + Tx: AnyPin, + Rx: AnyPin, + Tx::Id: ValidPinTx, + Rx::Id: ValidPinRx, { - type Rx = RX; - type Tx = TX; - type Cts = CTS; - type Rts = RTS; + type Tx = OptionTSome; + type Rx = OptionTSome; + type Cts = OptionTNone; + type Rts = OptionTNone; } -impl ValidUartPinout for (TX, RX) +impl ValidUartPinout for (Tx, Rx, Cts, Rts) where - UART: UartDevice, - TX: AnyPin, - RX: AnyPin, - TX::Id: ValidTxPin, - RX::Id: ValidRxPin, + Uart: UartDevice, + Tx: AnyPin, + Rx: AnyPin, + Cts: AnyPin, + Rts: AnyPin, + Tx::Id: ValidPinTx, + Rx::Id: ValidPinRx, + Cts::Id: ValidPinCts, + Rts::Id: ValidPinRts, { - type Rx = RX; - type Tx = TX; - type Cts = NoneT; - type Rts = NoneT; -} - -impl ValidUartPinout for (TX, RX, CTS, RTS) -where - UART: UartDevice, - TX: AnyPin, - RX: AnyPin, - CTS: AnyPin, - RTS: AnyPin, - TX::Id: ValidTxPin, - RX::Id: ValidRxPin, - CTS::Id: ValidCtsPin, - RTS::Id: ValidRtsPin, -{ - type Rx = RX; - type Tx = TX; - type Cts = CTS; - type Rts = RTS; + type Rx = OptionTSome; + type Tx = OptionTSome; + type Cts = OptionTSome; + type Rts = OptionTSome; } /// Customizable Uart pinout, allowing you to set the pins individually. @@ -75,7 +139,7 @@ where /// |UART0|0, 12, 16, 28|1, 13, 17, 29|2, 14, 18 |3, 15, 19 | /// |UART1|4, 8, 20, 24 |5, 9, 21, 25 |6, 10, 22, 26|7, 11, 23, 27| /// -/// Every field can be set to [`NoneT`] to not configure them. +/// Every field can be set to [`OptionTNone`] to not configure them. /// /// Note that you can also use tuples `(RX, TX)` or `(RX, TX, CTS, RTS)` instead of this type. /// @@ -86,117 +150,82 @@ where /// # use rp2040_hal::pac::UART0; /// # let gpio_pins: rp2040_hal::gpio::Pins = unsafe { core::mem::zeroed() }; /// let pins = Pins::default() -/// .tx(gpio_pins.gpio0.into_mode()) -/// .rx(gpio_pins.gpio1.into_mode()); +/// .tx(gpio_pins.gpio0.into_function()) +/// .rx(gpio_pins.gpio1.into_function()); /// /// fn assert_is_valid_uart0>(_: T) {} /// /// assert_is_valid_uart0(pins); /// ``` #[allow(missing_docs)] -pub struct Pins { - pub tx: TX, - pub rx: RX, - pub rts: RTS, - pub cts: CTS, +pub struct Pins { + pub tx: Tx, + pub rx: Rx, + pub cts: Cts, + pub rts: Rts, } -impl Default for Pins { +impl Default for Pins { fn default() -> Self { Self { - tx: NoneT, - rx: NoneT, - rts: NoneT, - cts: NoneT, + tx: OptionTNone, + rx: OptionTNone, + rts: OptionTNone, + cts: OptionTNone, } } } -impl Pins -where - TX: OptionalPin, - RX: OptionalPin, - CTS: OptionalPin, - RTS: OptionalPin, -{ +impl Pins { /// Set the TX pin - pub fn tx(self, tx: NTX) -> Pins { + pub fn tx(self, tx: NewTx) -> Pins, Rx, Cts, Rts> { Pins { - tx, + tx: OptionTSome(tx), rx: self.rx, rts: self.rts, cts: self.cts, } } /// Set the RX pin - pub fn rx(self, rx: NRX) -> Pins { + pub fn rx(self, rx: NewRx) -> Pins, Cts, Rts> { Pins { tx: self.tx, - rx, + rx: OptionTSome(rx), rts: self.rts, cts: self.cts, } } /// Set the CTS pin - pub fn cts(self, cts: NCTS) -> Pins { + pub fn cts(self, cts: NewCts) -> Pins, Rts> { Pins { tx: self.tx, rx: self.rx, rts: self.rts, - cts, + cts: OptionTSome(cts), } } /// Set the RTS pin - pub fn rts(self, rts: NRTS) -> Pins { + pub fn rts(self, rts: NewRts) -> Pins> { Pins { tx: self.tx, rx: self.rx, - rts, + rts: OptionTSome(rts), cts: self.cts, } } } -/// Indicates a valid TX pin for UART0 or UART1 -pub trait ValidTxPin: Sealed {} -/// Indicates a valid RX pin for UART0 or UART1 -pub trait ValidRxPin: Sealed {} -/// Indicates a valid CTS pin for UART0 or UART1 -pub trait ValidCtsPin: Sealed {} -/// Indicates a valid RTS pin for UART0 or UART1 -pub trait ValidRtsPin: Sealed {} - -macro_rules! impl_valid_uart { - ($($uart:ident: { - tx: [$($tx:ident),*], - rx: [$($rx:ident),*], - cts: [$($cts:ident),*], - rts: [$($rts:ident),*], - }),*) => { - $( - impl ValidRxPin<$uart> for NoneT {} - impl ValidTxPin<$uart> for NoneT {} - impl ValidCtsPin<$uart> for NoneT {} - impl ValidRtsPin<$uart> for NoneT {} - $(impl ValidTxPin<$uart> for bank0::$tx {})* - $(impl ValidRxPin<$uart> for bank0::$rx {})* - $(impl ValidCtsPin<$uart> for bank0::$cts {})* - $(impl ValidRtsPin<$uart> for bank0::$rts {})* - )* - }; +impl Sealed for Pins {} +impl ValidUartPinout for Pins +where + Uart: UartDevice, + Tx: ValidOptionTx, + Rx: ValidOptionRx, + Cts: ValidOptionCts, + Rts: ValidOptionRts, +{ + type Rx = Rx; + type Tx = Tx; + type Cts = Cts; + type Rts = Rts; } - -impl_valid_uart!( - UART0: { - tx: [Gpio0, Gpio12, Gpio16, Gpio28], - rx: [Gpio1, Gpio13, Gpio17, Gpio29], - cts: [Gpio2, Gpio14, Gpio18], - rts: [Gpio3, Gpio15, Gpio19], - }, - UART1: { - tx: [Gpio4, Gpio8, Gpio20, Gpio24], - rx: [Gpio5, Gpio9, Gpio21, Gpio25], - cts: [Gpio6, Gpio10, Gpio22, Gpio26], - rts: [Gpio7, Gpio11, Gpio23, Gpio27], - } -);