From 45580ec4c8c9955ba7cdc403ce3ba30ae6774e9f Mon Sep 17 00:00:00 2001 From: Jennifer Wilcox Date: Sat, 17 Apr 2021 18:18:10 -0500 Subject: [PATCH 01/16] Add basic output support for GPIOs --- .gitignore | 1 + Cargo.lock | 117 +++++++++++++++++++++++++++++++++ rp2040-hal/Cargo.toml | 2 +- rp2040-hal/src/gpio.rs | 132 ++++++++++++++++++++++++++++++++++++++ rp2040-hal/src/lib.rs | 1 + rp2040-hal/src/prelude.rs | 1 + 6 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock create mode 100644 rp2040-hal/src/gpio.rs diff --git a/.gitignore b/.gitignore index 9f11b75..2f5a8f4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .idea/ +target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c4cfdca --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,117 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + +[[package]] +name = "cortex-m" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643a210c1bdc23d0db511e2a576082f4ff4dcae9d0c37f50b431b8f8439d6d6b" +dependencies = [ + "bare-metal", + "bitfield", + "embedded-hal", + "volatile-register", +] + +[[package]] +name = "embedded-hal" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa998ce59ec9765d15216393af37a58961ddcefb14c753b4816ba2191d865fcb" +dependencies = [ + "nb 0.1.3", + "void", +] + +[[package]] +name = "nb" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" +dependencies = [ + "nb 1.0.0", +] + +[[package]] +name = "nb" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" + +[[package]] +name = "rp2040-hal" +version = "0.1.0" +dependencies = [ + "cortex-m", + "embedded-hal", + "nb 1.0.0", + "rp2040-pac", +] + +[[package]] +name = "rp2040-pac" +version = "0.1.1" +source = "git+https://github.com/rp-rs/rp2040-pac?branch=main#f4339cb856f082970767099a0079259f333cdc7e" +dependencies = [ + "cortex-m", + "vcell", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" +dependencies = [ + "vcell", +] diff --git a/rp2040-hal/Cargo.toml b/rp2040-hal/Cargo.toml index 782adb1..3d4600a 100644 --- a/rp2040-hal/Cargo.toml +++ b/rp2040-hal/Cargo.toml @@ -13,4 +13,4 @@ license = "MIT OR Apache-2.0" cortex-m = "0.7.1" embedded-hal = "0.2.4" nb = "1.0.0" -rp2040-pac = "0.1.1" +rp2040-pac = { git = "https://github.com/rp-rs/rp2040-pac", branch="main" } diff --git a/rp2040-hal/src/gpio.rs b/rp2040-hal/src/gpio.rs new file mode 100644 index 0000000..53af7e9 --- /dev/null +++ b/rp2040-hal/src/gpio.rs @@ -0,0 +1,132 @@ +//! General Purpose Input and Output (GPIO) +//! +//! TODO: Docs for this whole module +//! +//! For example, to turn on the LED on a Pico board: +//! +//! ```rust +//! let mut pac = rp2040_pac::Peripherals::take().unwrap(); +//! +//! let pins = pac.IO_BANK0.split(&mut pac.RESETS); +//! let mut led_pin = pins.gpio25.into_output(&mut pac.PADS_BANK0, &mut pac.SIO); +//! led_pin.set_low().unwrap(); +//! ``` +use core::convert::Infallible; +use core::marker::PhantomData; +use embedded_hal::digital::v2::OutputPin; + +pub struct Floating; +pub struct Input { + _mode: PhantomData, +} +pub struct Output; + +pub trait GpioExt { + type Parts; + + // TODO: Do we need a marker to check that clocks are up? + fn split(self, reset: &mut rp2040_pac::RESETS) -> Self::Parts; +} + +const FUNCTION_SIO: u8 = 5; + +macro_rules! gpio { + ($GPIOX:ident, $gpiox:ident, $PADSX:ident, [ + $($PXi:ident: ($pxi:ident, $i:expr),)+ + ]) => { + impl GpioExt for pac::$GPIOX { + type Parts = Parts; + + fn split(self, resets: &mut pac::RESETS) -> Parts { + resets.reset.modify(|_, w| w.$gpiox().clear_bit()); + Parts { + $( + $pxi: $PXi { _mode: PhantomData }, + )+ + } + } + } + + pub struct Parts { + $( + pub $pxi: $PXi>, + )+ + } + + $( + pub struct $PXi { + _mode: PhantomData, + } + + impl $PXi { + pub fn into_output( + self, + pads: &mut pac::$PADSX, + sio: &mut pac::SIO, + ) -> $PXi { + pads.gpio[$i].modify(|_, w| w.ie().set_bit().od().clear_bit()); + unsafe { + (*pac::$GPIOX::ptr()).gpio[$i] + .gpio_ctrl + .write_with_zero(|x| x.funcsel().bits(FUNCTION_SIO)); + } + sio.gpio_oe.modify(|_, x| unsafe { x.bits(1 << $i) }); + $PXi { _mode: PhantomData } + } + } + + impl OutputPin for $PXi { + type Error = Infallible; + + fn set_low(&mut self) -> Result<(), Self::Error> { + unsafe { + (*pac::SIO::ptr()).gpio_out_clr.write(|x| x.bits(1 << $i)); + } + Ok(()) + } + + fn set_high(&mut self) -> Result<(), Self::Error> { + unsafe { + (*pac::SIO::ptr()).gpio_out_set.write(|x| x.bits(1 << $i)); + } + Ok(()) + } + } + )+ + }; +} + +gpio!( + IO_BANK0, io_bank0, PADS_BANK0, [ + Gpio0: (gpio0, 0), + Gpio1: (gpio1, 1), + Gpio2: (gpio2, 2), + Gpio3: (gpio3, 3), + Gpio4: (gpio4, 4), + Gpio5: (gpio5, 5), + Gpio6: (gpio6, 6), + Gpio7: (gpio7, 7), + Gpio8: (gpio8, 8), + Gpio9: (gpio9, 9), + Gpio10: (gpio10, 10), + Gpio11: (gpio11, 11), + Gpio12: (gpio12, 12), + Gpio13: (gpio13, 13), + Gpio14: (gpio14, 14), + Gpio15: (gpio15, 15), + Gpio16: (gpio16, 16), + Gpio17: (gpio17, 17), + Gpio18: (gpio18, 18), + Gpio19: (gpio19, 19), + Gpio20: (gpio20, 20), + Gpio21: (gpio21, 21), + Gpio22: (gpio22, 22), + Gpio23: (gpio23, 23), + Gpio24: (gpio24, 24), + Gpio25: (gpio25, 25), + Gpio26: (gpio26, 26), + Gpio27: (gpio27, 27), + Gpio28: (gpio28, 28), + Gpio29: (gpio29, 29), + ] +); diff --git a/rp2040-hal/src/lib.rs b/rp2040-hal/src/lib.rs index e8d96e2..719afcc 100644 --- a/rp2040-hal/src/lib.rs +++ b/rp2040-hal/src/lib.rs @@ -13,6 +13,7 @@ extern crate nb; pub extern crate rp2040_pac as pac; pub mod adc; +pub mod gpio; pub mod i2c; pub mod prelude; pub mod pwm; diff --git a/rp2040-hal/src/prelude.rs b/rp2040-hal/src/prelude.rs index 8e3e645..57edf4f 100644 --- a/rp2040-hal/src/prelude.rs +++ b/rp2040-hal/src/prelude.rs @@ -1 +1,2 @@ //! Prelude +pub use crate::gpio::GpioExt; From 2c3a0956fa321062c0a7b68ad716596ce550ed1c Mon Sep 17 00:00:00 2001 From: Jennifer Wilcox Date: Sat, 24 Apr 2021 16:18:57 -0500 Subject: [PATCH 02/16] Add input support, examples, SIO/PADS ownership Sorry this is a large commit :( This adds support for input pins, including pulling them high or low. It also adds two examples: the start of a classic blinky LED example, and an example for reading input. --- .cargo/config | 8 + .gitignore | 1 + Cargo.lock | 117 --------------- memory.x | 13 ++ rp2040-hal/Cargo.toml | 7 +- rp2040-hal/examples/blinky.rs | 31 ++++ rp2040-hal/examples/gpio_in_out.rs | 36 +++++ rp2040-hal/src/gpio.rs | 225 ++++++++++++++++++++--------- 8 files changed, 253 insertions(+), 185 deletions(-) delete mode 100644 Cargo.lock create mode 100644 memory.x create mode 100644 rp2040-hal/examples/blinky.rs create mode 100644 rp2040-hal/examples/gpio_in_out.rs diff --git a/.cargo/config b/.cargo/config index 3500668..4dc2c04 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,3 +1,11 @@ [build] # Instruction set of Cortex-M0+ target = "thumbv6m-none-eabi" + +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +rustflags = [ + "-C", "link-arg=--nmagic", + "-C", "link-arg=-Tlink.x", + "-C", "inline-threshold=5", + "-C", "no-vectorize-loops", +] diff --git a/.gitignore b/.gitignore index 2f5a8f4..781f1c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .idea/ target +Cargo.lock diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index c4cfdca..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,117 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "bare-metal" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" -dependencies = [ - "rustc_version", -] - -[[package]] -name = "bitfield" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" - -[[package]] -name = "cortex-m" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643a210c1bdc23d0db511e2a576082f4ff4dcae9d0c37f50b431b8f8439d6d6b" -dependencies = [ - "bare-metal", - "bitfield", - "embedded-hal", - "volatile-register", -] - -[[package]] -name = "embedded-hal" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa998ce59ec9765d15216393af37a58961ddcefb14c753b4816ba2191d865fcb" -dependencies = [ - "nb 0.1.3", - "void", -] - -[[package]] -name = "nb" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" -dependencies = [ - "nb 1.0.0", -] - -[[package]] -name = "nb" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" - -[[package]] -name = "rp2040-hal" -version = "0.1.0" -dependencies = [ - "cortex-m", - "embedded-hal", - "nb 1.0.0", - "rp2040-pac", -] - -[[package]] -name = "rp2040-pac" -version = "0.1.1" -source = "git+https://github.com/rp-rs/rp2040-pac?branch=main#f4339cb856f082970767099a0079259f333cdc7e" -dependencies = [ - "cortex-m", - "vcell", -] - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "vcell" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - -[[package]] -name = "volatile-register" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286" -dependencies = [ - "vcell", -] diff --git a/memory.x b/memory.x new file mode 100644 index 0000000..23918e1 --- /dev/null +++ b/memory.x @@ -0,0 +1,13 @@ +MEMORY { + BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100 + FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100 + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} + +SECTIONS { + /* ### Boot loader */ + .boot2 ORIGIN(BOOT2) : + { + KEEP(*(.boot2)); + } > BOOT2 +} INSERT BEFORE .text; diff --git a/rp2040-hal/Cargo.toml b/rp2040-hal/Cargo.toml index 3d4600a..e567ef1 100644 --- a/rp2040-hal/Cargo.toml +++ b/rp2040-hal/Cargo.toml @@ -11,6 +11,11 @@ license = "MIT OR Apache-2.0" [dependencies] cortex-m = "0.7.1" -embedded-hal = "0.2.4" +embedded-hal = { version = "0.2.4", features = ["unproven"] } nb = "1.0.0" rp2040-pac = { git = "https://github.com/rp-rs/rp2040-pac", branch="main" } + +[dev-dependencies] +cortex-m-rt = "0.6.13" +panic-halt = "0.2.0" +rp2040-boot2 = { git = "https://github.com/rp-rs/rp2040-boot2-rs", branch="main" } diff --git a/rp2040-hal/examples/blinky.rs b/rp2040-hal/examples/blinky.rs new file mode 100644 index 0000000..a51c50c --- /dev/null +++ b/rp2040-hal/examples/blinky.rs @@ -0,0 +1,31 @@ +//! Blinks the LED on a Pico board +//! +//! This will blink an LED attached to GP25, which is the pin the Pico uses for the on-board LED. +#![no_std] +#![no_main] + +use panic_halt as _; + +use cortex_m_rt::entry; + +#[link_section = ".boot2"] +#[used] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER; + +use embedded_hal::digital::v2::OutputPin; +use rp2040_hal::prelude::*; + +#[entry] +fn main() -> ! { + let mut pac = rp2040_pac::Peripherals::take().unwrap(); + + let pins = pac.IO_BANK0.split(pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let mut led_pin = pins.gpio25.into_output(); + + loop { + led_pin.set_low().unwrap(); + // TODO: I dare not use delays until we've got clocks running + led_pin.set_high().unwrap(); + // TODO: Other delay + } +} diff --git a/rp2040-hal/examples/gpio_in_out.rs b/rp2040-hal/examples/gpio_in_out.rs new file mode 100644 index 0000000..77a9271 --- /dev/null +++ b/rp2040-hal/examples/gpio_in_out.rs @@ -0,0 +1,36 @@ +//! Toggle LED based on GPIO input +//! +//! This will control an LED on GP25 based on a button hooked up to GP15. The button should be tied +//! to ground, as the input pin is pulled high internally by this example. When the button is +//! pressed, the LED will turn off. +#![no_std] +#![no_main] + +use panic_halt as _; + +use cortex_m_rt::entry; + +#[link_section = ".boot2"] +#[used] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER; + +use embedded_hal::digital::v2::{InputPin, OutputPin}; +use rp2040_hal::prelude::*; + +#[entry] +fn main() -> ! { + let mut pac = rp2040_pac::Peripherals::take().unwrap(); + + let pins = pac.IO_BANK0.split(pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); + let mut led_pin = pins.gpio25.into_output(); + let mut button_pin = pins.gpio15.into_input(); + button_pin.pull_high(); + + loop { + if button_pin.is_high().unwrap() { + led_pin.set_high().unwrap(); + } else { + led_pin.set_low().unwrap(); + } + } +} diff --git a/rp2040-hal/src/gpio.rs b/rp2040-hal/src/gpio.rs index 53af7e9..b2e6fb6 100644 --- a/rp2040-hal/src/gpio.rs +++ b/rp2040-hal/src/gpio.rs @@ -7,97 +7,188 @@ //! ```rust //! let mut pac = rp2040_pac::Peripherals::take().unwrap(); //! -//! let pins = pac.IO_BANK0.split(&mut pac.RESETS); -//! let mut led_pin = pins.gpio25.into_output(&mut pac.PADS_BANK0, &mut pac.SIO); -//! led_pin.set_low().unwrap(); +//! let pins = pac.IO_BANK0.split(pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); +//! let mut led_pin = pins.gpio25.into_output(); +//! led_pin.set_high().unwrap(); //! ``` -use core::convert::Infallible; -use core::marker::PhantomData; -use embedded_hal::digital::v2::OutputPin; - -pub struct Floating; -pub struct Input { - _mode: PhantomData, -} +pub struct Input; pub struct Output; +pub struct Unknown; -pub trait GpioExt { +pub trait GpioExt { type Parts; // TODO: Do we need a marker to check that clocks are up? - fn split(self, reset: &mut rp2040_pac::RESETS) -> Self::Parts; + fn split(self, pads: PADS, sio: SIO, reset: &mut rp2040_pac::RESETS) -> Self::Parts; } +// Magic numbers from the datasheet +// const FUNCTION_SPI: u8 = 1; +// const FUNCTION_UART: u8 = 2; +// const FUNCTION_I2C: u8 = 3; +// const FUNCTION_PWM: u8 = 4; const FUNCTION_SIO: u8 = 5; +// const FUNCTION_PIO0: u8 = 6; +// const FUNCTION_PIO1: u8 = 7; +// const FUNCTION_CLOCK: u8 = 8; +// const FUNCTION_USB: u8 = 9; macro_rules! gpio { - ($GPIOX:ident, $gpiox:ident, $PADSX:ident, [ + ($GPIOX:ident, $gpiox:ident, $PADSX:ident, $padsx:ident, [ $($PXi:ident: ($pxi:ident, $i:expr),)+ ]) => { - impl GpioExt for pac::$GPIOX { - type Parts = Parts; + mod $gpiox { + use core::convert::Infallible; + use core::marker::PhantomData; + use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin}; + use super::{GpioExt, Input, Output, Unknown, FUNCTION_SIO}; - fn split(self, resets: &mut pac::RESETS) -> Parts { - resets.reset.modify(|_, w| w.$gpiox().clear_bit()); - Parts { - $( - $pxi: $PXi { _mode: PhantomData }, - )+ + impl GpioExt for pac::$GPIOX { + type Parts = Parts; + + fn split(self, pads: pac::$PADSX, sio: pac::SIO, resets: &mut pac::RESETS) -> Parts { + resets.reset.modify(|_, w| w.$gpiox().clear_bit()); + Parts { + _pads: pads, + _sio: sio, + $( + $pxi: $PXi { _mode: PhantomData }, + )+ + } } } - } - pub struct Parts { + pub struct Parts { + _pads: pac::$PADSX, + _sio: pac::SIO, + $( + pub $pxi: $PXi, + )+ + } + + // Puts pad in default state as far as this crate is concerned: + // - Input is enabled + // - Output is enabled (if also set in SIO) + // - Pull up/down is disabled + // + // TODO: Drive strength, smitty, slewing + fn setup_pad_io(pads: &pac::$padsx::RegisterBlock, index: usize) { + pads.gpio[index].modify(|_, w| w.ie().set_bit().od().clear_bit().pue().clear_bit().pde().clear_bit()); + } + + fn set_gpio_function(gpios: &pac::$gpiox::RegisterBlock, index: usize, function: u8) { + gpios.gpio[index] + .gpio_ctrl + .write_with_zero(|x| unsafe { x.funcsel().bits(function) }); + } + $( - pub $pxi: $PXi>, + pub struct $PXi { + _mode: PhantomData, + } + + // Safety: We own our $i slice of padsx, gpiox, and sio because the + // construction of Parts assumes ownership of all 3 and will not release + // them. Thus several of the methods below will reconstruct these objects + // as-needed + + impl $PXi { + pub fn into_output( + self, + ) -> $PXi { + unsafe { + setup_pad_io(&*pac::$PADSX::ptr(), $i); + } + unsafe { + set_gpio_function(&*pac::$GPIOX::ptr(), $i, FUNCTION_SIO); + } + unsafe { + (*pac::SIO::ptr()).gpio_oe_set.write(|x| { x.bits(1 << $i) }); + } + $PXi { _mode: PhantomData } + } + + pub fn into_input( + self, + ) -> $PXi { + unsafe { + setup_pad_io(&*pac::$PADSX::ptr(), $i); + } + unsafe { + set_gpio_function(&*pac::$GPIOX::ptr(), $i, FUNCTION_SIO); + } + unsafe { + (*pac::SIO::ptr()).gpio_oe_clr.write(|x| { x.bits(1 << $i) }); + } + $PXi { _mode: PhantomData } + } + } + + impl OutputPin for $PXi { + type Error = Infallible; + + fn set_low(&mut self) -> Result<(), Self::Error> { + unsafe { + (*pac::SIO::ptr()).gpio_out_clr.write(|x| x.bits(1 << $i)); + } + Ok(()) + } + + fn set_high(&mut self) -> Result<(), Self::Error> { + unsafe { + (*pac::SIO::ptr()).gpio_out_set.write(|x| x.bits(1 << $i)); + } + Ok(()) + } + } + + impl StatefulOutputPin for $PXi { + fn is_set_low(&self) -> Result { + Ok(!self.is_set_high()?) + } + + fn is_set_high(&self) -> Result { + unsafe { + Ok((*pac::SIO::ptr()).gpio_out_set.read().bits() & (1 << $i) != 0) + } + } + } + + // TODO: Don't allow Unknown since we need to fix up the pad at least + impl InputPin for $PXi { + type Error = Infallible; + + fn is_low(&self) -> Result { + Ok(!self.is_high()?) + } + + fn is_high(&self) -> Result { + unsafe { + Ok((*pac::SIO::ptr()).gpio_in.read().bits() & (1 << $i) != 0) + } + } + } + + impl $PXi { + pub fn pull_high(&mut self) { + unsafe { + (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.pue().set_bit().pde().clear_bit()); + } + } + + pub fn pull_low(&mut self) { + unsafe { + (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.pue().clear_bit().pde().set_bit()); + } + } + } )+ } - - $( - pub struct $PXi { - _mode: PhantomData, - } - - impl $PXi { - pub fn into_output( - self, - pads: &mut pac::$PADSX, - sio: &mut pac::SIO, - ) -> $PXi { - pads.gpio[$i].modify(|_, w| w.ie().set_bit().od().clear_bit()); - unsafe { - (*pac::$GPIOX::ptr()).gpio[$i] - .gpio_ctrl - .write_with_zero(|x| x.funcsel().bits(FUNCTION_SIO)); - } - sio.gpio_oe.modify(|_, x| unsafe { x.bits(1 << $i) }); - $PXi { _mode: PhantomData } - } - } - - impl OutputPin for $PXi { - type Error = Infallible; - - fn set_low(&mut self) -> Result<(), Self::Error> { - unsafe { - (*pac::SIO::ptr()).gpio_out_clr.write(|x| x.bits(1 << $i)); - } - Ok(()) - } - - fn set_high(&mut self) -> Result<(), Self::Error> { - unsafe { - (*pac::SIO::ptr()).gpio_out_set.write(|x| x.bits(1 << $i)); - } - Ok(()) - } - } - )+ }; } gpio!( - IO_BANK0, io_bank0, PADS_BANK0, [ + IO_BANK0, io_bank0, PADS_BANK0, pads_bank0, [ Gpio0: (gpio0, 0), Gpio1: (gpio1, 1), Gpio2: (gpio2, 2), From 2e5c1fc0e36e812dd05ca21614dd5f9b09191f38 Mon Sep 17 00:00:00 2001 From: Jennifer Wilcox Date: Sat, 24 Apr 2021 16:22:27 -0500 Subject: [PATCH 03/16] Cleanup wacky imports in examples --- rp2040-hal/examples/blinky.rs | 8 +++----- rp2040-hal/examples/gpio_in_out.rs | 8 +++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/rp2040-hal/examples/blinky.rs b/rp2040-hal/examples/blinky.rs index a51c50c..3e53311 100644 --- a/rp2040-hal/examples/blinky.rs +++ b/rp2040-hal/examples/blinky.rs @@ -4,17 +4,15 @@ #![no_std] #![no_main] -use panic_halt as _; - use cortex_m_rt::entry; +use embedded_hal::digital::v2::OutputPin; +use panic_halt as _; +use rp2040_hal::prelude::*; #[link_section = ".boot2"] #[used] pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER; -use embedded_hal::digital::v2::OutputPin; -use rp2040_hal::prelude::*; - #[entry] fn main() -> ! { let mut pac = rp2040_pac::Peripherals::take().unwrap(); diff --git a/rp2040-hal/examples/gpio_in_out.rs b/rp2040-hal/examples/gpio_in_out.rs index 77a9271..b162483 100644 --- a/rp2040-hal/examples/gpio_in_out.rs +++ b/rp2040-hal/examples/gpio_in_out.rs @@ -6,17 +6,15 @@ #![no_std] #![no_main] -use panic_halt as _; - use cortex_m_rt::entry; +use embedded_hal::digital::v2::{InputPin, OutputPin}; +use panic_halt as _; +use rp2040_hal::prelude::*; #[link_section = ".boot2"] #[used] pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER; -use embedded_hal::digital::v2::{InputPin, OutputPin}; -use rp2040_hal::prelude::*; - #[entry] fn main() -> ! { let mut pac = rp2040_pac::Peripherals::take().unwrap(); From 3536604b9e5b83056480f21d05da032a49220f73 Mon Sep 17 00:00:00 2001 From: Jennifer Wilcox Date: Sat, 24 Apr 2021 17:28:33 -0500 Subject: [PATCH 04/16] Block input reads on Unknown state --- rp2040-hal/src/gpio.rs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/rp2040-hal/src/gpio.rs b/rp2040-hal/src/gpio.rs index b2e6fb6..106a233 100644 --- a/rp2040-hal/src/gpio.rs +++ b/rp2040-hal/src/gpio.rs @@ -154,20 +154,26 @@ macro_rules! gpio { } } - // TODO: Don't allow Unknown since we need to fix up the pad at least - impl InputPin for $PXi { - type Error = Infallible; + macro_rules! impl_input_for { + ($MODE:ident) => { + impl InputPin for $PXi<$MODE> { + type Error = Infallible; - fn is_low(&self) -> Result { - Ok(!self.is_high()?) - } + fn is_low(&self) -> Result { + Ok(!self.is_high()?) + } - fn is_high(&self) -> Result { - unsafe { - Ok((*pac::SIO::ptr()).gpio_in.read().bits() & (1 << $i) != 0) + fn is_high(&self) -> Result { + unsafe { + Ok((*pac::SIO::ptr()).gpio_in.read().bits() & (1 << $i) != 0) + } + } } - } + }; } + // Not allowed for Unknown since we don't know what state the pad is in + impl_input_for!(Input); + impl_input_for!(Output); impl $PXi { pub fn pull_high(&mut self) { From 94f67f7ecac49c3b3e00c5b0e5ed259cd6ae2903 Mon Sep 17 00:00:00 2001 From: Jennifer Wilcox Date: Sat, 24 Apr 2021 18:03:03 -0500 Subject: [PATCH 05/16] Finish configuration options for IOs --- rp2040-hal/examples/gpio_in_out.rs | 3 +- rp2040-hal/src/gpio.rs | 82 ++++++++++++++++++++++++++---- 2 files changed, 73 insertions(+), 12 deletions(-) diff --git a/rp2040-hal/examples/gpio_in_out.rs b/rp2040-hal/examples/gpio_in_out.rs index b162483..7a6036a 100644 --- a/rp2040-hal/examples/gpio_in_out.rs +++ b/rp2040-hal/examples/gpio_in_out.rs @@ -21,8 +21,7 @@ fn main() -> ! { let pins = pac.IO_BANK0.split(pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); let mut led_pin = pins.gpio25.into_output(); - let mut button_pin = pins.gpio15.into_input(); - button_pin.pull_high(); + let button_pin = pins.gpio15.into_input().pull_high(); loop { if button_pin.is_high().unwrap() { diff --git a/rp2040-hal/src/gpio.rs b/rp2040-hal/src/gpio.rs index 106a233..f4d97ba 100644 --- a/rp2040-hal/src/gpio.rs +++ b/rp2040-hal/src/gpio.rs @@ -22,6 +22,20 @@ pub trait GpioExt { fn split(self, pads: PADS, sio: SIO, reset: &mut rp2040_pac::RESETS) -> Self::Parts; } +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub enum OutputDriveStrength { + Ma2, + Ma4, + Ma8, + Ma12, +} + +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub enum OutputSlewRate { + Slow, + Fast, +} + // Magic numbers from the datasheet // const FUNCTION_SPI: u8 = 1; // const FUNCTION_UART: u8 = 2; @@ -41,7 +55,7 @@ macro_rules! gpio { use core::convert::Infallible; use core::marker::PhantomData; use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin}; - use super::{GpioExt, Input, Output, Unknown, FUNCTION_SIO}; + use super::*; impl GpioExt for pac::$GPIOX { type Parts = Parts; @@ -66,14 +80,15 @@ macro_rules! gpio { )+ } - // Puts pad in default state as far as this crate is concerned: - // - Input is enabled - // - Output is enabled (if also set in SIO) - // - Pull up/down is disabled - // - // TODO: Drive strength, smitty, slewing + // Puts pad in same state as post-reset, according to datasheet fn setup_pad_io(pads: &pac::$padsx::RegisterBlock, index: usize) { - pads.gpio[index].modify(|_, w| w.ie().set_bit().od().clear_bit().pue().clear_bit().pde().clear_bit()); + pads.gpio[index].modify(|_, w| w.ie().set_bit() + .od().clear_bit() + .pue().clear_bit() + .pde().set_bit() + .schmitt().set_bit() + .drive()._4m_a() + .slewfast().clear_bit()); } fn set_gpio_function(gpios: &pac::$gpiox::RegisterBlock, index: usize, function: u8) { @@ -82,6 +97,8 @@ macro_rules! gpio { .write_with_zero(|x| unsafe { x.funcsel().bits(function) }); } + type PacDriveStrength = pac::$padsx::gpio::DRIVE_A; + $( pub struct $PXi { _mode: PhantomData, @@ -175,17 +192,62 @@ macro_rules! gpio { impl_input_for!(Input); impl_input_for!(Output); + impl $PXi { + pub fn drive_strength(self, strength: OutputDriveStrength) -> Self { + let converted = match strength { + OutputDriveStrength::Ma2 => PacDriveStrength::_2MA, + OutputDriveStrength::Ma4 => PacDriveStrength::_4MA, + OutputDriveStrength::Ma8 => PacDriveStrength::_8MA, + OutputDriveStrength::Ma12 => PacDriveStrength::_12MA, + }; + unsafe { + (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.drive().variant(converted)); + } + self + } + + pub fn slew_rate(self, slew_rate: OutputSlewRate) -> Self { + unsafe { + (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.slewfast().bit(slew_rate == OutputSlewRate::Fast)); + } + self + } + } + impl $PXi { - pub fn pull_high(&mut self) { + pub fn pull_high(self) -> Self { unsafe { (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.pue().set_bit().pde().clear_bit()); } + self } - pub fn pull_low(&mut self) { + pub fn pull_low(self) -> Self { unsafe { (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.pue().clear_bit().pde().set_bit()); } + self + } + + pub fn float(self) -> Self { + unsafe { + (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.pue().clear_bit().pde().clear_bit()); + } + self + } + + pub fn enable_schmitt_trigger(self) -> Self { + unsafe { + (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.schmitt().set_bit()); + } + self + } + + pub fn disable_schmitt_trigger(self) -> Self { + unsafe { + (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.schmitt().clear_bit()); + } + self } } )+ From 422a45fbc5b6779163912c7f88aca8d8645d2172 Mon Sep 17 00:00:00 2001 From: Jennifer Wilcox Date: Sat, 24 Apr 2021 18:41:12 -0500 Subject: [PATCH 06/16] Add documentation --- rp2040-hal/src/gpio.rs | 122 ++++++++++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 38 deletions(-) diff --git a/rp2040-hal/src/gpio.rs b/rp2040-hal/src/gpio.rs index f4d97ba..57207d0 100644 --- a/rp2040-hal/src/gpio.rs +++ b/rp2040-hal/src/gpio.rs @@ -1,38 +1,65 @@ //! General Purpose Input and Output (GPIO) //! -//! TODO: Docs for this whole module -//! -//! For example, to turn on the LED on a Pico board: +//! To access the GPIO pins you must call the `split` method on the IO bank. This will return a +//! `Parts` struct with access to the individual pins: //! //! ```rust +//! use rp2040_hal::prelude::*; //! let mut pac = rp2040_pac::Peripherals::take().unwrap(); -//! //! let pins = pac.IO_BANK0.split(pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); +//! ``` +//! +//! Once you have the GPIO pins struct, you can take individual pins and configure them: +//! +//! ```rust //! let mut led_pin = pins.gpio25.into_output(); //! led_pin.set_high().unwrap(); //! ``` +//! +//! Input pins support the following options: +//! - Pull high, pull low, or floating +//! - Schmitt trigger +//! +//! Output pins support the following options: +//! - Slew rate (fast or slow) +//! - Drive strength (2, 4, 8 or 12 mA) + +/// Mode marker for an input pin pub struct Input; +/// Mode marker for an output pin pub struct Output; +/// Mode marker for a pin in an unknown state (generally happens at startup) pub struct Unknown; +/// This trait adds a method to extract pins from an IO bank and convert them into HAL objects pub trait GpioExt { + /// The type of struct that will hold the pins once they're converted to HAL objects type Parts; + /// Convert the IO bank into a struct of HAL pin objects // TODO: Do we need a marker to check that clocks are up? fn split(self, pads: PADS, sio: SIO, reset: &mut rp2040_pac::RESETS) -> Self::Parts; } #[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 Ma2, + /// 4 mA Ma4, + /// 8 mA Ma8, + /// 12 mA Ma12, } #[derive(Clone, Copy, Eq, PartialEq, Debug)] +/// The slew rate of a pin when used as an output pub enum OutputSlewRate { + /// Slew slow Slow, + /// Slew fast Fast, } @@ -48,10 +75,13 @@ const FUNCTION_SIO: u8 = 5; // const FUNCTION_USB: u8 = 9; macro_rules! gpio { - ($GPIOX:ident, $gpiox:ident, $PADSX:ident, $padsx:ident, [ - $($PXi:ident: ($pxi:ident, $i:expr),)+ + ($GPIOX:ident, $gpiox:ident, $PADSX:ident, $padsx:ident, $gpioxs:expr, [ + $($PXi:ident: ($pxi:ident, $i:expr, $is:expr),)+ ]) => { - mod $gpiox { + #[doc = "HAL objects for the "] + #[doc = $gpioxs] + #[doc = " bank of GPIO pins"] + pub mod $gpiox { use core::convert::Infallible; use core::marker::PhantomData; use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin}; @@ -72,10 +102,15 @@ macro_rules! gpio { } } + #[doc = "Struct containing HAL objects for all the "] + #[doc = $gpioxs] + #[doc = " pins"] pub struct Parts { _pads: pac::$PADSX, _sio: pac::SIO, $( + #[doc = "GPIO pin "] + #[doc = $is] pub $pxi: $PXi, )+ } @@ -100,6 +135,8 @@ macro_rules! gpio { type PacDriveStrength = pac::$padsx::gpio::DRIVE_A; $( + #[doc = "HAL object for GPIO pin "] + #[doc = $is] pub struct $PXi { _mode: PhantomData, } @@ -110,6 +147,7 @@ macro_rules! gpio { // as-needed impl $PXi { + #[doc = "Configure this pin as an output"] pub fn into_output( self, ) -> $PXi { @@ -125,6 +163,7 @@ macro_rules! gpio { $PXi { _mode: PhantomData } } + #[doc = "Configure this pin as an input"] pub fn into_input( self, ) -> $PXi { @@ -193,6 +232,7 @@ macro_rules! gpio { impl_input_for!(Output); impl $PXi { + #[doc = "Configure the drive strength for this output pin"] pub fn drive_strength(self, strength: OutputDriveStrength) -> Self { let converted = match strength { OutputDriveStrength::Ma2 => PacDriveStrength::_2MA, @@ -206,6 +246,7 @@ macro_rules! gpio { self } + #[doc = "Configure the slew rate for this output pin"] pub fn slew_rate(self, slew_rate: OutputSlewRate) -> Self { unsafe { (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.slewfast().bit(slew_rate == OutputSlewRate::Fast)); @@ -215,6 +256,7 @@ macro_rules! gpio { } impl $PXi { + #[doc = "Pull this input pin high using internal resistors"] pub fn pull_high(self) -> Self { unsafe { (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.pue().set_bit().pde().clear_bit()); @@ -222,6 +264,7 @@ macro_rules! gpio { self } + #[doc = "Pull this input pin low using internal resistors"] pub fn pull_low(self) -> Self { unsafe { (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.pue().clear_bit().pde().set_bit()); @@ -229,6 +272,7 @@ macro_rules! gpio { self } + #[doc = "Allow this input pin to float (i.e. don't pull it high or low)"] pub fn float(self) -> Self { unsafe { (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.pue().clear_bit().pde().clear_bit()); @@ -236,6 +280,7 @@ macro_rules! gpio { self } + #[doc = "Enable the schmitt trigger for this input pin"] pub fn enable_schmitt_trigger(self) -> Self { unsafe { (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.schmitt().set_bit()); @@ -243,6 +288,7 @@ macro_rules! gpio { self } + #[doc = "Disable the schmitt trigger for this input pin"] pub fn disable_schmitt_trigger(self) -> Self { unsafe { (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.schmitt().clear_bit()); @@ -256,36 +302,36 @@ macro_rules! gpio { } gpio!( - IO_BANK0, io_bank0, PADS_BANK0, pads_bank0, [ - Gpio0: (gpio0, 0), - Gpio1: (gpio1, 1), - Gpio2: (gpio2, 2), - Gpio3: (gpio3, 3), - Gpio4: (gpio4, 4), - Gpio5: (gpio5, 5), - Gpio6: (gpio6, 6), - Gpio7: (gpio7, 7), - Gpio8: (gpio8, 8), - Gpio9: (gpio9, 9), - Gpio10: (gpio10, 10), - Gpio11: (gpio11, 11), - Gpio12: (gpio12, 12), - Gpio13: (gpio13, 13), - Gpio14: (gpio14, 14), - Gpio15: (gpio15, 15), - Gpio16: (gpio16, 16), - Gpio17: (gpio17, 17), - Gpio18: (gpio18, 18), - Gpio19: (gpio19, 19), - Gpio20: (gpio20, 20), - Gpio21: (gpio21, 21), - Gpio22: (gpio22, 22), - Gpio23: (gpio23, 23), - Gpio24: (gpio24, 24), - Gpio25: (gpio25, 25), - Gpio26: (gpio26, 26), - Gpio27: (gpio27, 27), - Gpio28: (gpio28, 28), - Gpio29: (gpio29, 29), + IO_BANK0, io_bank0, PADS_BANK0, pads_bank0, "IO_BANK0", [ + Gpio0: (gpio0, 0, "0"), + Gpio1: (gpio1, 1, "1"), + Gpio2: (gpio2, 2, "2"), + Gpio3: (gpio3, 3, "3"), + Gpio4: (gpio4, 4, "4"), + Gpio5: (gpio5, 5, "5"), + Gpio6: (gpio6, 6, "6"), + Gpio7: (gpio7, 7, "7"), + Gpio8: (gpio8, 8, "8"), + Gpio9: (gpio9, 9, "9"), + Gpio10: (gpio10, 10, "10"), + Gpio11: (gpio11, 11, "11"), + Gpio12: (gpio12, 12, "12"), + Gpio13: (gpio13, 13, "13"), + Gpio14: (gpio14, 14, "14"), + Gpio15: (gpio15, 15, "15"), + Gpio16: (gpio16, 16, "16"), + Gpio17: (gpio17, 17, "17"), + Gpio18: (gpio18, 18, "18"), + Gpio19: (gpio19, 19, "19"), + Gpio20: (gpio20, 20, "20"), + Gpio21: (gpio21, 21, "21"), + Gpio22: (gpio22, 22, "22"), + Gpio23: (gpio23, 23, "23"), + Gpio24: (gpio24, 24, "24"), + Gpio25: (gpio25, 25, "25"), + Gpio26: (gpio26, 26, "26"), + Gpio27: (gpio27, 27, "27"), + Gpio28: (gpio28, 28, "28"), + Gpio29: (gpio29, 29, "29"), ] ); From 037fc665b2ce57a43a070258ac529b0ee59ed811 Mon Sep 17 00:00:00 2001 From: Jennifer Wilcox Date: Sun, 25 Apr 2021 10:15:32 -0500 Subject: [PATCH 07/16] Apply suggestions from code review Co-authored-by: tdittr --- rp2040-hal/src/gpio.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rp2040-hal/src/gpio.rs b/rp2040-hal/src/gpio.rs index 57207d0..22c7eb6 100644 --- a/rp2040-hal/src/gpio.rs +++ b/rp2040-hal/src/gpio.rs @@ -205,7 +205,7 @@ macro_rules! gpio { fn is_set_high(&self) -> Result { unsafe { - Ok((*pac::SIO::ptr()).gpio_out_set.read().bits() & (1 << $i) != 0) + Ok((*pac::SIO::ptr()).gpio_out.read().bits() & (1 << $i) != 0) } } } From b3b7677f82fd59b1f1da1f94816a036a1750cb81 Mon Sep 17 00:00:00 2001 From: Jennifer Wilcox Date: Sun, 25 Apr 2021 11:13:21 -0500 Subject: [PATCH 08/16] More review comments --- rp2040-hal/examples/blinky.rs | 7 +-- rp2040-hal/examples/gpio_in_out.rs | 2 +- rp2040-hal/src/gpio.rs | 68 +++++++++++++++--------------- 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/rp2040-hal/examples/blinky.rs b/rp2040-hal/examples/blinky.rs index 3e53311..2bae343 100644 --- a/rp2040-hal/examples/blinky.rs +++ b/rp2040-hal/examples/blinky.rs @@ -21,9 +21,10 @@ fn main() -> ! { let mut led_pin = pins.gpio25.into_output(); loop { - led_pin.set_low().unwrap(); - // TODO: I dare not use delays until we've got clocks running led_pin.set_high().unwrap(); - // TODO: Other delay + // TODO: Replace with proper 1s delays once we have clocks working + cortex_m::asm::delay(500_000); + led_pin.set_low().unwrap(); + cortex_m::asm::delay(500_000); } } diff --git a/rp2040-hal/examples/gpio_in_out.rs b/rp2040-hal/examples/gpio_in_out.rs index 7a6036a..963b150 100644 --- a/rp2040-hal/examples/gpio_in_out.rs +++ b/rp2040-hal/examples/gpio_in_out.rs @@ -21,7 +21,7 @@ fn main() -> ! { let pins = pac.IO_BANK0.split(pac.PADS_BANK0, pac.SIO, &mut pac.RESETS); let mut led_pin = pins.gpio25.into_output(); - let button_pin = pins.gpio15.into_input().pull_high(); + let button_pin = pins.gpio15.into_input().pull_up(); loop { if button_pin.is_high().unwrap() { diff --git a/rp2040-hal/src/gpio.rs b/rp2040-hal/src/gpio.rs index 22c7eb6..fe0e8c7 100644 --- a/rp2040-hal/src/gpio.rs +++ b/rp2040-hal/src/gpio.rs @@ -45,13 +45,13 @@ pub trait GpioExt { /// The amount of current that a pin can drive when used as an output pub enum OutputDriveStrength { /// 2 mA - Ma2, + TwoMilliAmps, /// 4 mA - Ma4, + FourMilliAmps, /// 8 mA - Ma8, + EightMilliAmps, /// 12 mA - Ma12, + TwelveMilliAmps, } #[derive(Clone, Copy, Eq, PartialEq, Debug)] @@ -64,15 +64,19 @@ pub enum OutputSlewRate { } // Magic numbers from the datasheet -// const FUNCTION_SPI: u8 = 1; -// const FUNCTION_UART: u8 = 2; -// const FUNCTION_I2C: u8 = 3; -// const FUNCTION_PWM: u8 = 4; -const FUNCTION_SIO: u8 = 5; -// const FUNCTION_PIO0: u8 = 6; -// const FUNCTION_PIO1: u8 = 7; -// const FUNCTION_CLOCK: u8 = 8; -// const FUNCTION_USB: u8 = 9; +// Order is important! Do not rearrange these +#[allow(dead_code)] +enum GpioFunction { + Spi = 1, + Uart, + I2c, + Pwn, + Sio, + Pio0, + Pio1, + Clock, + Usb, +} macro_rules! gpio { ($GPIOX:ident, $gpiox:ident, $PADSX:ident, $padsx:ident, $gpioxs:expr, [ @@ -85,6 +89,7 @@ macro_rules! gpio { use core::convert::Infallible; use core::marker::PhantomData; use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin}; + use pac::generic::ResetValue; use super::*; impl GpioExt for pac::$GPIOX { @@ -115,21 +120,16 @@ macro_rules! gpio { )+ } - // Puts pad in same state as post-reset, according to datasheet - fn setup_pad_io(pads: &pac::$padsx::RegisterBlock, index: usize) { - pads.gpio[index].modify(|_, w| w.ie().set_bit() - .od().clear_bit() - .pue().clear_bit() - .pde().set_bit() - .schmitt().set_bit() - .drive()._4m_a() - .slewfast().clear_bit()); + fn reset_pad(pads: &pac::$padsx::RegisterBlock, index: usize) { + let reset_value: u32 = pac::$padsx::GPIO::reset_value(); + // This is safe, we get the value we're setting directly from the PAC + pads.gpio[index].write(|w| unsafe { w.bits(reset_value) }); } - fn set_gpio_function(gpios: &pac::$gpiox::RegisterBlock, index: usize, function: u8) { + fn set_gpio_function(gpios: &pac::$gpiox::RegisterBlock, index: usize, function: GpioFunction) { gpios.gpio[index] .gpio_ctrl - .write_with_zero(|x| unsafe { x.funcsel().bits(function) }); + .write_with_zero(|x| unsafe { x.funcsel().bits(function as _) }); } type PacDriveStrength = pac::$padsx::gpio::DRIVE_A; @@ -152,10 +152,10 @@ macro_rules! gpio { self, ) -> $PXi { unsafe { - setup_pad_io(&*pac::$PADSX::ptr(), $i); + reset_pad(&*pac::$PADSX::ptr(), $i); } unsafe { - set_gpio_function(&*pac::$GPIOX::ptr(), $i, FUNCTION_SIO); + set_gpio_function(&*pac::$GPIOX::ptr(), $i, GpioFunction::Sio); } unsafe { (*pac::SIO::ptr()).gpio_oe_set.write(|x| { x.bits(1 << $i) }); @@ -168,10 +168,10 @@ macro_rules! gpio { self, ) -> $PXi { unsafe { - setup_pad_io(&*pac::$PADSX::ptr(), $i); + reset_pad(&*pac::$PADSX::ptr(), $i); } unsafe { - set_gpio_function(&*pac::$GPIOX::ptr(), $i, FUNCTION_SIO); + set_gpio_function(&*pac::$GPIOX::ptr(), $i, GpioFunction::Sio); } unsafe { (*pac::SIO::ptr()).gpio_oe_clr.write(|x| { x.bits(1 << $i) }); @@ -235,10 +235,10 @@ macro_rules! gpio { #[doc = "Configure the drive strength for this output pin"] pub fn drive_strength(self, strength: OutputDriveStrength) -> Self { let converted = match strength { - OutputDriveStrength::Ma2 => PacDriveStrength::_2MA, - OutputDriveStrength::Ma4 => PacDriveStrength::_4MA, - OutputDriveStrength::Ma8 => PacDriveStrength::_8MA, - OutputDriveStrength::Ma12 => PacDriveStrength::_12MA, + OutputDriveStrength::TwoMilliAmps => PacDriveStrength::_2MA, + OutputDriveStrength::FourMilliAmps => PacDriveStrength::_4MA, + OutputDriveStrength::EightMilliAmps => PacDriveStrength::_8MA, + OutputDriveStrength::TwelveMilliAmps => PacDriveStrength::_12MA, }; unsafe { (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.drive().variant(converted)); @@ -257,7 +257,7 @@ macro_rules! gpio { impl $PXi { #[doc = "Pull this input pin high using internal resistors"] - pub fn pull_high(self) -> Self { + pub fn pull_up(self) -> Self { unsafe { (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.pue().set_bit().pde().clear_bit()); } @@ -265,7 +265,7 @@ macro_rules! gpio { } #[doc = "Pull this input pin low using internal resistors"] - pub fn pull_low(self) -> Self { + pub fn pull_down(self) -> Self { unsafe { (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.pue().clear_bit().pde().set_bit()); } From 7e45c96a658be5c2c4287503e7a68d92e3eae65a Mon Sep 17 00:00:00 2001 From: Jennifer Wilcox Date: Sun, 25 Apr 2021 11:17:33 -0500 Subject: [PATCH 09/16] Do reset in a good way instead of a wacky way --- rp2040-hal/src/gpio.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/rp2040-hal/src/gpio.rs b/rp2040-hal/src/gpio.rs index fe0e8c7..96dab87 100644 --- a/rp2040-hal/src/gpio.rs +++ b/rp2040-hal/src/gpio.rs @@ -89,7 +89,6 @@ macro_rules! gpio { use core::convert::Infallible; use core::marker::PhantomData; use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin}; - use pac::generic::ResetValue; use super::*; impl GpioExt for pac::$GPIOX { @@ -120,12 +119,6 @@ macro_rules! gpio { )+ } - fn reset_pad(pads: &pac::$padsx::RegisterBlock, index: usize) { - let reset_value: u32 = pac::$padsx::GPIO::reset_value(); - // This is safe, we get the value we're setting directly from the PAC - pads.gpio[index].write(|w| unsafe { w.bits(reset_value) }); - } - fn set_gpio_function(gpios: &pac::$gpiox::RegisterBlock, index: usize, function: GpioFunction) { gpios.gpio[index] .gpio_ctrl @@ -152,7 +145,7 @@ macro_rules! gpio { self, ) -> $PXi { unsafe { - reset_pad(&*pac::$PADSX::ptr(), $i); + (*pac::$PADSX::ptr()).gpio[$i].reset(); } unsafe { set_gpio_function(&*pac::$GPIOX::ptr(), $i, GpioFunction::Sio); @@ -168,7 +161,7 @@ macro_rules! gpio { self, ) -> $PXi { unsafe { - reset_pad(&*pac::$PADSX::ptr(), $i); + (*pac::$PADSX::ptr()).gpio[$i].reset(); } unsafe { set_gpio_function(&*pac::$GPIOX::ptr(), $i, GpioFunction::Sio); From 8290368c10a9ca54bd0ea5b5ffb8c63ce213371f Mon Sep 17 00:00:00 2001 From: Jennifer Wilcox Date: Sun, 25 Apr 2021 11:43:44 -0500 Subject: [PATCH 10/16] Pull pads out of reset and wait We're technically supposed to wait for these resets to finish before poking at registers. This seems to fix the instability I was seeing on the input example especially (TBH I have no idea how it ever worked) --- rp2040-hal/src/gpio.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/rp2040-hal/src/gpio.rs b/rp2040-hal/src/gpio.rs index 96dab87..03b7f9d 100644 --- a/rp2040-hal/src/gpio.rs +++ b/rp2040-hal/src/gpio.rs @@ -95,7 +95,13 @@ macro_rules! gpio { type Parts = Parts; fn split(self, pads: pac::$PADSX, sio: pac::SIO, resets: &mut pac::RESETS) -> Parts { - resets.reset.modify(|_, w| w.$gpiox().clear_bit()); + resets.reset.modify(|_, w| w.$gpiox().clear_bit().$padsx().clear_bit()); + while resets.reset_done.read().$gpiox().bit_is_clear() { + cortex_m::asm::delay(10); + } + while resets.reset_done.read().$padsx().bit_is_clear() { + cortex_m::asm::delay(10); + } Parts { _pads: pads, _sio: sio, From eaea9ae1ed663907ed434bc098cc35798023a654 Mon Sep 17 00:00:00 2001 From: Jennifer Wilcox Date: Sun, 25 Apr 2021 12:13:44 -0500 Subject: [PATCH 11/16] Concentrate the unsafe register constructs This moves almost all of the unsafe stuff together. The last remaining bit is going to need a PAC fix. --- rp2040-hal/src/gpio.rs | 129 +++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 83 deletions(-) diff --git a/rp2040-hal/src/gpio.rs b/rp2040-hal/src/gpio.rs index 03b7f9d..9a9152a 100644 --- a/rp2040-hal/src/gpio.rs +++ b/rp2040-hal/src/gpio.rs @@ -63,21 +63,6 @@ pub enum OutputSlewRate { Fast, } -// Magic numbers from the datasheet -// Order is important! Do not rearrange these -#[allow(dead_code)] -enum GpioFunction { - Spi = 1, - Uart, - I2c, - Pwn, - Sio, - Pio0, - Pio1, - Clock, - Usb, -} - macro_rules! gpio { ($GPIOX:ident, $gpiox:ident, $PADSX:ident, $padsx:ident, $gpioxs:expr, [ $($PXi:ident: ($pxi:ident, $i:expr, $is:expr),)+ @@ -125,12 +110,6 @@ macro_rules! gpio { )+ } - fn set_gpio_function(gpios: &pac::$gpiox::RegisterBlock, index: usize, function: GpioFunction) { - gpios.gpio[index] - .gpio_ctrl - .write_with_zero(|x| unsafe { x.funcsel().bits(function as _) }); - } - type PacDriveStrength = pac::$padsx::gpio::DRIVE_A; $( @@ -140,41 +119,47 @@ macro_rules! gpio { _mode: PhantomData, } - // Safety: We own our $i slice of padsx, gpiox, and sio because the - // construction of Parts assumes ownership of all 3 and will not release - // them. Thus several of the methods below will reconstruct these objects - // as-needed - impl $PXi { + // This is safe because Parts owns the pads, and each pin is responsible + // for its own pad + fn pad(&self) -> &pac::$padsx::GPIO { + unsafe { + &(*pac::$PADSX::ptr()).gpio[$i] + } + } + + // This is safe because Parts owns the SIO. But callers must only touch their + // own pin + fn sio(&self) -> &pac::sio::RegisterBlock { + unsafe { + &(*pac::SIO::ptr()) + } + } + + // This is safe because Parts owns the bank, and each pin is responsible + // for its own slice of the bank + fn gpio_ctrl(&self) -> &pac::$gpiox::gpio::GPIO_CTRL { + unsafe { + &(*pac::$GPIOX::ptr()).gpio[$i].gpio_ctrl + } + } + #[doc = "Configure this pin as an output"] - pub fn into_output( - self, - ) -> $PXi { - unsafe { - (*pac::$PADSX::ptr()).gpio[$i].reset(); - } - unsafe { - set_gpio_function(&*pac::$GPIOX::ptr(), $i, GpioFunction::Sio); - } - unsafe { - (*pac::SIO::ptr()).gpio_oe_set.write(|x| { x.bits(1 << $i) }); - } + pub fn into_output(self)-> $PXi { + self.pad().reset(); + self.gpio_ctrl().write_with_zero(|x| { x.funcsel().sio_0() }); + // TODO: Can we update the PAC to give us a safe register field + // instead of `bits`? + self.sio().gpio_oe_set.write(|x| unsafe { x.bits(1 << $i) }); $PXi { _mode: PhantomData } } #[doc = "Configure this pin as an input"] - pub fn into_input( - self, - ) -> $PXi { - unsafe { - (*pac::$PADSX::ptr()).gpio[$i].reset(); - } - unsafe { - set_gpio_function(&*pac::$GPIOX::ptr(), $i, GpioFunction::Sio); - } - unsafe { - (*pac::SIO::ptr()).gpio_oe_clr.write(|x| { x.bits(1 << $i) }); - } + pub fn into_input(self) -> $PXi { + self.pad().reset(); + self.gpio_ctrl().write_with_zero(|x| { x.funcsel().sio_0() }); + self.sio().gpio_oe_clr.write(|x| unsafe { x.bits(1 << $i) }); + $PXi { _mode: PhantomData } } } @@ -183,16 +168,12 @@ macro_rules! gpio { type Error = Infallible; fn set_low(&mut self) -> Result<(), Self::Error> { - unsafe { - (*pac::SIO::ptr()).gpio_out_clr.write(|x| x.bits(1 << $i)); - } + self.sio().gpio_out_clr.write(|x| unsafe { x.bits(1 << $i) }); Ok(()) } fn set_high(&mut self) -> Result<(), Self::Error> { - unsafe { - (*pac::SIO::ptr()).gpio_out_set.write(|x| x.bits(1 << $i)); - } + self.sio().gpio_out_set.write(|x| unsafe { x.bits(1 << $i) }); Ok(()) } } @@ -203,9 +184,7 @@ macro_rules! gpio { } fn is_set_high(&self) -> Result { - unsafe { - Ok((*pac::SIO::ptr()).gpio_out.read().bits() & (1 << $i) != 0) - } + Ok(self.sio().gpio_out.read().bits() & (1 << $i) != 0) } } @@ -219,9 +198,7 @@ macro_rules! gpio { } fn is_high(&self) -> Result { - unsafe { - Ok((*pac::SIO::ptr()).gpio_in.read().bits() & (1 << $i) != 0) - } + Ok(self.sio().gpio_in.read().bits() & (1 << $i) != 0) } } }; @@ -239,17 +216,13 @@ macro_rules! gpio { OutputDriveStrength::EightMilliAmps => PacDriveStrength::_8MA, OutputDriveStrength::TwelveMilliAmps => PacDriveStrength::_12MA, }; - unsafe { - (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.drive().variant(converted)); - } + self.pad().modify(|_, w| w.drive().variant(converted)); self } #[doc = "Configure the slew rate for this output pin"] pub fn slew_rate(self, slew_rate: OutputSlewRate) -> Self { - unsafe { - (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.slewfast().bit(slew_rate == OutputSlewRate::Fast)); - } + self.pad().modify(|_, w| w.slewfast().bit(slew_rate == OutputSlewRate::Fast)); self } } @@ -257,41 +230,31 @@ macro_rules! gpio { impl $PXi { #[doc = "Pull this input pin high using internal resistors"] pub fn pull_up(self) -> Self { - unsafe { - (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.pue().set_bit().pde().clear_bit()); - } + self.pad().modify(|_, w| w.pue().set_bit().pde().clear_bit()); self } #[doc = "Pull this input pin low using internal resistors"] pub fn pull_down(self) -> Self { - unsafe { - (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.pue().clear_bit().pde().set_bit()); - } + self.pad().modify(|_, w| w.pue().clear_bit().pde().set_bit()); self } #[doc = "Allow this input pin to float (i.e. don't pull it high or low)"] pub fn float(self) -> Self { - unsafe { - (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.pue().clear_bit().pde().clear_bit()); - } + self.pad().modify(|_, w| w.pue().clear_bit().pde().clear_bit()); self } #[doc = "Enable the schmitt trigger for this input pin"] pub fn enable_schmitt_trigger(self) -> Self { - unsafe { - (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.schmitt().set_bit()); - } + self.pad().modify(|_, w| w.schmitt().set_bit()); self } #[doc = "Disable the schmitt trigger for this input pin"] pub fn disable_schmitt_trigger(self) -> Self { - unsafe { - (*pac::$PADSX::ptr()).gpio[$i].modify(|_, w| w.schmitt().clear_bit()); - } + self.pad().modify(|_, w| w.schmitt().clear_bit()); self } } From c4ae152fda7a5499e1e4bf8cf4fb2f55d57e69f5 Mon Sep 17 00:00:00 2001 From: Jennifer Wilcox Date: Sun, 25 Apr 2021 12:17:05 -0500 Subject: [PATCH 12/16] Update rp2040-hal/src/gpio.rs Co-authored-by: tdittr --- rp2040-hal/src/gpio.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rp2040-hal/src/gpio.rs b/rp2040-hal/src/gpio.rs index 9a9152a..e954257 100644 --- a/rp2040-hal/src/gpio.rs +++ b/rp2040-hal/src/gpio.rs @@ -81,6 +81,7 @@ macro_rules! gpio { fn split(self, pads: pac::$PADSX, sio: pac::SIO, resets: &mut pac::RESETS) -> Parts { resets.reset.modify(|_, w| w.$gpiox().clear_bit().$padsx().clear_bit()); + // TODO: Implement Resets in the HAL while resets.reset_done.read().$gpiox().bit_is_clear() { cortex_m::asm::delay(10); } From d738626f70cfe40ef2659d262887af12578520df Mon Sep 17 00:00:00 2001 From: Jennifer Wilcox Date: Mon, 26 Apr 2021 12:00:23 -0500 Subject: [PATCH 13/16] Also check examples A quick check on my machine showed that just `cargo check --workspace` didn't verify the examples. Quite silly. --- .github/workflows/check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 3319f39..96b1663 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -16,7 +16,7 @@ jobs: - uses: actions-rs/cargo@v1 with: command: check - args: --workspace + args: --workspace --examples - uses: actions-rs/cargo@v1 with: command: fmt From 0badff4f1cdb6f1e2d476f288f3a25da2966e332 Mon Sep 17 00:00:00 2001 From: Jennifer Wilcox Date: Mon, 26 Apr 2021 12:04:11 -0500 Subject: [PATCH 14/16] Install arm-none-eabi-gcc in CI --- .github/workflows/check.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 96b1663..b4251f4 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -6,6 +6,8 @@ jobs: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 + - run: apt-get update + - run: apt-get install arm-none-eabi-gcc - uses: actions-rs/toolchain@v1 with: toolchain: stable From 09d9247f2f076a14b775b1cce9a695401241c07c Mon Sep 17 00:00:00 2001 From: Jennifer Wilcox Date: Mon, 26 Apr 2021 12:05:24 -0500 Subject: [PATCH 15/16] apt-get requires sudo --- .github/workflows/check.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index b4251f4..dc2144f 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -6,8 +6,8 @@ jobs: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - - run: apt-get update - - run: apt-get install arm-none-eabi-gcc + - run: sudo apt-get update + - run: sudo apt-get install arm-none-eabi-gcc - uses: actions-rs/toolchain@v1 with: toolchain: stable From a4b15a3547522bcac10a9c54eae2bd9390d9991b Mon Sep 17 00:00:00 2001 From: Jennifer Wilcox Date: Mon, 26 Apr 2021 12:06:57 -0500 Subject: [PATCH 16/16] Fix ARM toolchain apt package name --- .github/workflows/check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index dc2144f..1e1b275 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -7,7 +7,7 @@ jobs: steps: - uses: actions/checkout@v2 - run: sudo apt-get update - - run: sudo apt-get install arm-none-eabi-gcc + - run: sudo apt-get install gcc-arm-none-eabi - uses: actions-rs/toolchain@v1 with: toolchain: stable