feat: add basic support for nRF51 chips to embassy-nrf

This commit is contained in:
Ulf Lilleengen 2024-01-19 23:13:46 +01:00
parent fb22b46ebb
commit 4410aacafb
13 changed files with 311 additions and 7 deletions

View File

@ -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 }

View File

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

View File

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

View File

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

View File

@ -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) });
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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"

20
examples/nrf51/Cargo.toml Normal file
View File

@ -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

35
examples/nrf51/build.rs Normal file
View File

@ -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");
}

5
examples/nrf51/memory.x Normal file
View File

@ -0,0 +1,5 @@
MEMORY
{
FLASH : ORIGIN = 0x00000000, LENGTH = 256K
RAM : ORIGIN = 0x20000000, LENGTH = 32K
}

View File

@ -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;
}
}