diff --git a/on-target-tests/tests/dma_spi_loopback_u16.rs b/on-target-tests/tests/dma_spi_loopback_u16.rs index 98bc2ea2b..df264b65d 100644 --- a/on-target-tests/tests/dma_spi_loopback_u16.rs +++ b/on-target-tests/tests/dma_spi_loopback_u16.rs @@ -7,6 +7,7 @@ use crate::hal::dma::Channels; use defmt_rtt as _; // defmt transport use defmt_test as _; +use hal::gpio::{self, Pin}; use panic_probe as _; use rp2040_hal as hal; // memory layout // panic handler use rp2040_hal::pac::SPI0; @@ -24,9 +25,12 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H; /// if your board has a different frequency const XTAL_FREQ_HZ: u32 = 12_000_000u32; +type MISO = Pin; +type MOSI = Pin; +type SCLK = Pin; struct State { channels: Option, - spi: Option>, + spi: Option>, } mod testdata { @@ -91,10 +95,10 @@ mod tests { ); // 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::<_, _, 16>::new(pac.SPI0); + let spi_sclk = pins.gpio6.into(); + let spi_mosi = pins.gpio7.into(); + let spi_miso = pins.gpio4.into(); + let spi = hal::spi::Spi::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/on-target-tests/tests/dma_spi_loopback_u8.rs b/on-target-tests/tests/dma_spi_loopback_u8.rs index 138b5d141..b6fef3fe3 100644 --- a/on-target-tests/tests/dma_spi_loopback_u8.rs +++ b/on-target-tests/tests/dma_spi_loopback_u8.rs @@ -9,6 +9,7 @@ use defmt_rtt as _; // defmt transport use defmt_test as _; use panic_probe as _; use rp2040_hal as hal; // memory layout // panic handler +use rp2040_hal::gpio::{self, Pin}; use rp2040_hal::pac::SPI0; use rp2040_hal::spi; @@ -24,9 +25,12 @@ pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H; /// if your board has a different frequency const XTAL_FREQ_HZ: u32 = 12_000_000u32; +type MISO = Pin; +type MOSI = Pin; +type SCLK = Pin; struct State { channels: Option, - spi: Option>, + spi: Option>, } mod testdata { @@ -92,10 +96,10 @@ mod tests { ); // 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_sclk = pins.gpio6.into(); + let spi_mosi = pins.gpio7.into(); + let spi_miso = pins.gpio4.into(); + let spi = hal::spi::Spi::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/CHANGELOG.md b/rp2040-hal/CHANGELOG.md index 55fc77af7..e120b7bd5 100644 --- a/rp2040-hal/CHANGELOG.md +++ b/rp2040-hal/CHANGELOG.md @@ -19,17 +19,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - multicore: remove the requirement on the closure to never return - #594 @ithinuel - Updated dependency on rp2040-boot2 to version 0.3.0. - @jannic - - This doubles the flash access speed to the value used by the C SDK by - default. So it should usually be safe. However, if you are overclocking - the RP2040, you might need to lower the flash speed accordingly. -- timer: Make sure clocks are initialized before creating a timer - #618 @jannic + This doubles the flash access speed to the value used by the C SDK by + default. So it should usually be safe. However, if you are overclocking + the RP2040, you might need to lower the flash speed accordingly. - Doc: Several improvements have been made to documentation: #607 #597 - DMA: Check for valid word sizes at compile time - #600 @jannic +- Use an enum for core identification. - @ithinuel +- 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 +- Reduce code repetition in i2c modules. - @ithinuel +- Rename `DontInvert` to `Normal`. - @ithinuel +- Prevent the creation of multiple instances of `adc::TempSensor` - @ithinuel ### Added - timer::Timer implements the embedded-hal delay traits and Copy/Clone - #614 @ithinuel @jannic - DMA: Allow access to the DMA engine's byteswapping feature - #603 @Gip-Gip +- 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. ## [0.8.1] - 2023-05-05 diff --git a/rp2040-hal/Cargo.toml b/rp2040-hal/Cargo.toml index eb5757dae..63026199a 100644 --- a/rp2040-hal/Cargo.toml +++ b/rp2040-hal/Cargo.toml @@ -40,6 +40,8 @@ defmt = { version = ">=0.2.0, <0.4", optional = true } rtic-monotonic = { version = "1.0.0", optional = true } +frunk = { version = "0.4.1", default-features = false } + [dev-dependencies] cortex-m-rt = "0.7" panic-halt = "0.2.0" diff --git a/rp2040-hal/examples/adc.rs b/rp2040-hal/examples/adc.rs index bdb773f1f..773de7e36 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 @@ -107,10 +107,10 @@ fn main() -> ! { let mut adc = hal::Adc::new(pac.ADC, &mut pac.RESETS); // Enable the temperature sense channel - let mut temperature_sensor = adc.enable_temp_sensor(); + let mut temperature_sensor = adc.take_temp_sensor().unwrap(); // 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..5421c32ba 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 @@ -119,12 +119,12 @@ fn main() -> ! { ); // Configure GPIO 25 as an output to drive our LED. - // we can use into_mode() instead of into_pull_up_input() + // we can use reconfigure() 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.reconfigure(); // Set up the GPIO pin that will be our input - let in_pin = pins.gpio26.into_mode(); + let in_pin = pins.gpio26.reconfigure(); // 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..54b815f20 100644 --- a/rp2040-hal/examples/i2c.rs +++ b/rp2040-hal/examples/i2c.rs @@ -75,9 +75,9 @@ 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 not_an_scl_pin = pins.gpio20.into_mode::(); + let sda_pin = pins.gpio18.into_function::(); + let scl_pin = pins.gpio19.into_function::(); + // let not_an_scl_pin = pins.gpio20.into_function::(); // Create the I²C drive, using the two pre-configured pins. This will fail // at compile time if the pins are in the wrong mode, or if this I²C 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..8cb088202 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,17 +134,17 @@ fn main() -> ! { pwm.enable(); // Connect to GPI O1 as the input to channel B on PWM0 + let input_pin = pins.gpio1.reconfigure(); 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) input_pin.set_interrupt_enabled(gpio::Interrupt::EdgeLow, true); // Configure GPIO 25 as an output to drive our LED. - // we can use into_mode() instead of into_pull_up_input() + // we can use reconfigure() 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.reconfigure(); // 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 05aa8ae8f..eb212dc2c 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 9d48fb79b..67ba993d1 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 febc21ca1..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 9c9235db8..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 16f2a053f..b1c083069 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..205966c96 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(); //! ``` @@ -29,7 +29,7 @@ //! // Enable adc //! let mut adc = Adc::new(peripherals.ADC, &mut peripherals.RESETS); //! // Enable the temperature sensor -//! let mut temperature_sensor = adc.enable_temp_sensor(); +//! let mut temperature_sensor = adc.take_temp_sensor().unwrap(); //! // Read the ADC counts from the ADC channel //! let temperature_adc_counts: u16 = adc.read(&mut temperature_sensor).unwrap(); //! ``` @@ -37,21 +37,113 @@ //! 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, DynPinId, Function, OutputEnableOverride, Pin, PullType, ValidFunction, }, resets::SubsystemReset, }; const TEMPERATURE_SENSOR_CHANNEL: u8 = 4; -/// Adc +/// The pin was invalid for the requested operation +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct InvalidPinError; + +/// 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) + } +} + +macro_rules! channel { + ($pin:ident, $channel:expr) => { + impl Channel for AdcPin> + where + $pin: crate::gpio::ValidFunction, + { + type ID = u8; // ADC channels are identified numerically + + fn channel() -> u8 { + $channel + } + } + }; +} + +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 { + __private: (), +} + +impl Channel for TempSense { + type ID = u8; // ADC channels are identified numerically + + fn channel() -> u8 { + TEMPERATURE_SENSOR_CHANNEL + } +} + +/// Analog to Digital Convertor (ADC). +/// +/// Represents an ADC within the RP2040. Each ADC has multiple channels, and each +/// channel is either associated with a specific GPIO pin or attached to the internal +/// temperature sensor. You should put the relevant pin into ADC mode by creating an +/// [`AdcPin`] object with it, or you can put the ADC into `Temperature Sensing Mode` +/// by calling [`Adc::take_temp_sensor()`]. Either way, the resulting objects can be +/// passed to the [`OneShot::read()`][a] trait method to actually do the read. +/// +/// [a]: embedded_hal::adc::OneShot::read pub struct Adc { device: ADC, } @@ -81,75 +173,89 @@ impl Adc { self.device.result.read().result().bits() } - /// Enable temperature sensor, returns a channel to use + /// Enable temperature sensor, returns a channel to use. + /// + /// This can only be done once before calling [`Adc::disable_temp_sensor()`]. If the sensor has already + /// been enabled, this method will panic. + #[deprecated( + note = "This method may panic, use `take_temp_sensor()` instead.", + since = "0.9.0" + )] pub fn enable_temp_sensor(&mut self) -> TempSense { - self.device.cs.modify(|_, w| w.ts_en().set_bit()); + self.take_temp_sensor() + .expect("Temp sensor is already enabled.") + } - TempSense { __private: () } + /// Enable temperature sensor, returns a channel to use + /// + /// If the sensor has already been enabled, this method returns `None`. + pub fn take_temp_sensor(&mut self) -> Option { + let mut disabled = false; + self.device.cs.modify(|r, w| { + disabled = r.ts_en().bit_is_clear(); + // if bit was already set, this is a nop + w.ts_en().set_bit() + }); + disabled.then_some(TempSense { __private: () }) } /// Disable temperature sensor, consumes channel pub fn disable_temp_sensor(&mut self, _: TempSense) { self.device.cs.modify(|_, w| w.ts_en().clear_bit()); } -} -macro_rules! channel { - ($pin:ident, $channel:expr) => { - impl Channel for Pin<$pin, FloatingInput> { - type ID = u8; // ADC channels are identified numerically - - fn channel() -> u8 { - $channel - } + fn read(&mut self, chan: u8) -> u16 { + while !self.device.cs.read().ready().bit_is_set() { + cortex_m::asm::nop(); } - }; -} -channel!(Gpio26, 0); -channel!(Gpio27, 1); -channel!(Gpio28, 2); -channel!(Gpio29, 3); - -/// Internal temperature sensor type -pub struct TempSense { - __private: (), -} + self.device + .cs + .modify(|_, w| unsafe { w.ainsel().bits(chan).start_once().set_bit() }); -impl Channel for TempSense { - type ID = u8; // ADC channels are identified numerically + while !self.device.cs.read().ready().bit_is_set() { + cortex_m::asm::nop(); + } - fn channel() -> u8 { - TEMPERATURE_SENSOR_CHANNEL + self.device.result.read().result().bits() } } -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; - if chan == TEMPERATURE_SENSOR_CHANNEL { - self.device.cs.modify(|_, w| w.ts_en().set_bit()) - } + fn read(&mut self, _pin: &mut SRC) -> nb::Result { + let chan = SRC::channel(); - while !self.device.cs.read().ready().bit_is_set() { - cortex_m::asm::nop(); - } + Ok(self.read(chan).into()) + } +} - self.device - .cs - .modify(|_, w| unsafe { w.ainsel().bits(chan).start_once().set_bit() }); +// Implementation for dyn-pins +impl OneShot>> for Adc +where + WORD: From, + F: Function, + M: PullType, + DynPinId: ValidFunction, + AdcPin>: Channel, +{ + type Error = InvalidPinError; - while !self.device.cs.read().ready().bit_is_set() { - cortex_m::asm::nop(); - } + 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.device.result.read().result().bits().into()) + 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 3f717ce61..429eb101f 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 6b24b5220..000000000 --- a/rp2040-hal/src/gpio/dynpin.rs +++ /dev/null @@ -1,650 +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, -} - -#[cfg(feature = "eh1_0_alpha")] -impl eh1::Error for Error { - fn kind(&self) -> eh1::ErrorKind { - eh1::ErrorKind::Other - } -} - -//============================================================================== -// 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..6eb32c188 --- /dev/null +++ b/rp2040-hal/src/gpio/func.rs @@ -0,0 +1,210 @@ +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 {} + +/// Describes the function currently assigned to a pin with a dynamic type. +/// +/// A 'pin' on the RP2040 can be connected to different parts of the chip +/// internally - for example, it could be configured as a GPIO pin and connected +/// to the SIO block, or it could be configured as a UART pin and connected to +/// the UART block. +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DynFunction { + /// The 'XIP' (or Execute-in-place) function, which means talking to the QSPI Flash. + Xip, + /// The 'SPI' (or serial-peripheral-interface) function. + Spi, + /// The 'UART' (or serial-port) function. + Uart, + /// The 'I2C' (or inter-integrated circuit) function. This is sometimes also called TWI (for + /// two-wire-interface). + I2c, + /// The 'PWM' (or pulse-width-modulation) function. + Pwm, + /// The 'SIO' (or single-cycle input-output) function. This is the function to use for + /// 'manually' controlling the GPIO. + Sio(DynSioConfig), + /// The 'PIO' (or programmable-input-output) function for the PIO0 peripheral block. + Pio0, + /// The 'PIO' (or programmable-input-output) function for the PIO1 peripheral block. + Pio1, + /// The 'Clock' function. This can be used to input or output clock references to or from the + /// rp2040. + Clock, + /// The 'USB' function. Only VBUS detect, VBUS enable and overcurrent detect are configurable. + /// Other USB io have dedicated pins. + Usb, + /// The 'Null' function for unused pins. + Null, +} + +/// Value-level `enum` for SIO configuration. +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DynSioConfig { + /// Pin is configured as Input. + Input, + /// Pin is configured as Output. + Output, +} + +impl Function for DynFunction {} +impl func_sealed::Function for DynFunction { + #[inline] + fn from(f: DynFunction) -> Self { + f + } + + #[inline] + 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 [] { + #[inline] + fn from(_f: DynFunction) -> Self { + Self(()) + } + #[inline] + 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. +/// +/// Where `impl ValidFunction for I` reads as `F is a valid function implemented for the pin I`. +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 4d7329859..dae579ce7 100644 --- a/rp2040-hal/src/gpio/mod.rs +++ b/rp2040-hal/src/gpio/mod.rs @@ -1,7 +1,5 @@ //! General Purpose Input and Output (GPIO) //! -//! See [`pin`](self::pin) for implementation details and in-depth documentation. -//! //! ## Basic usage //! ```no_run //! use embedded_hal::digital::v2::{InputPin, OutputPin}; @@ -32,18 +30,36 @@ //! ``` //! 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 use embedded_hal::digital::v2::PinState; + +use crate::{ + atomic_register_access::{write_bitmask_clear, write_bitmask_set}, + sio::Sio, + typelevel::{self, Sealed}, +}; -pub mod dynpin; -pub use dynpin::*; +mod func; +pub(crate) mod pin; +mod pin_group; +mod pull; -mod reg; +pub use func::*; +pub use pin::{DynBankId, DynPinId, PinId}; +pub use pin_group::PinGroup; +pub use pull::*; +/// The amount of current that a pin can drive when used as an output. #[allow(clippy::enum_variant_names)] #[derive(Clone, Copy, Eq, PartialEq, Debug)] -/// The amount of current that a pin can drive when used as an output pub enum OutputDriveStrength { /// 2 mA TwoMilliAmps, @@ -55,8 +71,8 @@ pub enum OutputDriveStrength { TwelveMilliAmps, } +/// The slew rate of a pin when used as an output. #[derive(Clone, Copy, Eq, PartialEq, Debug)] -/// The slew rate of a pin when used as an output pub enum OutputSlewRate { /// Slew slow Slow, @@ -64,8 +80,8 @@ pub enum OutputSlewRate { Fast, } +/// Interrupt kind. #[derive(Clone, Copy, Eq, PartialEq, Debug)] -/// Interrupt kind pub enum Interrupt { /// While low LevelLow, @@ -76,12 +92,22 @@ 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, + Normal = 0, /// Invert the interrupt. Invert = 1, /// Drive interrupt low. @@ -90,11 +116,11 @@ 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, + Normal = 0, /// Invert the peripheral input. Invert = 1, /// Drive peripheral input low. @@ -107,7 +133,7 @@ pub enum InputOverride { /// Output enable override state. pub enum OutputEnableOverride { /// Use the original output enable signal from selected peripheral. - DontInvert = 0, + Normal = 0, /// Invert the output enable signal from selected peripheral. Invert = 1, /// Disable output. @@ -128,3 +154,1299 @@ pub enum OutputOverride { /// Drive output high. AlwaysHigh = 3, } + +/// Represents a pin, with a given ID (e.g. Gpio3), a given function (e.g. FunctionUart) and a given pull type +/// (e.g. pull-down). +pub struct Pin { + id: I, + function: F, + pull_type: P, +} + +/// Create a new pin instance. +/// +/// # Safety +/// The uniqueness 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::BusKeep, + (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: P2::from(self.pull_type.as_dyn()), + } + } + + /// Convert the pin from one state to the other. + pub fn reconfigure(self) -> Pin + where + F2: func::Function, + P2: PullType, + I: func::ValidFunction, + { + self.into_function().into_pull_type() + } + + /// 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_type(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.reconfigure() + } + + /// Disable the pin and set it to pull down + #[inline] + pub fn into_pull_down_disabled(self) -> Pin + where + I: ValidFunction, + { + self.reconfigure() + } + + /// Disable the pin and set it to pull up + #[inline] + pub fn into_pull_up_disabled(self) -> Pin + where + I: ValidFunction, + { + self.reconfigure() + } + + /// 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_type() + } + + /// 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_type() + } + + /// 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_type() + } + + /// Configure the pin to operate as a bus keep input + #[inline] + pub fn into_bus_keep_input(self) -> Pin, PullBusKeep> + where + I: ValidFunction>, + { + self.into_function().into_pull_type() + } + + /// 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, P> + 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, P> + 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.", + since = "0.9.0" + )] + pub fn into_readable_output(self) -> Pin, P> + 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.", + since = "0.9.0" + )] + pub fn into_readable_output_in_state(self, state: PinState) -> Pin, P> + 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_schmitt_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::Normal => 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::Normal => 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::Normal => 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, P> { + /// Is bypass enabled + #[inline] + pub fn is_sync_bypass(&self) -> bool { + let mask = self.id.mask(); + self.id.proc_in_by_pass().read().bits() & mask == mask + } + + /// Bypass the input sync stages. + /// + /// This saves two clock cycles in the input signal's path at the risks of intruducing metastability. + #[inline] + pub fn set_sync_bypass(&mut self, bypass: bool) { + let mask = self.id.mask(); + let reg = self.id.proc_in_by_pass(); + unsafe { + if bypass { + write_bitmask_set(reg.as_ptr(), mask); + } else { + write_bitmask_clear(reg.as_ptr(), mask); + } + } + } +} +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 P2::ID == self.id { + Ok(Pin { + id: P2::new(), + function: self.function, + pull_type: self.pull_type, + }) + } else { + Err(self) + } + } + + /// Try to change the pin's function. + pub fn try_into_function(self) -> Result, Pin> + where + F2: func::Function, + { + // 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 function_as_dyn = function.as_dyn(); + + use func_sealed::Function; + if function_as_dyn.is_valid(&self.id) { + if function_as_dyn != prev_function.as_dyn() { + pin::set_function(&self.id, function_as_dyn); + } + Ok(Pin { + function, + id: self.id, + 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_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_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, P> +where + I: PinId, + P: 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, P> +where + I: PinId, + P: 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, P> +where + I: PinId, + P: 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, P> +where + I: PinId, + P: PullType, +{ + type Error = Error; + + fn toggle(&mut self) -> Result<(), Self::Error> { + self._toggle(); + Ok(()) + } +} +impl embedded_hal::digital::v2::InputPin for Pin, P> +where + I: PinId, + P: 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_type: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_type),*); + + 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_type)),+) + } + } + } + } + }; + (struct: $bank:ident $prefix:ident $($PXi:ident, $id:expr, $func:ident, $pull_type: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_type: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>, +{ + /// [`PinId`] of the corresponding [`Pin`] + type Id: PinId; + /// [`func::Function`] of the corresponding [`Pin`] + type Function: func::Function; + /// [`PullType`] of the corresponding [`Pin`] + type Pull: PullType; +} + +impl Sealed for Pin +where + I: PinId, + F: func::Function, + P: PullType, +{ +} + +impl AnyPin for Pin +where + I: PinId, + F: func::Function, + P: PullType, +{ + type Id = I; + type Function = F; + type Pull = P; +} + +/// 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,

::Pull>; + +//============================================================================== +// 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`]. +/// +/// # 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_function] 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 [`AnyPin`]`` emulating open-drain function. +/// +/// This wrapper implements both InputPin and OutputPin, to simulate an open-drain pin as needed for +/// example by the wire protocol the DHT11 sensor speaks. +/// +/// +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 } + } +} + +impl InOutPin +where + T: AnyPin, + T::Id: ValidFunction, +{ + /// Releases the pin reverting to its previous function. + pub fn release(self) -> T { + // restore the previous typestate first + let mut inner = self.inner.reconfigure(); + // disable override + inner.set_output_enable_override(OutputEnableOverride::Normal); + // typelevel-return + 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::{ + ErrorType, InputPin, OutputPin, StatefulOutputPin, ToggleableOutputPin, + }; + + use super::{Error, FunctionSio, Pin, PinId, PullType, SioConfig, SioInput, SioOutput}; + + impl ErrorType for Pin, P> + where + I: PinId, + P: PullType, + S: SioConfig, + { + type Error = Error; + } + + impl OutputPin for Pin, P> + where + I: PinId, + P: 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, P> + where + I: PinId, + P: PullType, + { + fn is_high(&self) -> Result { + Ok(self._is_high()) + } + + fn is_low(&self) -> Result { + Ok(self._is_low()) + } + } + + impl StatefulOutputPin for Pin, P> + where + I: PinId, + P: 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, P> + where + I: PinId, + P: PullType, + { + fn toggle(&mut self) -> Result<(), Self::Error> { + self._toggle(); + Ok(()) + } + } + impl InputPin for Pin, P> + where + I: PinId, + P: 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 0c275cf6d..95b3e3c90 100644 --- a/rp2040-hal/src/gpio/pin.rs +++ b/rp2040-hal/src/gpio/pin.rs @@ -1,1289 +1,167 @@ -//! # 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}; - -use core::mem::transmute; - -/// Type-level marker for tracking which pin modes are valid for which pins -pub trait ValidPinMode: Sealed {} - -//============================================================================== -// 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; -} +//! This modules bridges that gap by adding a trait definition per register type and implementing it +//! for each of the relevant registers. -impl PinMode for Disabled { - const DYN: DynPinMode = DynPinMode::Disabled(C::DYN); -} - -impl PinMode for Input { - const DYN: DynPinMode = DynPinMode::Input(C::DYN); -} +use super::{DynFunction, DynPullType}; -impl PinMode for Output { - const DYN: DynPinMode = DynPinMode::Output(C::DYN); -} - -impl PinMode for Function { - const DYN: DynPinMode = DynPinMode::Function(C::DYN); -} +pub(crate) mod pin_sealed; -//============================================================================== -// 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, - }; - } - }; +/// 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, } -//============================================================================== -// OptionalPinId -//============================================================================== +/// Type-level `enum` for the pin's bank ID. +pub trait BankId: crate::typelevel::Sealed {} -/// 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 {} +/// Type-level `variant` of `BankId` +pub struct BankBank0; +impl crate::typelevel::Sealed for BankBank0 {} +impl BankId for BankBank0 {} -impl OptionalPinId for NoneT {} +/// Type-level `variant` of `BankId` +pub struct BankQspi; +impl crate::typelevel::Sealed for BankQspi {} +impl BankId for BankQspi {} -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, +/// 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; } -// [`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 - } +/// Value-level representation for the pin (bank + id). +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct DynPinId { + /// Pin bank. + pub bank: DynBankId, + /// Pin number. + pub num: u8, } - -impl Registers { - /// Create a new instance of [`Registers`] - /// - /// # Safety - /// - /// Users must never create two simultaneous instances of this `struct` with - /// the same [`PinId`] +impl PinId for DynPinId { #[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); + fn as_dyn(&self) -> DynPinId { + *self } } -//============================================================================== -// Pin -//============================================================================== - -/// A type-level GPIO pin, parameterized by [`PinId`] and [`PinMode`] types -pub struct Pin -where - I: PinId, - M: PinMode + ValidPinMode, -{ - regs: Registers, - mode: PhantomData, -} - -impl Pin -where - I: PinId, - M: PinMode + 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() - } - - /// 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) - } - - /// 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); - } - - #[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: PinMode + ValidPinMode, -{ -} - -impl AnyPin for Pin -where - I: PinId, - M: PinMode + ValidPinMode, -{ - type Id = I; - 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,

::Mode>; - -impl AsRef

for SpecificPin

{ - #[inline] - fn as_ref(&self) -> &P { - // SAFETY: This is guaranteed to be safe, because P == SpecificPin

- // Transmuting between `v1` and `v2` `Pin` types is also safe, because - // both are zero-sized, and single-field, newtype structs are guaranteed - // to have the same layout as the field anyway, even for repr(Rust). - unsafe { transmute(self) } - } -} - -impl AsMut

for SpecificPin

{ - #[inline] - fn as_mut(&mut self) -> &mut P { - // SAFETY: This is guaranteed to be safe, because P == SpecificPin

- // Transmuting between `v1` and `v2` `Pin` types is also safe, because - // both are zero-sized, and single-field, newtype structs are guaranteed - // to have the same layout as the field anyway, ValidPinMode en for repr(Rust). - unsafe { transmute(self) } - } -} - -//============================================================================== -// 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 { - #[allow(missing_docs)] - type Id: OptionalPinId; -} - -impl OptionalPin for NoneT { - type Id = NoneT; -} - -impl OptionalPin for P { - type Id = P::Id; -} - -/// 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: AnyPin + Sealed {} -impl SomePin for P {} - -//============================================================================== -// 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 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>, - )+ +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>] { + #[inline] + fn as_dyn(&self) -> DynPinId { + DynPinId { + bank: DynBankId::$bank, + num: $id + } + } } + impl pin_sealed::TypeLevelPinId for [<$prefix $name>] { + type Bank = []; - 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); + const ID: DynPinId = DynPinId { + bank: DynBankId::$bank, + num: $id + }; - io.reset_bring_up(reset); - pads.reset_bring_up(reset); - unsafe { - Self { - _io: io, - _pads: pads, - _sio: sio, - $( - [<$PXi:lower>]: Pin::new(), - )+ - } - } + fn new() -> Self { + Self(()) } } - - $( impl super::ValidPinMode for super::[] {} )+ - } + )* } - } + }; } - -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, BankBank0, 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, BankQspi, 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::BusKeep => (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..fc1f56e53 --- /dev/null +++ b/rp2040-hal/src/gpio/pin/pin_sealed.rs @@ -0,0 +1,206 @@ +use crate::sio::CoreId; + +use super::{DynBankId, DynPinId}; + +pub trait TypeLevelPinId: super::PinId { + type Bank: super::BankId; + + 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 proc_in_by_pass(&self) -> &pac::syscfg::PROC_IN_SYNC_BYPASS; + + 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 proc_in_by_pass(&self) -> &rp2040_pac::syscfg::PROC_IN_SYNC_BYPASS { + let pin = self.as_dyn(); + unsafe { + let syscfg = &*pac::SYSCFG::PTR; + match pin.bank { + DynBankId::Bank0 => &syscfg.proc_in_sync_bypass, + DynBankId::Qspi => core::mem::transmute(&syscfg.proc_in_sync_bypass_hi), + } + } + } + + 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/pin_group.rs b/rp2040-hal/src/gpio/pin_group.rs new file mode 100644 index 000000000..61657818a --- /dev/null +++ b/rp2040-hal/src/gpio/pin_group.rs @@ -0,0 +1,170 @@ +use embedded_hal::digital::v2::PinState; +use frunk::{hlist::Plucker, HCons, HNil}; + +use crate::typelevel::Sealed; + +use super::{ + pin::pin_sealed::TypeLevelPinId, AnyPin, FunctionSio, FunctionSioInput, FunctionSioOutput, Pin, + PinId, PullType, SioConfig, +}; + +/// Generate a read mask for a pin list. +pub trait ReadPinHList: Sealed { + /// Generate a mask for a pin list. + fn read_mask(&self) -> u32; +} +impl ReadPinHList for HNil { + fn read_mask(&self) -> u32 { + 0 + } +} +impl ReadPinHList for HCons { + fn read_mask(&self) -> u32 { + (1 << self.head.borrow().id().num) | self.tail.read_mask() + } +} + +/// Generate a write mask for a pin list. +pub trait WritePinHList: Sealed { + /// Generate a mask for a pin list. + fn write_mask(&self) -> u32; +} +impl WritePinHList for HNil { + fn write_mask(&self) -> u32 { + 0 + } +} +impl WritePinHList + for HCons, T> +{ + fn write_mask(&self) -> u32 { + self.tail.write_mask() + } +} +impl WritePinHList + for HCons, T> +{ + fn write_mask(&self) -> u32 { + self.tail.write_mask() + } +} + +/// A group of pins to be controlled together and guaranty single cycle control of several pins. +/// +/// ```no_run +/// # macro_rules! defmt { ($($a:tt)*) => {}} +/// use rp2040_hal::{pac, gpio::{bank0::Gpio12, Pin, Pins, PinState, PinGroup}, 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 group = PinGroup::new(); +/// let group = group.add_pin(pins.gpio0.into_pull_up_input()); +/// let mut group = group.add_pin(pins.gpio4.into_push_pull_output_in_state(PinState::High)); +/// +/// defmt!("Group's state is: {}", group.read()); +/// group.toggle(); +/// defmt!("Group's state is: {}", group.read()); +/// ``` +pub struct PinGroup(T); +impl PinGroup { + /// Creates an empty pin group. + pub fn new() -> Self { + PinGroup(HNil) + } + + /// Add a pin to the group. + pub fn add_pin(self, pin: P) -> PinGroup> + where + C: SioConfig, + P: AnyPin>, + P::Id: TypeLevelPinId, + { + PinGroup(HCons { + head: pin, + tail: self.0, + }) + } +} +impl PinGroup> +where + H::Id: TypeLevelPinId, + H: AnyPin, +{ + /// Add a pin to the group. + pub fn add_pin(self, pin: P) -> PinGroup>> + where + C: SioConfig, + P: AnyPin>, + P::Id: TypeLevelPinId::Bank>, + { + PinGroup(HCons { + head: pin, + tail: self.0, + }) + } + + /// Pluck a pin from the group. + #[allow(clippy::type_complexity)] + pub fn remove_pin( + self, + ) -> (P, PinGroup< as Plucker>::Remainder>) + where + HCons: Plucker, + { + let (p, rest): (P, _) = self.0.pluck(); + (p, PinGroup(rest)) + } +} +impl PinGroup> +where + HCons: ReadPinHList + WritePinHList, + H: AnyPin, +{ + /// Read the whole group at once. + /// + /// The returned value is a bit field where each pin populates its own index. Therefore, there + /// might be "holes" in the value. Unoccupied bits will always read as 0. + /// + /// For example, if the group contains Gpio1 and Gpio3, a read may yield: + /// ```text + /// 0b0000_0000__0000_0000__0000_0000__0000_1010 + /// This is Gpio3 ↑↑↑ + /// Gpio2 is not used || + /// This is Gpio1 | + /// ``` + pub fn read(&self) -> u32 { + let mask = self.0.read_mask(); + crate::sio::Sio::read_bank0() & mask + } + + /// Write this set of pins all at the same time. + pub fn set(&mut self, state: PinState) { + use super::pin::pin_sealed::PinIdOps; + let mask = self.0.write_mask(); + let head_id = self.0.head.borrow().id(); + if state == PinState::Low { + head_id.sio_out_clr().write(|w| unsafe { w.bits(mask) }); + } else { + head_id.sio_out_set().write(|w| unsafe { w.bits(mask) }); + } + } + + /// Toggles this set of pins all at the same time. + pub fn toggle(&mut self) { + use super::pin::pin_sealed::PinIdOps; + let mask = self.0.write_mask(); + self.0 + .head + .borrow() + .id() + .sio_out_xor() + .write(|w| unsafe { w.bits(mask) }); + } +} +impl Default for PinGroup { + fn default() -> Self { + Self::new() + } +} diff --git a/rp2040-hal/src/gpio/pull.rs b/rp2040-hal/src/gpio/pull.rs new file mode 100644 index 000000000..4b1061a2e --- /dev/null +++ b/rp2040-hal/src/gpio/pull.rs @@ -0,0 +1,63 @@ +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 { + /// No pull enabled. + None, + /// Enable pull up. + Up, + /// Enable pull down. + Down, + /// This enables bus-keep mode. + /// + /// This is not documented in the datasheet but discribed in the + /// [c-sdk](https://github.com/raspberrypi/pico-sdk/blob/e7267f99febc70486923e17a8210088af058c915/src/rp2_common/hardware_gpio/gpio.c#L53) + /// as: + /// + /// > […] on RP2040, setting both pulls enables a "bus keep" function, + /// > i.e. weak pull to whatever is current high/low state of GPIO. + BusKeep, +} + +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_pull_type { + ($($pull_type: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::[<$pull_type>] + } + } + })* + }; +} +pin_pull_type!(None, Up, Down, BusKeep); 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 54ce88721..5bfc52fc7 100644 --- a/rp2040-hal/src/i2c.rs +++ b/rp2040-hal/src/i2c.rs @@ -12,8 +12,8 @@ //! //! let mut i2c = I2C::i2c1( //! peripherals.I2C1, -//! pins.gpio18.into_mode(), // sda -//! pins.gpio19.into_mode(), // scl +//! pins.gpio18.into_function(), // sda +//! pins.gpio19.into_function(), // scl //! 400.kHz(), //! &mut peripherals.RESETS, //! 125_000_000.Hz(), @@ -46,12 +46,7 @@ use core::{marker::PhantomData, ops::Deref}; use crate::{ - gpio::pin::bank0::{ - BankPinId, 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::{FunctionI2C, Pin, PinId}, + gpio::{bank0::*, pin::pin_sealed::TypeLevelPinId, AnyPin, FunctionI2c}, resets::SubsystemReset, typelevel::Sealed, }; @@ -63,6 +58,20 @@ pub mod controller; /// Peripheral implementation pub mod peripheral; +/// Pac I2C device +pub trait I2cDevice: Deref + SubsystemReset + Sealed { + /// Index of the peripheral. + const ID: usize; +} +impl Sealed for pac::I2C0 {} +impl I2cDevice for pac::I2C0 { + const ID: usize = 0; +} +impl Sealed for pac::I2C1 {} +impl I2cDevice for pac::I2C1 { + const ID: usize = 1; +} + /// I2C error #[non_exhaustive] #[cfg_attr(not(feature = "eh1_0_alpha"), derive(Debug))] @@ -140,56 +149,78 @@ impl eh1_0_alpha::i2c::Error for Error { } } -/// SCL pin -pub trait SclPin: 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 {} +macro_rules! pin_validation { + ($p:ident) => { + paste::paste!{ + #[doc = "Marker for PinId that can serve as " $p] + pub trait []: Sealed {} -impl SdaPin for Gpio14 {} -impl SclPin for Gpio15 {} + #[doc = "Valid " $p] + pub trait []: Sealed {} -impl SdaPin for Gpio16 {} -impl SclPin for Gpio17 {} - -impl SdaPin for Gpio18 {} -impl SclPin for Gpio19 {} - -impl SdaPin for Gpio20 {} -impl SclPin for Gpio21 {} + impl [] for T + where + T: AnyPin, + T::Id: [], + { + } -impl SdaPin for Gpio22 {} -impl SclPin for Gpio23 {} + #[doc = "A runtime validated " $p " pin for I2C."] + pub struct [](P, PhantomData); + impl Sealed for [] {} + impl [] for [] {} + impl [] + where + P: AnyPin, + S: I2cDevice, + { + /// Validate a pin's function on a i2c peripheral. + /// + #[doc = "Will err if the pin cannot be used as a " $p " pin for that I2C."] + pub fn validate(p: P, _u: &S) -> Result { + if [<$p:upper>].contains(&(p.borrow().id().num, S::ID)) && + p.borrow().id().bank == crate::gpio::DynBankId::Bank0 { + Ok(Self(p, PhantomData)) + } else { + Err(p) + } + } + } + } + }; + ($($p:ident),*) => { + $( + pin_validation!($p); + )* + }; +} -impl SdaPin for Gpio24 {} -impl SclPin for Gpio25 {} +pin_validation!(Scl, Sda); -impl SdaPin for Gpio26 {} -impl SclPin for Gpio27 {} +macro_rules! valid_pins { + ($($i2c:ident: { + sda: [$($sda:ident),*], + scl: [$($scl:ident),*] + }),*) => { + $( + $(impl ValidPinIdSda<$i2c> for $sda {})* + $(impl ValidPinIdScl<$i2c> for $scl {})* + )* -impl SdaPin for Gpio28 {} -impl SclPin for Gpio29 {} + const SDA: &[(u8, usize)] = &[$($(($sda::ID.num, $i2c::ID)),*),*]; + const SCL: &[(u8, usize)] = &[$($(($scl::ID.num, $i2c::ID)),*),*]; + }; +} +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 { @@ -223,19 +254,13 @@ fn i2c_reserved_addr(addr: u16) -> bool { (addr & 0x78) == 0 || (addr & 0x78) == 0x78 } -impl I2C, Pin), Mode> +impl I2C where Block: SubsystemReset + Deref, - Sda: PinId + BankPinId, - Scl: PinId + BankPinId, - Mode: I2CMode, { /// Releases the I2C peripheral and associated pins #[allow(clippy::type_complexity)] - pub fn free( - self, - resets: &mut RESETS, - ) -> (Block, (Pin, Pin)) { + pub fn free(self, resets: &mut RESETS) -> (Block, (Sda, Scl)) { self.i2c.reset_bring_down(resets); (self.i2c, self.pins) @@ -283,20 +308,19 @@ impl, PINS, Mode> I2C { macro_rules! hal { ($($I2CX:ident: ($i2cX:ident),)+) => { $( - impl - I2C<$I2CX, (Pin, Pin)> { + impl I2C<$I2CX, (Sda, Scl)> { /// Configures the I2C peripheral to work in master mode pub fn $i2cX( i2c: $I2CX, - sda_pin: Pin, - scl_pin: Pin, + sda_pin: Sda, + scl_pin: Scl, freq: F, resets: &mut RESETS, system_clock: SystemF) -> Self where F: Into, - Sda: SdaPin<$I2CX>, - Scl: SclPin<$I2CX>, + Sda: ValidPinSda<$I2CX>, + Scl: ValidPinScl<$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 afee8c24d..130efdb6b 100644 --- a/rp2040-hal/src/i2c/controller.rs +++ b/rp2040-hal/src/i2c/controller.rs @@ -1,10 +1,6 @@ use core::{marker::PhantomData, ops::Deref}; -use crate::{ - gpio::pin::bank0::BankPinId, - gpio::pin::{FunctionI2C, Pin, PinId}, - resets::SubsystemReset, -}; +use crate::resets::SubsystemReset; use fugit::HertzU32; use hal::blocking::i2c::{Read, Write, WriteRead}; use pac::{i2c0::RegisterBlock as Block, RESETS}; @@ -12,24 +8,23 @@ 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, ValidPinScl, ValidPinSda, I2C}; -impl, Sda: PinId + BankPinId, Scl: PinId + BankPinId> - I2C, Pin), Controller> +impl I2C +where + T: SubsystemReset + Deref, + Sda: ValidPinSda, + Scl: ValidPinScl, { /// Configures the I2C peripheral to work in controller mode pub fn new_controller( i2c: T, - sda_pin: Pin, - scl_pin: Pin, + sda_pin: Sda, + scl_pin: Scl, freq: HertzU32, resets: &mut RESETS, system_clock: HertzU32, - ) -> Self - where - Sda: SdaPin, - Scl: SclPin, - { + ) -> Self { let freq = freq.to_Hz(); assert!(freq <= 1_000_000); assert!(freq > 0); diff --git a/rp2040-hal/src/i2c/peripheral.rs b/rp2040-hal/src/i2c/peripheral.rs index cd633f47b..b61dad9c5 100644 --- a/rp2040-hal/src/i2c/peripheral.rs +++ b/rp2040-hal/src/i2c/peripheral.rs @@ -1,13 +1,9 @@ use core::{marker::PhantomData, ops::Deref}; -use crate::{ - gpio::pin::bank0::BankPinId, - gpio::pin::{FunctionI2C, Pin, PinId}, - resets::SubsystemReset, -}; +use crate::resets::SubsystemReset; use pac::{i2c0::RegisterBlock as I2CBlock, RESETS}; -use super::{Peripheral, SclPin, SdaPin, I2C}; +use super::{Peripheral, ValidPinScl, ValidPinSda, I2C}; /// I2C bus events #[derive(Debug, PartialEq, Eq)] @@ -38,11 +34,11 @@ pub struct I2CPeripheralEventIterator { state: State, } -impl I2C, Pin), Peripheral> +impl I2C where T: SubsystemReset + Deref, - Sda: PinId + BankPinId, - Scl: PinId + BankPinId, + Sda: ValidPinSda, + Scl: ValidPinScl, { /// Configures the I2C peripheral to work in peripheral mode /// @@ -50,15 +46,11 @@ where #[allow(clippy::type_complexity)] pub fn new_peripheral_event_iterator( i2c: T, - sda_pin: Pin, - scl_pin: Pin, + sda_pin: Sda, + scl_pin: Scl, resets: &mut RESETS, addr: u16, - ) -> I2CPeripheralEventIterator, Pin)> - where - Sda: SdaPin, - Scl: SclPin, - { + ) -> I2CPeripheralEventIterator { i2c.reset_bring_down(resets); i2c.reset_bring_up(resets); @@ -184,19 +176,13 @@ impl, PINS> Iterator for I2CPeripheralEventIterator< } } -impl - I2CPeripheralEventIterator, Pin)> +impl I2CPeripheralEventIterator where Block: SubsystemReset + Deref, - Sda: PinId + BankPinId, - Scl: PinId + BankPinId, { /// Releases the I2C peripheral and associated pins #[allow(clippy::type_complexity)] - pub fn free( - self, - resets: &mut RESETS, - ) -> (Block, (Pin, Pin)) { + pub fn free(self, resets: &mut RESETS) -> (Block, (Sda, Scl)) { self.i2c.free(resets) } } diff --git a/rp2040-hal/src/pio.rs b/rp2040-hal/src/pio.rs index 967f13bbd..505c83356 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 } @@ -447,8 +455,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. @@ -474,7 +480,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, } @@ -536,7 +542,7 @@ impl UninitStateMachine { &*self.sm } - unsafe fn pio(&self) -> &rp2040_pac::pio0::RegisterBlock { + unsafe fn pio(&self) -> &RegisterBlock { &*self.block } } @@ -1273,7 +1279,7 @@ impl StateMachine { /// PIO RX FIFO handle. pub struct Rx { - block: *const rp2040_pac::pio0::RegisterBlock, + block: *const RegisterBlock, _phantom: core::marker::PhantomData, } @@ -1411,7 +1417,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, } @@ -1604,7 +1610,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>, } @@ -1770,7 +1776,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 5c2dafee6..61ffc223e 100644 --- a/rp2040-hal/src/pwm/mod.rs +++ b/rp2040-hal/src/pwm/mod.rs @@ -79,9 +79,9 @@ use core::marker::PhantomData; use crate::{ - gpio::{bank0::*, FunctionPwm, Pin, PinId, PinMode, ValidPinMode}, + gpio::{bank0::*, AnyPin, FunctionPwm, Pin, ValidFunction}, resets::SubsystemReset, - typelevel::Sealed, + typelevel::{Is, Sealed}, }; use embedded_hal::PwmPin; use pac::PWM; @@ -130,7 +130,7 @@ pub struct CountRisingEdge; pub struct CountFallingEdge; /// Type-level marker for tracking which slice modes are valid for which slices -pub trait ValidSliceMode: Sealed {} +pub trait ValidSliceMode: Sealed + SliceMode {} /// Type-level marker for tracking which slice modes are valid for which slices pub trait ValidSliceInputMode: Sealed + ValidSliceMode {} @@ -192,6 +192,54 @@ macro_rules! slice_id { }; } +//============================================================================== +// AnySlice +//============================================================================== + +/// Type class for [`Slice`] types +/// +/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for +/// [`Slice`] 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 AnySlice +where + Self: Sealed, + Self: Is>, + ::Mode: ValidSliceMode<::Id>, +{ + /// [`SliceId`] of the corresponding [`Slice`] + type Id: SliceId; + /// [`SliceMode`] of the corresponding [`Slice`] + type Mode: SliceMode; +} + +impl Sealed for Slice +where + S: SliceId, + M: ValidSliceMode, +{ +} + +impl AnySlice for Slice +where + S: SliceId, + M: ValidSliceMode, +{ + type Id = S; + type Mode = M; +} + +/// Type alias to recover the specific [`Slice`] type from an implementation of +/// [`AnySlice`] +/// +/// See the [`AnyKind`] documentation for more details on the pattern. +/// +/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern +type SpecificSlice = Slice<::Id, ::Mode>; + //============================================================================== // Registers //============================================================================== @@ -228,7 +276,7 @@ impl Registers { /// Provide a type-level equivalent for the /// [`RegisterInterface::change_mode`] method. #[inline] - fn change_mode>(&mut self) { + fn change_mode>(&mut self) { RegisterInterface::do_change_mode(self, M::DYN); } } @@ -237,20 +285,20 @@ impl Registers { pub struct Slice where I: SliceId, - M: SliceMode + ValidSliceMode, + M: ValidSliceMode, { regs: Registers, mode: PhantomData, /// Channel A (always output) - pub channel_a: Channel, + pub channel_a: Channel, /// Channel B (input or output) - pub channel_b: Channel, + pub channel_b: Channel, } impl Slice where I: SliceId, - M: SliceMode + ValidSliceMode, + M: ValidSliceMode, { /// Create a new [`Slice`] /// @@ -271,7 +319,7 @@ where /// Convert the slice to the requested [`SliceMode`] #[inline] - pub fn into_mode>(mut self) -> Slice { + pub fn into_mode>(mut self) -> Slice { if N::DYN != M::DYN { self.regs.change_mode::(); } @@ -491,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. @@ -519,7 +567,7 @@ impl Slices { // C: ChannelId, // G: PinId + BankPinId + ValidPwmOutputPin, // PM: PinMode + ValidPinMode, - // SM: SliceMode + ValidSliceMode, + // SM: ValidSliceMode, // >(&mut self, _: &Pin) -> &mut Slice{ // match S::DYN { // DynSliceId{num} if num == 0 => &mut self.pwm0, @@ -538,15 +586,15 @@ impl Slices { /// A Channel from the Pwm subsystem. /// /// Its attached to one of the eight slices and can be an A or B side channel -pub struct Channel { - regs: Registers, - slice_mode: PhantomData, +pub struct Channel { + regs: Registers, + slice_mode: PhantomData, channel_id: PhantomData, duty_cycle: u16, enabled: bool, } -impl Channel { +impl Channel { pub(super) unsafe fn new(duty_cycle: u16) -> Self { Channel { regs: Registers::new(), @@ -558,9 +606,9 @@ impl Channel { } } -impl Sealed for Channel {} +impl Sealed for Channel {} -impl PwmPin for Channel { +impl PwmPin for Channel { type Duty = u16; /// We cant disable the channel without disturbing the other channel. @@ -600,7 +648,7 @@ impl PwmPin for Channel { } } -impl PwmPin for Channel { +impl PwmPin for Channel { type Duty = u16; /// We cant disable the channel without disturbing the other channel. @@ -640,16 +688,13 @@ impl PwmPin for Channel { } } -impl> Channel { +impl Channel { /// Capture a gpio pin and use it as pwm output for channel A - pub fn output_to< - G: PinId + BankPinId + ValidPwmOutputPin, - PM: PinMode + ValidPinMode, - >( - &mut self, - pin: Pin, - ) -> Pin { - pin.into_mode() + pub fn output_to(&mut self, pin: P) -> Pin + where + P::Id: ValidPwmOutputPin, + { + pin.into().into_function() } /// Invert channel output @@ -665,16 +710,13 @@ impl> Channel { } } -impl> Channel { +impl Channel { /// Capture a gpio pin and use it as pwm output for channel B - pub fn output_to< - G: PinId + BankPinId + ValidPwmOutputPin, - PM: PinMode + ValidPinMode, - >( - &mut self, - pin: Pin, - ) -> Pin { - pin.into_mode() + pub fn output_to(&mut self, pin: P) -> Pin + where + P::Id: ValidPwmOutputPin, + { + pin.into().into_function() } /// Invert channel output @@ -690,36 +732,35 @@ impl> Channel { } } -impl> Channel { +impl Channel +where + S::Mode: ValidSliceInputMode, +{ /// Capture a gpio pin and use it as pwm input for channel B - pub fn input_from, PM: PinMode + ValidPinMode>( - &mut self, - pin: Pin, - ) -> Pin { - pin.into_mode() + pub fn input_from(&mut self, pin: P) -> Pin + where + P::Id: ValidPwmInputPin, + { + pin.into().into_function() } } -impl> Slice { +impl> Slice { /// Capture a gpio pin and use it as pwm output - pub fn output_to< - G: PinId + BankPinId + ValidPwmOutputPin, - PM: PinMode + ValidPinMode, - C: ChannelId, - >( - &mut self, - pin: Pin, - ) -> Pin { - pin.into_mode() + pub fn output_to(&mut self, pin: P) -> Pin + where + P::Id: ValidPwmOutputPin, + { + pin.into().into_function() } } -impl> Slice { +impl> Slice { /// Capture a gpio pin and use it as pwm input for channel B - pub fn input_from, PM: PinMode + ValidPinMode>( - &mut self, - pin: Pin, - ) -> Pin { - pin.into_mode() + pub fn input_from(&mut self, pin: P) -> Pin + where + P::Id: ValidPwmInputPin, + { + pin.into().into_function() } } diff --git a/rp2040-hal/src/sio.rs b/rp2040-hal/src/sio.rs index 9238dd3f6..ead75c858 100644 --- a/rp2040-hal/src/sio.rs +++ b/rp2040-hal/src/sio.rs @@ -23,6 +23,17 @@ use crate::typelevel::Sealed; use super::*; use core::convert::Infallible; +/// Id of the core. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum CoreId { + #[allow(missing_docs)] + Core0 = 0, + #[allow(missing_docs)] + Core1 = 1, +} + /// Marker struct for ownership of SIO gpio bank0 pub struct SioGpioBank0 { _private: (), @@ -88,10 +99,19 @@ 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() -> u8 { + pub fn core() -> CoreId { // Safety: it is always safe to read this read-only register - unsafe { (*pac::SIO::ptr()).cpuid.read().bits() as u8 } + match unsafe { (*pac::SIO::ptr()).cpuid.read().bits() as u8 } { + 0 => CoreId::Core0, + 1 => CoreId::Core1, + _ => unreachable!("This MCU only has 2 cores."), + } } } diff --git a/rp2040-hal/src/spi.rs b/rp2040-hal/src/spi.rs index c6304a1f1..c9c816e31 100644 --- a/rp2040-hal/src/spi.rs +++ b/rp2040-hal/src/spi.rs @@ -13,26 +13,30 @@ //! 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_function::(); +//! let mosi = pins.gpio3.into_function::(); //! -//! 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, 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, 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::*; /// Spi mode pub struct Mode(embedded_hal::spi::Mode); @@ -94,13 +98,18 @@ impl Sealed for Enabled {} /// Pac SPI device pub trait SpiDevice: Deref + SubsystemReset + Sealed { + /// Index of the peripheral. + const ID: usize; + /// The DREQ number for which TX DMA requests are triggered. fn tx_dreq() -> u8; /// The DREQ number for which RX DMA requests are triggered. fn rx_dreq() -> u8; } +impl Sealed for pac::SPI0 {} impl SpiDevice for pac::SPI0 { + const ID: usize = 0; fn tx_dreq() -> u8 { TREQ_SEL_A::SPI0_TX.into() } @@ -108,8 +117,9 @@ impl SpiDevice for pac::SPI0 { TREQ_SEL_A::SPI0_RX.into() } } -impl Sealed for pac::SPI0 {} +impl Sealed for pac::SPI1 {} impl SpiDevice for pac::SPI1 { + const ID: usize = 1; fn tx_dreq() -> u8 { TREQ_SEL_A::SPI1_TX.into() } @@ -117,7 +127,6 @@ impl SpiDevice for pac::SPI1 { TREQ_SEL_A::SPI1_RX.into() } } -impl Sealed for pac::SPI1 {} /// Data size used in spi pub trait DataSize: Sealed {} @@ -128,15 +137,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, } } @@ -195,11 +206,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, } } @@ -232,7 +244,7 @@ impl Spi { baudrate: B, mode: Mode, slave: bool, - ) -> Spi { + ) -> Spi { self.device.reset_bring_down(resets); self.device.reset_bring_up(resets); @@ -257,12 +269,12 @@ impl Spi { peri_frequency: F, baudrate: B, mode: M, - ) -> Spi { + ) -> Spi { self.init_spi(resets, peri_frequency, baudrate, mode.into(), false) } /// Initialize the SPI in slave mode - pub fn init_slave>(self, resets: &mut RESETS, mode: M) -> Spi { + pub fn init_slave>(self, resets: &mut RESETS, mode: M) -> 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. @@ -270,7 +282,7 @@ impl Spi { } } -impl Spi { +impl, const DS: u8> Spi { fn is_writable(&self) -> bool { self.device.sspsr.read().tnf().bit_is_set() } @@ -284,7 +296,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: () }) @@ -295,7 +307,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> { @@ -320,17 +332,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(()) @@ -338,7 +350,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 @@ -356,7 +368,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 @@ -374,7 +386,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 { @@ -414,7 +426,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); @@ -437,7 +449,7 @@ macro_rules! impl_write { } } - impl ReadTarget for Spi { + impl> ReadTarget for Spi { type ReceivedWord = $type; fn rx_treq() -> Option { @@ -456,9 +468,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 { @@ -477,7 +489,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..5a1bac718 --- /dev/null +++ b/rp2040-hal/src/spi/pins.rs @@ -0,0 +1,141 @@ +use core::marker::PhantomData; + +use crate::gpio::{pin::pin_sealed::TypeLevelPinId, AnyPin, FunctionSpi}; +use crate::{ + gpio::bank0::*, + typelevel::{OptionT, OptionTNone, OptionTSome, Sealed}, +}; +use pac::{SPI0, SPI1}; + +use super::SpiDevice; + +macro_rules! pin_validation { + ($p:ident) => { + paste::paste!{ + #[doc = "Indicates a valid " $p " pin for SPI0 or SPI1"] + pub trait []: Sealed {} + + #[doc = "Indicates a valid " $p " pin for SPI0 or SPI1"] + pub trait []: Sealed {} + + impl [] for T + where + T: AnyPin, + T::Id: [], + { + } + + #[doc = "A runtime validated " $p " pin for spi."] + pub struct [](P, PhantomData); + impl Sealed for [] {} + impl [] for [] {} + impl [] + where + P: AnyPin, + S: SpiDevice, + { + /// Validate a pin's function on a spi peripheral. + /// + #[doc = "Will err if the pin cannot be used as a " $p " pin for that Spi."] + pub fn validate(p: P, _u: &S) -> Result { + if [<$p:upper>].contains(&(p.borrow().id().num, S::ID)) && + p.borrow().id().bank == crate::gpio::DynBankId::Bank0 { + Ok(Self(p, PhantomData)) + } else { + Err(p) + } + } + } + + #[doc = "Indicates a valid optional " $p " pin for SPI0 or SPI1"] + pub trait []: OptionT {} + + impl [] for OptionTNone {} + impl [] for OptionTSome + where + U: SpiDevice, + T: [], + { + } + } + }; + ($($p:ident),*) => { + $( + pin_validation!($p); + )* + }; +} +pin_validation!(Tx, Rx, Sck, Cs); + +macro_rules! impl_valid_spi { + ($($spi:ident: { + rx: [$($rx:ident),*], + cs: [$($cs:ident),*], + sck: [$($sck:ident),*], + tx: [$($tx:ident),*], + }),*) => { + $( + $(impl ValidPinIdRx<$spi> for $rx {})* + $(impl ValidPinIdTx<$spi> for $tx {})* + $(impl ValidPinIdSck<$spi> for $sck {})* + $(impl ValidPinIdCs<$spi> for $cs {})* + )* + + const RX: &[(u8, usize)] = &[$($(($rx::ID.num, $spi::ID)),*),*]; + const TX: &[(u8, usize)] = &[$($(($tx::ID.num, $spi::ID)),*),*]; + const SCK: &[(u8, usize)] = &[$($(($sck::ID.num, $spi::ID)),*),*]; + const CS: &[(u8, usize)] = &[$($(($cs::ID.num, $spi::ID)),*),*]; + }; +} + +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], + } +); + +/// 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: ValidPinTx, + Sck: ValidPinSck, +{ + type Rx = OptionTNone; + type Cs = OptionTNone; + type Sck = OptionTSome; + type Tx = OptionTSome; +} + +impl ValidSpiPinout for (Tx, Rx, Sck) +where + Spi: SpiDevice, + Tx: ValidPinTx, + Sck: ValidPinSck, + Rx: 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 c0d783275..eb509d408 100644 --- a/rp2040-hal/src/typelevel.rs +++ b/rp2040-hal/src/typelevel.rs @@ -1,637 +1,8 @@ //! Module supporting type-level programming //! -//! Copied from [atsamd-hal](https://github.com/atsamd-rs/atsamd). -//! -//! # Introduction -//! -//! Embedded software is often difficult to debug, so there is a strong -//! motivation to catch as many bugs as possible at compile-time. However, the -//! performance requirements of embedded software also make it difficult to -//! justify changes that impose additional overhead in terms of size or speed. -//! Ideally, we would like to add as many compile-time checks as possible, while -//! also producing the fewest possible assembly instructions. -//! -//! The Rust type system can help accomplish this goal. By expressing software -//! constraints within the type system, developers can enforce invariants at -//! compile-time. -//! -//! Sometimes this is done using Rust macros. However, that approach can produce -//! code that is difficult to read and understand. Moreover, macro-generated -//! code can only be extended by *more* macros, which further spreads the -//! problem. In `atsamd-hal` specifically, issue -//! [#214](https://github.com/atsamd-rs/atsamd/issues/214) discussed the extent -//! to which macros were once used in the repository. -//! -//! Alternatively, many of the same goals can be accomplished with the Rust -//! type & trait system directly, which is quite powerful. In fact, it is -//! [turing complete](https://sdleffler.github.io/RustTypeSystemTuringComplete/). -//! By expressing our invariants entirely within the type system, we can encode -//! the desired compile-time checks in a form that is easier to read, understand -//! and document. -//! -//! This module documents some of the type-level programming techniques used -//! throughout this HAL, and it contains a few items used to implement them. -//! -//! ## Contents -//! -//! - [Basics of type-level programming](#basics-of-type-level-programming) -//! - [Type-level enums](#type-level-enums) -//! - [Type classes](#type-classes) -//! - [Type-level containers](#type-level-containers) -//! - [Type-level functions](#type-level-functions) -//! - [`OptionalKind` trait pattern](#optionalkind-trait-pattern) -//! - [`AnyKind` trait pattern](#anykind-trait-pattern) -//! - [Defining an `AnyKind` trait](#defining-an-anykind-trait) -//! - [Using an `AnyKind` trait](#using-an-anykind-trait) -//! -//! # Basics of type-level programming -//! -//! Type-level programming aims to execute a form of compile-time computation. -//! But to perform such computation, we need to map our traditional notions of -//! programming to the Rust type system. -//! -//! In normal Rust, individual values are grouped or categorized into types. For -//! example, `0`, `1`, `2`, etc. are all members of the `usize` type. Similarly, -//! `Enum::A` and `Enum::B` are members of the `Enum` type, defined as -//! -//! ```ignore -//! enum Enum { A, B } -//! ``` -//! -//! We use composite types and containers to create more complex data -//! structures, and we use functions to map between values. -//! -//! All of these concepts can also be expressed within the Rust type system. -//! However, in this case, types are grouped and categorized into traits. For -//! instance, the [`typenum`](https://docs.rs/typenum/1.13.0/typenum/index.html) -//! crate provides the types `U0`, `U1`, `U2`, etc., which are all members of -//! the `Unsigned` trait. Similarly, the following sections will illustrate how -//! to define type-level enums, containers and functions. -//! -//! ## Type-level enums -//! -//! Type-level enums are one of the foundational concepts of type-level -//! programming used in this HAL. -//! -//! At the value-level, a typical Rust enum represents some set of variants that -//! can be assigned to a particular variable. Similarly, a type-level enum -//! represents some set of types that can be assigned to a particular type -//! parameter. -//! -//! To lift an enum from the value level to the type level, you typically map -//! the enum variants to types and the enum itself to a trait. For instance, the -//! value-level enum -//! -//! ```ignore -//! enum Enum { -//! A, -//! B, -//! } -//! ``` -//! -//! would be mapped to the type level like so. -//! -//! ```ignore -//! trait Enum {} -//! -//! enum A {} -//! enum B {} -//! -//! impl Enum for A {} -//! impl Enum for B {} -//! ``` -//! -//! At the value level, the variants `A` and `B` are grouped by the `Enum` type, -//! while at the type level, the types `A` and `B` are grouped by the `Enum` -//! trait. -//! -//! ## Type classes -//! -//! At the value-level, a type restricts the possible values that can be taken -//! by some free variable. While at the type-level, a trait bound restricts the -//! possible types that can be taken by some free type parameter. In effect, -//! trait bounds can be used to create a kind of meta-type, or type class. The -//! type-level enums in the previous section represent the most primitive -//! application of the concept, but type classes can take other forms. The -//! `OptionalKind` and `AnyKind` trait patterns discussed below are more -//! advanced applications of the same concept. -//! -//! ## Type-level containers -//! -//! To represent more complex relationships, we need a way to form composite -//! data structures at the type level. -//! -//! At the value level, a container holds an instance of a particular type. The -//! exact value of that instance is usually not known to the author, it is only -//! known at run-time. -//! -//! At the type level, we don't have the same notion of "run-time", but we do -//! have two different notions of "compile-time" that form a similar -//! relationship. There is compile time for the HAL authors, and there is a -//! separate compile-time for the HAL users. We want to create a type-level -//! container where the exact type is not known at author-time, but it is known -//! at user-time. -//! -//! For example, take the following, value-level container struct. It contains -//! two fields, `a` and `b`, of different types, `EnumOne` and `EnumTwo`. -//! -//! ```ignore -//! struct Container { -//! a: EnumOne, -//! b: EnumTwo, -//! } -//! ``` -//! -//! We can create an instance of this container with specific values. -//! -//! ```ignore -//! let x = Container { a: EnumOne::VariantX, b: EnumTwo::VariantY }; -//! ``` -//! -//! Next, suppose we had already translated `EnumOne` and `EnumTwo` to the type -//! level using the technique in the previous section. If we wanted to create a -//! similar, composite data structure at the type level, we could use type -//! parameters in place of struct fields to represent the unknown types. -//! -//! ```ignore -//! struct Container -//! where -//! A: EnumOne, -//! B: EnumTwo, -//! { -//! a: PhantomData, -//! b: PhantomData, -//! } -//! ``` -//! -//! And we could create an instance of this container with specific types. -//! -//! ```ignore -//! type X = Container; -//! ``` -//! -//! You might notice the use of `PhantomData` in the definition of the -//! type-level container. Because it is geared more toward value-level -//! programming, Rust requires all type parameters actually be used by the -//! corresponding type. However, we don't need to "store" a type in the same way -//! we store values. The compiler is responsible for tracking the concrete type -//! for each type parameter. But the language still requires us to act as if we -//! used each type parameter. `PhantomData` is the solution here, because it -//! lets us make use of the type parameters without actually storing any values. -//! -//! Separately, `PhantomData` also allows us to create "instances" of types that -//! normally can't be instantiated, like empty enums. For example, instances of -//! `Enum` below can never exist directly. -//! -//! ```ignore -//! enum Enum {} -//! ``` -//! -//! But instances of `PhantomData` are perfectly valid. In this way, -//! library authors can create types that only exist at the type level, which -//! can sometimes simplify a design. -//! -//! ## Type-level functions -//! -//! To perform type-level computations, we need some way to map or transform -//! types into other types. -//! -//! At the value level, functions and methods map values of the input types to -//! values of the output types. The same can be accomplished at the type level -//! using traits and associated types. Type-level functions are implemented as -//! traits, where the implementing type and any type parameters are the inputs, -//! and associated types are the outputs. -//! -//! For example, consider the value level `not` method below. -//! -//! ```ignore -//! enum Bool { -//! False, -//! True, -//! } -//! -//! impl Bool { -//! fn not(self) -> Self { -//! use Bool::*; -//! match self { -//! True => False, -//! False => True, -//! } -//! } -//! } -//! ``` -//! -//! We can translate this example to the type level like so. -//! -//! ```ignore -//! trait Bool {} -//! -//! enum True {} -//! enum False {} -//! -//! impl Bool for True {} -//! impl Bool for False {} -//! -//! trait Not: Bool { -//! type Result: Bool; -//! } -//! -//! impl Not for True { -//! type Result = False; -//! } -//! -//! impl Not for False { -//! type Result = True; -//! } -//! ``` -//! -//! We can use the `Not` trait bound to transform one type to another. For -//! instance, we can create a container that accepts one type parameter but -//! stores a different one. -//! -//! ```ignore -//! struct Container { -//! not: PhantomData; -//! } -//! ``` -//! -//! Alternatively, we could redefine the trait and declar a corresponding type -//! alias as -//! -//! ```ignore -//! trait NotFunction: Bool { -//! type Result: Bool; -//! } -//! -//! type Not = ::Result; -//! ``` -//! -//! Doing so would allow us to us reframe the last example as -//! -//! ```ignore -//! struct Container { -//! not: PhantomData>; -//! } -//! ``` -//! -//! Type-level functions can be more complicated than this example, but they -//! ultimately represent a mapping from a set of input types (the implementing -//! type and any type parameters) to a set of output types (the associated -//! types). -//! -//! # `OptionalKind` trait pattern -//! -//! As mentioned above, traits can be used to define a kind of meta-type or type -//! class, essentially forming a set of valid types for a given type parameter. -//! They also represent the concept of types lifted from the value level to the -//! type level. -//! -//! What if we want to define a type class representing either a set of useful -//! types or some useless, null type? Essentially, how do we take the notion of -//! an [`Option`] type and raise it to the type level? -//! -//! Suppose we have some existing type class, defined by the `Class` trait, that -//! we want to make optional. We can define a new type class that includes all -//! instances of `Class` as well as some null type. For the latter we use -//! [`NoneT`], defined in this module. -//! -//! ```ignore -//! trait OptionalClass {} -//! -//! impl OptionalClass for NoneT {} -//! impl OptionalClass for C {} -//! ``` -//! -//! We can use this new type class to store an optional instance of a `Class` -//! type in a struct. -//! -//! ```ignore -//! struct Container { -//! class: PhantomData, -//! } -//! ``` -//! -//! And we can restrict some of its methods to only operate on instances with a -//! valid `Class`. -//! -//! ```ignore -//! impl Container { -//! fn method(self) { ... } -//! } -//! ``` -//! -//! Although it is not strictly necessary, we can also introduce a new type -//! class to differentiate the bare usage of `Class` from instances of some -//! `Class` where an `OptionalClass` is accepted. -//! -//! ```ignore -//! trait SomeClass: OptionalClass + Class {} -//! -//! impl SomeClass for C {} -//! ``` -//! -//! This new trait doesn't add any new information, but it can still help -//! readers understand that a particular type parameter is restricted to an -//! instances of `Class` when an `OptionalClass` could be accepted. -//! -//! Note that when `Class` and `OptionalClass` contain associated types, name -//! clashes may occur when using `SomeClass` as a trait bound. This can be -//! avoided by removing the `OptionalClass` super trait from `SomeClass`. -//! Ultimately, it is redundant anyway, because any implementer of `Class` also -//! implements `OptionalClass`. -//! -//! # `AnyKind` trait pattern -//! -//! The `AnyKind` trait pattern allows you to encapsulate types with multiple -//! type parameters and represent them with only a single type parameter. It -//! lets you introduce a layer of abstraction, which can simplify interfaces and -//! make them more readable. But most of all, it does so without sacrificing any -//! of our normal, type-level abilities. -//! -//! ## Defining an `AnyKind` trait -//! -//! Suppose you had a composite, type-level data structure. For example, the -//! GPIO `Pin` struct contains instances of two type-level enums, a `PinId` and -//! a `PinMode`. It looks something like this. -//! -//! ```ignore -//! struct Pin { -//! // ... -//! } -//! ``` -//! -//! Rust does not provide any way to speak about a `Pin` generally. Any mention -//! of the `Pin` type must also include its type parameters, i.e. `Pin`. -//! This is not a deal-breaker, but it is less than ideal for type-level -//! programming. It would be nice if there were a way to succinctly refer to any -//! `Pin`, regardless of its type parameters. -//! -//! We've seen above that we can use traits to form a type class. What if we -//! were to introduce a new trait to label all instances of `Pin`? It would look -//! something like this. -//! -//! ```ignore -//! trait AnyPin {} -//! -//! impl AnyPin for Pin {} -//! ``` -//! -//! Now, instead of refering to `Pin`, we can refer to instances of the -//! `AnyPin` type class. -//! -//! ```ignore -//! fn example(pin: P) { ... } -//! ``` -//! -//! Unfortunately, while this is more ergonomic, it is not very useful. As -//! authors of the code, we know that `AnyPin` is only implemented for `Pin` -//! types. But the compiler doesn't know that. Traits in Rust are open, so the -//! compiler must consider that `AnyPin` could be implemented for other types. -//! -//! As a consequence, the compiler knows very little about the type `P` in the -//! function above. In fact, because the `AnyPin` trait is completely empty, the -//! compiler knows *absolutely nothing* about the type `P`. -//! -//! Is there a way to make the `AnyPin` trait more useful? We can see from the -//! current implementation that we are throwing away information. -//! -//! ```ignore -//! impl AnyPin for Pin {} -//! ``` -//! -//! The implementation of `AnyPin` is identical for every `Pin`, regardless of -//! the type parameters `I` and `M`, which erases that information. Instead, we -//! could choose to save that information in the form of associated types. -//! -//! Let's redesign the `AnyPin` trait to record the `PinId` and `PinMode`. -//! -//! ```ignore -//! trait AnyPin { -//! type Id: PinId; -//! type Mode: PinMode; -//! } -//! -//! impl AnyPin for Pin { -//! type Id = I; -//! type Mode = M; -//! } -//! ``` -//! -//! This is better. When `P` implements `AnyPin`, we can at least recover the -//! corresponding `PinId` and `PinMode` types. However, `AnyPin` still doesn't -//! include any trait methods nor any super traits, so the compiler won't allow -//! us to do anything useful with an instances of `P`. -//! -//! We need some way to tell the compiler that when `P` implements `AnyPin`, -//! it is equivalent to saying `P` is exactly `Pin`. -//! Essentially, we want to take a generic type parameter `P` and treat it as if -//! it were an instance of a specific `Pin` type. -//! -//! We can start by defining a trait alias to recover the specific `Pin` type. -//! -//! ```ignore -//! type SpecificPin

= Pin<

::Id,

::Mode>; -//! ``` -//! -//! With this new definition, we can rephrase our statement above. We need some -//! way to tell the compiler that when `P` implements `AnyPin`, -//! `P == SpecificPin

`. There's no way to do that exactly, but we can come -//! close with some useful trait bounds: [`From`], [`Into`], [`AsRef`] and -//! [`AsMut`]. -//! -//! ```ignore -//! trait AnyPin -//! where -//! Self: From>, -//! Self: Into>, -//! Self: AsRef>, -//! Self: AsMut>, -//! { -//! type Id: PinId; -//! type Mode: PinMode; -//! } -//! ``` -//! -//! Now we've given the compiler some useful information. When a type implements -//! `AnyPin`, it can be converted from and into instances of `Pin`. And -//! references to types that implement `AnyPin` can be converted into references -//! to `Pin`s. -//! -//! ```ignore -//! fn example(mut any_pin: P) { -//! // None of the type annotations here are necessary -//! // Everything can be inferred -//! // Remember that SpecificPin

is Pin -//! let pin_mut: &mut SpecificPin

= any_pin.as_mut(); -//! let pin_ref: &SpecificPin

= any_pin.as_ref(); -//! let pin: SpecificPin

= any_pin.into(); -//! } -//! ``` -//! -//! Finally, to simplify this pattern, we can gather all of the super trait -//! bounds into a single, reusable trait. -//! -//! ```ignore -//! trait Is -//! where -//! Self: From>, -//! Self: Into>, -//! Self: AsRef>, -//! Self: AsMut>, -//! { -//! type Type; -//! } -//! -//! type IsType = ::Type; -//! -//! impl + AsMut> Is for T { -//! type Type = T; -//! } -//! ``` -//! -//! And we can rewrite our `AnyPin` trait as -//! -//! ```ignore -//! trait AnyPin: Is> { -//! type Id: PinId; -//! type Mode: PinMode; -//! } -//! ``` -//! -//! ## Using an `AnyKind` trait -//! -//! If a type takes multiple type parameters, storing it within a container -//! requires repeating all of the corresponding type parameters. For instance, -//! imagine a container that stores two completely generic `Pin` types. -//! -//! ```ignore -//! struct TwoPins -//! where -//! I1: PinId, -//! I2: PinId, -//! M1: PinMode, -//! M2: PinMode, -//! { -//! pin1: Pin, -//! pin2: Pin, -//! } -//! ``` -//! -//! This struct has already ballooned to four type parameters, without even -//! doing much useful work. Given its heavy use of type parameters, this -//! limitation can make type-level programming tedious, cumbersome and -//! error-prone. -//! -//! Instead, we can use the `AnyKind` trait pattern to encapsulate each `Pin` -//! with a single type parameter. -//! -//! ```ignore -//! struct TwoPins -//! where -//! P1: AnyPin, -//! P2: AnyPin, -//! { -//! pin1: P1, -//! pin2: P2, -//! } -//! ``` -//! -//! The result is far more readable and generally more comprehensible. Moreover, -//! although we no longer have direct access to the `PinId` and `PinMode` type -//! parameters, we haven't actually lost any expressive power. -//! -//! In the first version of `TwoPins`, suppose we wanted to implement a method -//! for pins in `FloatingInput` mode while simultaneously restricting the -//! possible `PinId`s based on some type class. The result might look like -//! this. -//! -//! ```ignore -//! impl for TwoPins -//! where -//! I1: PinId + Class, -//! I2: PinId + Class, -//! { -//! fn method(&self) { -//! // ... -//! } -//! } -//! ``` -//! -//! The same method could be expressed with the `AnyPin` approach like so -//! -//! ```ignore -//! impl for TwoPins -//! where -//! P1: AnyPin, -//! P2: AnyPin, -//! P1::Id: Class, -//! P2::Id: Class, -//! { -//! fn method(&self) { -//! // ... -//! } -//! } -//! ``` -//! -//! This example demonstrates the simultaneous readability and expressive power -//! of the `AnyKind` pattern. -//! -//! However, remember that when working with a type `P` that implements -//! `AnyPin`, the compiler can only use what it knows about the `AnyPin` trait. -//! But all of the functionality for GPIO pins is defined on the `Pin` type. To -//! make use of a generic type `P` implementing `AnyPin`, you must first convert -//! it to its corresponding `SpecificPin` using [`Into`], [`AsRef`] or -//! [`AsMut`]. And, in some instances, you may also need to convert back to the -//! type `P`. -//! -//! Suppose you wanted to store a completely generic `Pin` within a struct. -//! -//! ```ignore -//! pub struct Example { -//! pin: P, -//! } -//! ``` -//! -//! Next, suppose you want to create a method that would take the `Pin` out of -//! the struct, perform some operations in different `PinMode`s, and put it back -//! into the struct before returning. The `elided` method below shows such an -//! example. However, it can be a bit tricky to follow all of the type -//! conversions here. For clarity, the `expanded` method shows the same behavior -//! with each transformation given its proper type annotation. -//! -//! ```ignore -//! impl Example

{ -//! 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 = pin.into_floating_input(); -//! let _bit = pin.is_low().unwrap(); -//! let pin = pin.into_mode(); -//! self.pin = pin.into(); -//! self -//! } -//! 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: Pin = pin.into_floating_input(); -//! let _bit = pin.is_low().unwrap(); -//! let pin: SpecificPin

= pin.into_mode::(); -//! self.pin = pin.into(); -//! self -//! } -//! } -//! ``` -//! -//! Notice that it is not enough to simply put back the correct `SpecificPin`. -//! Even though the `SpecificPin` implements -//! `AnyPin` the compiler doesn't understand that -//! `SpecificPin

== P` for all `P`. As far as the compiler is concerned, -//! there could be several different types that implement -//! `AnyPin`. Instead, the compiler requires that -//! you put back an instance of `P` exactly. The final use of [`Into`] is key -//! here. It transforms the `SpecificPin` back into `P` itself. +//! This is heavily inspired by the work in [`atsamd-rs`](https://github.com/atsamd-rs/atsamd). Please refer to the +//! [documentation](https://docs.rs/atsamd-hal/0.15.1/atsamd_hal/typelevel/index.html) +//! over there for more details. mod private { /// Super trait used to mark traits with an exhaustive set of @@ -639,12 +10,16 @@ mod private { pub trait Sealed {} } +use core::borrow::{Borrow, BorrowMut}; + pub(crate) use private::Sealed; -/// Type-level version of the [None] variant -#[derive(Default)] -pub struct NoneT; -impl Sealed for NoneT {} +impl Sealed for (A, B) {} +impl Sealed for (A, B, C) {} +impl Sealed for (A, B, C, D) {} + +impl Sealed for frunk::HNil {} +impl Sealed for frunk::HCons {} /// Marker trait for type identity /// @@ -655,32 +30,34 @@ impl Sealed for NoneT {} /// specific type. Stated differently, it guarantees that `T == Specific` in /// the following example. /// -/// ```ignore +/// ```text /// where T: Is /// ``` /// /// Moreover, the super traits guarantee that any instance of or reference to a /// type `T` can be converted into the `Specific` type. /// -/// ```ignore +/// ``` +/// # use rp2040_hal::typelevel::Is; +/// # struct Specific; /// fn example(mut any: T) /// where /// T: Is, /// { -/// let specific_mut: &mut Specific = any.as_mut(); -/// let specific_ref: &Specific = any.as_ref(); +/// let specific_mut: &mut Specific = any.borrow_mut(); +/// let specific_ref: &Specific = any.borrow(); /// let specific: Specific = any.into(); /// } /// ``` /// -/// [`AnyKind`]: #anykind-trait-pattern +/// [`AnyKind`]: https://docs.rs/atsamd-hal/0.15.1/atsamd_hal/typelevel/index.html#anykind-trait-pattern pub trait Is where Self: Sealed, Self: From>, Self: Into>, - Self: AsRef>, - Self: AsMut>, + Self: Borrow>, + Self: BorrowMut>, { #[allow(missing_docs)] type Type; @@ -691,7 +68,31 @@ pub type IsType = ::Type; impl Is for T where - T: Sealed + AsRef + AsMut, + T: Sealed + Borrow + BorrowMut, { 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 f75095526..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 c46bae42e..ed7d2c71a 100644 --- a/rp2040-hal/src/uart/peripheral.rs +++ b/rp2040-hal/src/uart/peripheral.rs @@ -5,6 +5,7 @@ use super::*; 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}; @@ -73,10 +74,10 @@ impl> UartPeripheral { // Enable the UART, and the TX,RC,CTS and RTS based on the pins device.uartcr.write(|w| { w.uarten().set_bit(); - w.txe().bit(P::TX_ENABLED); - w.rxe().bit(P::RX_ENABLED); - w.ctsen().bit(P::CTS_ENABLED); - w.rtsen().bit(P::RTS_ENABLED); + w.txe().bit(P::Tx::IS_SOME); + w.rxe().bit(P::Rx::IS_SOME); + w.ctsen().bit(P::Cts::IS_SOME); + w.rtsen().bit(P::Rts::IS_SOME); w }); @@ -274,8 +275,8 @@ fn calculate_baudrate_dividers( } /// Baudrate configuration. Code loosely inspired from the C SDK. -fn configure_baudrate( - device: &mut dyn UartDevice, +fn configure_baudrate( + device: &mut U, wanted_baudrate: HertzU32, frequency: HertzU32, ) -> Result { diff --git a/rp2040-hal/src/uart/pins.rs b/rp2040-hal/src/uart/pins.rs index ec02546e8..3c742b14a 100644 --- a/rp2040-hal/src/uart/pins.rs +++ b/rp2040-hal/src/uart/pins.rs @@ -1,59 +1,141 @@ -use crate::gpio::{bank0, FunctionUart, Pin}; +use core::marker::PhantomData; + +use crate::gpio::{bank0::*, pin::pin_sealed::TypeLevelPinId, AnyPin, FunctionUart}; use crate::pac::{UART0, UART1}; -use crate::typelevel::Sealed; +use crate::typelevel::{OptionT, OptionTNone, OptionTSome, Sealed}; use super::UartDevice; -/// Declares a valid UART pinout. -pub trait ValidUartPinout { - /// Indicates TX should be enabled for this pinout - const TX_ENABLED: bool; - /// Indicates RX should be enabled for this pinout - const RX_ENABLED: bool; - /// Indicates CTS should be enabled for this pinout - const CTS_ENABLED: bool; - /// Indicates RTS should be enabled for this pinout - const RTS_ENABLED: bool; +// All type level checked pins are inherently valid. +macro_rules! pin_validation { + ($p:ident) => { + paste::paste!{ + #[doc = "Indicates a valid " $p " pin for UART0 or UART1"] + pub trait []: Sealed {} + + #[doc = "Indicates a valid " $p " pin for UART0 or UART1"] + pub trait []: Sealed {} + + impl [] for T + where + T: AnyPin, + T::Id: [], + { + } + + #[doc = "A runtime validated " $p " pin for uart."] + pub struct [](P, PhantomData); + impl Sealed for [] {} + impl [] for [] {} + impl [] + where + P: AnyPin, + U: UartDevice, + { + /// Validate a pin's function on a uart peripheral. + /// + #[doc = "Will err if the pin cannot be used as a " $p " pin for that Uart."] + pub fn validate(p: P, _u: &U) -> Result { + if [<$p:upper>].contains(&(p.borrow().id().num, U::ID)) && + p.borrow().id().bank == crate::gpio::DynBankId::Bank0 { + Ok(Self(p, PhantomData)) + } else { + Err(p) + } + } + } + + #[doc = "Indicates a valid optional " $p " pin for UART0 or UART1"] + pub trait []: OptionT {} + + impl [] for OptionTNone {} + impl [] for OptionTSome + where + U: UartDevice, + T: [], + { + } + } + }; + ($($p:ident),*) => { + $( + pin_validation!($p); + )* + }; } +pin_validation!(Tx, Rx, Cts, Rts); -impl ValidUartPinout for Pins -where - UART: UartDevice, - TX: Tx, - RX: Rx, - CTS: Cts, - RTS: Rts, -{ - const TX_ENABLED: bool = TX::ENABLED; - const RX_ENABLED: bool = RX::ENABLED; - const CTS_ENABLED: bool = CTS::ENABLED; - const RTS_ENABLED: bool = RTS::ENABLED; +macro_rules! impl_valid_uart { + ($($uart:ident: { + tx: [$($tx:ident),*], + rx: [$($rx:ident),*], + cts: [$($cts:ident),*], + rts: [$($rts:ident),*], + }),*) => { + $( + $(impl ValidPinIdTx<$uart> for $tx {})* + $(impl ValidPinIdRx<$uart> for $rx {})* + $(impl ValidPinIdCts<$uart> for $cts {})* + $(impl ValidPinIdRts<$uart> for $rts {})* + )* + const RX: &[(u8, usize)] = &[$($(($rx::ID.num, $uart::ID)),*),*]; + const TX: &[(u8, usize)] = &[$($(($tx::ID.num, $uart::ID)),*),*]; + const CTS: &[(u8, usize)] = &[$($(($cts::ID.num, $uart::ID)),*),*]; + const RTS: &[(u8, usize)] = &[$($(($rts::ID.num, $uart::ID)),*),*]; + }; } -impl ValidUartPinout for (TX, RX) +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], + } +); + +/// Declares a valid UART pinout. +pub trait ValidUartPinout: Sealed { + #[allow(missing_docs)] + type Rx: ValidOptionRx; + #[allow(missing_docs)] + type Tx: ValidOptionTx; + #[allow(missing_docs)] + type Cts: ValidOptionCts; + #[allow(missing_docs)] + type Rts: ValidOptionRts; +} + +impl ValidUartPinout for (Tx, Rx) where - UART: UartDevice, - TX: Tx, - RX: Rx, + Uart: UartDevice, + Tx: ValidPinTx, + Rx: ValidPinRx, { - const TX_ENABLED: bool = TX::ENABLED; - const RX_ENABLED: bool = RX::ENABLED; - const CTS_ENABLED: bool = false; - const RTS_ENABLED: bool = false; + type Tx = OptionTSome; + type Rx = OptionTSome; + type Cts = OptionTNone; + type Rts = OptionTNone; } -impl ValidUartPinout for (TX, RX, CTS, RTS) +impl ValidUartPinout for (Tx, Rx, Cts, Rts) where - UART: UartDevice, - TX: Tx, - RX: Rx, - CTS: Cts, - RTS: Rts, + Uart: UartDevice, + Tx: ValidPinTx, + Rx: ValidPinRx, + Cts: ValidPinCts, + Rts: ValidPinRts, { - const TX_ENABLED: bool = TX::ENABLED; - const RX_ENABLED: bool = RX::ENABLED; - const CTS_ENABLED: bool = CTS::ENABLED; - const RTS_ENABLED: bool = RTS::ENABLED; + type Rx = OptionTSome; + type Tx = OptionTSome; + type Cts = OptionTSome; + type Rts = OptionTSome; } /// Customizable Uart pinout, allowing you to set the pins individually. @@ -65,7 +147,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 `()` 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. /// @@ -76,149 +158,85 @@ 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 { + #[allow(missing_docs)] + pub tx: Tx, + #[allow(missing_docs)] + pub rx: Rx, + #[allow(missing_docs)] + pub cts: Cts, + #[allow(missing_docs)] + pub rts: Rts, } -impl Default for Pins<(), (), (), ()> { +impl Default for Pins { fn default() -> Self { Self { - tx: (), - rx: (), - rts: (), - cts: (), + tx: OptionTNone, + rx: OptionTNone, + rts: OptionTNone, + cts: OptionTNone, } } } -impl Pins { +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 Tx: Sealed { - #[allow(missing_docs)] - const ENABLED: bool; -} -/// Indicates a valid RX pin for UART0 or UART1 -pub trait Rx: Sealed { - #[allow(missing_docs)] - const ENABLED: bool; -} -/// Indicates a valid CTS pin for UART0 or UART1 -pub trait Cts: Sealed { - #[allow(missing_docs)] - const ENABLED: bool; -} -/// Indicates a valid RTS pin for UART0 or UART1 -pub trait Rts: Sealed { - #[allow(missing_docs)] - const ENABLED: bool; -} - -impl Tx for () { - const ENABLED: bool = false; -} -impl Rx for () { - const ENABLED: bool = false; -} -impl Cts for () { - const ENABLED: bool = false; -} -impl Rts for () { - const ENABLED: bool = false; -} -impl Sealed for () {} - -macro_rules! impl_valid_uart { - ($($uart:ident: { - tx: [$($tx:ident),*], - rx: [$($rx:ident),*], - cts: [$($cts:ident),*], - rts: [$($rts:ident),*], - }),*) => { - $( - $( - impl Tx<$uart> for Pin { - const ENABLED: bool = true; - } - )* - $( - impl Rx<$uart> for Pin { - const ENABLED: bool = true; - } - )* - $( - impl Cts<$uart> for Pin { - const ENABLED: bool = true; - } - )* - $( - impl Rts<$uart> for Pin { - const ENABLED: bool = true; - } - )* - )* - }; +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], - } -); diff --git a/rp2040-hal/src/uart/utils.rs b/rp2040-hal/src/uart/utils.rs index 135969048..0c0e54bf2 100644 --- a/rp2040-hal/src/uart/utils.rs +++ b/rp2040-hal/src/uart/utils.rs @@ -17,6 +17,9 @@ pub trait State: Sealed {} /// Trait to handle both underlying devices (UART0 & UART1) pub trait UartDevice: Deref + SubsystemReset + Sealed + 'static { + /// Index of the Uart. + const ID: usize; + /// The DREQ number for which TX DMA requests are triggered. fn tx_dreq() -> u8 where @@ -28,6 +31,8 @@ pub trait UartDevice: Deref + SubsystemReset + Sealed + } impl UartDevice for UART0 { + const ID: usize = 0; + /// The DREQ number for which TX DMA requests are triggered. fn tx_dreq() -> u8 { TREQ_SEL_A::UART0_TX.into() @@ -39,6 +44,8 @@ impl UartDevice for UART0 { } impl Sealed for UART0 {} impl UartDevice for UART1 { + const ID: usize = 1; + /// The DREQ number for which TX DMA requests are triggered. fn tx_dreq() -> u8 { TREQ_SEL_A::UART1_TX.into()