STM32F407VET BOARD-RTC TIME NOT SAVED? - Page 1
Forum section: electronics / general

author: kgavionics
author_rank: Regular Contributor
timestamp: September 06, 2022, 01:07:28 pm
content: HelloI have this board (See pix below) which has a backup battery connected to VBAT pin on the MCU, but for some reason, it doesn't save the RTC time and each time I unplug the board, the time resets.Do I have to enable something by software to make the board save the RTC time ?Thanks in advance

author: wek
author_rank: Frequent Contributor
timestamp: September 06, 2022, 08:17:37 pm
content: > Do I have to enable something by software to make the board save the RTC time ?No, but I've heard complaints about CubeMX generated code unconditionally resetting the backup domain.Write your own software.Just to check, it's enough to enable LSE, select LSE as RTC clock and enable RTC in RCC_BDTR, you can do it in debugger, no need to write a single line of code (a plain blinky do). Then just read RTC in debugger (with caution). The time register should be changing, and it should continue running after you remove/resume VDD.JW

author: kgavionics
author_rank: Regular Contributor
timestamp: September 06, 2022, 09:39:07 pm
content: Quote from: wek on September 06, 2022, 08:17:37 pmI don't use CubeMx at all, I write my own bare metal code using Keil uvision!

author: wek
author_rank: Frequent Contributor
timestamp: September 06, 2022, 09:52:50 pm
content: QuoteOK, then show.JW

author: kgavionics
author_rank: Regular Contributor
timestamp: September 06, 2022, 11:00:08 pm
content: Quote from: wek on September 06, 2022, 09:52:50 pmCode:RTC_Clock_Enable ();RCC->BDCR |= RCC_BDCR_RTCEN; // Enable the clock of RTC// Disable write protection of RTC registers by writing disarm keysRTC->WPR = 0xCA; RTC->WPR = 0x53;// Enter initialization mode to program TR and DR registersRTC->ISR |= RTC_ISR_INIT;// Wait until INITF has been setwhile((RTC->ISR & RTC_ISR_INITF) == 0);// Hour format: e = 24 hour/day; 1 = AM/PM hourRTC->CR &= ~RTC_CR_FMT;// Generate a lHz clock for the RTC time counter // LSE = 32.768 kHz= 2~15 HzRTC->PRER= (2U«7 - 1) « 16; // Asynch_Prescaler = 127RTC->PRER= (2U«8 - 1); // Synch_Prescaler = 255// Set time as 11:32:ee amRTC->TR = 0U<<22 | 1U<<20 | 7U<<16 | 3U<<12 | 8U<<8;// Exit initialization modeRTC->ISR &= ~RTC_ISR_INIT;// Enable write protection for RTC registersRTC->WPR = 0xFF;}void RTC_Clock_Enable(void){// Enable write access to the backup domainif ((RCC->APB1ENR & RCC_APB1ENR_PWREN) == 0) { // Enable power interface clockRCC->APB1ENR |= RCC_APB1ENR_PWREN; // Short delay after enabling an RCC peripheral clock(void) RCC->APB1ENR;}// Select LSE as RTC clock source // RTCILCD Clock: (1) LSE is in the backup domain (2) HSE and LSI are notif ((PWR->CR & PWR_CR_DBP) == 0) {// Enable write access to RTC and registers in backup domainPWR->CR |= PWR_CR_DBP;// Wait until the backup domain write protection has been disabledwhile((PWR->CR & PWR_CR_DBP) == 0);}// Reset LSEON and LSEBYP bits before configuring LSE // BDCR = Backup Domain Control RegisterRCC->BDCR &= ~(RCC_BDCR_LSEON | RCC_BDCR_LSEBYP);// RTC Clock selection can be changed only if the backup domain is resetRCC->BDCR |= RCC_BDCR_BDRST;RCC->BDCR &= ~RCC_BDCR_BDRST;// Wait until LSE clock is readywhile((RCC->BDCR & RCC_BDCR_LSERDY) == 0) {RCC->BDCR |= RCC_BDCR_LSEON; // Enable LSE oscillator}// Select LSE as RTC clock source // RTCSEL[l:B]: ee = No Clock, Bl = LSE, 1e = LSI, 11 = HSERCC->BDCR &= ~RCC_BDCR_RTCSEL;// Clear RTCSEL bitsRCC->BDCR |= RCC_BDCR_RTCSEL_0; // Select LSE as RTC clock// Disable power interface clockRCC->APB1ENR &= ~RCC_APB1ENR_PWREN;}void rtc_get_time (void){su= (RTC->TR &0X0F) | 0x30;st =((RTC->TR&0x70)>>4 )| 0x30;mnu=((RTC->TR &0xf00)>>8) | 0x30;mnt=((RTC->TR &0x7000)>>12) | 0x30;hu=((RTC->TR &0xf0000)>>16) | 0x30;ht=((RTC->TR &0x300000)>>20) | 0x30;}

author: wek
author_rank: Frequent Contributor
timestamp: September 06, 2022, 11:04:25 pm
content: OK and you call this code when? After reset? Unconditionally?JW

author: kgavionics
author_rank: Regular Contributor
timestamp: September 06, 2022, 11:46:22 pm
content: This is the main functionCode:#include<stm32f407xx.h>#include"functions.h"volatile uint8_t su,st,mnu,mnt,hu,ht;int main(void){SysClockConfig();RTC_Clock_Enable();RTC_Init();gpio_init();glcd_init();glcd_clear();glcd_setCursor_small(2,0);glcd_putchar(':');glcd_setCursor_small(5,0);glcd_putchar(':');while(1){rtc_get_time();glcd_setCursor_small(7,0);glcd_putchar(su);glcd_setCursor_small(6,0);glcd_putchar(st);glcd_setCursor_small(4,0);glcd_putchar(mnu);glcd_setCursor_small(3,0);glcd_putchar(mnt);glcd_setCursor_small(1,0);glcd_putchar(hu);glcd_setCursor_small(0,0);glcd_putchar(ht);}}

author: thm_w
author_rank: Super Contributor
timestamp: September 07, 2022, 12:06:22 am
content: If that is your RTC_Init function above then as wek points out, you are calling this every reset, so the time is reset to:"// Set time as 11:32:ee am RTC->TR = 0U<<22 | 1U<<20 | 7U<<16 | 3U<<12 | 8U<<8;"You want some way to either manually enter the time, or only set the time if the RTC register has been reset to 0.

author: kgavionics
author_rank: Regular Contributor
timestamp: September 07, 2022, 12:27:57 am
content: Quote from: thm_w on September 07, 2022, 12:06:22 amwhat I did, is set the time using: RTC->TR, flash the MCU, then comment that line out and flash again, this should preserve the time, but it's not!

author: uer166
author_rank: Super Contributor
timestamp: September 07, 2022, 01:33:57 am
content: Quote from: kgavionics on September 07, 2022, 12:27:57 amYou realize that during the flashing process the MCU gets reset and re-runs the old code multiple times right? Flash one set of code, and set the time without using the SWD (e.g. set time to something known when toggling a specific IO), then that specific issue will be fixed.

author: wek
author_rank: Frequent Contributor
timestamp: September 07, 2022, 04:52:20 am
content: Your main problem is the unconditional Backup domain Reset, i.e. setting RCC_BDCR.BDRST. Perform the whole setup thing conditionally, i.e. only if RCC_BDCR.RTCEN is zero.Btw. your RTC dividers look suspicious, the reset values are usually OK and you don't need to set that register. When reading time, read RTC_TR only once in a variable and then decompose that variable. Also, read RTC_DR after RTC_TR.JW

author: peter-h
author_rank: Super Contributor
timestamp: September 07, 2022, 10:29:05 am
content: FWIW, in my project, a magic value is stored in the backup domain RAM, and if it is found tonotbe there at startup, the RTC is initialised, with the magic value also, and set to some date which is roughly the project build date.This is an old challenge to which there is no solution because the RTC cannot itself tell you whether it lost power / got corrupted / etc. All you can do is hope that there is a reasonable probability that the SRAM reflects a "total loss" of backup power and soon enough the RTC will be set from something.RTCs in embedded are always a problem. It is useless unless you somehow set itMy product has some ways to set the RTC: HTTP server web page with "set time from PC" link, NTP client auto set from ethernet, GPS time (serial port GPS or built-in UBLOX SPI GPS module).

author: kgavionics
author_rank: Regular Contributor
timestamp: September 07, 2022, 12:13:00 pm
content: Quote from: uer166 on September 07, 2022, 01:33:57 amhow can I set the time without using the SWD please?

author: wek
author_rank: Frequent Contributor
timestamp: September 07, 2022, 01:24:09 pm
content: QuoteYou'd need some sort of input, the simplest are pushbuttons (think usual digital clocks).But if you do what I've said, i.e. perform the initialization (and thus time setup) conditionally only if RCC_BDCR.RTCEN is zero, you can leave the RTC_TR setting as it is now.JW

author: kgavionics
author_rank: Regular Contributor
timestamp: September 07, 2022, 09:46:08 pm
content: I think, I’m going to use an external RTC, as I have few Ds1307 laying around.It’s so much simpler to use than the internal one.

author: peter-h
author_rank: Super Contributor
timestamp: September 08, 2022, 10:52:45 am
content: External ones certainly draw much less power. The DS1302 is probably the lowest, at about 200nA (I have designed them in, since ~1990). Whether you use a battery or a supercap, the impact is about 5x to 10x.Unfortunately the Maxim parts will burn you; they are one of the least dependable companies.

author: kgavionics
author_rank: Regular Contributor
timestamp: September 08, 2022, 11:32:59 am
content: Quote from: peter-h on September 08, 2022, 10:52:45 amIs their components have tendency to fail or what?

author: langwadt
author_rank: Super Contributor
timestamp: September 08, 2022, 01:35:42 pm
content: Quote from: kgavionics on September 08, 2022, 11:32:59 am(in)famous for obsoleting parts or not being able to deliver parts

author: peter-h
author_rank: Super Contributor
timestamp: September 08, 2022, 02:33:44 pm
content: They are incredibly arrogant. A few years ago they turned off all email or phone means of contact; you have to open support tickets just to get pricing. Not kidding... We had a direct account with them, still have, but it doesn't really function.They are also shafting people on pricing, even though they are not the only one doing that right now.I have designed out everything from Maxim. It's been a beneficial exercise, reducing costs quite a bit sometimes. Except MAX3089ESD which I can't get rid of.If it was me, I would stick with the ST internal RTC. It works perfectly, if you use it right. Dig out previous threads here. There is a specific read sequence. I am using a 0.22F supercap which lasts about 5 days until actual RTC stoppage. Save yourself an extra part, and the cost.Read wek's gotcha texthttp://efton.sk/STM32/gotcha/g6.htmlMy code on the 32F417 is here:Code:// Magic value written to RTC SRAM (RTC_BKP_DR19) to indicate that the time has been set#define RTC_STATUS_TIME_OK 0x4321// =========== RTC PRESCALER VALUES ============// Sync and async pre-division for clock// Power consumption measured at 1.46uA// These give subseconds resolution of 1/1024 sec#define RTC_ASYNC_PREDIV 0x1F#define RTC_SYNC_PREDIV 0x3FF// These are supposed to give a lower RTC power consumption but actually do not!// 1.75uA measured.// These give subseconds resolution of 1/256 sec//#define RTC_ASYNC_PREDIV 127//#define RTC_SYNC_PREDIV 255// ==============================================static RTC_HandleTypeDef hrtc;extern osMutexId_t g_RTC_Mutex;// mutex for RTCvolatile bool g_RTC_trusted=false;// This tells anyone calling getrtc() that the RTC is valid, with a high degree of certaintyvolatile uint32_t g_SubSeconds=0;// Subseconds value returned by getrtc(), in microsecondsstatic const uint8_t d_o_w = { "MonTueWedThuFriSatSun" };/** Mutex (semaphore) funcs for RTC** These are provided by the CMSIS RTOS2 wrapper for FreeRTOS** To set this up, you have to* osMutexId_t g_RTC_Mutex;* and then call (before osKernelStart(), but not before osKernelInitialize())*g_RTC_Mutex = osMutexNew(NULL);* to get the next mutex ID* That is done in main()**/void RTC_Lock(){osMutexAcquire(g_RTC_Mutex,osWaitForever);}void RTC_Unlock(){osMutexRelease(g_RTC_Mutex);}void RTC_Config(void){RCC_OscInitTypeDef RCC_OscInitStruct;RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;/* We are updating RTC clock */PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;/* Do not use PLL */RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;/* LSE is used */RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;RCC_OscInitStruct.LSEState = RCC_LSE_ON;PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;/* Config oscillator */HAL_RCC_OscConfig(&RCC_OscInitStruct);/* Select peripheral clock */HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);/* Enable RTC Clock */__HAL_RCC_RTC_ENABLE();}/** RTC initialise function.** Set up the RTC hardware and optionally the date/time.* It checks if the last NVRAM location holds a funny value RTC_STATUS_TIME_OK and if not, it loads the RTC* date/time as well, otherwise it just sets up the other registers. Note that this funny value will always* be there OK unless power on the backup RAM has totally died. This value doesn't say anything about the* clock date/time being valid.** This function is called at every power-up.**/uint32_t KDE_rtc_init(void){uint32_t status = 0;uint32_t result = 0;// Set handle structurehrtc.Instance = RTC;hrtc.Init.AsynchPrediv = RTC_ASYNC_PREDIV;hrtc.Init.SynchPrediv = RTC_SYNC_PREDIV;hrtc.Init.HourFormat = RTC_HOURFORMAT_24;hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_PUSHPULL;hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;// Enable PWR peripheral clock__HAL_RCC_PWR_CLK_ENABLE();// Allow access to BKP DomainHAL_PWR_EnableBkUpAccess();// Get RTC statusstatus = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR19);// Check if RTC already initializedif (status == RTC_STATUS_TIME_OK){// Wait for RTC APB registers synchronisation (needed after start-up from Reset)HAL_RTC_WaitForSynchro(&hrtc);// Get date and timechar datetime;KDE_get_date_time(datetime);// Clear reset flags__HAL_RCC_CLEAR_RESET_FLAGS();// RTC already initialisedresult = 1;}else {// Start RTC clockRTC_Config();// Initialise RTC and set the Time and DateRTC_TimeTypeDef sTime = {0};RTC_DateTypeDef sDate = {0};// Load Sunday 9th May 2021 00:00:00sDate.WeekDay = RTC_WEEKDAY_SUNDAY;sDate.Month = RTC_MONTH_MAY;sDate.Date = 9;sDate.Year = 21;HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN);sTime.Hours = 0;sTime.Minutes = 0;sTime.Seconds = 0;sTime.TimeFormat = RTC_HOURFORMAT_24;sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;sTime.StoreOperation = RTC_STOREOPERATION_RESET;HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN);// Init RTCHAL_RTC_Init(&hrtc);// Save data to backup registerHAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR19, RTC_STATUS_TIME_OK);// RTC is now initialisedresult = 0;}return result;}// This function is handy if you want date/time as a string// NOTE that *both* date and time must always be read out, due to locking of the shadow registers// Read Time and Date, in that order.// Does NOT fill in g_subseconds.// Not really used anywhere currently (8/2021) except web server hack codevoid KDE_get_date_time(char *datetime){RTC_TimeTypeDef sTime = {0};RTC_DateTypeDef sDate = {0};HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);int i =(sDate.WeekDay - 1) * 3;sprintf(datetime, "%c%c%c %02d%02d20%02d %02d%02d%02d DOY=%03d",d_o_w,d_o_w[i+1],d_o_w[i+2],sDate.Date, sDate.Month, sDate.Year,sTime.Hours, sTime.Minutes, sTime.Seconds, day_of_year(sDate.Date, sDate.Month-1, sDate.Year+100));}// Get the date/time in a concise universal format that may be used for timestamps which sort chronologically:// YYYYMMDD_HHMMSSvoid KDE_get_timestamp(char *timestamp){RTC_TimeTypeDef sTime = {0};RTC_DateTypeDef sDate = {0};HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);sprintf(timestamp, "%04d%02d%02d_%02d%02d%02d",sDate.Year+2000, sDate.Month, sDate.Date,sTime.Hours, sTime.Minutes, sTime.Seconds);}// Not used// Does NOT fill in g_subseconds.void KDE_get_time(char *time){RTC_TimeTypeDef sTime = {0};RTC_DateTypeDef sDate = {0};//uint8_t ok = 0;HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);// read date to unlock the RTCsprintf(time, "%02d%02d%02d", sTime.Hours, sTime.Minutes, sTime.Seconds);}// This function was used only for debugging, using the hex switch to set the clockuint8_t KDE_set_date_time(uint8_t hex){uint8_t ok = 0;RTC_TimeTypeDef sTime = {0};RTC_DateTypeDef sDate = {0};if (HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN) == HAL_OK) {sDate.WeekDay = (hex == 4) ? 1 : 0;sDate.WeekDay %= 8;sDate.Month += (hex == 6) ? 1 : 0;sDate.Month %= 13;sDate.Date += (hex == 5) ? 1 : 0;sDate.Date %= 32;if (sDate.Date == 0) sDate.Date = 1;if (sDate.Month == 0) sDate.Month = 1;if (sDate.WeekDay == 0) sDate.WeekDay = 1;sDate.Year += (hex == 7) ? 1 : 0;sDate.Year %= 100;if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) == HAL_OK) {if (HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN) == HAL_OK) {sTime.Hours += (hex == 1) ? 1 : 0;sTime.Hours %= 24;sTime.Minutes += (hex == 2) ? 1 : 0;sTime.Minutes %= 60;sTime.Seconds += (hex == 3) ? 1 : 0;sTime.Seconds %= 60;sTime.TimeFormat = RTC_HOURFORMAT12_PM;sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;sTime.StoreOperation = RTC_STOREOPERATION_RESET;if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) == HAL_OK) {ok = 1;}}}}return ok;}/** Set KDE real time clock.* Just fill in the structure 'mytime' and call it. This is the standard C library structure tm*inttm_sec;//seconds after the minute (0..59)inttm_min;//minutes after the hour (0..59)inttm_hour;//hours since midnight (0..23)inttm_mday;//day of the month (1..31)inttm_mon;//months since January (0..11)inttm_year;//years since 1900 (100..199)inttm_wday;//days since Sunday (0..6) - calculated by this function & loaded into RTCinttm_yday;//days since Jan 1 (0..365) - ignored by this function, calculated by getrtcinttm_isdst;//Daylight Saving Time flag (0..1) unimplemented, set to -1 by getrtc* Returns* 0 if no errors* 1 if RTC not present (not implemented; the RTS is on the CPU chip, but the battery/supercap may not be there)* 2 if the HAL code returns an error (should never happen)* 3+ for various wrong input values** Note that the tm_year is years since 1900 as per the C standard, but the 32F4 RTC holds just 2 digits* for the year, so the 32F4 must have been designed to do leap year correction based on the value in those* two digits being years since 2000.** Daylight savings is basically impossible on an embedded system. It needs internet access and online updates* to handle the ~200 time zones and other complexity. Accordingly, the RTC is usable only for UTC, or local time.** The g_RTC_trusted flag gets set here, because this function should not be called unless the data is trusted.* So it should not be called at e.g. power-up to set some base date like 1/1/2000.**/int setrtc (struct tm * mytime){RTC_TimeTypeDef sTime = {0};RTC_DateTypeDef sDate = {0};// Convert the K&R standard C clock structure "tm" to the one used for the 32F4 RTC API// K&R year is years since 1900 but ST year is 0..99 where 0 = year 2000, fairly obviously!if ((mytime->tm_year>=0) && (mytime->tm_year<=199))sDate.Year = mytime->tm_year-100;else return 3;// K&R month is 0..11 whereas ST month is 1..12if ((mytime->tm_mon>=0) && (mytime->tm_mon<=11))sDate.Month = mytime->tm_mon+1;else return 4;// Day of month is 1..31 in both structuresif ((mytime->tm_mday>=1) && (mytime->tm_mday<=31))sDate.Date = mytime->tm_mday;else return 5;// Calculate day of week and load it into tmmytime->tm_wday = day_of_week(mytime->tm_mday, mytime->tm_mon, mytime->tm_year);// K&R day of week is Sun..Sat being 0..6 whereas with ST Mon..Sun is 1..7if ((mytime->tm_wday>=0) && (mytime->tm_wday<=6)){sDate.WeekDay = mytime->tm_wday;// Mon-Sat are the sameif (mytime->tm_wday == 0)// but Sun changes from 0 to 7sDate.WeekDay = 7;}else return 6;// Hours is 0..23 in both structuresif ((mytime->tm_hour>=0) && (mytime->tm_hour<=23))sTime.Hours = mytime->tm_hour;else return 7;// Mins and Secs 0..59 in both structuresif ((mytime->tm_min>=0) && (mytime->tm_min<=59))sTime.Minutes = mytime->tm_min;else return 8;if ((mytime->tm_sec>=0) && (mytime->tm_sec<=59))sTime.Seconds = mytime->tm_sec;else return 9;sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;sTime.StoreOperation = RTC_STOREOPERATION_RESET;int error=0;RTC_Lock();if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)error=2;if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)error=2;g_RTC_trusted = true;// This tells anyone calling getrtc() that the RTC is valid, with a high degree of certainty.RTC_Unlock();return error;}/** Read KDE real time clock.* Just call it. It fills in the structure 'mytime'. This is the standard C library structure tm*inttm_sec;//seconds after the minute (0..59)inttm_min;//minutes after the hour (0..59)inttm_hour;//hours since midnight (0..23)inttm_mday;//day of the month (1..31)inttm_mon;//months since January (0..11)inttm_year;//years since 1900 (100..199)inttm_wday;//days since Sunday (0..6)inttm_yday;//days since Jan 1 (0..365) calculated from above datainttm_isdst;//Daylight Saving Time flag (0..1) unimplemented, set to -1* Returns* 0 if no errors* 1 if RTC not present (not implemented; the RTC is on the CPU chip, but the battery/supercap may not be there)* 2 if SSR had to be read too many times before we got two same values** Code 1 is never going to happen because we have no way to detect the lack of the supercap.* Code 1 could be hard coded on systems where the RTC cannot be used for some reason (supercap not fitted AND* no provision to set the RTC).** Code 2 is related to the errata sheet which says that the SSR (the prescaler) may be incorrectly read* so one has to read it twice and compare. And the date/time needs to be read in between those two SSR reads.** Other comments as per setrtc above.** Also returns the subseconds counter in a global variable g_SubSeconds. This is related to the config* in KDE_rtc_init() and the two values RTC_ASYNC_PREDIV and RTC_SYNC_PREDIV which are #defined at the* top of this file.**/int getrtc (struct tm * mytime){#define MAX_SSR_READS 10volatile RTC_TimeTypeDef sTime = {0};// volatile to prevent compiler optimising these out (for debug)volatile RTC_DateTypeDef sDate = {0};// Mutex to prevent concurrent setrtc and getrtc, under RTOSRTC_Lock();uint32_t last_subsec;uint32_t loopcnt=0;uint32_t tmpreg = 0U;uint32_t datetmpreg = 0U;// Implement RTC errata sheet (SSR needs reading before and after date/time and compared, until same)// There is a max loop count, which is simpler than a timeout.// It was found experimentally that you get a dud SSR read every ~10k operations here.do{// Get subseconds, and save for later comparisonlast_subsec = (uint32_t)(hrtc.Instance->SSR);// Always read both time and date, in that order//HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);sTime.SubSeconds = last_subsec;// (uint32_t)(hrtc.Instance->SSR);// Get SecondFraction structure field from the corresponding register field - not used//sTime.SecondFraction = (uint32_t)(hrtc.Instance->PRER & RTC_PRER_PREDIV_S);/* Get the TR register */tmpreg = (uint32_t)(hrtc.Instance->TR & RTC_TR_RESERVED_MASK);//HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);/* Get the DR register */datetmpreg = (uint32_t)(hrtc.Instance->DR & RTC_DR_RESERVED_MASK);loopcnt++;}while ( (last_subsec != (uint32_t)(hrtc.Instance->SSR)) && (loopcnt < MAX_SSR_READS) );if (loopcnt >= MAX_SSR_READS) return 2;// A dummy DR read to unlock the regs because last read above is the SSR, but the// last read should be the DRhrtc.Instance->DR;RTC_Unlock();// The following code could be simplified.../* Fill the structure fields with the read parameters */sTime.Hours = (uint8_t)((tmpreg & (RTC_TR_HT | RTC_TR_HU)) >> 16U);sTime.Minutes = (uint8_t)((tmpreg & (RTC_TR_MNT | RTC_TR_MNU)) >> 8U);sTime.Seconds = (uint8_t)(tmpreg & (RTC_TR_ST | RTC_TR_SU));sTime.TimeFormat = (uint8_t)((tmpreg & (RTC_TR_PM)) >> 16U);/* Convert the time structure parameters to Binary format */sTime.Hours = (uint8_t)RTC_Bcd2ToByte(sTime.Hours);sTime.Minutes = (uint8_t)RTC_Bcd2ToByte(sTime.Minutes);sTime.Seconds = (uint8_t)RTC_Bcd2ToByte(sTime.Seconds);/* Fill the structure fields with the read parameters */sDate.Year = (uint8_t)((datetmpreg & (RTC_DR_YT | RTC_DR_YU)) >> 16U);sDate.Month = (uint8_t)((datetmpreg & (RTC_DR_MT | RTC_DR_MU)) >> 8U);sDate.Date = (uint8_t)(datetmpreg & (RTC_DR_DT | RTC_DR_DU));sDate.WeekDay = (uint8_t)((datetmpreg & (RTC_DR_WDU)) >> 13U);/* Convert the date structure parameters to Binary format */sDate.Year = (uint8_t)RTC_Bcd2ToByte(sDate.Year);sDate.Month = (uint8_t)RTC_Bcd2ToByte(sDate.Month);sDate.Date = (uint8_t)RTC_Bcd2ToByte(sDate.Date);// Convert the 32F4 RTC API to the K&R standard C clock structure "tm"// Comments in setrtc above// No error checking - would be meaninglessmytime->tm_year = sDate.Year+100;mytime->tm_mon = sDate.Month-1;mytime->tm_mday = sDate.Date;mytime->tm_wday = sDate.WeekDay;// assume the RTC maintained the day of week correctlyif (sDate.WeekDay==7) mytime->tm_wday = 0;mytime->tm_hour = sTime.Hours;mytime->tm_min = sTime.Minutes;mytime->tm_sec = sTime.Seconds;// Load subseconds into a global variable, as microseconds// There were problems with this when using the SHIFTR method to advance the RTC...g_SubSeconds = ((RTC_SYNC_PREDIV-sTime.SubSeconds)*1000000L)/(RTC_SYNC_PREDIV+1);// Calculate day of year from the date, because that doesn't come from the RTC.mytime->tm_yday = day_of_year(mytime->tm_mday, mytime->tm_mon, mytime->tm_year);// Return -1 for daylight saving, as per K&Rmytime->tm_isdst = -1;return 0;}int getrtc_subs( struct tm * mytime, uint32_t *subs_ms ){int retval= getrtc (mytime);*subs_ms = g_SubSeconds / 1000;return retval;}/** Calculate day of week tm_wday from date.* Used to set the RTC from GPS, or from a user program which knows the date but not day of week.* DOW and DMY are in the same values as in the tm structure* This function is not fast but that doesn't matter because it gets called only when setting the RTC.* It works only for year 1900+.**/int day_of_week(int day, int month, int year){uint16_t mtab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};uint16_t i;// Seed: 1 January 1900 is a Mondayuint32_t origin_day = 1;// adjust for input values being from the standard tm structure, day 1.. year since 1900 etcday--;year += 1900;// add on how many days in previous years (since 1900)for (i = 1900; i < year; i++){if (is_leap(i))origin_day += 366;elseorigin_day += 365;}// fix up Feb on this year in mtab (i = current year)if (is_leap(i))mtab = 29;// add on how many days in whole preceeding months in the current year so far// if month=0 (Jan) this is skippedfor (i=0; i<month; i++)origin_day += mtab;// add on days in current monthorigin_day += day;// return value 0-6return origin_day % 7;}// A leap year is every year divisible by four except for years which are divisible by both 100 and 400// year is any year > 0 e.g. 2021bool is_leap(uint16_t year){if ( (year % 4) != 0 )return false;if ( ( (year % 100) == 0) && ( (year % 400) != 0) )return false;elsereturn true;}/** Calculate day of year tm_yday from date.* Returns the number of days since 1st Jan i.e. 1st Jan = 0.* Called from getrtc to fill in the day of year in the tm structure because the 32F4 doesn't* have a day of year register.* Day of year and DMY are in the same values as in the tm structure.* Works for any year, not limited to 1900+ etc.**/int day_of_year(int day, int month, int year){static char day_tab = {{0,31,28,31,30,31,30,31,31,30,31,30,31},{0,31,29,31,30,31,30,31,31,30,31,30,31}};int i,leap;year=year+1900;month++;day--;if (is_leap(year))leap=1;elseleap=0;for(i=1; i<month; i++){day += day_tab;}return day;}

author: kgavionics
author_rank: Regular Contributor
timestamp: September 08, 2022, 05:53:10 pm
content: Thank you Peter for the heads-up, I really appreciate it!