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),