nrf: add nrf54l base: gpio and time driver.

This commit is contained in:
Dario Nieuwenhuis 2024-11-17 14:32:35 +01:00
parent 8d8cd78f63
commit a8d7a5eb1e
15 changed files with 668 additions and 49 deletions

3
ci.sh
View File

@ -70,6 +70,8 @@ cargo batch \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-s,gpiote,time,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-ns,gpiote,time,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-net,gpiote,time,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf54l15-app-s,gpiote,time,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf54l15-app-ns,gpiote,time,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time-driver-rtc1 \
--- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time,time-driver-rtc1 \
@ -197,6 +199,7 @@ cargo batch \
--- build --release --manifest-path examples/nrf52810/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52810 \
--- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840 \
--- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf5340 \
--- build --release --manifest-path examples/nrf54l15/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf54l15 \
--- build --release --manifest-path examples/nrf9160/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf9160 \
--- build --release --manifest-path examples/nrf9151/s/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf9151/s \
--- build --release --manifest-path examples/nrf9151/ns/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf9151/ns \

View File

@ -17,7 +17,7 @@ log = [ "dep:log" ]
defmt = { version = "0.3", optional = true }
log = { version = "0.4.14", optional = true }
nrf-pac = { git = "https://github.com/embassy-rs/nrf-pac", rev = "12e2461859acb0bfea9b2ef5cd73f1283c139ac0" }
nrf-pac = { git = "https://github.com/embassy-rs/nrf-pac", rev = "52e3a757f06035c94291bfc42b0c03f71e4d677e" }
cortex-m = "0.7.7"
embassy-time = { version = "0.3.1", path = "../embassy-time" }

View File

@ -83,6 +83,11 @@ nrf5340-app-s = ["_nrf5340-app", "_s"]
nrf5340-app-ns = ["_nrf5340-app", "_ns"]
## nRF5340 network core
nrf5340-net = ["_nrf5340-net"]
## nRF54L15 application core in Secure mode
nrf54l15-app-s = ["_nrf54l15-app", "_s"]
## nRF54L15 application core in Non-Secure mode
nrf54l15-app-ns = ["_nrf54l15-app", "_ns"]
## nRF9160 in Secure mode
nrf9160-s = ["_nrf9160", "_s", "_nrf91"]
## nRF9160 in Non-Secure mode
@ -103,6 +108,10 @@ nrf9161-ns = ["nrf9120-ns"]
_nrf5340-app = ["_nrf5340", "nrf-pac/nrf5340-app"]
_nrf5340-net = ["_nrf5340", "nrf-pac/nrf5340-net"]
_nrf5340 = ["_gpio-p1", "_dppi"]
_nrf54l15-app = ["_nrf54l15", "nrf-pac/nrf54l15-app"]
_nrf54l15 = ["_nrf54l", "_gpio-p1", "_gpio-p2"]
_nrf54l = ["_dppi"]
_nrf9160 = ["nrf-pac/nrf9160", "_dppi"]
_nrf9120 = ["nrf-pac/nrf9120", "_dppi"]
_nrf52 = ["_ppi"]
@ -118,6 +127,7 @@ _ns = []
_ppi = []
_dppi = []
_gpio-p1 = []
_gpio-p2 = []
# Errata workarounds
_nrf52832_anomaly_109 = []
@ -136,7 +146,7 @@ embedded-hal-async = { version = "1.0" }
embedded-io = { version = "0.6.0" }
embedded-io-async = { version = "0.6.1" }
nrf-pac = { git = "https://github.com/embassy-rs/nrf-pac", rev = "12e2461859acb0bfea9b2ef5cd73f1283c139ac0" }
nrf-pac = { git = "https://github.com/embassy-rs/nrf-pac", rev = "52e3a757f06035c94291bfc42b0c03f71e4d677e" }
defmt = { version = "0.3", optional = true }
bitflags = "2.4.2"

View File

@ -0,0 +1,346 @@
/// Peripheral Access Crate
#[allow(unused_imports)]
#[rustfmt::skip]
pub mod pac {
pub use nrf_pac::*;
#[cfg(feature = "_ns")]
#[doc(no_inline)]
pub use nrf_pac::{
FICR_NS as FICR,
DPPIC00_NS as DPPIC00,
PPIB00_NS as PPIB00,
PPIB01_NS as PPIB01,
AAR00_NS as AAR00,
CCM00_NS as CCM00,
ECB00_NS as ECB00,
SPIM00_NS as SPIM00,
SPIS00_NS as SPIS00,
UARTE00_NS as UARTE00,
VPR00_NS as VPR00,
P2_NS as P2,
CTRLAP_NS as CTRLAP,
TAD_NS as TAD,
TIMER00_NS as TIMER00,
DPPIC10_NS as DPPIC10,
PPIB10_NS as PPIB10,
PPIB11_NS as PPIB11,
TIMER10_NS as TIMER10,
RTC10_NS as RTC10,
EGU10_NS as EGU10,
RADIO_NS as RADIO,
DPPIC20_NS as DPPIC20,
PPIB20_NS as PPIB20,
PPIB21_NS as PPIB21,
PPIB22_NS as PPIB22,
SPIM20_NS as SPIM20,
SPIS20_NS as SPIS20,
TWIM20_NS as TWIM20,
TWIS20_NS as TWIS20,
UARTE20_NS as UARTE20,
SPIM21_NS as SPIM21,
SPIS21_NS as SPIS21,
TWIM21_NS as TWIM21,
TWIS21_NS as TWIS21,
UARTE21_NS as UARTE21,
SPIM22_NS as SPIM22,
SPIS22_NS as SPIS22,
TWIM22_NS as TWIM22,
TWIS22_NS as TWIS22,
UARTE22_NS as UARTE22,
EGU20_NS as EGU20,
TIMER20_NS as TIMER20,
TIMER21_NS as TIMER21,
TIMER22_NS as TIMER22,
TIMER23_NS as TIMER23,
TIMER24_NS as TIMER24,
MEMCONF_NS as MEMCONF,
PDM20_NS as PDM20,
PDM21_NS as PDM21,
PWM20_NS as PWM20,
PWM21_NS as PWM21,
PWM22_NS as PWM22,
SAADC_NS as SAADC,
NFCT_NS as NFCT,
TEMP_NS as TEMP,
P1_NS as P1,
GPIOTE20_NS as GPIOTE20,
I2S20_NS as I2S20,
QDEC20_NS as QDEC20,
QDEC21_NS as QDEC21,
GRTC_NS as GRTC,
DPPIC30_NS as DPPIC30,
PPIB30_NS as PPIB30,
SPIM30_NS as SPIM30,
SPIS30_NS as SPIS30,
TWIM30_NS as TWIM30,
TWIS30_NS as TWIS30,
UARTE30_NS as UARTE30,
RTC30_NS as RTC30,
COMP_NS as COMP,
LPCOMP_NS as LPCOMP,
WDT31_NS as WDT31,
P0_NS as P0,
GPIOTE30_NS as GPIOTE30,
CLOCK_NS as CLOCK,
POWER_NS as POWER,
RESET_NS as RESET,
OSCILLATORS_NS as OSCILLATORS,
REGULATORS_NS as REGULATORS,
TPIU_NS as TPIU,
ETM_NS as ETM,
};
#[cfg(feature = "_s")]
#[doc(no_inline)]
pub use nrf_pac::{
SICR_S as SICR,
ICACHEDATA_S as ICACHEDATA,
ICACHEINFO_S as ICACHEINFO,
SWI00_S as SWI00,
SWI01_S as SWI01,
SWI02_S as SWI02,
SWI03_S as SWI03,
SPU00_S as SPU00,
MPC00_S as MPC00,
DPPIC00_S as DPPIC00,
PPIB00_S as PPIB00,
PPIB01_S as PPIB01,
KMU_S as KMU,
AAR00_S as AAR00,
CCM00_S as CCM00,
ECB00_S as ECB00,
CRACEN_S as CRACEN,
SPIM00_S as SPIM00,
SPIS00_S as SPIS00,
UARTE00_S as UARTE00,
GLITCHDET_S as GLITCHDET,
RRAMC_S as RRAMC,
VPR00_S as VPR00,
P2_S as P2,
CTRLAP_S as CTRLAP,
TAD_S as TAD,
TIMER00_S as TIMER00,
SPU10_S as SPU10,
DPPIC10_S as DPPIC10,
PPIB10_S as PPIB10,
PPIB11_S as PPIB11,
TIMER10_S as TIMER10,
RTC10_S as RTC10,
EGU10_S as EGU10,
RADIO_S as RADIO,
SPU20_S as SPU20,
DPPIC20_S as DPPIC20,
PPIB20_S as PPIB20,
PPIB21_S as PPIB21,
PPIB22_S as PPIB22,
SPIM20_S as SPIM20,
SPIS20_S as SPIS20,
TWIM20_S as TWIM20,
TWIS20_S as TWIS20,
UARTE20_S as UARTE20,
SPIM21_S as SPIM21,
SPIS21_S as SPIS21,
TWIM21_S as TWIM21,
TWIS21_S as TWIS21,
UARTE21_S as UARTE21,
SPIM22_S as SPIM22,
SPIS22_S as SPIS22,
TWIM22_S as TWIM22,
TWIS22_S as TWIS22,
UARTE22_S as UARTE22,
EGU20_S as EGU20,
TIMER20_S as TIMER20,
TIMER21_S as TIMER21,
TIMER22_S as TIMER22,
TIMER23_S as TIMER23,
TIMER24_S as TIMER24,
MEMCONF_S as MEMCONF,
PDM20_S as PDM20,
PDM21_S as PDM21,
PWM20_S as PWM20,
PWM21_S as PWM21,
PWM22_S as PWM22,
SAADC_S as SAADC,
NFCT_S as NFCT,
TEMP_S as TEMP,
P1_S as P1,
GPIOTE20_S as GPIOTE20,
TAMPC_S as TAMPC,
I2S20_S as I2S20,
QDEC20_S as QDEC20,
QDEC21_S as QDEC21,
GRTC_S as GRTC,
SPU30_S as SPU30,
DPPIC30_S as DPPIC30,
PPIB30_S as PPIB30,
SPIM30_S as SPIM30,
SPIS30_S as SPIS30,
TWIM30_S as TWIM30,
TWIS30_S as TWIS30,
UARTE30_S as UARTE30,
RTC30_S as RTC30,
COMP_S as COMP,
LPCOMP_S as LPCOMP,
WDT30_S as WDT30,
WDT31_S as WDT31,
P0_S as P0,
GPIOTE30_S as GPIOTE30,
CLOCK_S as CLOCK,
POWER_S as POWER,
RESET_S as RESET,
OSCILLATORS_S as OSCILLATORS,
REGULATORS_S as REGULATORS,
CRACENCORE_S as CRACENCORE,
CPUC_S as CPUC,
ICACHE_S as ICACHE,
};
}
/// The maximum buffer size that the EasyDMA can send/recv in one operation.
pub const EASY_DMA_SIZE: usize = (1 << 16) - 1;
//pub const FORCE_COPY_BUFFER_SIZE: usize = 1024;
//pub const FLASH_SIZE: usize = 1024 * 1024;
embassy_hal_internal::peripherals! {
// GPIO port 0
P0_00,
P0_01,
P0_02,
P0_03,
P0_04,
P0_05,
P0_06,
// GPIO port 1
P1_00,
P1_01,
P1_02,
P1_03,
P1_04,
P1_05,
P1_06,
P1_07,
P1_08,
P1_09,
P1_10,
P1_11,
P1_12,
P1_13,
P1_14,
P1_15,
P1_16,
// GPIO port 2
P2_00,
P2_01,
P2_02,
P2_03,
P2_04,
P2_05,
P2_06,
P2_07,
P2_08,
P2_09,
P2_10,
}
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!(P1_00, 1, 0);
impl_pin!(P1_01, 1, 1);
impl_pin!(P1_02, 1, 2);
impl_pin!(P1_03, 1, 3);
impl_pin!(P1_04, 1, 4);
impl_pin!(P1_05, 1, 5);
impl_pin!(P1_06, 1, 6);
impl_pin!(P1_07, 1, 7);
impl_pin!(P1_08, 1, 8);
impl_pin!(P1_09, 1, 9);
impl_pin!(P1_10, 1, 10);
impl_pin!(P1_11, 1, 11);
impl_pin!(P1_12, 1, 12);
impl_pin!(P1_13, 1, 13);
impl_pin!(P1_14, 1, 14);
impl_pin!(P1_15, 1, 15);
impl_pin!(P1_16, 1, 16);
impl_pin!(P2_00, 2, 0);
impl_pin!(P2_01, 2, 1);
impl_pin!(P2_02, 2, 2);
impl_pin!(P2_03, 2, 3);
impl_pin!(P2_04, 2, 4);
impl_pin!(P2_05, 2, 5);
impl_pin!(P2_06, 2, 6);
impl_pin!(P2_07, 2, 7);
impl_pin!(P2_08, 2, 8);
impl_pin!(P2_09, 2, 9);
impl_pin!(P2_10, 2, 10);
embassy_hal_internal::interrupt_mod!(
SWI00,
SWI01,
SWI02,
SWI03,
SPU00,
MPC00,
AAR00_CCM00,
ECB00,
CRACEN,
SERIAL00,
RRAMC,
VPR00,
CTRLAP,
TIMER00,
SPU10,
TIMER10,
RTC10,
EGU10,
RADIO_0,
RADIO_1,
SPU20,
SERIAL20,
SERIAL21,
SERIAL22,
EGU20,
TIMER20,
TIMER21,
TIMER22,
TIMER23,
TIMER24,
PDM20,
PDM21,
PWM20,
PWM21,
PWM22,
SAADC,
NFCT,
TEMP,
GPIOTE20_0,
GPIOTE20_1,
TAMPC,
I2S20,
QDEC20,
QDEC21,
GRTC_0,
GRTC_1,
GRTC_2,
GRTC_3,
SPU30,
SERIAL30,
RTC30,
COMP_LPCOMP,
WDT30,
WDT31,
GPIOTE30_0,
GPIOTE30_1,
CLOCK_POWER,
);

View File

@ -23,6 +23,10 @@ pub enum Port {
/// Port 1, only available on some MCUs.
#[cfg(feature = "_gpio-p1")]
Port1,
/// Port 2, only available on some MCUs.
#[cfg(feature = "_gpio-p2")]
Port2,
}
/// Pull setting for an input.
@ -99,8 +103,83 @@ impl From<Level> for bool {
}
}
/// Drive strength settings for a given output level.
// These numbers match vals::Drive exactly so hopefully the compiler will unify them.
#[cfg(feature = "_nrf54l")]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum LevelDrive {
/// Disconnect (do not drive the output at all)
Disconnect = 2,
/// Standard
Standard = 0,
/// High drive
High = 1,
/// Extra high drive
ExtraHigh = 3,
}
/// Drive strength settings for an output pin.
///
/// This is a combination of two drive levels, used when the pin is set
/// low and high respectively.
#[cfg(feature = "_nrf54l")]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct OutputDrive {
low: LevelDrive,
high: LevelDrive,
}
#[cfg(feature = "_nrf54l")]
#[allow(non_upper_case_globals)]
impl OutputDrive {
/// Standard '0', standard '1'
pub const Standard: Self = Self {
low: LevelDrive::Standard,
high: LevelDrive::Standard,
};
/// High drive '0', standard '1'
pub const HighDrive0Standard1: Self = Self {
low: LevelDrive::High,
high: LevelDrive::Standard,
};
/// Standard '0', high drive '1'
pub const Standard0HighDrive1: Self = Self {
low: LevelDrive::Standard,
high: LevelDrive::High,
};
/// High drive '0', high 'drive '1'
pub const HighDrive: Self = Self {
low: LevelDrive::High,
high: LevelDrive::High,
};
/// Disconnect '0' standard '1' (normally used for wired-or connections)
pub const Disconnect0Standard1: Self = Self {
low: LevelDrive::Disconnect,
high: LevelDrive::Standard,
};
/// Disconnect '0', high drive '1' (normally used for wired-or connections)
pub const Disconnect0HighDrive1: Self = Self {
low: LevelDrive::Disconnect,
high: LevelDrive::High,
};
/// Standard '0'. disconnect '1' (also known as "open drain", normally used for wired-and connections)
pub const Standard0Disconnect1: Self = Self {
low: LevelDrive::Standard,
high: LevelDrive::Disconnect,
};
/// High drive '0', disconnect '1' (also known as "open drain", normally used for wired-and connections)
pub const HighDrive0Disconnect1: Self = Self {
low: LevelDrive::High,
high: LevelDrive::Disconnect,
};
}
/// Drive strength settings for an output pin.
// These numbers match vals::Drive exactly so hopefully the compiler will unify them.
#[cfg(not(feature = "_nrf54l"))]
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
@ -185,8 +264,10 @@ impl<'d> Output<'d> {
}
}
pub(crate) fn convert_drive(drive: OutputDrive) -> vals::Drive {
match drive {
pub(crate) fn convert_drive(w: &mut pac::gpio::regs::PinCnf, drive: OutputDrive) {
#[cfg(not(feature = "_nrf54l"))]
{
let drive = match drive {
OutputDrive::Standard => vals::Drive::S0S1,
OutputDrive::HighDrive0Standard1 => vals::Drive::H0S1,
OutputDrive::Standard0HighDrive1 => vals::Drive::S0H1,
@ -195,6 +276,23 @@ pub(crate) fn convert_drive(drive: OutputDrive) -> vals::Drive {
OutputDrive::Disconnect0HighDrive1 => vals::Drive::D0H1,
OutputDrive::Standard0Disconnect1 => vals::Drive::S0D1,
OutputDrive::HighDrive0Disconnect1 => vals::Drive::H0D1,
};
w.set_drive(drive);
}
#[cfg(feature = "_nrf54l")]
{
fn convert(d: LevelDrive) -> vals::Drive {
match d {
LevelDrive::Disconnect => vals::Drive::D,
LevelDrive::Standard => vals::Drive::S,
LevelDrive::High => vals::Drive::H,
LevelDrive::ExtraHigh => vals::Drive::E,
}
}
w.set_drive0(convert(drive.low));
w.set_drive0(convert(drive.high));
}
}
@ -234,7 +332,7 @@ impl<'d> Flex<'d> {
w.set_dir(vals::Dir::INPUT);
w.set_input(vals::Input::CONNECT);
w.set_pull(convert_pull(pull));
w.set_drive(vals::Drive::S0S1);
convert_drive(w, OutputDrive::Standard);
w.set_sense(vals::Sense::DISABLED);
});
}
@ -249,9 +347,10 @@ impl<'d> Flex<'d> {
w.set_dir(vals::Dir::OUTPUT);
w.set_input(vals::Input::DISCONNECT);
w.set_pull(vals::Pull::DISABLED);
w.set_drive(convert_drive(drive));
convert_drive(w, drive);
w.set_sense(vals::Sense::DISABLED);
});
info!("pin_cnf: {:08x}", self.pin.conf().read().0);
}
/// Put the pin into input + output mode.
@ -269,7 +368,7 @@ impl<'d> Flex<'d> {
w.set_dir(vals::Dir::OUTPUT);
w.set_input(vals::Input::CONNECT);
w.set_pull(convert_pull(pull));
w.set_drive(convert_drive(drive));
convert_drive(w, drive);
w.set_sense(vals::Sense::DISABLED);
});
}
@ -377,6 +476,8 @@ pub(crate) trait SealedPin {
0 => pac::P0,
#[cfg(feature = "_gpio-p1")]
1 => pac::P1,
#[cfg(feature = "_gpio-p2")]
2 => pac::P2,
_ => unsafe { unreachable_unchecked() },
}
}
@ -415,6 +516,8 @@ pub trait Pin: Peripheral<P = Self> + Into<AnyPin> + SealedPin + Sized + 'static
0 => Port::Port0,
#[cfg(feature = "_gpio-p1")]
1 => Port::Port1,
#[cfg(feature = "_gpio-p2")]
2 => Port::Port2,
_ => unsafe { unreachable_unchecked() },
}
}
@ -463,6 +566,7 @@ impl SealedPin for AnyPin {
// ====================
#[cfg(not(feature = "_nrf51"))]
#[cfg_attr(feature = "_nrf54l", allow(unused))] // TODO
pub(crate) trait PselBits {
fn psel_bits(&self) -> pac::shared::regs::Psel;
}
@ -479,6 +583,7 @@ impl<'a, P: Pin> PselBits for Option<PeripheralRef<'a, P>> {
}
#[cfg(not(feature = "_nrf51"))]
#[cfg_attr(feature = "_nrf54l", allow(unused))] // TODO
pub(crate) const DISCONNECTED: Psel = Psel(1 << 31);
#[cfg(not(feature = "_nrf51"))]

View File

@ -22,6 +22,8 @@
feature = "nrf5340-app-s",
feature = "nrf5340-app-ns",
feature = "nrf5340-net",
feature = "nrf54l15-app-s",
feature = "nrf54l15-app-ns",
feature = "nrf9160-s",
feature = "nrf9160-ns",
feature = "nrf9120-s",
@ -44,6 +46,8 @@ compile_error!(
nrf5340-app-s,
nrf5340-app-ns,
nrf5340-net,
nrf54l15-app-s,
nrf54l15-app-ns,
nrf9160-s,
nrf9160-ns,
nrf9120-s,
@ -68,21 +72,22 @@ pub(crate) mod util;
#[cfg(feature = "_time-driver")]
mod time_driver;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(feature = "_nrf51"))]
pub mod buffered_uarte;
pub mod gpio;
#[cfg(feature = "gpiote")]
pub mod gpiote;
// TODO: tested on other chips
#[cfg(not(any(feature = "_nrf91", feature = "_nrf5340-app")))]
pub mod radio;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(feature = "_nrf51"))]
pub mod egu;
pub mod gpio;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(feature = "gpiote")]
pub mod gpiote;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))]
pub mod i2s;
#[cfg(not(feature = "_nrf54l"))] // TODO
pub mod nvmc;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(any(
feature = "nrf52810",
feature = "nrf52811",
@ -93,7 +98,9 @@ pub mod nvmc;
feature = "_nrf91",
))]
pub mod pdm;
#[cfg(not(feature = "_nrf54l"))] // TODO
pub mod ppi;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(any(
feature = "_nrf51",
feature = "nrf52805",
@ -101,27 +108,42 @@ pub mod ppi;
feature = "_nrf5340-net"
)))]
pub mod pwm;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(any(feature = "_nrf51", feature = "_nrf91", feature = "_nrf5340-net")))]
pub mod qdec;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))]
pub mod qspi;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(any(feature = "_nrf91", feature = "_nrf5340-app")))]
pub mod radio;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))]
pub mod rng;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))]
pub mod saadc;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(feature = "_nrf51"))]
pub mod spim;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(feature = "_nrf51"))]
pub mod spis;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))]
pub mod temp;
#[cfg(not(feature = "_nrf54l"))] // TODO
pub mod timer;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(feature = "_nrf51"))]
pub mod twim;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(feature = "_nrf51"))]
pub mod twis;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(feature = "_nrf51"))]
pub mod uarte;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(any(
feature = "_nrf5340-app",
feature = "nrf52820",
@ -129,6 +151,7 @@ pub mod uarte;
feature = "nrf52840"
))]
pub mod usb;
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(feature = "_nrf5340"))]
pub mod wdt;
@ -143,6 +166,7 @@ pub mod wdt;
#[cfg_attr(feature = "nrf52840", path = "chips/nrf52840.rs")]
#[cfg_attr(feature = "_nrf5340-app", path = "chips/nrf5340_app.rs")]
#[cfg_attr(feature = "_nrf5340-net", path = "chips/nrf5340_net.rs")]
#[cfg_attr(feature = "_nrf54l15-app", path = "chips/nrf54l15_app.rs")]
#[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")]
#[cfg_attr(feature = "_nrf9120", path = "chips/nrf9120.rs")]
mod chip;
@ -249,10 +273,10 @@ pub mod config {
/// External source from xtal.
ExternalXtal,
/// External source from xtal with low swing applied.
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))]
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf91", feature = "_nrf54l")))]
ExternalLowSwing,
/// External source from xtal with full swing applied.
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))]
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf91", feature = "_nrf54l")))]
ExternalFullSwing,
}
@ -325,7 +349,7 @@ pub mod config {
pub hfclk_source: HfclkSource,
/// Low frequency clock source.
pub lfclk_source: LfclkSource,
#[cfg(not(feature = "_nrf5340-net"))]
#[cfg(not(any(feature = "_nrf5340-net", feature = "_nrf54l")))]
/// DCDC configuration.
pub dcdc: DcdcConfig,
/// GPIOTE interrupt priority. Should be lower priority than softdevice if used.
@ -346,7 +370,7 @@ pub mod config {
// xtals if they know they have them.
hfclk_source: HfclkSource::Internal,
lfclk_source: LfclkSource::InternalRC,
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))]
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf91", feature = "_nrf54l")))]
dcdc: DcdcConfig {
#[cfg(feature = "nrf52840")]
reg0: false,
@ -415,7 +439,7 @@ mod consts {
pub const APPROTECT_DISABLED: u32 = 0x0000_005a;
}
#[cfg(not(feature = "_nrf51"))]
#[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum WriteResult {
@ -427,12 +451,12 @@ enum WriteResult {
Failed,
}
#[cfg(not(feature = "_nrf51"))]
#[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))]
unsafe fn uicr_write(address: *mut u32, value: u32) -> WriteResult {
uicr_write_masked(address, value, 0xFFFF_FFFF)
}
#[cfg(not(feature = "_nrf51"))]
#[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))]
unsafe fn uicr_write_masked(address: *mut u32, value: u32, mask: u32) -> WriteResult {
let curr_val = address.read_volatile();
if curr_val & mask == value & mask {
@ -469,6 +493,7 @@ pub fn init(config: config::Config) -> Peripherals {
let mut needs_reset = false;
// Setup debug protection.
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(not(feature = "_nrf51"))]
match config.debug {
config::Debug::Allowed => {
@ -592,15 +617,25 @@ pub fn init(config: config::Config) -> Peripherals {
match config.hfclk_source {
config::HfclkSource::Internal => {}
config::HfclkSource::ExternalXtal => {
#[cfg(feature = "_nrf54l")]
{
r.events_xostarted().write_value(0);
r.tasks_xostart().write_value(1);
while r.events_xostarted().read() == 0 {}
}
#[cfg(not(feature = "_nrf54l"))]
{
// Datasheet says this is likely to take 0.36ms
r.events_hfclkstarted().write_value(0);
r.tasks_hfclkstart().write_value(1);
while r.events_hfclkstarted().read() == 0 {}
}
}
}
// Configure LFCLK.
#[cfg(not(any(feature = "_nrf51", feature = "_nrf5340", feature = "_nrf91")))]
#[cfg(not(any(feature = "_nrf51", feature = "_nrf5340", feature = "_nrf91", feature = "_nrf54l")))]
match config.lfclk_source {
config::LfclkSource::InternalRC => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::RC)),
config::LfclkSource::Synthesized => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::SYNTH)),
@ -621,6 +656,12 @@ pub fn init(config: config::Config) -> Peripherals {
config::LfclkSource::InternalRC => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFRC)),
config::LfclkSource::ExternalXtal => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFXO)),
}
#[cfg(feature = "_nrf54l")]
match config.lfclk_source {
config::LfclkSource::InternalRC => r.lfclk().src().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFRC)),
config::LfclkSource::Synthesized => r.lfclk().src().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFSYNT)),
config::LfclkSource::ExternalXtal => r.lfclk().src().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFXO)),
}
// Start LFCLK.
// Datasheet says this could take 100us from synth source
@ -629,7 +670,7 @@ pub fn init(config: config::Config) -> Peripherals {
r.tasks_lfclkstart().write_value(1);
while r.events_lfclkstarted().read() == 0 {}
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))]
#[cfg(not(any(feature = "_nrf5340", feature = "_nrf91", feature = "_nrf54l")))]
{
// Setup DCDCs.
#[cfg(feature = "nrf52840")]
@ -663,6 +704,7 @@ pub fn init(config: config::Config) -> Peripherals {
}
// Init GPIOTE
#[cfg(not(feature = "_nrf54l"))] // TODO
#[cfg(feature = "gpiote")]
gpiote::init(config.gpiote_interrupt_priority);

View File

@ -133,7 +133,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
pin.conf().write(|w| {
w.set_dir(gpiovals::Dir::OUTPUT);
w.set_input(gpiovals::Input::DISCONNECT);
w.set_drive(convert_drive(config.ch0_drive));
convert_drive(w, config.ch0_drive);
});
}
if let Some(pin) = &ch1 {
@ -141,7 +141,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
pin.conf().write(|w| {
w.set_dir(gpiovals::Dir::OUTPUT);
w.set_input(gpiovals::Input::DISCONNECT);
w.set_drive(convert_drive(config.ch1_drive));
convert_drive(w, config.ch1_drive);
});
}
if let Some(pin) = &ch2 {
@ -149,7 +149,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
pin.conf().write(|w| {
w.set_dir(gpiovals::Dir::OUTPUT);
w.set_input(gpiovals::Input::DISCONNECT);
w.set_drive(convert_drive(config.ch2_drive));
convert_drive(w, config.ch2_drive);
});
}
if let Some(pin) = &ch3 {
@ -157,7 +157,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> {
pin.conf().write(|w| {
w.set_dir(gpiovals::Dir::OUTPUT);
w.set_input(gpiovals::Input::DISCONNECT);
w.set_drive(convert_drive(config.ch3_drive));
convert_drive(w, config.ch3_drive);
});
}
@ -832,7 +832,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> {
#[inline(always)]
pub fn set_ch0_drive(&self, drive: OutputDrive) {
if let Some(pin) = &self.ch0 {
pin.conf().modify(|w| w.set_drive(convert_drive(drive)));
pin.conf().modify(|w| convert_drive(w, drive));
}
}
@ -840,7 +840,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> {
#[inline(always)]
pub fn set_ch1_drive(&self, drive: OutputDrive) {
if let Some(pin) = &self.ch1 {
pin.conf().modify(|w| w.set_drive(convert_drive(drive)));
pin.conf().modify(|w| convert_drive(w, drive));
}
}
@ -848,7 +848,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> {
#[inline(always)]
pub fn set_ch2_drive(&self, drive: OutputDrive) {
if let Some(pin) = &self.ch2 {
pin.conf().modify(|w| w.set_drive(convert_drive(drive)));
pin.conf().modify(|w| convert_drive(w, drive));
}
}
@ -856,7 +856,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> {
#[inline(always)]
pub fn set_ch3_drive(&self, drive: OutputDrive) {
if let Some(pin) = &self.ch3 {
pin.conf().modify(|w| w.set_drive(convert_drive(drive)));
pin.conf().modify(|w| convert_drive(w, drive));
}
}
}

View File

@ -173,13 +173,13 @@ impl<'d, T: Instance> Spim<'d, T> {
if let Some(sck) = &sck {
sck.conf().write(|w| {
w.set_dir(gpiovals::Dir::OUTPUT);
w.set_drive(convert_drive(config.sck_drive))
convert_drive(w, config.sck_drive);
});
}
if let Some(mosi) = &mosi {
mosi.conf().write(|w| {
w.set_dir(gpiovals::Dir::OUTPUT);
w.set_drive(convert_drive(config.mosi_drive))
convert_drive(w, config.mosi_drive);
});
}
if let Some(miso) = &miso {

View File

@ -203,7 +203,7 @@ impl<'d, T: Instance> Spis<'d, T> {
if let Some(miso) = &miso {
miso.conf().write(|w| {
w.set_dir(gpiovals::Dir::OUTPUT);
w.set_drive(convert_drive(config.miso_drive))
convert_drive(w, config.miso_drive);
});
r.psel().miso().write_value(miso.psel_bits());
}

View File

@ -10,6 +10,11 @@ use embassy_time_driver::{AlarmHandle, Driver};
use crate::interrupt::InterruptExt;
use crate::{interrupt, pac};
#[cfg(feature = "_nrf54l")]
fn rtc() -> pac::rtc::Rtc {
pac::RTC30
}
#[cfg(not(feature = "_nrf54l"))]
fn rtc() -> pac::rtc::Rtc {
pac::RTC1
}
@ -141,9 +146,17 @@ impl RtcDriver {
// Wait for clear
while r.counter().read().0 != 0 {}
#[cfg(feature = "_nrf54l")]
{
interrupt::RTC30.set_priority(irq_prio);
unsafe { interrupt::RTC30.enable() };
}
#[cfg(not(feature = "_nrf54l"))]
{
interrupt::RTC1.set_priority(irq_prio);
unsafe { interrupt::RTC1.enable() };
}
}
fn on_interrupt(&self) {
let r = rtc();
@ -292,6 +305,14 @@ impl Driver for RtcDriver {
}
}
#[cfg(feature = "_nrf54l")]
#[cfg(feature = "rt")]
#[interrupt]
fn RTC30() {
DRIVER.on_interrupt()
}
#[cfg(not(feature = "_nrf54l"))]
#[cfg(feature = "rt")]
#[interrupt]
fn RTC1() {

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 = "../../sshprobe.sh"
[build]
target = "thumbv8m.main-none-eabihf"
[env]
DEFMT_LOG = "trace"

View File

@ -0,0 +1,20 @@
[package]
edition = "2021"
name = "embassy-nrf54l15-examples"
version = "0.1.0"
license = "MIT OR Apache-2.0"
[dependencies]
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
defmt = "0.3"
defmt-rtt = "0.4"
panic-probe = { version = "0.3", features = ["print-defmt"] }
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
cortex-m-rt = "0.7.0"
[profile.release]
debug = 2

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

View File

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

View File

@ -0,0 +1,23 @@
#![no_std]
#![no_main]
use defmt::info;
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.P2_09, Level::Low, OutputDrive::Standard);
loop {
info!("high!");
led.set_high();
Timer::after_millis(300).await;
info!("low!");
led.set_low();
Timer::after_millis(300).await;
}
}