This is a RTC additional function. This is only for Nucleo F401RE & F411RE mbed(Added L152RE, F334R8, L476RG & F746xx). If you connected battery backup circuit for internal RTC, you can make a power-off and reset condition. RTC still has proper time and date.

Dependents:   Nucleo_rtos_sample PB_Emma_Ethernet

Please refer following NOTE information.
/users/kenjiArai/notebook/nucleo-series-rtc-control-under-power-onoff-and-re/

SetRTC.cpp

Committer:
kenjiArai
Date:
2015-02-19
Revision:
6:ef7d2c83034d
Parent:
5:1a8e7aed053d
Child:
7:fa32602e23ec
Child:
9:1af4e107ca7b

File content as of revision 6:ef7d2c83034d:

/*
 * mbed Library program
 *      Check & set RTC function and set proper clock if we can set
 *      ONLY FOR "Nucleo Board"
 *
 *  Copyright (c) 2014-2015 Kenji Arai / JH1PJL
 *  http://www.page.sannet.ne.jp/kenjia/index.html
 *  http://mbed.org/users/kenjiArai/
 *      Created:  October   24th, 2014
 *      Revised:  Feburary  19th, 2015
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#if defined(TARGET_NUCLEO_F401RE) || defined(TARGET_NUCLEO_F411RE) || defined(TARGET_NUCLEO_L152RE)

//#define DEBUG         // use Communication with PC(UART)

//  Include ---------------------------------------------------------------------------------------
#include "mbed.h"
#include "SetRTC.h"

//  Definition ------------------------------------------------------------------------------------
#ifdef DEBUG
#define BAUD(x)         pcr.baud(x)
#define GETC(x)         pcr.getc(x)
#define PUTC(x)         pcr.putc(x)
#define PRINTF(...)     pcr.printf(__VA_ARGS__)
#define READABLE(x)     pcr.readable(x)
#else
#define BAUD(x)         {;}
#define GETC(x)         {;}
#define PUTC(x)         {;}
#define PRINTF(...)     {;}
#define READABLE(x)     {;}
#endif

//  Object ----------------------------------------------------------------------------------------
Serial pcr(USBTX, USBRX);

//  RAM -------------------------------------------------------------------------------------------

//  ROM / Constant data ---------------------------------------------------------------------------

//  Function prototypes ---------------------------------------------------------------------------
static int32_t set_RTC_LSI(void);
static int32_t set_RTC_LSE(void);
static int32_t rtc_external_osc_init(void);
static uint32_t read_RTC_reg(uint32_t RTC_BKP_DR);
static uint32_t check_RTC_backup_reg( void );
static int xatoi (char **str, unsigned long *res);
static void get_line (char *buff, int len);
static void set_5v_drop_detect(void);

//-------------------------------------------------------------------------------------------------
//  Control Program
//-------------------------------------------------------------------------------------------------
int32_t SetRTC()
{
    if (rtc_external_osc_init() == 1) {
        set_5v_drop_detect();
        return 1;
    } else {
        return 0;
    }
}

int32_t set_RTC_LSE(void)
{
    uint32_t timeout = 0;

    //---------------------------- LSE Configuration -------------------------
    // Enable Power Clock
    __PWR_CLK_ENABLE();
    // Enable write access to Backup domain
    PWR->CR |= PWR_CR_DBP;
    // Wait for Backup domain Write protection disable
    timeout = HAL_GetTick() + DBP_TIMEOUT_VALUE;
    PRINTF("Time-Out %d\r\n", timeout);
    while((PWR->CR & PWR_CR_DBP) == RESET) {
        if(HAL_GetTick() >= timeout) {
            PRINTF("Time-Out 1\r\n");
            return 0;
        } else {
            PRINTF("GetTick: %d\r\n",HAL_GetTick());
        }
    }
    // Reset LSEON and LSEBYP bits before configuring the LSE ----------------
    __HAL_RCC_LSE_CONFIG(RCC_LSE_OFF);
    // Get timeout
    timeout = HAL_GetTick() + TIMEOUT;
    PRINTF("Time-Out %d\r\n", timeout);
    // Wait till LSE is ready
    while(__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) != RESET) {
        if(HAL_GetTick() >= timeout) {
            PRINTF("Time-Out 2\r\n");
            return 0;
        } else {
            PRINTF("GetTick: %d\r\n",HAL_GetTick());
        }
    }
    // Set the new LSE configuration -----------------------------------------
    __HAL_RCC_LSE_CONFIG(RCC_LSE_ON);
    // Get timeout
    timeout = HAL_GetTick() + TIMEOUT;
    PRINTF("Time-Out %d\r\n", timeout);
    // Wait till LSE is ready
#if defined(TARGET_NUCLEO_F401RE) || defined(TARGET_NUCLEO_F411RE)
    while((RCC->BDCR & 0x02) != 2){
#else
    while(__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == RESET) {
#endif
//    while(__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == RESET) {
        if(HAL_GetTick() >= timeout) {
            PRINTF("Time-Out 3\r\n");
            return 0;
        } else {
            PRINTF("GetTick: %d\r\n",HAL_GetTick());
        }
    }
    PRINTF("OK\r\n");
    return 1;
}

int32_t set_RTC_LSI(void)
{
    uint32_t timeout = 0;

    // Enable Power clock
    __PWR_CLK_ENABLE();
    // Enable access to Backup domain
    HAL_PWR_EnableBkUpAccess();
    // Reset Backup domain
    __HAL_RCC_BACKUPRESET_FORCE();
    __HAL_RCC_BACKUPRESET_RELEASE();
    // Enable Power Clock
    __PWR_CLK_ENABLE();
    // Enable write access to Backup domain
    PWR->CR |= PWR_CR_DBP;
    // Wait for Backup domain Write protection disable
    timeout = HAL_GetTick() + DBP_TIMEOUT_VALUE;
    while((PWR->CR & PWR_CR_DBP) == RESET) {
        if(HAL_GetTick() >= timeout) {
            return 0;
        }
    }
    __HAL_RCC_LSE_CONFIG(RCC_LSE_OFF);
    // Enable LSI
    __HAL_RCC_LSI_ENABLE();
    timeout = HAL_GetTick() + TIMEOUT;
    // Wait till LSI is ready
    while(__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY) == RESET) {
        if(HAL_GetTick() >= timeout) {
            return 0;
        }
    }
    // Connect LSI to RTC
    __HAL_RCC_RTC_CLKPRESCALER(RCC_RTCCLKSOURCE_LSI);
    __HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSI);
    return 1;
}

int32_t rtc_external_osc_init(void)
{
    uint32_t timeout = 0;
    time_t seconds;
    uint8_t external_ok = 1;

    // Enable Power clock
    __PWR_CLK_ENABLE();
    // Enable access to Backup domain
    HAL_PWR_EnableBkUpAccess();
    // Check backup condition
    if ( check_RTC_backup_reg() ) {
#if defined(TARGET_NUCLEO_F401RE) || defined(TARGET_NUCLEO_F411RE)
        if ((RCC->BDCR & 0x8307) == 0x8103){
#else
        if ((RCC->CSR & 0x430703) == 0x410300) {
#endif
        //if (__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) != RESET) {
            timeout = HAL_GetTick() + TIMEOUT / 5;
            seconds = time(NULL);
            while (seconds == time(NULL)){
                if(HAL_GetTick() >= timeout) {
                    PRINTF("Not available External Xtal\r\n");
                    external_ok = 0;
                    break;
                }
            }
            if (external_ok){
                PRINTF("OK everything\r\n");
                return 1;
            }
        }
    }
    PRINTF("Reset RTC LSE config.\r\n");
    // Reset Backup domain
    __HAL_RCC_BACKUPRESET_FORCE();
    __HAL_RCC_BACKUPRESET_RELEASE();
    // Enable LSE Oscillator
    if (set_RTC_LSE() == 1) {
        // Connect LSE to RTC
        __HAL_RCC_RTC_CLKPRESCALER(RCC_RTCCLKSOURCE_LSE);
        __HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSE);
        PRINTF("Set LSE/External\r\n");
        return 1;
    } else {
        set_RTC_LSI();
        PRINTF("Set LSI/Internal\r\n");
        return 0;
    }
}

uint32_t read_RTC_reg(uint32_t RTC_BKP_DR)
{
    __IO uint32_t tmp = 0;

    // Check the parameters
    assert_param(IS_RTC_BKP(RTC_BKP_DR));
    tmp = RTC_BASE + 0x50;
    tmp += (RTC_BKP_DR * 4);
    // Read the specified register
    return (*(__IO uint32_t *)tmp);
}

// Check RTC Backup registers contents
uint32_t check_RTC_backup_reg( void )
{
    if ( read_RTC_reg( RTC_BKP_DR0 ) == RTC_DAT0 ) {
        if ( read_RTC_reg( RTC_BKP_DR1 ) == RTC_DAT1 ) {
            return 1;
        }
    }
    return 0;
}

void show_RTC_reg( void )
{
    // Show registers
    pcr.printf( "\r\nShow RTC registers\r\n" );
    pcr.printf( " Reg0  =0x%08x, Reg1  =0x%08x\r\n",
                read_RTC_reg( RTC_BKP_DR0 ),
                read_RTC_reg( RTC_BKP_DR1 )
              );
    pcr.printf( " TR    =0x..%06x, DR    =0x..%06x, CR    =0x..%06x\r\n",
                RTC->TR, RTC->DR, RTC->CR
              );
    pcr.printf( " ISR   =0x...%05x, PRER  =0x..%06x, WUTR  =0x....%04x\r\n",
                RTC->ISR, RTC->PRER, RTC->WUTR
              );
    pcr.printf( " CALIBR=0x....%04x, ALRMAR=0x%08x, ALRMBR=0x%08x\r\n",
                RTC->CALIBR, RTC->ALRMAR, RTC->ALRMBR
              );
    pcr.printf(
        " WPR   =0x......%02x, SSR   =0x....%04x, SHIFTR=0x....%04x\r\n",
        RTC->WPR, RTC->SSR, RTC->SHIFTR
    );
    pcr.printf(
        " TSTR  =0x..%06x, TSDR  =0x....%04x, TSSSR =0x....%04x\r\n",
        RTC->TSTR, RTC->TSDR, RTC->TSSSR
    );
    pcr.printf( "Show RCC registers (only RTC Related reg.)\r\n" );
#if defined(TARGET_NUCLEO_F401RE) || defined(TARGET_NUCLEO_F411RE)
    pcr.printf( " RCC_BDCR=0x...%05x\r\n", RCC->BDCR);
#else
    pcr.printf( " RCC_CSR =0x%08x\r\n", RCC->CSR);
#endif
    pcr.printf( "\r\n");
}

//  Change string -> integer
int xatoi (char **str, unsigned long *res)
{
    unsigned long val;
    unsigned char c, radix, s = 0;

    while ((c = **str) == ' ') (*str)++;
    if (c == '-') {
        s = 1;
        c = *(++(*str));
    }
    if (c == '0') {
        c = *(++(*str));
        if (c <= ' ') {
            *res = 0;
            return 1;
        }
        if (c == 'x') {
            radix = 16;
            c = *(++(*str));
        } else {
            if (c == 'b') {
                radix = 2;
                c = *(++(*str));
            } else {
                if ((c >= '0')&&(c <= '9')) {
                    radix = 8;
                }   else {
                    return 0;
                }
            }
        }
    } else {
        if ((c < '1')||(c > '9')) {
            return 0;
        }
        radix = 10;
    }
    val = 0;
    while (c > ' ') {
        if (c >= 'a') c -= 0x20;
        c -= '0';
        if (c >= 17) {
            c -= 7;
            if (c <= 9) return 0;
        }
        if (c >= radix) return 0;
        val = val * radix + c;
        c = *(++(*str));
    }
    if (s) val = -val;
    *res = val;
    return 1;
}

//  Get key input data
void get_line (char *buff, int len)
{
    char c;
    int idx = 0;

    for (;;) {
        c = pcr.getc();
        if (c == '\r') {
            buff[idx++] = c;
            break;
        }
        if ((c == '\b') && idx) {
            idx--;
            pcr.putc(c);
            pcr.putc(' ');
            pcr.putc(c);
        }
        if (((uint8_t)c >= ' ') && (idx < len - 1)) {
            buff[idx++] = c;
            pcr.putc(c);
        }
    }
    buff[idx] = 0;
    pcr.putc('\n');
}

// RTC related subroutines
void chk_and_set_time(char *ptr)
{
    unsigned long p1;
    struct tm t;
    time_t seconds;

    if (xatoi(&ptr, &p1)) {
        t.tm_year       = (uint8_t)p1 + 100;
        PRINTF("Year:%d ",p1);
        xatoi( &ptr, &p1 );
        t.tm_mon        = (uint8_t)p1 - 1;
        PRINTF("Month:%d ",p1);
        xatoi( &ptr, &p1 );
        t.tm_mday       = (uint8_t)p1;
        PRINTF("Day:%d ",p1);
        xatoi( &ptr, &p1 );
        t.tm_hour       = (uint8_t)p1;
        PRINTF("Hour:%d ",p1);
        xatoi( &ptr, &p1 );
        t.tm_min        = (uint8_t)p1;
        PRINTF("Min:%d ",p1);
        xatoi( &ptr, &p1 );
        t.tm_sec        = (uint8_t)p1;
        PRINTF("Sec: %d \r\n",p1);
    } else {
        return;
    }
    seconds = mktime(&t);
    set_time(seconds);
    // Show Time with several example
    // ex.1
    pcr.printf("Date: %04d/%02d/%02d, %02d:%02d:%02d\r\n",
               t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
#if 0
    time_t seconds;
    char buf[40];

    seconds = mktime(&t);
    // ex.2
    strftime(buf, 40, "%x %X", localtime(&seconds));
    pcr.printf("Date: %s\r\n", buf);
    // ex.3
    strftime(buf, 40, "%I:%M:%S %p (%Y/%m/%d)", localtime(&seconds));
    pcr.printf("Date: %s\r\n", buf);
    // ex.4
    strftime(buf, 40, "%B %d,'%y, %H:%M:%S", localtime(&seconds));
    pcr.printf("Date: %s\r\n", buf);
#endif
}

void time_enter_mode(void)
{
    char *ptr;
    char linebuf[64];

    pcr.printf("\r\nSet time into RTC\r\n");
    pcr.printf(" e.g. >15 2 7 10 11 12 -> Feb. 7th, '15, 10:11:12\r\n");
    pcr.printf(" If time is fine, just hit enter\r\n");
    pcr.putc('>');
    ptr = linebuf;
    get_line(ptr, sizeof(linebuf));
    pcr.printf("\r");
    chk_and_set_time(ptr);
}

#if defined(TARGET_NUCLEO_L152RE)
void goto_standby(void)
{
    RCC->AHBENR |= (RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN |
                    RCC_AHBENR_GPIODEN | RCC_AHBENR_GPIOHEN);
#if 0
    GPIO_InitTypeDef GPIO_InitStruct;
    // All other ports are analog input mode
    GPIO_InitStruct.Pin = GPIO_PIN_All;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
    HAL_GPIO_Init(GPIOH, &GPIO_InitStruct);
#else
    GPIOA->MODER = 0xffffffff;
    GPIOB->MODER = 0xffffffff;
    GPIOC->MODER = 0xffffffff;
    GPIOD->MODER = 0xffffffff;
    GPIOE->MODER = 0xffffffff;
    GPIOH->MODER = 0xffffffff;
#endif
    RCC->AHBENR &= ~(RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN |RCC_AHBENR_GPIOCEN |
                     RCC_AHBENR_GPIODEN | RCC_AHBENR_GPIOHEN);
    while(1) {
#if 0
        // Stop mode
        HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
#else
        // Standby mode
        HAL_PWR_EnterSTANDBYMode();
#endif
    }
}
#else
void goto_standby(void)
{
    deepsleep();   // Not Standby Mode but Deep Sleep Mode
}
#endif

#if defined(TARGET_NUCLEO_L152RE)
#if defined(USE_IRQ_FOR_RTC_BKUP)
// COMP2 Interrupt routine
void irq_comp2_handler(void)
{
    __disable_irq();
    goto_standby();
}

COMP_HandleTypeDef COMP_HandleStruct;
GPIO_InitTypeDef GPIO_InitStruct;

// Set BOR level3 (2.54 to 2.74V)
void set_BOR_level3(void)
{
    FLASH_OBProgramInitTypeDef my_flash;

    HAL_FLASHEx_OBGetConfig(&my_flash); // read current configuration
    if (my_flash.BORLevel != OB_BOR_LEVEL3) {
        my_flash.BORLevel = OB_BOR_LEVEL3;
        HAL_FLASHEx_OBProgram(&my_flash);
    }
}

void set_5v_drop_detect(void)
{
    set_BOR_level3();
    // Set Analog voltage input (PB5 or PB6)
#if defined(USE_PB5_FOR_COMP)
    GPIO_InitStruct.Pin = GPIO_PIN_5; // PB5 comp input
#elif defined(USE_PB6_FOR_COMP)
    GPIO_InitStruct.Pin = GPIO_PIN_6; // PB6 comp input
#else
#error "Please define USE_PB5_FOR_COMP or USE_PB6_FOR_COMP in SetRTC.h"
#endif
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    // COMP2 sets for low volatage detection
    __COMP_CLK_ENABLE();
    COMP_HandleStruct.Instance = COMP2;
    COMP_HandleStruct.Init.InvertingInput =  COMP_INVERTINGINPUT_VREFINT;
#if defined(USE_PB5_FOR_COMP)
    COMP_HandleStruct.Init.NonInvertingInput = COMP_NONINVERTINGINPUT_PB5;
#elif defined(USE_PB6_FOR_COMP)
    COMP_HandleStruct.Init.NonInvertingInput = COMP_NONINVERTINGINPUT_PB6;
#endif
    COMP_HandleStruct.Init.Output = COMP_OUTPUT_NONE;
    COMP_HandleStruct.Init.Mode = COMP_MODE_HIGHSPEED;
    COMP_HandleStruct.Init.WindowMode = COMP_WINDOWMODE_DISABLED;
    COMP_HandleStruct.Init.TriggerMode = COMP_TRIGGERMODE_IT_FALLING;
    HAL_COMP_Init(&COMP_HandleStruct);
    // Interrupt configuration
    NVIC_SetVector(COMP_IRQn, (uint32_t)irq_comp2_handler);
    HAL_NVIC_SetPriority(COMP_IRQn, 0, 0);
    HAL_NVIC_ClearPendingIRQ(COMP_IRQn);
    HAL_COMP_Start_IT(&COMP_HandleStruct);
    HAL_NVIC_EnableIRQ(COMP_IRQn);
}
#else   // defined(USE_IRQ_FOR_RTC_BKUP)
void set_5v_drop_detect(void)
{
    ;   // No implementation
}
#endif  // defined(USE_IRQ_FOR_RTC_BKUP)

#else   // defined(TARGET_NUCLEO_L152RE)
void set_5v_drop_detect(void)
{
    ;   // No implementation
}
#endif  // defined(TARGET_NUCLEO_L152RE)

#else   // defined(TARGET_NUCLEO_F401RE,TARGET_NUCLEO_F411RE,TARGET_NUCLEO_L152RE)
#error "No suport this mbed, only for Nucleo mbed"
#endif  // defined(TARGET_NUCLEO_F401RE,TARGET_NUCLEO_F411RE,TARGET_NUCLEO_L152RE)