From 5cc5961c94568db768c241e8e8bf91a692c1803e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 9 Jul 2022 02:12:17 +0200 Subject: [PATCH 1/2] rp/gpio: add Flex. --- embassy-rp/src/gpio.rs | 458 ++++++++++++++++++++++++++++------------- 1 file changed, 313 insertions(+), 145 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index ae771e84..131330e7 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -30,187 +30,228 @@ pub enum Bank { } pub struct Input<'d, T: Pin> { - pin: T, - phantom: PhantomData<&'d mut T>, + pin: Flex<'d, T>, } impl<'d, T: Pin> Input<'d, T> { pub fn new(pin: impl Unborrow + 'd, pull: Pull) -> Self { + let mut pin = Flex::new(pin); + pin.set_as_input(); + pin.set_pull(pull); + Self { pin } + } + + pub fn is_high(&self) -> bool { + self.pin.is_high() + } + + pub fn is_low(&self) -> bool { + self.pin.is_low() + } +} + +pub struct Output<'d, T: Pin> { + pin: Flex<'d, T>, +} + +impl<'d, T: Pin> Output<'d, T> { + #[inline] + pub fn new(pin: impl Unborrow + 'd, initial_output: Level) -> Self { + let mut pin = Flex::new(pin); + match initial_output { + Level::High => pin.set_high(), + Level::Low => pin.set_low(), + } + + pin.set_as_output(); + Self { pin } + } + + /// Set the output as high. + #[inline] + pub fn set_high(&mut self) { + self.pin.set_high() + } + + /// Set the output as low. + #[inline] + pub fn set_low(&mut self) { + self.pin.set_low() + } + + /// Is the output pin set as high? + #[inline] + pub fn is_set_high(&self) -> bool { + self.pin.is_set_high() + } + + /// Is the output pin set as low? + #[inline] + pub fn is_set_low(&self) -> bool { + self.pin.is_set_low() + } + + /// Toggle pin output + #[inline] + pub fn toggle(&mut self) { + self.pin.toggle() + } +} + +/// GPIO output open-drain. +pub struct OutputOpenDrain<'d, T: Pin> { + pin: Flex<'d, T>, +} + +impl<'d, T: Pin> OutputOpenDrain<'d, T> { + #[inline] + pub fn new(pin: impl Unborrow + 'd, initial_output: Level) -> Self { + let mut pin = Flex::new(pin); + pin.set_low(); + match initial_output { + Level::High => pin.set_as_input(), + Level::Low => pin.set_as_output(), + } + Self { pin } + } + + /// Set the output as high. + #[inline] + pub fn set_high(&mut self) { + // For Open Drain High, disable the output pin. + self.pin.set_as_input() + } + + /// Set the output as low. + #[inline] + pub fn set_low(&mut self) { + // For Open Drain Low, enable the output pin. + self.pin.set_as_output() + } + + /// Is the output level high? + #[inline] + pub fn is_set_high(&self) -> bool { + !self.is_set_low() + } + + /// Is the output level low? + #[inline] + pub fn is_set_low(&self) -> bool { + self.pin.is_set_as_output() + } + + /// Toggle pin output + #[inline] + pub fn toggle(&mut self) { + self.pin.toggle_set_as_output() + } +} + +/// GPIO flexible pin. +/// +/// This pin can be either an input or output pin. The output level register bit will remain +/// set while not in output mode, so the pin's level will be 'remembered' when it is not in output +/// mode. +pub struct Flex<'d, T: Pin> { + pin: T, + phantom: PhantomData<&'d mut T>, +} + +impl<'d, T: Pin> Flex<'d, T> { + #[inline] + pub fn new(pin: impl Unborrow + 'd) -> Self { unborrow!(pin); unsafe { pin.pad_ctrl().write(|w| { w.set_ie(true); + }); + + pin.io().ctrl().write(|w| { + w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::SIO_0.0); + }); + } + + Self { + pin, + phantom: PhantomData, + } + } + + #[inline] + fn bit(&self) -> u32 { + 1 << self.pin.pin() + } + + /// Set the pin's pull. + #[inline] + pub fn set_pull(&mut self, pull: Pull) { + unsafe { + self.pin.pad_ctrl().write(|w| { + w.set_ie(true); match pull { Pull::Up => w.set_pue(true), Pull::Down => w.set_pde(true), Pull::None => {} } }); - - // disable output in SIO, to use it as input - pin.sio_oe().value_clr().write_value(1 << pin.pin()); - - pin.io().ctrl().write(|w| { - w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::SIO_0.0); - }); - } - - Self { - pin, - phantom: PhantomData, } } + /// Put the pin into input mode. + /// + /// The pull setting is left unchanged. + #[inline] + pub fn set_as_input(&mut self) { + unsafe { self.pin.sio_oe().value_clr().write_value(self.bit()) } + } + + /// Put the pin into output mode. + /// + /// The pin level will be whatever was set before (or low by default). If you want it to begin + /// at a specific level, call `set_high`/`set_low` on the pin first. + #[inline] + pub fn set_as_output(&mut self) { + unsafe { self.pin.sio_oe().value_set().write_value(self.bit()) } + } + + #[inline] + fn is_set_as_output(&self) -> bool { + unsafe { (self.pin.sio_oe().value().read() & self.bit()) != 0 } + } + + #[inline] + pub fn toggle_set_as_output(&mut self) { + unsafe { self.pin.sio_oe().value_xor().write_value(self.bit()) } + } + + #[inline] pub fn is_high(&self) -> bool { !self.is_low() } + #[inline] pub fn is_low(&self) -> bool { - let val = 1 << self.pin.pin(); - unsafe { self.pin.sio_in().read() & val == 0 } - } -} - -impl<'d, T: Pin> Drop for Input<'d, T> { - fn drop(&mut self) { - // todo - } -} - -pub struct Output<'d, T: Pin> { - pin: T, - phantom: PhantomData<&'d mut T>, -} - -impl<'d, T: Pin> Output<'d, T> { - pub fn new(pin: impl Unborrow + 'd, initial_output: Level) -> Self { - unborrow!(pin); - - unsafe { - match initial_output { - Level::High => pin.sio_out().value_set().write_value(1 << pin.pin()), - Level::Low => pin.sio_out().value_clr().write_value(1 << pin.pin()), - } - pin.sio_oe().value_set().write_value(1 << pin.pin()); - - pin.io().ctrl().write(|w| { - w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::SIO_0.0); - }); - } - - Self { - pin, - phantom: PhantomData, - } - } - - /// Set the output as high. - pub fn set_high(&mut self) { - let val = 1 << self.pin.pin(); - unsafe { self.pin.sio_out().value_set().write_value(val) }; - } - - /// Set the output as low. - pub fn set_low(&mut self) { - let val = 1 << self.pin.pin(); - unsafe { self.pin.sio_out().value_clr().write_value(val) }; - } - - /// Is the output pin set as high? - pub fn is_set_high(&self) -> bool { - !self.is_set_low() - } - - /// Is the output pin set as low? - pub fn is_set_low(&self) -> bool { - // Reading from SIO: GPIO_OUT gives the last value written. - let val = 1 << self.pin.pin(); - unsafe { (self.pin.sio_out().value().read() & val) == 0 } - } - - /// Toggle pin output - #[inline] - pub fn toggle(&mut self) { - let val = 1 << self.pin.pin(); - unsafe { - self.pin.sio_out().value_xor().write_value(val); - } - } -} - -impl<'d, T: Pin> Drop for Output<'d, T> { - fn drop(&mut self) { - let val = 1 << self.pin.pin(); - unsafe { - self.pin.sio_out().value_clr().write_value(val); - self.pin.sio_oe().value_clr().write_value(val); - self.pin.io().ctrl().write(|w| { - w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::NULL.0); - }); - }; - } -} - -/// GPIO output open-drain. -pub struct OutputOpenDrain<'d, T: Pin> { - pin: T, - phantom: PhantomData<&'d mut T>, -} - -impl<'d, T: Pin> OutputOpenDrain<'d, T> { - #[inline] - pub fn new(pin: impl Unborrow + 'd, initial_output: Level) -> Self { - unborrow!(pin); - - unsafe { - let val = 1 << pin.pin(); - pin.io().ctrl().write(|w| { - w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::SIO_0.0); - }); - pin.sio_out().value_clr().write_value(val); - - match initial_output { - Level::High => { - // For Open Drain High, disable the output pin. - pin.sio_oe().value_clr().write_value(val); - } - Level::Low => { - // For Open Drain Low, enable the output pin. - pin.sio_oe().value_set().write_value(val); - } - } - } - - Self { - pin, - phantom: PhantomData, - } + unsafe { self.pin.sio_in().read() & self.bit() == 0 } } /// Set the output as high. #[inline] pub fn set_high(&mut self) { - // For Open Drain High, disable the output pin. - unsafe { - self.pin.sio_oe().value_clr().write_value(1 << self.pin.pin()); - } + unsafe { self.pin.sio_out().value_set().write_value(self.bit()) } } /// Set the output as low. #[inline] pub fn set_low(&mut self) { - // For Open Drain Low, enable the output pin. - unsafe { - self.pin.sio_oe().value_set().write_value(1 << self.pin.pin()); - } + unsafe { self.pin.sio_out().value_clr().write_value(self.bit()) } } /// Is the output level high? #[inline] pub fn is_set_high(&self) -> bool { - let val = 1 << self.pin.pin(); - unsafe { (self.pin.sio_oe().value().read() & val) == 0 } + unsafe { (self.pin.sio_out().value().read() & self.bit()) == 0 } } /// Is the output level low? @@ -222,9 +263,18 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> { /// Toggle pin output #[inline] pub fn toggle(&mut self) { - let val = 1 << self.pin.pin(); + unsafe { self.pin.sio_out().value_xor().write_value(self.bit()) } + } +} + +impl<'d, T: Pin> Drop for Flex<'d, T> { + #[inline] + fn drop(&mut self) { unsafe { - self.pin.sio_out().value_xor().write_value(val); + self.pin.pad_ctrl().write(|_| {}); + self.pin.io().ctrl().write(|w| { + w.set_funcsel(pac::io::vals::Gpio0CtrlFuncsel::NULL.0); + }); } } } @@ -428,6 +478,48 @@ mod eh02 { Ok(self.toggle()) } } + + impl<'d, T: Pin> embedded_hal_02::digital::v2::InputPin for Flex<'d, T> { + type Error = Infallible; + + fn is_high(&self) -> Result { + Ok(self.is_high()) + } + + fn is_low(&self) -> Result { + Ok(self.is_low()) + } + } + + impl<'d, T: Pin> embedded_hal_02::digital::v2::OutputPin for Flex<'d, T> { + type Error = Infallible; + + fn set_high(&mut self) -> Result<(), Self::Error> { + Ok(self.set_high()) + } + + fn set_low(&mut self) -> Result<(), Self::Error> { + Ok(self.set_low()) + } + } + + impl<'d, T: Pin> embedded_hal_02::digital::v2::StatefulOutputPin for Flex<'d, T> { + fn is_set_high(&self) -> Result { + Ok(self.is_set_high()) + } + + fn is_set_low(&self) -> Result { + Ok(self.is_set_low()) + } + } + + impl<'d, T: Pin> embedded_hal_02::digital::v2::ToggleableOutputPin for Flex<'d, T> { + type Error = Infallible; + #[inline] + fn toggle(&mut self) -> Result<(), Self::Error> { + Ok(self.toggle()) + } + } } #[cfg(feature = "unstable-traits")] @@ -471,4 +563,80 @@ mod eh1 { Ok(self.is_set_low()) } } + + impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for Output<'d, T> { + fn toggle(&mut self) -> Result<(), Self::Error> { + Ok(self.toggle()) + } + } + + impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for OutputOpenDrain<'d, T> { + type Error = Infallible; + } + + impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for OutputOpenDrain<'d, T> { + fn set_high(&mut self) -> Result<(), Self::Error> { + Ok(self.set_high()) + } + + fn set_low(&mut self) -> Result<(), Self::Error> { + Ok(self.set_low()) + } + } + + impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for OutputOpenDrain<'d, T> { + fn is_set_high(&self) -> Result { + Ok(self.is_set_high()) + } + + fn is_set_low(&self) -> Result { + Ok(self.is_set_low()) + } + } + + impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for OutputOpenDrain<'d, T> { + fn toggle(&mut self) -> Result<(), Self::Error> { + Ok(self.toggle()) + } + } + + impl<'d, T: Pin> embedded_hal_1::digital::ErrorType for Flex<'d, T> { + type Error = Infallible; + } + + impl<'d, T: Pin> embedded_hal_1::digital::blocking::InputPin for Flex<'d, T> { + fn is_high(&self) -> Result { + Ok(self.is_high()) + } + + fn is_low(&self) -> Result { + Ok(self.is_low()) + } + } + + impl<'d, T: Pin> embedded_hal_1::digital::blocking::OutputPin for Flex<'d, T> { + fn set_high(&mut self) -> Result<(), Self::Error> { + Ok(self.set_high()) + } + + fn set_low(&mut self) -> Result<(), Self::Error> { + Ok(self.set_low()) + } + } + + impl<'d, T: Pin> embedded_hal_1::digital::blocking::StatefulOutputPin for Flex<'d, T> { + fn is_set_high(&self) -> Result { + Ok(self.is_set_high()) + } + + fn is_set_low(&self) -> Result { + Ok(self.is_set_low()) + } + } + + impl<'d, T: Pin> embedded_hal_1::digital::blocking::ToggleableOutputPin for Flex<'d, T> { + fn toggle(&mut self) -> Result<(), Self::Error> { + Ok(self.toggle()) + } + } } From ccf57cfab63d9562d617a667ef6dfa2c9edd572f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 9 Jul 2022 02:13:43 +0200 Subject: [PATCH 2/2] rp: add GPIO HIL test. --- ci.sh | 1 + tests/rp/.cargo/config.toml | 20 +++ tests/rp/Cargo.toml | 48 +++++++ tests/rp/build.rs | 16 +++ tests/rp/link_ram.x | 255 ++++++++++++++++++++++++++++++++++++ tests/rp/src/bin/gpio.rs | 192 +++++++++++++++++++++++++++ 6 files changed, 532 insertions(+) create mode 100644 tests/rp/.cargo/config.toml create mode 100644 tests/rp/Cargo.toml create mode 100644 tests/rp/build.rs create mode 100644 tests/rp/link_ram.x create mode 100644 tests/rp/src/bin/gpio.rs diff --git a/ci.sh b/ci.sh index 6d7c9b04..315b60ef 100755 --- a/ci.sh +++ b/ci.sh @@ -104,6 +104,7 @@ cargo batch \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi --out-dir out/tests/nucleo-stm32h755zi \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb55rg --out-dir out/tests/nucleo-stm32wb55rg \ --- build --release --manifest-path tests/stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32u585ai --out-dir out/tests/iot-stm32u585ai \ + --- build --release --manifest-path tests/rp/Cargo.toml --target thumbv6m-none-eabi --out-dir out/tests/rpi-pico \ function run_elf { diff --git a/tests/rp/.cargo/config.toml b/tests/rp/.cargo/config.toml new file mode 100644 index 00000000..0330025e --- /dev/null +++ b/tests/rp/.cargo/config.toml @@ -0,0 +1,20 @@ +[unstable] +build-std = ["core"] +build-std-features = ["panic_immediate_abort"] + +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +#runner = "teleprobe client run --target bluepill-stm32f103c8 --elf" +runner = "teleprobe local run --chip RP2040 --elf" + +rustflags = [ + # Code-size optimizations. + "-Z", "trap-unreachable=no", + "-C", "inline-threshold=5", + "-C", "no-vectorize-loops", +] + +[build] +target = "thumbv6m-none-eabi" + +[env] +DEFMT_LOG = "trace" diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml new file mode 100644 index 00000000..b3067fff --- /dev/null +++ b/tests/rp/Cargo.toml @@ -0,0 +1,48 @@ +[package] +edition = "2021" +name = "embassy-rp-tests" +version = "0.1.0" + +[dependencies] +embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt"] } +embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["nightly", "defmt", "unstable-pac", "unstable-traits"] } + +defmt = "0.3.0" +defmt-rtt = "0.3.0" + +cortex-m = "0.7.3" +cortex-m-rt = "0.7.0" +embedded-hal = "0.2.6" +embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" } +embedded-hal-async = { version = "0.1.0-alpha.1" } +panic-probe = { version = "0.3.0", features = ["print-defmt"] } + +[profile.dev] +debug = 2 +debug-assertions = true +opt-level = 's' +overflow-checks = true + +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false +incremental = false +lto = "fat" +opt-level = 's' +overflow-checks = false + +# do not optimize proc-macro crates = faster builds from scratch +[profile.dev.build-override] +codegen-units = 8 +debug = false +debug-assertions = false +opt-level = 0 +overflow-checks = false + +[profile.release.build-override] +codegen-units = 8 +debug = false +debug-assertions = false +opt-level = 0 +overflow-checks = false diff --git a/tests/rp/build.rs b/tests/rp/build.rs new file mode 100644 index 00000000..6f487224 --- /dev/null +++ b/tests/rp/build.rs @@ -0,0 +1,16 @@ +use std::error::Error; +use std::path::PathBuf; +use std::{env, fs}; + +fn main() -> Result<(), Box> { + let out = PathBuf::from(env::var("OUT_DIR").unwrap()); + fs::write(out.join("link_ram.x"), include_bytes!("link_ram.x")).unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + println!("cargo:rerun-if-changed=link_ram.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); + + Ok(()) +} diff --git a/tests/rp/link_ram.x b/tests/rp/link_ram.x new file mode 100644 index 00000000..86a11e87 --- /dev/null +++ b/tests/rp/link_ram.x @@ -0,0 +1,255 @@ +/* ##### EMBASSY NOTE + Originally from https://github.com/rust-embedded/cortex-m-rt/blob/master/link.x.in + Adjusted to put everything in RAM +*/ + +/* # Developer notes + +- Symbols that start with a double underscore (__) are considered "private" + +- Symbols that start with a single underscore (_) are considered "semi-public"; they can be + overridden in a user linker script, but should not be referred from user code (e.g. `extern "C" { + static mut __sbss }`). + +- `EXTERN` forces the linker to keep a symbol in the final binary. We use this to make sure a + symbol if not dropped if it appears in or near the front of the linker arguments and "it's not + needed" by any of the preceding objects (linker arguments) + +- `PROVIDE` is used to provide default values that can be overridden by a user linker script + +- On alignment: it's important for correctness that the VMA boundaries of both .bss and .data *and* + the LMA of .data are all 4-byte aligned. These alignments are assumed by the RAM initialization + routine. There's also a second benefit: 4-byte aligned boundaries means that you won't see + "Address (..) is out of bounds" in the disassembly produced by `objdump`. +*/ + +/* Provides information about the memory layout of the device */ +MEMORY { + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} + +/* # Entry point = reset vector */ +EXTERN(__RESET_VECTOR); +EXTERN(Reset); +ENTRY(Reset); + +/* # Exception vectors */ +/* This is effectively weak aliasing at the linker level */ +/* The user can override any of these aliases by defining the corresponding symbol themselves (cf. + the `exception!` macro) */ +EXTERN(__EXCEPTIONS); /* depends on all the these PROVIDED symbols */ + +EXTERN(DefaultHandler); + +PROVIDE(NonMaskableInt = DefaultHandler); +EXTERN(HardFaultTrampoline); +PROVIDE(MemoryManagement = DefaultHandler); +PROVIDE(BusFault = DefaultHandler); +PROVIDE(UsageFault = DefaultHandler); +PROVIDE(SecureFault = DefaultHandler); +PROVIDE(SVCall = DefaultHandler); +PROVIDE(DebugMonitor = DefaultHandler); +PROVIDE(PendSV = DefaultHandler); +PROVIDE(SysTick = DefaultHandler); + +PROVIDE(DefaultHandler = DefaultHandler_); +PROVIDE(HardFault = HardFault_); + +/* # Interrupt vectors */ +EXTERN(__INTERRUPTS); /* `static` variable similar to `__EXCEPTIONS` */ + +/* # Pre-initialization function */ +/* If the user overrides this using the `pre_init!` macro or by creating a `__pre_init` function, + then the function this points to will be called before the RAM is initialized. */ +PROVIDE(__pre_init = DefaultPreInit); + +/* # Sections */ +SECTIONS +{ + PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM)); + + /* ## Sections in RAM */ + /* ### Vector table */ + .vector_table ORIGIN(RAM) : + { + /* Initial Stack Pointer (SP) value */ + LONG(_stack_start); + + /* Reset vector */ + KEEP(*(.vector_table.reset_vector)); /* this is the `__RESET_VECTOR` symbol */ + __reset_vector = .; + + /* Exceptions */ + KEEP(*(.vector_table.exceptions)); /* this is the `__EXCEPTIONS` symbol */ + __eexceptions = .; + + /* Device specific interrupts */ + KEEP(*(.vector_table.interrupts)); /* this is the `__INTERRUPTS` symbol */ + } > RAM + + PROVIDE(_stext = ADDR(.vector_table) + SIZEOF(.vector_table)); + + /* ### .text */ + .text _stext : + { + __stext = .; + *(.Reset); + + *(.text .text.*); + + /* The HardFaultTrampoline uses the `b` instruction to enter `HardFault`, + so must be placed close to it. */ + *(.HardFaultTrampoline); + *(.HardFault.*); + + . = ALIGN(4); /* Pad .text to the alignment to workaround overlapping load section bug in old lld */ + __etext = .; + } > RAM + + /* ### .rodata */ + .rodata : ALIGN(4) + { + . = ALIGN(4); + __srodata = .; + *(.rodata .rodata.*); + + /* 4-byte align the end (VMA) of this section. + This is required by LLD to ensure the LMA of the following .data + section will have the correct alignment. */ + . = ALIGN(4); + __erodata = .; + } > RAM + + /* ## Sections in RAM */ + /* ### .data */ + .data : ALIGN(4) + { + . = ALIGN(4); + __sdata = .; + __edata = .; + *(.data .data.*); + . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ + } > RAM + /* Allow sections from user `memory.x` injected using `INSERT AFTER .data` to + * use the .data loading mechanism by pushing __edata. Note: do not change + * output region or load region in those user sections! */ + . = ALIGN(4); + + /* LMA of .data */ + __sidata = LOADADDR(.data); + + /* ### .gnu.sgstubs + This section contains the TrustZone-M veneers put there by the Arm GNU linker. */ + /* Security Attribution Unit blocks must be 32 bytes aligned. */ + /* Note that this pads the RAM usage to 32 byte alignment. */ + .gnu.sgstubs : ALIGN(32) + { + . = ALIGN(32); + __veneer_base = .; + *(.gnu.sgstubs*) + . = ALIGN(32); + __veneer_limit = .; + } > RAM + + /* ### .bss */ + .bss (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + __sbss = .; + *(.bss .bss.*); + *(COMMON); /* Uninitialized C statics */ + . = ALIGN(4); /* 4-byte align the end (VMA) of this section */ + } > RAM + /* Allow sections from user `memory.x` injected using `INSERT AFTER .bss` to + * use the .bss zeroing mechanism by pushing __ebss. Note: do not change + * output region or load region in those user sections! */ + . = ALIGN(4); + __ebss = .; + + /* ### .uninit */ + .uninit (NOLOAD) : ALIGN(4) + { + . = ALIGN(4); + __suninit = .; + *(.uninit .uninit.*); + . = ALIGN(4); + __euninit = .; + } > RAM + + /* Place the heap right after `.uninit` in RAM */ + PROVIDE(__sheap = __euninit); + + /* ## .got */ + /* Dynamic relocations are unsupported. This section is only used to detect relocatable code in + the input files and raise an error if relocatable code is found */ + .got (NOLOAD) : + { + KEEP(*(.got .got.*)); + } + + /* ## Discarded sections */ + /DISCARD/ : + { + /* Unused exception related info that only wastes space */ + *(.ARM.exidx); + *(.ARM.exidx.*); + *(.ARM.extab.*); + } +} + +/* Do not exceed this mark in the error messages below | */ +/* # Alignment checks */ +ASSERT(ORIGIN(RAM) % 4 == 0, " +ERROR(cortex-m-rt): the start of the RAM region must be 4-byte aligned"); + +ASSERT(__sdata % 4 == 0 && __edata % 4 == 0, " +BUG(cortex-m-rt): .data is not 4-byte aligned"); + +ASSERT(__sidata % 4 == 0, " +BUG(cortex-m-rt): the LMA of .data is not 4-byte aligned"); + +ASSERT(__sbss % 4 == 0 && __ebss % 4 == 0, " +BUG(cortex-m-rt): .bss is not 4-byte aligned"); + +ASSERT(__sheap % 4 == 0, " +BUG(cortex-m-rt): start of .heap is not 4-byte aligned"); + +/* # Position checks */ + +/* ## .vector_table */ +ASSERT(__reset_vector == ADDR(.vector_table) + 0x8, " +BUG(cortex-m-rt): the reset vector is missing"); + +ASSERT(__eexceptions == ADDR(.vector_table) + 0x40, " +BUG(cortex-m-rt): the exception vectors are missing"); + +ASSERT(SIZEOF(.vector_table) > 0x40, " +ERROR(cortex-m-rt): The interrupt vectors are missing. +Possible solutions, from most likely to less likely: +- Link to a svd2rust generated device crate +- Check that you actually use the device/hal/bsp crate in your code +- Disable the 'device' feature of cortex-m-rt to build a generic application (a dependency +may be enabling it) +- Supply the interrupt handlers yourself. Check the documentation for details."); + +/* ## .text */ +ASSERT(ADDR(.vector_table) + SIZEOF(.vector_table) <= _stext, " +ERROR(cortex-m-rt): The .text section can't be placed inside the .vector_table section +Set _stext to an address greater than the end of .vector_table (See output of `nm`)"); + +ASSERT(_stext + SIZEOF(.text) < ORIGIN(RAM) + LENGTH(RAM), " +ERROR(cortex-m-rt): The .text section must be placed inside the RAM memory. +Set _stext to an address smaller than 'ORIGIN(RAM) + LENGTH(RAM)'"); + +/* # Other checks */ +ASSERT(SIZEOF(.got) == 0, " +ERROR(cortex-m-rt): .got section detected in the input object files +Dynamic relocations are not supported. If you are linking to C code compiled using +the 'cc' crate then modify your build script to compile the C code _without_ +the -fPIC flag. See the documentation of the `cc::Build.pic` method for details."); +/* Do not exceed this mark in the error messages above | */ + + +/* Provides weak aliases (cf. PROVIDED) for device specific interrupt handlers */ +/* This will usually be provided by a device crate generated using svd2rust (see `device.x`) */ +INCLUDE device.x \ No newline at end of file diff --git a/tests/rp/src/bin/gpio.rs b/tests/rp/src/bin/gpio.rs new file mode 100644 index 00000000..0be9d9f2 --- /dev/null +++ b/tests/rp/src/bin/gpio.rs @@ -0,0 +1,192 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert, *}; +use embassy::executor::Spawner; +use embassy_rp::gpio::{Flex, Input, Level, Output, OutputOpenDrain, Pull}; +use embassy_rp::Peripherals; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy::main] +async fn main(_spawner: Spawner, p: Peripherals) { + info!("Hello World!"); + + let (mut a, mut b) = (p.PIN_0, p.PIN_1); + + // Test initial output + { + let b = Input::new(&mut b, Pull::None); + + { + let _a = Output::new(&mut a, Level::Low); + delay(); + assert!(b.is_low()); + } + { + let _a = Output::new(&mut a, Level::High); + delay(); + assert!(b.is_high()); + } + } + + // Test input no pull + { + let b = Input::new(&mut b, Pull::None); + // no pull, the status is undefined + + let mut a = Output::new(&mut a, Level::Low); + delay(); + assert!(b.is_low()); + a.set_high(); + delay(); + assert!(b.is_high()); + } + + // Test input pulldown + { + let b = Input::new(&mut b, Pull::Down); + delay(); + assert!(b.is_low()); + + let mut a = Output::new(&mut a, Level::Low); + delay(); + assert!(b.is_low()); + a.set_high(); + delay(); + assert!(b.is_high()); + } + + // Test input pullup + { + let b = Input::new(&mut b, Pull::Up); + delay(); + assert!(b.is_high()); + + let mut a = Output::new(&mut a, Level::Low); + delay(); + assert!(b.is_low()); + a.set_high(); + delay(); + assert!(b.is_high()); + } + + // OUTPUT OPEN DRAIN + { + let mut b = OutputOpenDrain::new(&mut b, Level::High); + let mut a = Flex::new(&mut a); + a.set_as_input(); + + // When an OutputOpenDrain is high, it doesn't drive the pin. + a.set_pull(Pull::Up); + delay(); + assert!(a.is_high()); + a.set_pull(Pull::Down); + delay(); + assert!(a.is_low()); + + b.set_low(); + + // When an OutputOpenDrain is low, it drives the pin low. + a.set_pull(Pull::Up); + delay(); + assert!(a.is_low()); + a.set_pull(Pull::Down); + delay(); + assert!(a.is_low()); + + b.set_high(); + + a.set_pull(Pull::Up); + delay(); + assert!(a.is_high()); + a.set_pull(Pull::Down); + delay(); + assert!(a.is_low()); + } + + // FLEX + // Test initial output + { + //Flex pin configured as input + let mut b = Flex::new(&mut b); + b.set_as_input(); + + { + //Flex pin configured as output + let mut a = Flex::new(&mut a); //Flex pin configured as output + a.set_low(); // Pin state must be set before configuring the pin, thus we avoid unknown state + a.set_as_output(); + delay(); + assert!(b.is_low()); + } + { + //Flex pin configured as output + let mut a = Flex::new(&mut a); + a.set_high(); + a.set_as_output(); + + delay(); + assert!(b.is_high()); + } + } + + // Test input no pull + { + let mut b = Flex::new(&mut b); + b.set_as_input(); // no pull by default. + + let mut a = Flex::new(&mut a); + a.set_low(); + a.set_as_output(); + + delay(); + assert!(b.is_low()); + a.set_high(); + delay(); + assert!(b.is_high()); + } + + // Test input pulldown + { + let mut b = Flex::new(&mut b); + b.set_as_input(); + b.set_pull(Pull::Down); + delay(); + assert!(b.is_low()); + + let mut a = Flex::new(&mut a); + a.set_low(); + a.set_as_output(); + delay(); + assert!(b.is_low()); + a.set_high(); + delay(); + assert!(b.is_high()); + } + + // Test input pullup + { + let mut b = Flex::new(&mut b); + b.set_as_input(); + b.set_pull(Pull::Up); + delay(); + assert!(b.is_high()); + + let mut a = Flex::new(&mut a); + a.set_high(); + a.set_as_output(); + delay(); + assert!(b.is_high()); + a.set_low(); + delay(); + assert!(b.is_low()); + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +fn delay() { + cortex_m::asm::delay(10000); +}