mirror of
https://github.com/embassy-rs/embassy.git
synced 2025-09-27 20:30:29 +00:00
Merge pull request #4686 from robamu/add-nrf-rtc-driver
add basic RTC driver for nRF
This commit is contained in:
commit
a25c5c4b2a
@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
- changed: nrf54l: Disable glitch detection and enable DC/DC in init.
|
- changed: nrf54l: Disable glitch detection and enable DC/DC in init.
|
||||||
- changed: Add embassy-net-driver-channel implementation for IEEE 802.15.4
|
- changed: Add embassy-net-driver-channel implementation for IEEE 802.15.4
|
||||||
|
- changed: add persist() method for gpio and ppi
|
||||||
|
- added: basic RTC driver
|
||||||
- changed: add persist() method for gpio, gpiote, timer and ppi
|
- changed: add persist() method for gpio, gpiote, timer and ppi
|
||||||
- changed: impl Drop for Timer
|
- changed: impl Drop for Timer
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ pub const FLASH_SIZE: usize = 128 * 1024;
|
|||||||
embassy_hal_internal::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
RTC1,
|
RTC1,
|
||||||
|
|
||||||
// WDT
|
// WDT
|
||||||
@ -110,6 +111,10 @@ impl_timer!(TIMER2, TIMER2, TIMER2);
|
|||||||
|
|
||||||
impl_rng!(RNG, RNG, RNG);
|
impl_rng!(RNG, RNG, RNG);
|
||||||
|
|
||||||
|
impl_rtc!(RTC0, RTC0, RTC0);
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
|
impl_rtc!(RTC1, RTC1, RTC1);
|
||||||
|
|
||||||
impl_pin!(P0_00, 0, 0);
|
impl_pin!(P0_00, 0, 0);
|
||||||
impl_pin!(P0_01, 0, 1);
|
impl_pin!(P0_01, 0, 1);
|
||||||
impl_pin!(P0_02, 0, 2);
|
impl_pin!(P0_02, 0, 2);
|
||||||
|
@ -12,6 +12,7 @@ pub const APPROTECT_MIN_BUILD_CODE: u8 = b'B';
|
|||||||
embassy_hal_internal::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
|
#[cfg(not(feature="time-driver-rtc1"))]
|
||||||
RTC1,
|
RTC1,
|
||||||
|
|
||||||
// WDT
|
// WDT
|
||||||
@ -156,6 +157,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
|
|||||||
impl_timer!(TIMER1, TIMER1, TIMER1);
|
impl_timer!(TIMER1, TIMER1, TIMER1);
|
||||||
impl_timer!(TIMER2, TIMER2, TIMER2);
|
impl_timer!(TIMER2, TIMER2, TIMER2);
|
||||||
|
|
||||||
|
impl_rtc!(RTC0, RTC0, RTC0);
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
|
impl_rtc!(RTC1, RTC1, RTC1);
|
||||||
|
|
||||||
impl_pin!(P0_00, 0, 0);
|
impl_pin!(P0_00, 0, 0);
|
||||||
impl_pin!(P0_01, 0, 1);
|
impl_pin!(P0_01, 0, 1);
|
||||||
impl_pin!(P0_02, 0, 2);
|
impl_pin!(P0_02, 0, 2);
|
||||||
@ -234,12 +239,12 @@ embassy_hal_internal::interrupt_mod!(
|
|||||||
TIMER0,
|
TIMER0,
|
||||||
TIMER1,
|
TIMER1,
|
||||||
TIMER2,
|
TIMER2,
|
||||||
RTC0,
|
|
||||||
TEMP,
|
TEMP,
|
||||||
RNG,
|
RNG,
|
||||||
ECB,
|
ECB,
|
||||||
AAR_CCM,
|
AAR_CCM,
|
||||||
WDT,
|
WDT,
|
||||||
|
RTC0,
|
||||||
RTC1,
|
RTC1,
|
||||||
QDEC,
|
QDEC,
|
||||||
EGU0_SWI0,
|
EGU0_SWI0,
|
||||||
|
@ -12,6 +12,7 @@ pub const APPROTECT_MIN_BUILD_CODE: u8 = b'E';
|
|||||||
embassy_hal_internal::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
|
#[cfg(not(feature="time-driver-rtc1"))]
|
||||||
RTC1,
|
RTC1,
|
||||||
|
|
||||||
// WDT
|
// WDT
|
||||||
@ -166,6 +167,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
|
|||||||
impl_timer!(TIMER1, TIMER1, TIMER1);
|
impl_timer!(TIMER1, TIMER1, TIMER1);
|
||||||
impl_timer!(TIMER2, TIMER2, TIMER2);
|
impl_timer!(TIMER2, TIMER2, TIMER2);
|
||||||
|
|
||||||
|
impl_rtc!(RTC0, RTC0, RTC0);
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
|
impl_rtc!(RTC1, RTC1, RTC1);
|
||||||
|
|
||||||
impl_pin!(P0_00, 0, 0);
|
impl_pin!(P0_00, 0, 0);
|
||||||
impl_pin!(P0_01, 0, 1);
|
impl_pin!(P0_01, 0, 1);
|
||||||
impl_pin!(P0_02, 0, 2);
|
impl_pin!(P0_02, 0, 2);
|
||||||
|
@ -12,6 +12,7 @@ pub const APPROTECT_MIN_BUILD_CODE: u8 = b'B';
|
|||||||
embassy_hal_internal::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
|
#[cfg(not(feature="time-driver-rtc1"))]
|
||||||
RTC1,
|
RTC1,
|
||||||
|
|
||||||
// WDT
|
// WDT
|
||||||
@ -168,6 +169,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
|
|||||||
impl_timer!(TIMER1, TIMER1, TIMER1);
|
impl_timer!(TIMER1, TIMER1, TIMER1);
|
||||||
impl_timer!(TIMER2, TIMER2, TIMER2);
|
impl_timer!(TIMER2, TIMER2, TIMER2);
|
||||||
|
|
||||||
|
impl_rtc!(RTC0, RTC0, RTC0);
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
|
impl_rtc!(RTC1, RTC1, RTC1);
|
||||||
|
|
||||||
impl_pin!(P0_00, 0, 0);
|
impl_pin!(P0_00, 0, 0);
|
||||||
impl_pin!(P0_01, 0, 1);
|
impl_pin!(P0_01, 0, 1);
|
||||||
impl_pin!(P0_02, 0, 2);
|
impl_pin!(P0_02, 0, 2);
|
||||||
|
@ -15,6 +15,7 @@ embassy_hal_internal::peripherals! {
|
|||||||
|
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
|
#[cfg(not(feature="time-driver-rtc1"))]
|
||||||
RTC1,
|
RTC1,
|
||||||
|
|
||||||
// WDT
|
// WDT
|
||||||
@ -164,6 +165,10 @@ impl_timer!(TIMER1, TIMER1, TIMER1);
|
|||||||
impl_timer!(TIMER2, TIMER2, TIMER2);
|
impl_timer!(TIMER2, TIMER2, TIMER2);
|
||||||
impl_timer!(TIMER3, TIMER3, TIMER3, extended);
|
impl_timer!(TIMER3, TIMER3, TIMER3, extended);
|
||||||
|
|
||||||
|
impl_rtc!(RTC0, RTC0, RTC0);
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
|
impl_rtc!(RTC1, RTC1, RTC1);
|
||||||
|
|
||||||
impl_qdec!(QDEC, QDEC, QDEC);
|
impl_qdec!(QDEC, QDEC, QDEC);
|
||||||
|
|
||||||
impl_rng!(RNG, RNG, RNG);
|
impl_rng!(RNG, RNG, RNG);
|
||||||
|
@ -16,6 +16,7 @@ pub const APPROTECT_MIN_BUILD_CODE: u8 = b'G';
|
|||||||
embassy_hal_internal::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
|
#[cfg(not(feature="time-driver-rtc1"))]
|
||||||
RTC1,
|
RTC1,
|
||||||
RTC2,
|
RTC2,
|
||||||
|
|
||||||
@ -182,6 +183,11 @@ impl_twim!(TWISPI1, TWIM1, TWISPI1);
|
|||||||
impl_twis!(TWISPI0, TWIS0, TWISPI0);
|
impl_twis!(TWISPI0, TWIS0, TWISPI0);
|
||||||
impl_twis!(TWISPI1, TWIS1, TWISPI1);
|
impl_twis!(TWISPI1, TWIS1, TWISPI1);
|
||||||
|
|
||||||
|
impl_rtc!(RTC0, RTC0, RTC0);
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
|
impl_rtc!(RTC1, RTC1, RTC1);
|
||||||
|
impl_rtc!(RTC2, RTC2, RTC2);
|
||||||
|
|
||||||
impl_pwm!(PWM0, PWM0, PWM0);
|
impl_pwm!(PWM0, PWM0, PWM0);
|
||||||
impl_pwm!(PWM1, PWM1, PWM1);
|
impl_pwm!(PWM1, PWM1, PWM1);
|
||||||
impl_pwm!(PWM2, PWM2, PWM2);
|
impl_pwm!(PWM2, PWM2, PWM2);
|
||||||
|
@ -15,6 +15,7 @@ embassy_hal_internal::peripherals! {
|
|||||||
|
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
RTC1,
|
RTC1,
|
||||||
RTC2,
|
RTC2,
|
||||||
|
|
||||||
@ -223,6 +224,11 @@ impl_timer!(TIMER2, TIMER2, TIMER2);
|
|||||||
impl_timer!(TIMER3, TIMER3, TIMER3, extended);
|
impl_timer!(TIMER3, TIMER3, TIMER3, extended);
|
||||||
impl_timer!(TIMER4, TIMER4, TIMER4, extended);
|
impl_timer!(TIMER4, TIMER4, TIMER4, extended);
|
||||||
|
|
||||||
|
impl_rtc!(RTC0, RTC0, RTC0);
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
|
impl_rtc!(RTC1, RTC1, RTC1);
|
||||||
|
impl_rtc!(RTC2, RTC2, RTC2);
|
||||||
|
|
||||||
impl_pin!(P0_00, 0, 0);
|
impl_pin!(P0_00, 0, 0);
|
||||||
impl_pin!(P0_01, 0, 1);
|
impl_pin!(P0_01, 0, 1);
|
||||||
impl_pin!(P0_02, 0, 2);
|
impl_pin!(P0_02, 0, 2);
|
||||||
|
@ -15,6 +15,7 @@ embassy_hal_internal::peripherals! {
|
|||||||
|
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
RTC1,
|
RTC1,
|
||||||
RTC2,
|
RTC2,
|
||||||
|
|
||||||
@ -220,6 +221,11 @@ impl_timer!(TIMER2, TIMER2, TIMER2);
|
|||||||
impl_timer!(TIMER3, TIMER3, TIMER3, extended);
|
impl_timer!(TIMER3, TIMER3, TIMER3, extended);
|
||||||
impl_timer!(TIMER4, TIMER4, TIMER4, extended);
|
impl_timer!(TIMER4, TIMER4, TIMER4, extended);
|
||||||
|
|
||||||
|
impl_rtc!(RTC0, RTC0, RTC0);
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
|
impl_rtc!(RTC1, RTC1, RTC1);
|
||||||
|
impl_rtc!(RTC2, RTC2, RTC2);
|
||||||
|
|
||||||
impl_qspi!(QSPI, QSPI, QSPI);
|
impl_qspi!(QSPI, QSPI, QSPI);
|
||||||
|
|
||||||
impl_pdm!(PDM, PDM, PDM);
|
impl_pdm!(PDM, PDM, PDM);
|
||||||
|
@ -168,6 +168,7 @@ embassy_hal_internal::peripherals! {
|
|||||||
|
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
RTC1,
|
RTC1,
|
||||||
|
|
||||||
// WDT
|
// WDT
|
||||||
@ -369,6 +370,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
|
|||||||
impl_timer!(TIMER1, TIMER1, TIMER1);
|
impl_timer!(TIMER1, TIMER1, TIMER1);
|
||||||
impl_timer!(TIMER2, TIMER2, TIMER2);
|
impl_timer!(TIMER2, TIMER2, TIMER2);
|
||||||
|
|
||||||
|
impl_rtc!(RTC0, RTC0, RTC0);
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
|
impl_rtc!(RTC1, RTC1, RTC1);
|
||||||
|
|
||||||
impl_qspi!(QSPI, QSPI, QSPI);
|
impl_qspi!(QSPI, QSPI, QSPI);
|
||||||
|
|
||||||
impl_pdm!(PDM0, PDM0, PDM0);
|
impl_pdm!(PDM0, PDM0, PDM0);
|
||||||
|
@ -59,6 +59,7 @@ pub const FLASH_SIZE: usize = 256 * 1024;
|
|||||||
embassy_hal_internal::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
RTC1,
|
RTC1,
|
||||||
|
|
||||||
// WDT
|
// WDT
|
||||||
@ -218,6 +219,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
|
|||||||
impl_timer!(TIMER1, TIMER1, TIMER1);
|
impl_timer!(TIMER1, TIMER1, TIMER1);
|
||||||
impl_timer!(TIMER2, TIMER2, TIMER2);
|
impl_timer!(TIMER2, TIMER2, TIMER2);
|
||||||
|
|
||||||
|
impl_rtc!(RTC0, RTC0, RTC0);
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
|
impl_rtc!(RTC1, RTC1, RTC1);
|
||||||
|
|
||||||
impl_rng!(RNG, RNG, RNG);
|
impl_rng!(RNG, RNG, RNG);
|
||||||
|
|
||||||
impl_pin!(P0_00, 0, 0);
|
impl_pin!(P0_00, 0, 0);
|
||||||
|
@ -131,6 +131,7 @@ pub const FLASH_SIZE: usize = 1024 * 1024;
|
|||||||
embassy_hal_internal::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
RTC1,
|
RTC1,
|
||||||
|
|
||||||
// WDT
|
// WDT
|
||||||
@ -276,6 +277,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
|
|||||||
impl_timer!(TIMER1, TIMER1, TIMER1);
|
impl_timer!(TIMER1, TIMER1, TIMER1);
|
||||||
impl_timer!(TIMER2, TIMER2, TIMER2);
|
impl_timer!(TIMER2, TIMER2, TIMER2);
|
||||||
|
|
||||||
|
impl_rtc!(RTC0, RTC0, RTC0);
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
|
impl_rtc!(RTC1, RTC1, RTC1);
|
||||||
|
|
||||||
impl_pin!(P0_00, 0, 0);
|
impl_pin!(P0_00, 0, 0);
|
||||||
impl_pin!(P0_01, 0, 1);
|
impl_pin!(P0_01, 0, 1);
|
||||||
impl_pin!(P0_02, 0, 2);
|
impl_pin!(P0_02, 0, 2);
|
||||||
|
@ -131,6 +131,7 @@ pub const FLASH_SIZE: usize = 1024 * 1024;
|
|||||||
embassy_hal_internal::peripherals! {
|
embassy_hal_internal::peripherals! {
|
||||||
// RTC
|
// RTC
|
||||||
RTC0,
|
RTC0,
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
RTC1,
|
RTC1,
|
||||||
|
|
||||||
// WDT
|
// WDT
|
||||||
@ -276,6 +277,10 @@ impl_timer!(TIMER0, TIMER0, TIMER0);
|
|||||||
impl_timer!(TIMER1, TIMER1, TIMER1);
|
impl_timer!(TIMER1, TIMER1, TIMER1);
|
||||||
impl_timer!(TIMER2, TIMER2, TIMER2);
|
impl_timer!(TIMER2, TIMER2, TIMER2);
|
||||||
|
|
||||||
|
impl_rtc!(RTC0, RTC0, RTC0);
|
||||||
|
#[cfg(not(feature = "time-driver-rtc1"))]
|
||||||
|
impl_rtc!(RTC1, RTC1, RTC1);
|
||||||
|
|
||||||
impl_pin!(P0_00, 0, 0);
|
impl_pin!(P0_00, 0, 0);
|
||||||
impl_pin!(P0_01, 0, 1);
|
impl_pin!(P0_01, 0, 1);
|
||||||
impl_pin!(P0_02, 0, 2);
|
impl_pin!(P0_02, 0, 2);
|
||||||
|
@ -155,6 +155,8 @@ pub mod reset;
|
|||||||
#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))]
|
#[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))]
|
||||||
pub mod rng;
|
pub mod rng;
|
||||||
#[cfg(not(feature = "_nrf54l"))] // TODO
|
#[cfg(not(feature = "_nrf54l"))] // TODO
|
||||||
|
pub mod rtc;
|
||||||
|
#[cfg(not(feature = "_nrf54l"))] // TODO
|
||||||
#[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))]
|
#[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))]
|
||||||
pub mod saadc;
|
pub mod saadc;
|
||||||
#[cfg(not(feature = "_nrf54l"))] // TODO
|
#[cfg(not(feature = "_nrf54l"))] // TODO
|
||||||
|
265
embassy-nrf/src/rtc.rs
Normal file
265
embassy-nrf/src/rtc.rs
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
//! Low-level RTC driver.
|
||||||
|
|
||||||
|
#![macro_use]
|
||||||
|
|
||||||
|
use embassy_hal_internal::{Peri, PeripheralType};
|
||||||
|
|
||||||
|
use crate::chip::interrupt::typelevel::Interrupt as _;
|
||||||
|
use crate::pac;
|
||||||
|
|
||||||
|
/// Prescaler has an invalid value which exceeds 12 bits.
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct PrescalerOutOfRangeError(u32);
|
||||||
|
|
||||||
|
/// Compare value has an invalid value which exceeds 24 bits.
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct CompareOutOfRangeError(u32);
|
||||||
|
|
||||||
|
/// Interrupts/Events that can be generated by the RTCn peripheral.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum Interrupt {
|
||||||
|
/// Tick interrupt.
|
||||||
|
Tick,
|
||||||
|
/// Overflow interrupt.
|
||||||
|
Overflow,
|
||||||
|
/// Compare 0 interrupt.
|
||||||
|
Compare0,
|
||||||
|
/// Compare 1 interrupt.
|
||||||
|
Compare1,
|
||||||
|
/// Compare 2 interrupt.
|
||||||
|
Compare2,
|
||||||
|
/// Compare 3 interrupt. Only implemented for RTC1 and RTC2.
|
||||||
|
Compare3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare registers available on the RTCn.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum CompareChannel {
|
||||||
|
/// Channel 0
|
||||||
|
_0,
|
||||||
|
/// Channel 1
|
||||||
|
_1,
|
||||||
|
/// Channel 2
|
||||||
|
_2,
|
||||||
|
/// Channel 3. Only implemented for RTC1 and RTC2.
|
||||||
|
_3,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait SealedInstance {
|
||||||
|
fn regs() -> pac::rtc::Rtc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Basic RTC instance.
|
||||||
|
#[allow(private_bounds)]
|
||||||
|
pub trait Instance: SealedInstance + PeripheralType + 'static + Send {
|
||||||
|
/// Interrupt for this peripheral.
|
||||||
|
type Interrupt: crate::interrupt::typelevel::Interrupt;
|
||||||
|
|
||||||
|
/// Unsafely create a peripheral instance.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Potentially allows to create multiple instances of the driver for the same peripheral
|
||||||
|
/// which can lead to undefined behavior.
|
||||||
|
unsafe fn steal() -> Peri<'static, Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_rtc {
|
||||||
|
($type:ident, $pac_type:ident, $irq:ident) => {
|
||||||
|
impl crate::rtc::SealedInstance for peripherals::$type {
|
||||||
|
#[inline]
|
||||||
|
fn regs() -> pac::rtc::Rtc {
|
||||||
|
unsafe { pac::rtc::Rtc::from_ptr(pac::$pac_type.as_ptr()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl crate::rtc::Instance for peripherals::$type {
|
||||||
|
type Interrupt = crate::interrupt::typelevel::$irq;
|
||||||
|
|
||||||
|
unsafe fn steal() -> embassy_hal_internal::Peri<'static, Self> {
|
||||||
|
unsafe { peripherals::$type::steal() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// nRF RTC driver.
|
||||||
|
pub struct Rtc<'d, T: Instance>(Peri<'d, T>);
|
||||||
|
|
||||||
|
impl<'d, T: Instance> Rtc<'d, T> {
|
||||||
|
/// Create a new `Rtc` driver.
|
||||||
|
///
|
||||||
|
/// fRTC \[Hz\] = 32_768 / (`prescaler` + 1 )
|
||||||
|
pub fn new(rtc: Peri<'d, T>, prescaler: u32) -> Result<Self, PrescalerOutOfRangeError> {
|
||||||
|
if prescaler >= (1 << 12) {
|
||||||
|
return Err(PrescalerOutOfRangeError(prescaler));
|
||||||
|
}
|
||||||
|
|
||||||
|
T::regs().prescaler().write(|w| w.set_prescaler(prescaler as u16));
|
||||||
|
Ok(Self(rtc))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `Rtc` driver, configuring it to run at the given frequency.
|
||||||
|
pub fn new_for_freq(rtc: Peri<'d, T>, freq_hz: u32) -> Result<Self, PrescalerOutOfRangeError> {
|
||||||
|
let prescaler = (32_768 / freq_hz).saturating_sub(1);
|
||||||
|
Self::new(rtc, prescaler)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Steal the RTC peripheral, without checking if it's already taken.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Potentially allows to create multiple instances of the driver for the same peripheral
|
||||||
|
/// which can lead to undefined behavior.
|
||||||
|
pub unsafe fn steal() -> Self {
|
||||||
|
Self(unsafe { T::steal() })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Direct access to the RTC registers.
|
||||||
|
#[cfg(feature = "unstable-pac")]
|
||||||
|
#[inline]
|
||||||
|
pub fn regs(&mut self) -> pac::rtc::Rtc {
|
||||||
|
T::regs()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable the RTC.
|
||||||
|
#[inline]
|
||||||
|
pub fn enable(&mut self) {
|
||||||
|
T::regs().tasks_start().write_value(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable the RTC.
|
||||||
|
#[inline]
|
||||||
|
pub fn disable(&mut self) {
|
||||||
|
T::regs().tasks_stop().write_value(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables interrupts for the given [Interrupt] source.
|
||||||
|
///
|
||||||
|
/// Optionally also enables the interrupt in the NVIC.
|
||||||
|
pub fn enable_interrupt(&mut self, int: Interrupt, enable_in_nvic: bool) {
|
||||||
|
let regs = T::regs();
|
||||||
|
match int {
|
||||||
|
Interrupt::Tick => regs.intenset().write(|w| w.set_tick(true)),
|
||||||
|
Interrupt::Overflow => regs.intenset().write(|w| w.set_ovrflw(true)),
|
||||||
|
Interrupt::Compare0 => regs.intenset().write(|w| w.set_compare(0, true)),
|
||||||
|
Interrupt::Compare1 => regs.intenset().write(|w| w.set_compare(1, true)),
|
||||||
|
Interrupt::Compare2 => regs.intenset().write(|w| w.set_compare(2, true)),
|
||||||
|
Interrupt::Compare3 => regs.intenset().write(|w| w.set_compare(3, true)),
|
||||||
|
}
|
||||||
|
if enable_in_nvic {
|
||||||
|
unsafe { T::Interrupt::enable() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disables interrupts for the given [Interrupt] source.
|
||||||
|
///
|
||||||
|
/// Optionally also disables the interrupt in the NVIC.
|
||||||
|
pub fn disable_interrupt(&mut self, int: Interrupt, disable_in_nvic: bool) {
|
||||||
|
let regs = T::regs();
|
||||||
|
match int {
|
||||||
|
Interrupt::Tick => regs.intenclr().write(|w| w.set_tick(true)),
|
||||||
|
Interrupt::Overflow => regs.intenclr().write(|w| w.set_ovrflw(true)),
|
||||||
|
Interrupt::Compare0 => regs.intenclr().write(|w| w.set_compare(0, true)),
|
||||||
|
Interrupt::Compare1 => regs.intenclr().write(|w| w.set_compare(1, true)),
|
||||||
|
Interrupt::Compare2 => regs.intenclr().write(|w| w.set_compare(2, true)),
|
||||||
|
Interrupt::Compare3 => regs.intenclr().write(|w| w.set_compare(3, true)),
|
||||||
|
}
|
||||||
|
if disable_in_nvic {
|
||||||
|
T::Interrupt::disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable the generation of a hardware event from a given stimulus.
|
||||||
|
pub fn enable_event(&mut self, evt: Interrupt) {
|
||||||
|
let regs = T::regs();
|
||||||
|
match evt {
|
||||||
|
Interrupt::Tick => regs.evtenset().write(|w| w.set_tick(true)),
|
||||||
|
Interrupt::Overflow => regs.evtenset().write(|w| w.set_ovrflw(true)),
|
||||||
|
Interrupt::Compare0 => regs.evtenset().write(|w| w.set_compare(0, true)),
|
||||||
|
Interrupt::Compare1 => regs.evtenset().write(|w| w.set_compare(1, true)),
|
||||||
|
Interrupt::Compare2 => regs.evtenset().write(|w| w.set_compare(2, true)),
|
||||||
|
Interrupt::Compare3 => regs.evtenset().write(|w| w.set_compare(3, true)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disable the generation of a hardware event from a given stimulus.
|
||||||
|
pub fn disable_event(&mut self, evt: Interrupt) {
|
||||||
|
let regs = T::regs();
|
||||||
|
match evt {
|
||||||
|
Interrupt::Tick => regs.evtenclr().write(|w| w.set_tick(true)),
|
||||||
|
Interrupt::Overflow => regs.evtenclr().write(|w| w.set_ovrflw(true)),
|
||||||
|
Interrupt::Compare0 => regs.evtenclr().write(|w| w.set_compare(0, true)),
|
||||||
|
Interrupt::Compare1 => regs.evtenclr().write(|w| w.set_compare(1, true)),
|
||||||
|
Interrupt::Compare2 => regs.evtenclr().write(|w| w.set_compare(2, true)),
|
||||||
|
Interrupt::Compare3 => regs.evtenclr().write(|w| w.set_compare(3, true)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resets the given event.
|
||||||
|
pub fn reset_event(&mut self, evt: Interrupt) {
|
||||||
|
let regs = T::regs();
|
||||||
|
match evt {
|
||||||
|
Interrupt::Tick => regs.events_tick().write_value(0),
|
||||||
|
Interrupt::Overflow => regs.events_ovrflw().write_value(0),
|
||||||
|
Interrupt::Compare0 => regs.events_compare(0).write_value(0),
|
||||||
|
Interrupt::Compare1 => regs.events_compare(1).write_value(0),
|
||||||
|
Interrupt::Compare2 => regs.events_compare(2).write_value(0),
|
||||||
|
Interrupt::Compare3 => regs.events_compare(3).write_value(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the given event has been triggered.
|
||||||
|
pub fn is_event_triggered(&self, evt: Interrupt) -> bool {
|
||||||
|
let regs = T::regs();
|
||||||
|
let val = match evt {
|
||||||
|
Interrupt::Tick => regs.events_tick().read(),
|
||||||
|
Interrupt::Overflow => regs.events_ovrflw().read(),
|
||||||
|
Interrupt::Compare0 => regs.events_compare(0).read(),
|
||||||
|
Interrupt::Compare1 => regs.events_compare(1).read(),
|
||||||
|
Interrupt::Compare2 => regs.events_compare(2).read(),
|
||||||
|
Interrupt::Compare3 => regs.events_compare(3).read(),
|
||||||
|
};
|
||||||
|
val == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the compare value of a given register. The compare registers have a width
|
||||||
|
/// of 24 bits.
|
||||||
|
pub fn set_compare(&mut self, reg: CompareChannel, val: u32) -> Result<(), CompareOutOfRangeError> {
|
||||||
|
if val >= (1 << 24) {
|
||||||
|
return Err(CompareOutOfRangeError(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
let reg = match reg {
|
||||||
|
CompareChannel::_0 => 0,
|
||||||
|
CompareChannel::_1 => 1,
|
||||||
|
CompareChannel::_2 => 2,
|
||||||
|
CompareChannel::_3 => 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
T::regs().cc(reg).write(|w| w.set_compare(val));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the Real Time Counter.
|
||||||
|
#[inline]
|
||||||
|
pub fn clear(&self) {
|
||||||
|
T::regs().tasks_clear().write_value(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Obtain the current value of the Real Time Counter, 24 bits of range.
|
||||||
|
#[inline]
|
||||||
|
pub fn read(&self) -> u32 {
|
||||||
|
T::regs().counter().read().counter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Relase the RTC, returning the underlying peripheral instance.
|
||||||
|
#[inline]
|
||||||
|
pub fn release(self) -> Peri<'d, T> {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
@ -35,6 +35,7 @@ embedded-hal-async = { version = "1.0" }
|
|||||||
embedded-hal-bus = { version = "0.1", features = ["async"] }
|
embedded-hal-bus = { version = "0.1", features = ["async"] }
|
||||||
num-integer = { version = "0.1.45", default-features = false }
|
num-integer = { version = "0.1.45", default-features = false }
|
||||||
microfft = "0.5.0"
|
microfft = "0.5.0"
|
||||||
|
portable-atomic = "1"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = 2
|
debug = 2
|
||||||
|
57
examples/nrf52840/src/bin/rtc.rs
Normal file
57
examples/nrf52840/src/bin/rtc.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use core::cell::RefCell;
|
||||||
|
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_nrf::gpio::{Level, Output, OutputDrive};
|
||||||
|
use embassy_nrf::interrupt;
|
||||||
|
use embassy_nrf::rtc::Rtc;
|
||||||
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||||
|
use embassy_sync::blocking_mutex::Mutex;
|
||||||
|
use portable_atomic::AtomicU64;
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
// 64 bit counter which will never overflow.
|
||||||
|
static TICK_COUNTER: AtomicU64 = AtomicU64::new(0);
|
||||||
|
static RTC: Mutex<CriticalSectionRawMutex, RefCell<Option<Rtc<'static, embassy_nrf::peripherals::RTC0>>>> =
|
||||||
|
Mutex::new(RefCell::new(None));
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
defmt::println!("nRF52840 RTC example");
|
||||||
|
let p = embassy_nrf::init(Default::default());
|
||||||
|
let mut led = Output::new(p.P0_13, Level::High, OutputDrive::Standard);
|
||||||
|
// Counter resolution is 125 ms.
|
||||||
|
let mut rtc = Rtc::new(p.RTC0, (1 << 12) - 1).unwrap();
|
||||||
|
rtc.enable_interrupt(embassy_nrf::rtc::Interrupt::Tick, true);
|
||||||
|
rtc.enable_event(embassy_nrf::rtc::Interrupt::Tick);
|
||||||
|
rtc.enable();
|
||||||
|
RTC.lock(|r| {
|
||||||
|
let mut rtc_borrow = r.borrow_mut();
|
||||||
|
*rtc_borrow = Some(rtc);
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut last_counter_val = 0;
|
||||||
|
loop {
|
||||||
|
let current = TICK_COUNTER.load(core::sync::atomic::Ordering::Relaxed);
|
||||||
|
if current != last_counter_val {
|
||||||
|
led.toggle();
|
||||||
|
last_counter_val = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn RTC0() {
|
||||||
|
// For 64-bit, we do not need to worry about overflowing, at least not for realistic program
|
||||||
|
// lifetimes.
|
||||||
|
TICK_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
|
||||||
|
RTC.lock(|r| {
|
||||||
|
let mut rtc_borrow = r.borrow_mut();
|
||||||
|
rtc_borrow
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.reset_event(embassy_nrf::rtc::Interrupt::Tick);
|
||||||
|
});
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user