#include <stdbool.h>
#include <stdint.h>
#include "nrf.h"
#include "nrf_clock.h"
#include "nrf_power.h"
#include "nrfx_rtc.h"
#include "app_error.h"
#define WAKEUP_SECONDS 10
#define TICKS 8
// Configuration
const nrfx_rtc_t rtc_inst = NRFX_RTC_INSTANCE(2);
/** @brief Handler for RTC interrupts */
void rtc_handler(nrfx_rtc_int_type_t int_type)
{
if (int_type == NRFX_RTC_INT_COMPARE0)
{
// 1. Get the current counter value
uint32_t current_ticks = nrfx_rtc_counter_get(&rtc_inst);
// 2. Set the next wakeup relative to "now"
uint32_t next_wakeup = current_ticks + (WAKEUP_SECONDS * TICKS);
// 3. Handle the 24-bit mask (RTC counter is 24-bit)
next_wakeup &= RTC_COUNTER_COUNTER_Msk;
nrfx_rtc_cc_set(&rtc_inst, 0, next_wakeup, true);
}
}
/** @brief Start LFRC (Internal RC) for ION_RAMOFF_RTC 1.50 uA profile */
static void lfclk_config(void)
{
// Use direct register access to avoid "nrf_clock_lf_src_t" type errors
// 0 = RC Oscillator
NRF_CLOCK->LFCLKSRC = (CLOCK_LFCLKSRC_SRC_RC << CLOCK_LFCLKSRC_SRC_Pos);
NRF_CLOCK->TASKS_LFCLKSTART = 1;
while (NRF_CLOCK->EVENTS_LFCLKSTARTED == 0);
NRF_CLOCK->EVENTS_LFCLKSTARTED = 0;
}
/** @brief Configure RTC2 */
static void rtc_config(void)
{
nrfx_rtc_config_t config = NRFX_RTC_DEFAULT_CONFIG;
config.prescaler = 4095; // 8 Hz
uint32_t err_code = nrfx_rtc_init(&rtc_inst, &config, rtc_handler);
APP_ERROR_CHECK(err_code);
nrfx_rtc_cc_set(&rtc_inst, 0,WAKEUP_SECONDS*TICKS, true);
nrfx_rtc_enable(&rtc_inst);
}
/** @brief Disable RAM retention to hit the 1.50 uA target */
static void disable_ram_retention(void)
{
// On nRF52840, there are 9 RAM sections.
// We disable sections 2-8 to preserve the stack (usually in section 0 or 1).
// Writing 0xFFFFFFFF to POWERCLR turns off Power and Retention for that block.
for (int i = 1; i <= 6; i++)
{
NRF_POWER->RAM[i].POWERCLR = /*0xFFFFFFFF*/1;
}
}
void power_off_all_unused_peripherals(void)
{
// 1. Reset all GPIO pins to 'Input Disconnected' (The default reset state)
// nRF52840 has 32 pins on Port 0 and 16 pins on Port 1.
for (uint32_t i = 0; i < 32; i++) {
NRF_P0->PIN_CNF[i] = (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos);
}
for (uint32_t i = 0; i < 16; i++) {
NRF_P1->PIN_CNF[i] = (GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos);
}
// 2. Disable high-power EasyDMA peripherals
NRF_UARTE0->ENABLE = 0; NRF_UARTE1->ENABLE = 0;
NRF_SPIM0->ENABLE = 0; NRF_SPIM1->ENABLE = 0;
NRF_SPIM2->ENABLE = 0; NRF_SPIM3->ENABLE = 0;
NRF_TWIM0->ENABLE = 0; NRF_TWIM1->ENABLE = 0;
NRF_QSPI->ENABLE = 0;
// 3. Power cycle DMA blocks (Workaround for base current leakage)
*(volatile uint32_t *)0x40002FFC = 0; // UARTE0
*(volatile uint32_t *)0x40003FFC = 0; // SPIM0/TWIM0
*(volatile uint32_t *)0x40004FFC = 0; // SPIM1/TWIM1
*(volatile uint32_t *)0x40023FFC = 0; // SPIM2
*(volatile uint32_t *)0x40028FFC = 0; // UARTE1
*(volatile uint32_t *)0x40029FFC = 0; // QSPI
*(volatile uint32_t *)0x4002FFFC = 0; // SPIM3
// 4. Disable Radio and Analog blocks
NRF_SAADC->ENABLE = 0;
NRF_RADIO->TASKS_DISABLE = 1;
NRF_NFCT->TASKS_DISABLE = 1;
NRF_COMP->ENABLE = 0;
NRF_LPCOMP->ENABLE = 0;
// 5. Disable PWM, PDM, and I2S
NRF_PWM0->ENABLE = 0; NRF_PWM1->ENABLE = 0;
NRF_PWM2->ENABLE = 0; NRF_PWM3->ENABLE = 0;
NRF_PDM->ENABLE = 0; NRF_I2S->ENABLE = 0;
// 6. Stop unused Timers/RTCs (Keep RTC2 active!)
NRF_TIMER0->TASKS_STOP = 1; NRF_TIMER1->TASKS_STOP = 1;
NRF_TIMER2->TASKS_STOP = 1; NRF_TIMER3->TASKS_STOP = 1;
NRF_TIMER4->TASKS_STOP = 1;
NRF_RTC0->TASKS_STOP = 1; NRF_RTC1->TASKS_STOP = 1;
// 7. Kill PPI and GPIOTE
NRF_PPI->CHEN = 0;
for (int i = 0; i < 8; i++) { NRF_GPIOTE->CONFIG[i] = 0; }
NRF_CLOCK->TASKS_HFCLKSTOP = 1;
}
int main(void)
{
// 1. Initialize Clock
// log_init();
power_off_all_unused_peripherals();
lfclk_config();
// 2. Initialize RTC
rtc_config();
// 3. Drop RAM power (RAMOFF state)
disable_ram_retention();
// 4. Enter System ON sleep
while (true)
{
__WFE();
__SEV();
__WFE();
}
}