Deep standby mode

GR-PEACHでディープスタンバイする際のメモ。
復帰条件はRTCのアラーム割り込み。5秒間隔でディープスタンバイから復帰する。
NV_DATA1は保持RAM領域。保持RAMを使用する際はリンクファイル(GCCの場合は後述のRZA1H.ld)も変更する必要がある。

main.cpp

#include "mbed.h"
#include "rza_io_regrw.h"
#include "rtc_api.h"

// On-Chip Data Retention RAM
static int wake_up_cnt __attribute((section("NV_DATA1")));

void Deep_standby_mode() {
    volatile uint32_t dummy_32;
    volatile uint16_t dummy_16;
    volatile uint8_t dummy_8;

    // 1. Set the standby_mode_en bit of the power control register in the PL310 to 1.
    L2C.REG15_POWER_CTRL = 0x00000001uL;
    dummy_32 = L2C.REG15_POWER_CTRL;

    // 2. Set the RRAMKP3 to RRAMKP0 bits in RRAMKP for the corresponding on-chip data-retention RAM area
    //    that must be retained. Transfer the programs to be retained to the specified areas of the on-chip
    //    data-retention RAM.
    CPG.RRAMKP = CPG_RRAMKP_RRAMKP1 | CPG_RRAMKP_RRAMKP2 | CPG_RRAMKP_RRAMKP3;
    dummy_8 = CPG.RRAMKP;

    // 3. Set the RAMBOOT and EBUSKEEPE bits in DSCTR to specify the activation method for returning from
    //    deep standby mode and to select whether the external memory control pin status is retained or not.
    CPG.DSCTR = 0;                 // Activation Method : External memory
    dummy_8 = CPG.DSCTR;

    // 4. When canceling deep standby mode by an interrupt, set the corresponding bit in DSSSR to select
    //    the pin or source to cancel deep standby mode. In this case, specify the input signal detection
    //    mode for the selected pin with the corresponding bit in DSESR.
    CPG.DSSSR = CPG_DSSSR_RTCAR;  // Deep standby mode is canceled by a realtime clock alarm interrupt.
    dummy_16 = CPG.DSSSR;
    CPG.DSESR = 0;
    dummy_16 = CPG.DSESR;

    // 5. Execute read and write of an arbitrary but the same address for each page in the on-chip data-
    //    retention RAM area. When this is not executed, data last written may not be written to the on-chip
    //    data-retention RAM. If there is a write to the on-chip data-retention RAM after this time, execute
    //    this processing after the last write to the on-chip dataretention RAM.
    L1C_CleanInvalidateDCacheAll();  // Clean the whole data cache.

    // On-Chip Data Retention RAM   Page 1 of bank 0
    dummy_32 = *((uint32_t *)0x20004000);  // Read
    *((uint32_t *)0x20004000) = dummy_32;  // Write

    // On-Chip Data Retention RAM   Page 2 of bank 0
    dummy_32 = *((uint32_t *)0x20008000);  // Read
    *((uint32_t *)0x20008000) = dummy_32;  // Write

    // On-Chip Data Retention RAM   Page 3 of bank 0
    dummy_32 = *((uint32_t *)0x20010000);  // Read
    *((uint32_t *)0x20010000) = dummy_32;  // Write

    L1C_CleanDCacheAll();  // Clean the whole data cache.

    // 6. Set the STBY and DEEP bits in the STBCR1 register to 1, and then read this register.
    CPG.STBCR1 = CPG_STBCR1_DEEP | CPG_STBCR1_STBY; // deep standby mode
    dummy_8 = CPG.STBCR1;

    // 7. Clear the flag in the DSFR register.
    CPG.DSFR = 0;
    dummy_16 = CPG.DSFR;

    // 8. Set the CPU interface control register (ICCICR) of the interrupt controller to 0 so that the CPU
    //    is not notified of interrupts other than NMIs. Then, read the ICCICR register.
    INTC.ICCICR = 0;
    dummy_32 = INTC.ICCICR;

    // Compiler warning measures
    (void)dummy_32;
    (void)dummy_16;
    (void)dummy_8;

    // Execute the WFI instruction
    while (1) {
        __WFI();
    }
}

static void int_alarm(void) {
    RTC.RSECAR &= ~0x80;
    RTC.RMINAR &= ~0x80;
    RTC.RHRAR  &= ~0x80;
    RTC.RDAYAR &= ~0x80;
    RTC.RMONAR &= ~0x80;
    RTC.RCR1 &= ~0x01u; // AF = 0
}

static uint8_t hex8_to_dec(uint8_t hex_val) {
    uint32_t calc_data;

    calc_data  = hex_val / 10 * 0x10;
    calc_data += hex_val % 10;

    if (calc_data > 0x99) {
        calc_data = 0;
    }

    return (uint8_t)calc_data;
}

static uint16_t hex16_to_dec(uint16_t hex_val) {
    uint32_t calc_data;
    calc_data  =   hex_val / 1000       * 0x1000;
    calc_data += ((hex_val / 100) % 10) * 0x100;
    calc_data += ((hex_val / 10)  % 10) * 0x10;
    calc_data +=   hex_val        % 10;

    if (calc_data > 0x9999) {
        calc_data = 0;
    }
    return (uint16_t)calc_data;
}

void set_alarm(time_t seconds) {
    struct tm *t = localtime(&seconds);

    RTC.RCR1 &= ~0x08u;   // AIE = 0

    GIC_DisableIRQ(ARM_IRQn);
    InterruptHandlerRegister(ARM_IRQn, &int_alarm);
    GIC_SetPriority(ARM_IRQn, 5);
    GIC_SetConfiguration(ARM_IRQn, 1);
    GIC_EnableIRQ(ARM_IRQn);

    RTC.RSECAR = 0x80 + hex8_to_dec(t->tm_sec);
    RTC.RMINAR = 0x80 + hex8_to_dec(t->tm_min);
    RTC.RHRAR  = 0x80 + hex8_to_dec(t->tm_hour);
    RTC.RDAYAR = 0x80 + hex8_to_dec(t->tm_mday);
    RTC.RMONAR = 0x80 + hex8_to_dec(t->tm_mon + 1);
    RTC.RYRAR  = hex16_to_dec(t->tm_year + 1900);

    RTC.RCR1 &= ~0x01u;   // AF = 0
    RTC.RCR1 |= 0x08u;    // AIE = 1
}

int main() {
    time_t seconds;
    uint16_t dsfr = CPG.DSFR;

    if (dsfr == 0x0000) {
        // initialization of On-Chip Data Retention RAM
        wake_up_cnt = 0;

        printf("Reset start\r\n");
        rtc_init();
        rtc_write(0);  // Set RTC time : 01/01/1970 00:00:00
    } else {
        wake_up_cnt++;
        printf("Wake up %d\r\n", wake_up_cnt);
        rtc_init();
    }

    // RTC time
    seconds = rtc_read();
    struct tm *t = localtime(&seconds);
    printf("RTC : %02d/%02d/%04d %02d:%02d:%02d\r\n",
           t->tm_mon + 1, t->tm_mday, t->tm_year + 1900, t->tm_hour, t->tm_min, t->tm_sec);

    // Set alarm interrupt
    set_alarm(seconds + 5); // 5 seconds later

    // Deep standby
    Deep_standby_mode();
}

RZA1H.ld

/* Linker script for mbed RZ_A1H */

== omit ==

MEMORY
{
  ROM   (rx)       : ORIGIN = 0x00000000,       LENGTH = 0x02000000
  BOOT_LOADER (rx) : ORIGIN = BOOT_LOADER_ADDR, LENGTH = BOOT_LOADER_SIZE 
  SFLASH (rx)      : ORIGIN = SFLASH_ADDR,      LENGTH = SFLASH_SIZE 
  L_TTB (rw)       : ORIGIN = 0x20000000,       LENGTH = 0x00004000 
  RAM_NV1 (rwx)    : ORIGIN = 0x20004000,       LENGTH = 0x00004000 /* add */
  RAM_NV2 (rwx)    : ORIGIN = 0x20008000,       LENGTH = 0x00008000 /* add */
  RAM_NV3 (rwx)    : ORIGIN = 0x20010000,       LENGTH = 0x00010000 /* add */
  RAM (rwx)        : ORIGIN = 0x20020000,       LENGTH = 0x008E0000
  RAM_NC (rwx)     : ORIGIN = 0x20900000,       LENGTH = 0x00100000
}

== omit ==

SECTIONS
{
== omit ==

    .nv_data1 (NOLOAD) :
    {
        *(NV_DATA1)
    } > RAM_NV1

    .nv_data2 (NOLOAD) :
    {
        *(NV_DATA2)
    } > RAM_NV2

    .nv_data3 (NOLOAD) :
    {
        *(NV_DATA3)
    } > RAM_NV3
}

実行するとターミナル上に以下のように出力されます。

Reset start
RTC : 01/01/1970 00:00:00
Wake up 1
RTC : 01/01/1970 00:00:05
Wake up 2
RTC : 01/01/1970 00:00:10
Wake up 3
RTC : 01/01/1970 00:00:15


Please log in to post comments.