From 4410aacafb2141c1c0838598d11581e24aab3b0f Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 19 Jan 2024 23:13:46 +0100 Subject: [PATCH] feat: add basic support for nRF51 chips to embassy-nrf --- embassy-nrf/Cargo.toml | 8 ++ embassy-nrf/src/chips/nrf51.rs | 168 ++++++++++++++++++++++++++++++ embassy-nrf/src/gpio.rs | 12 +++ embassy-nrf/src/lib.rs | 19 +++- embassy-nrf/src/ppi/ppi.rs | 1 + embassy-nrf/src/rng.rs | 2 +- embassy-nrf/src/time_driver.rs | 2 +- embassy-nrf/src/timer.rs | 16 ++- examples/nrf51/.cargo/config.toml | 9 ++ examples/nrf51/Cargo.toml | 20 ++++ examples/nrf51/build.rs | 35 +++++++ examples/nrf51/memory.x | 5 + examples/nrf51/src/bin/blinky.rs | 21 ++++ 13 files changed, 311 insertions(+), 7 deletions(-) create mode 100644 embassy-nrf/src/chips/nrf51.rs create mode 100644 examples/nrf51/.cargo/config.toml create mode 100644 examples/nrf51/Cargo.toml create mode 100644 examples/nrf51/build.rs create mode 100644 examples/nrf51/memory.x create mode 100644 examples/nrf51/src/bin/blinky.rs diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 10f268b51..529a56c46 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -15,6 +15,7 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nrf/s features = ["time", "defmt", "unstable-pac", "gpiote", "time-driver-rtc1"] flavors = [ + { regex_feature = "nrf51", target = "thumbv6m-none-eabi" }, { regex_feature = "nrf52.*", target = "thumbv7em-none-eabihf" }, { regex_feature = "nrf53.*", target = "thumbv8m.main-none-eabihf" }, { regex_feature = "nrf91.*", target = "thumbv8m.main-none-eabihf" }, @@ -28,6 +29,7 @@ rustdoc-args = ["--cfg", "docsrs"] default = ["rt"] ## Cortex-M runtime (enabled by default) rt = [ + "nrf51-pac?/rt", "nrf52805-pac?/rt", "nrf52810-pac?/rt", "nrf52811-pac?/rt", @@ -71,6 +73,8 @@ reset-pin-as-gpio = [] qspi-multiwrite-flash = [] #! ### Chip selection features +## nRF51 +nrf51 = ["nrf51-pac", "_nrf51", "portable-atomic/unsafe-assume-single-core"] ## nRF52805 nrf52805 = ["nrf52805-pac", "_nrf52"] ## nRF52810 @@ -104,6 +108,7 @@ _nrf5340-net = ["_nrf5340", "nrf5340-net-pac"] _nrf5340 = ["_gpio-p1", "_dppi"] _nrf9160 = ["nrf9160-pac", "_dppi"] _nrf52 = ["_ppi"] +_nrf51 = ["_ppi"] _time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768"] @@ -132,6 +137,8 @@ embedded-hal-async = { version = "1.0" } embedded-io = { version = "0.6.0" } embedded-io-async = { version = "0.6.1" } +portable-atomic = { version = "1", default-features = false, features = ["require-cas"] } + defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } cortex-m-rt = ">=0.6.15,<0.8" @@ -144,6 +151,7 @@ embedded-storage-async = "0.4.0" cfg-if = "1.0.0" document-features = "0.2.7" +nrf51-pac = { version = "0.12.0", optional = true } nrf52805-pac = { version = "0.12.0", optional = true } nrf52810-pac = { version = "0.12.0", optional = true } nrf52811-pac = { version = "0.12.0", optional = true } diff --git a/embassy-nrf/src/chips/nrf51.rs b/embassy-nrf/src/chips/nrf51.rs new file mode 100644 index 000000000..f5db4b847 --- /dev/null +++ b/embassy-nrf/src/chips/nrf51.rs @@ -0,0 +1,168 @@ +pub use nrf51_pac as pac; + +/// The maximum buffer size that the EasyDMA can send/recv in one operation. +pub const EASY_DMA_SIZE: usize = (1 << 14) - 1; +pub const FORCE_COPY_BUFFER_SIZE: usize = 256; + +pub const FLASH_SIZE: usize = 128 * 1024; + +embassy_hal_internal::peripherals! { + // RTC + RTC0, + RTC1, + + // WDT + WDT, + + // NVMC + NVMC, + + // RNG + RNG, + + // UARTE + UART0, + + // SPI/TWI + TWI0, + SPI0, + + // ADC + ADC, + + // TIMER + TIMER0, + TIMER1, + TIMER2, + + // GPIOTE + GPIOTE_CH0, + GPIOTE_CH1, + GPIOTE_CH2, + GPIOTE_CH3, + + // PPI + PPI_CH0, + PPI_CH1, + PPI_CH2, + PPI_CH3, + PPI_CH4, + PPI_CH5, + PPI_CH6, + PPI_CH7, + PPI_CH8, + PPI_CH9, + PPI_CH10, + PPI_CH11, + PPI_CH12, + PPI_CH13, + PPI_CH14, + PPI_CH15, + + PPI_GROUP0, + PPI_GROUP1, + PPI_GROUP2, + PPI_GROUP3, + + // GPIO port 0 + P0_00, + P0_01, + P0_02, + P0_03, + P0_04, + P0_05, + P0_06, + P0_07, + P0_08, + P0_09, + P0_10, + P0_11, + P0_12, + P0_13, + P0_14, + P0_15, + P0_16, + P0_17, + P0_18, + P0_19, + P0_20, + P0_21, + P0_22, + P0_23, + P0_24, + P0_25, + P0_26, + P0_27, + P0_28, + P0_29, + P0_30, + P0_31, + + // TEMP + TEMP, +} + +// impl_timer!(TIMER0, TIMER0, TIMER0); +// impl_timer!(TIMER1, TIMER1, TIMER1); +// impl_timer!(TIMER2, TIMER2, TIMER2); + +impl_pin!(P0_00, 0, 0); +impl_pin!(P0_01, 0, 1); +impl_pin!(P0_02, 0, 2); +impl_pin!(P0_03, 0, 3); +impl_pin!(P0_04, 0, 4); +impl_pin!(P0_05, 0, 5); +impl_pin!(P0_06, 0, 6); +impl_pin!(P0_07, 0, 7); +impl_pin!(P0_08, 0, 8); +impl_pin!(P0_09, 0, 9); +impl_pin!(P0_10, 0, 10); +impl_pin!(P0_11, 0, 11); +impl_pin!(P0_12, 0, 12); +impl_pin!(P0_13, 0, 13); +impl_pin!(P0_14, 0, 14); +impl_pin!(P0_15, 0, 15); +impl_pin!(P0_16, 0, 16); +impl_pin!(P0_17, 0, 17); +impl_pin!(P0_18, 0, 18); +impl_pin!(P0_19, 0, 19); +impl_pin!(P0_20, 0, 20); +impl_pin!(P0_21, 0, 21); +impl_pin!(P0_22, 0, 22); +impl_pin!(P0_23, 0, 23); +impl_pin!(P0_24, 0, 24); +impl_pin!(P0_25, 0, 25); +impl_pin!(P0_26, 0, 26); +impl_pin!(P0_27, 0, 27); +impl_pin!(P0_28, 0, 28); +impl_pin!(P0_29, 0, 29); +impl_pin!(P0_30, 0, 30); +impl_pin!(P0_31, 0, 31); + +embassy_hal_internal::interrupt_mod!( + POWER_CLOCK, + RADIO, + UART0, + SPI0_TWI0, + SPI1_TWI1, + GPIOTE, + ADC, + TIMER0, + TIMER1, + TIMER2, + RTC0, + TEMP, + RNG, + ECB, + CCM_AAR, + WDT, + RTC1, + QDEC, + LPCOMP, + SWI0, + SWI1, + SWI2, + SWI3, + SWI4, + SWI5, +); diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index 287811e61..164363460 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -8,8 +8,17 @@ use cfg_if::cfg_if; use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use self::sealed::Pin as _; + +#[cfg(not(feature = "nrf51"))] use crate::pac::p0 as gpio; +#[cfg(not(feature = "nrf51"))] use crate::pac::p0::pin_cnf::{DRIVE_A, PULL_A}; + +#[cfg(feature = "nrf51")] +use crate::pac::gpio; +#[cfg(feature = "nrf51")] +use crate::pac::gpio::pin_cnf::{DRIVE_A, PULL_A}; + use crate::{pac, Peripheral}; /// A GPIO port with up to 32 pins. @@ -376,6 +385,9 @@ pub(crate) mod sealed { fn block(&self) -> &gpio::RegisterBlock { unsafe { match self.pin_port() / 32 { + #[cfg(feature = "nrf51")] + 0 => &*pac::GPIO::ptr(), + #[cfg(not(feature = "nrf51"))] 0 => &*pac::P0::ptr(), #[cfg(feature = "_gpio-p1")] 1 => &*pac::P1::ptr(), diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index d9c92a76d..923e48ec2 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -40,6 +40,7 @@ pub(crate) mod util; #[cfg(feature = "_time-driver")] mod time_driver; +#[cfg(not(feature = "nrf51"))] pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] @@ -58,7 +59,12 @@ pub mod nvmc; ))] pub mod pdm; pub mod ppi; -#[cfg(not(any(feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net")))] +#[cfg(not(any( + feature = "nrf51", + feature = "nrf52805", + feature = "nrf52820", + feature = "_nrf5340-net" +)))] pub mod pwm; #[cfg(not(any(feature = "nrf51", feature = "_nrf9160", feature = "_nrf5340-net")))] pub mod qdec; @@ -66,15 +72,20 @@ pub mod qdec; pub mod qspi; #[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf9160")))] pub mod rng; -#[cfg(not(any(feature = "nrf52820", feature = "_nrf5340-net")))] +#[cfg(not(any(feature = "nrf51", feature = "nrf52820", feature = "_nrf5340-net")))] pub mod saadc; +#[cfg(not(feature = "nrf51"))] pub mod spim; +#[cfg(not(feature = "nrf51"))] pub mod spis; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] pub mod temp; pub mod timer; +#[cfg(not(feature = "nrf51"))] pub mod twim; +#[cfg(not(feature = "nrf51"))] pub mod twis; +#[cfg(not(feature = "nrf51"))] pub mod uarte; #[cfg(any( feature = "_nrf5340-app", @@ -87,6 +98,7 @@ pub mod usb; pub mod wdt; // This mod MUST go last, so that it sees all the `impl_foo!` macros +#[cfg_attr(feature = "nrf51", path = "chips/nrf51.rs")] #[cfg_attr(feature = "nrf52805", path = "chips/nrf52805.rs")] #[cfg_attr(feature = "nrf52810", path = "chips/nrf52810.rs")] #[cfg_attr(feature = "nrf52811", path = "chips/nrf52811.rs")] @@ -374,6 +386,7 @@ pub fn init(config: config::Config) -> Peripherals { let mut needs_reset = false; // Setup debug protection. + #[cfg(not(feature = "nrf51"))] match config.debug { config::Debug::Allowed => { #[cfg(feature = "_nrf52")] @@ -489,7 +502,7 @@ pub fn init(config: config::Config) -> Peripherals { } // Configure LFCLK. - #[cfg(not(any(feature = "_nrf5340", feature = "_nrf9160")))] + #[cfg(not(any(feature = "nrf51", feature = "_nrf5340", feature = "_nrf9160")))] match config.lfclk_source { config::LfclkSource::InternalRC => r.lfclksrc.write(|w| w.src().rc()), config::LfclkSource::Synthesized => r.lfclksrc.write(|w| w.src().synth()), diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs index 3e9e9fc81..4a12764ad 100644 --- a/embassy-nrf/src/ppi/ppi.rs +++ b/embassy-nrf/src/ppi/ppi.rs @@ -84,6 +84,7 @@ impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop for let n = self.ch.number(); r.ch[n].eep.write(|w| unsafe { w.bits(0) }); r.ch[n].tep.write(|w| unsafe { w.bits(0) }); + #[cfg(not(feature = "nrf51"))] r.fork[n].tep.write(|w| unsafe { w.bits(0) }); } } diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index e2803f0d3..7f2e76a55 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -5,7 +5,7 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::ptr; -use core::sync::atomic::{AtomicPtr, Ordering}; +use portable_atomic::{AtomicPtr, Ordering}; use core::task::Poll; use embassy_hal_internal::drop::OnDrop; diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index 042f7c5f7..855d133df 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -1,6 +1,6 @@ use core::cell::Cell; -use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; use core::{mem, ptr}; +use portable_atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; use critical_section::CriticalSection; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 3dbfdac42..272fffc0a 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -122,12 +122,16 @@ impl<'d, T: Instance> Timer<'d, T> { // since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification. this.stop(); + #[cfg(not(feature = "nrf51"))] if is_counter { regs.mode.write(|w| w.mode().low_power_counter()); } else { regs.mode.write(|w| w.mode().timer()); } + #[cfg(feature = "nrf51")] + regs.mode.write(|w| w.mode().timer()); + // Make the counter's max value as high as possible. // TODO: is there a reason someone would want to set this lower? regs.bitmode.write(|w| w.bitmode()._32bit()); @@ -238,7 +242,11 @@ pub struct Cc<'d, T: Instance> { impl<'d, T: Instance> Cc<'d, T> { /// Get the current value stored in the register. pub fn read(&self) -> u32 { - T::regs().cc[self.n].read().cc().bits() + #[cfg(not(feature = "nrf51"))] + return T::regs().cc[self.n].read().cc().bits(); + + #[cfg(feature = "nrf51")] + return T::regs().cc[self.n].read().bits(); } /// Set the value stored in the register. @@ -246,7 +254,11 @@ impl<'d, T: Instance> Cc<'d, T> { /// `event_compare` will fire when the timer's counter reaches this value. pub fn write(&self, value: u32) { // SAFETY: there are no invalid values for the CC register. - T::regs().cc[self.n].write(|w| unsafe { w.cc().bits(value) }) + #[cfg(not(feature = "nrf51"))] + T::regs().cc[self.n].write(|w| unsafe { w.cc().bits(value) }); + + #[cfg(feature = "nrf51")] + T::regs().cc[self.n].write(|w| unsafe { w.bits(value) }); } /// Capture the current value of the timer's counter in this register, and return it. diff --git a/examples/nrf51/.cargo/config.toml b/examples/nrf51/.cargo/config.toml new file mode 100644 index 000000000..84f0e1572 --- /dev/null +++ b/examples/nrf51/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip nRF51822_xxAA" + +[build] +target = "thumbv6m-none-eabi" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/nrf51/Cargo.toml b/examples/nrf51/Cargo.toml new file mode 100644 index 000000000..8507c415c --- /dev/null +++ b/examples/nrf51/Cargo.toml @@ -0,0 +1,20 @@ +[package] +edition = "2021" +name = "embassy-nrf51-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "time-driver-rtc1", "unstable-pac", "time", "rt"] } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7" +panic-probe = { version = "0.3", features = ["print-defmt"] } + +[profile.release] +debug = 2 diff --git a/examples/nrf51/build.rs b/examples/nrf51/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/nrf51/build.rs @@ -0,0 +1,35 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/nrf51/memory.x b/examples/nrf51/memory.x new file mode 100644 index 000000000..ad5f80292 --- /dev/null +++ b/examples/nrf51/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x00000000, LENGTH = 256K + RAM : ORIGIN = 0x20000000, LENGTH = 32K +} diff --git a/examples/nrf51/src/bin/blinky.rs b/examples/nrf51/src/bin/blinky.rs new file mode 100644 index 000000000..ff686c6ae --- /dev/null +++ b/examples/nrf51/src/bin/blinky.rs @@ -0,0 +1,21 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + + let p = embassy_nrf::init(Default::default()); + let mut led = Output::new(p.P0_21, Level::Low, OutputDrive::Standard); + + loop { + led.set_high(); + Timer::after_millis(300).await; + led.set_low(); + Timer::after_millis(300).await; + } +}