Solution how to fix broken RTC on Nucleo_F103RB / STM32F103 BluePill etc..
Fork of Nucleo_RTC_battery_bkup_pwr_off_okay by
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 }