From a8d7a5eb1e2038d0961e5dda8b1d5d04826fe1fd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 17 Nov 2024 14:32:35 +0100 Subject: [PATCH] nrf: add nrf54l base: gpio and time driver. --- ci.sh | 3 + embassy-net-nrf91/Cargo.toml | 2 +- embassy-nrf/Cargo.toml | 12 +- embassy-nrf/src/chips/nrf54l15_app.rs | 346 ++++++++++++++++++++++++++ embassy-nrf/src/gpio.rs | 131 +++++++++- embassy-nrf/src/lib.rs | 84 +++++-- embassy-nrf/src/pwm.rs | 16 +- embassy-nrf/src/spim.rs | 4 +- embassy-nrf/src/spis.rs | 2 +- embassy-nrf/src/time_driver.rs | 25 +- examples/nrf54l15/.cargo/config.toml | 9 + examples/nrf54l15/Cargo.toml | 20 ++ examples/nrf54l15/build.rs | 35 +++ examples/nrf54l15/memory.x | 5 + examples/nrf54l15/src/bin/blinky.rs | 23 ++ 15 files changed, 668 insertions(+), 49 deletions(-) create mode 100644 embassy-nrf/src/chips/nrf54l15_app.rs create mode 100644 examples/nrf54l15/.cargo/config.toml create mode 100644 examples/nrf54l15/Cargo.toml create mode 100644 examples/nrf54l15/build.rs create mode 100644 examples/nrf54l15/memory.x create mode 100644 examples/nrf54l15/src/bin/blinky.rs diff --git a/ci.sh b/ci.sh index 9f7a7a037..307e268c4 100755 --- a/ci.sh +++ b/ci.sh @@ -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 \ diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml index 77a1c4cb5..bc31f93f4 100644 --- a/embassy-net-nrf91/Cargo.toml +++ b/embassy-net-nrf91/Cargo.toml @@ -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" } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 7b20d2643..ed9aba4e7 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -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" diff --git a/embassy-nrf/src/chips/nrf54l15_app.rs b/embassy-nrf/src/chips/nrf54l15_app.rs new file mode 100644 index 000000000..b133eb565 --- /dev/null +++ b/embassy-nrf/src/chips/nrf54l15_app.rs @@ -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, +); diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index 35b0f2e7b..d271dbcff 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -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 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,16 +264,35 @@ impl<'d> Output<'d> { } } -pub(crate) fn convert_drive(drive: OutputDrive) -> vals::Drive { - match drive { - OutputDrive::Standard => vals::Drive::S0S1, - OutputDrive::HighDrive0Standard1 => vals::Drive::H0S1, - OutputDrive::Standard0HighDrive1 => vals::Drive::S0H1, - OutputDrive::HighDrive => vals::Drive::H0H1, - OutputDrive::Disconnect0Standard1 => vals::Drive::D0S1, - OutputDrive::Disconnect0HighDrive1 => vals::Drive::D0H1, - OutputDrive::Standard0Disconnect1 => vals::Drive::S0D1, - OutputDrive::HighDrive0Disconnect1 => vals::Drive::H0D1, +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, + OutputDrive::HighDrive => vals::Drive::H0H1, + OutputDrive::Disconnect0Standard1 => vals::Drive::D0S1, + 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

+ Into + 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> { } #[cfg(not(feature = "_nrf51"))] +#[cfg_attr(feature = "_nrf54l", allow(unused))] // TODO pub(crate) const DISCONNECTED: Psel = Psel(1 << 31); #[cfg(not(feature = "_nrf51"))] diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 8167b44f3..33111e1bd 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -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 => { - // 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 {} + #[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); diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 7f1f568f4..6247ff6a5 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -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)); } } } diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index ffaee2dc5..bd193cfe8 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -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 { diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index a363e5909..88230fa26 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -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()); } diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index b6492ac97..81aabb11c 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -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,8 +146,16 @@ impl RtcDriver { // Wait for clear while r.counter().read().0 != 0 {} - interrupt::RTC1.set_priority(irq_prio); - unsafe { interrupt::RTC1.enable() }; + #[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) { @@ -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() { diff --git a/examples/nrf54l15/.cargo/config.toml b/examples/nrf54l15/.cargo/config.toml new file mode 100644 index 000000000..4a026ebbd --- /dev/null +++ b/examples/nrf54l15/.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 = "../../sshprobe.sh" + +[build] +target = "thumbv8m.main-none-eabihf" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/nrf54l15/Cargo.toml b/examples/nrf54l15/Cargo.toml new file mode 100644 index 000000000..6d11269f7 --- /dev/null +++ b/examples/nrf54l15/Cargo.toml @@ -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 diff --git a/examples/nrf54l15/build.rs b/examples/nrf54l15/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/nrf54l15/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/nrf54l15/memory.x b/examples/nrf54l15/memory.x new file mode 100644 index 000000000..1064c8a5c --- /dev/null +++ b/examples/nrf54l15/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x00000000, LENGTH = 1536K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} diff --git a/examples/nrf54l15/src/bin/blinky.rs b/examples/nrf54l15/src/bin/blinky.rs new file mode 100644 index 000000000..71fcc461f --- /dev/null +++ b/examples/nrf54l15/src/bin/blinky.rs @@ -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; + } +}