Solution how to fix broken RTC on Nucleo_F103RB / STM32F103 BluePill etc..

Dependencies:   mbed-dev

Fork of Nucleo_RTC_battery_bkup_pwr_off_okay by Kenji Arai

Experimental fork https://os.mbed.com/users/kenjiArai/code/Nucleo_RTC_battery_bkup_pwr_off_okay/ to fix broken RTC on Nucleo_F103RB / STM32F103 BluePill etc..

At this moment (7/11/17) use forked mbed-dev https://os.mbed.com/users/maxxir/code/mbed-dev/.

Or require patch for ./mbed-dev/targets/TARGET_STM/rtc_api.c.

You can manual add to your project fresh mbed-dev and change file mbed-dev\targets\TARGET_STM\rtc_api.c from root project patched rtc_api.c.stm32f10x.txt.

Exploring origin errors, I saw that something wrong with HAL API on STM32F1xx with this functions:

 HAL_RTC_GetTime(&RtcHandle, &timeStruct, RTC_FORMAT_BIN);

 HAL_RTC_GetDate(&RtcHandle, &dateStruct, RTC_FORMAT_BIN);


 HAL_RTC_SetDate(&RtcHandle, &dateStruct, RTC_FORMAT_BIN);

 HAL_RTC_SetTime(&RtcHandle, &timeStruct, RTC_FORMAT_BIN);

Look here (as I understand it possible broken on STM32CUBE HAL level now):

https://community.st.com/thread/43218-stm32f103-loss-rtc-date-when-reset

So I use direct RTC register manipulation for STM32F1xx:

rtc_read(), rtc_write() (native rtc_init() - works good).

Also added stub for non-working on STM32F1xx rtc_read_subseconds().

Now the stm32F103 can survive power off, and allows you to get and set the time.

Tested OK on boards:

NUCLEO STM32F103RB, DIY STM32F100CB (forked from DISCO_F100RB)

Happy coding!

maxxir

10/11/17

main.cpp

Committer:
maxxir
Date:
2017-11-10
Revision:
12:dfd6bc5c6f2f
Parent:
10:0c6dfc996c1a

File content as of revision 12:dfd6bc5c6f2f:

/*
 Experimental fork https://os.mbed.com/users/kenjiArai/code/Nucleo_RTC_battery_bkup_pwr_off_okay/
 to fix broken RTC on Nucleo_F103RB / STM32F103 BluePill etc..
 at this moment (7/11/17) require patch for ./mbed-dev/targets/TARGET_STM/rtc_api.c.
 
 Something wrong with STM32CUBE API on STM32F1xx with this functions:
 HAL_RTC_GetTime(&RtcHandle, &timeStruct, RTC_FORMAT_BIN);
 HAL_RTC_GetDate(&RtcHandle, &dateStruct, RTC_FORMAT_BIN);
 HAL_RTC_SetDate(&RtcHandle, &dateStruct, RTC_FORMAT_BIN);
 HAL_RTC_SetTime(&RtcHandle, &timeStruct, RTC_FORMAT_BIN);
 
 Look here (as I understand it broken on STM32CUBE level now):
 https://community.st.com/thread/43218-stm32f103-loss-rtc-date-when-reset
 
 So I use direct RTC register manipulation for STM32F1xx
 rtc_read() && rtc_write()  (native rtc_init() - works good)
 also added stub for non-working on STM32F1xx rtc_read_subseconds(). 
 Now RTC on stm32F103 survive poweroff.
 
 Happy coding!
 Ibragimov Maxim aka maxxir
 Russia Togliatty 07/10/17
*/
/*
 * mbed Application program
 *      RTC (inside STM32x CPU) test program
 *
 * Copyright (c) 2015,'16,'17 Kenji Arai / JH1PJL
 *  http://www.page.sannet.ne.jp/kenjia/index.html
 *  http://mbed.org/users/kenjiArai/
 *      Created: January   17th, 2015
 *      Revised: January   16th, 2017
 *
 * 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.
 */

/*  mbed library now suports RTC continuous operation at Reset & Power ON/OFF
    --------------------------------------------------------------------------
    In the past, rtc_api.c (inside mbed) alway made a reset RTC registers
    when user push a reset buttom or terninate a power.
    Even if user configures a back-up circuit for RTC, mbed board could not
    keep proper time.
    I have checked mbed rev.133 and mbed-dev rev.155.
 */

/*
    ----- Tested board -----
                  / Reset: / Stanby: / power off and restart:
    Nucleo-F401RE /  ok    /  ok     / ok (need following Back-up Circuit)
    Nucleo-F411RE /  ok    /  ok     / ok (need following Back-up Circuit)
    Nucleo-F446RE /  ok    /  ok     / ok (need following Back-up Circuit)
    Nucleo-F334R8 /  ok    /  ok     / ok (need following Back-up Circuit)
    Nucleo-L476RG /  ok    /  ok     / ok (need following Back-up Circuit)
    Nucleo-L152RE /  ok    /  ok     / no check (Need additional hardware)
    Nucleo-L073RZ /  ok    /  ok     / no check (Need additional hardware)
    Nucleo-L053R8 /  ok    /  ok     / no check (Need additional hardware)

    < Back-up circuit >
    CN7 VIN <- SBD <- 330 Ohm <- CR2032 + - -> CN7 GND
    Remove SB45 Zero Ohm resistor
 */

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

//  Definition -----------------------------------------------------------------
#if (defined(TARGET_STM32F746NG) || defined(TARGET_STM32F746ZG))
#define PUSHED_SW   1   // Active high
#else
#define PUSHED_SW   0   // Active low
#endif

//  Object ---------------------------------------------------------------------
DigitalIn userSW(USER_BUTTON);
DigitalOut myled(LED1);         // Indicate the sampling period
Serial pc(USBTX, USBRX);

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

//  ROM / Constant data --------------------------------------------------------
char *const msg0 = "Is a time correct? If no, please hit any key. ";
char *const msg1 = "<Push USER SW then enter sleep mode> ";
char *const msg2 = "\r\nEnter Standby Mode, please push RESET to wake-up\r\n";

//  Function prototypes --------------------------------------------------------
static void time_enter_mode(void);
static void chk_and_set_time(char *ptr);
static void goto_standby(void);
static int xatoi (char **str, unsigned long *res);
static void get_line (char *buff, int len);

//------------------------------------------------------------------------------
//  Control Program
//------------------------------------------------------------------------------
int main()
{
    char buf[64];               // data buffer for text
    time_t seconds;
    uint8_t wait_counter = 0;

    wait(2.0);
    pc.printf("\r\n\r\nTest Nucleo RTC Function\r\n");
    myled = !myled;
    // waiting for Initial screen
    myled = 1;
    wait(1.0);
    myled = !myled;
    wait(1.0);
    while(1) {
        seconds = time(NULL);
        strftime(buf, 50, " %B %d,'%y, %H:%M:%S\r\n", localtime(&seconds));
        pc.printf("[Time] %s", buf);
        pc.printf(msg0);
        pc.printf("%s\r", msg1);
        wait_counter = 0;
        while (seconds == time(NULL)){
            if (pc.readable() == 1){
                buf[0] = pc.getc();  // dummy read
                time_enter_mode();
            }
            if (userSW == PUSHED_SW){
                pc.printf(msg2);
                wait(1.0);
                myled = 0;
                goto_standby();
            }
            wait(0.05);
            if (++wait_counter > (2000 / 50)){
                break;
            }
        }
        uint8_t n = strlen(msg0) + strlen(msg1);
        for (uint8_t i = 0; i < n; i++){
            pc.putc(' ');
        }
        pc.printf("      \r"); // Not use '\n'
        myled = !myled;
    }
}

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

    pc.printf("\r\nSet time into RTC\r\n");
    pc.printf(" e.g. >17 1 16 20 55 23 -> January 16th,'17, 20:55:23\r\n");
    pc.printf(" If time is fine, just hit enter\r\n");
    pc.putc('>');
    ptr = linebuf;
    get_line(ptr, sizeof(linebuf));
    pc.printf("\r");
    chk_and_set_time(ptr);
}

void goto_standby(void)
{
    deepsleep();   // Not Standby Mode but Deep Sleep Mode
}

//  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 = pc.getc();
        if (c == '\r') {
            buff[idx++] = c;
            break;
        }
        if ((c == '\b') && idx) {
            idx--;
            pc.putc(c);
            pc.putc(' ');
            pc.putc(c);
        }
        if (((uint8_t)c >= ' ') && (idx < len - 1)) {
            buff[idx++] = c;
            pc.putc(c);
        }
    }
    buff[idx] = 0;
    pc.putc('\n');
}

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;
        pc.printf("Year:%d ",p1);
        xatoi( &ptr, &p1 );
        t.tm_mon        = (uint8_t)p1 - 1;
        pc.printf("Month:%d ",p1);
        xatoi( &ptr, &p1 );
        t.tm_mday       = (uint8_t)p1;
        pc.printf("Day:%d ",p1);
        xatoi( &ptr, &p1 );
        t.tm_hour       = (uint8_t)p1;
        pc.printf("Hour:%d ",p1);
        xatoi( &ptr, &p1 );
        t.tm_min        = (uint8_t)p1;
        pc.printf("Min:%d ",p1);
        xatoi( &ptr, &p1 );
        t.tm_sec        = (uint8_t)p1;
        pc.printf("Sec: %d \r\n",p1);
    } else {
        return;
    }
    seconds = mktime(&t);
    set_time(seconds);
    // Show Time with several example
    // ex.1
    pc.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));
    pc.printf("Date: %s\r\n", buf);
    // ex.3
    strftime(buf, 40, "%I:%M:%S %p (%Y/%m/%d)", localtime(&seconds));
    pc.printf("Date: %s\r\n", buf);
    // ex.4
    strftime(buf, 40, "%B %d,'%y, %H:%M:%S", localtime(&seconds));
    pc.printf("Date: %s\r\n", buf);
#endif
}