Library 19- Use internal RTC on STM32F4
It’s been some time when I post last library. Next one is internal RTC library. STM32F4xx devices have Real Time Clock (RTC) inside, with support of internal calibrated 32768 Hz oscillator or external (more accurate) 32768 Hz oscillator.
RTC peripheral has also backup register support, which means that when you reset MCU, registers are not reset or if power is off and you have battery connected on Vbat pin, clock is still working.
RTC can also wakeup MCU from all powerdown modes. It has interrupt connected to EXTI lines which allows him to do that. As of version 1.1, you can now also wakeup MCU with alarm interrupt. This is now supported in library.
Library also provide converting time to epoch time (seconds from 01.01.1970 00:00:00) and back.
RTC peripheral has 2 between independent alarm sources which can trigger an interrupt once a week or once a month according to its settings. I have made a little explanation down, but you will see how to use it in example 2.
RTC Library
Features
- Support Internal or external clock source
- PC14 and PC15 pins are used for external crystal oscillator
- STM32F4/429 Discovery does not have RTC crystal onboard. Check board’s manual on how to set it up
- Support wakeup interrupt
- Get seconds from 01.01.1970 00:00:00
- Get readable time from seconds from 01.01.1970 00:00:00
- Support to save/get data in binary or BCD format
- Support for read/write data to/from RTC backup registers
- Version 1.1 – October 20, 2014
- Added support to set 2 internal alarms to trigger interrupts
- They can also wake up STM32F4 from any low power mode
- Version 1.2 – October 27, 2014
- Added support for subsecond
- Version 1.4 – December 21, 2014
- Added support to write data in string format,
- Date&Time are checked before saved for valid input data
- Version 1.5Â – December 21, 2014
- Added functions to get days in a month and days in a year
- Version 1.7Â – March 15, 2015
- Added support for read/write from/to RTC backup registers
Dependencies
- CMSIS
- STM32F4xx
- STM32F4xx RCC
- STM32F4xx RTC
- STM32F4xx PWR
- STM32F4xx EXTI
- MISC
- TM
- defines.h
- attributes.h
- defines.h
Oscillator
STM32F4 has internal 32768 calibrated oscillator, which is not so calibrated as it is maybe heard. For any clock accuracy, you need external clock source. For this purpose, you can connect external 32768Hz crystal.
OSC 32768Hz | STM32F4xx | Description |
---|---|---|
OSC1 | PC14 | Oscillator pin 1 |
OSC2 | PC15 | Oscillator pin 2 |
Datetime structure
Time structure is used to work with time.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
/** * Struct for date/time * * Parameters: * - uint8_t seconds: * Seconds parameter, from 00 to 59 * - uint16_t subseconds: * Subsecond downcounter. When it reaches zero, it's reload value is the same as * RTC_SYNC_PREDIV, so in our case 0x3FF = 1023, 1024 steps in one second * - uint8_t minutes: * Minutes parameter, from 00 to 59 * - uint8_t hours: * Hours parameter, 24Hour mode, 00 to 23 * - uint8_t day: * Day in a week, from 1 to 7 * - uint8_t date: * Date in a month, 1 to 31 * - uint8_t month: * Month in a year, 1 to 12 * - uint8_t year: * Year parameter, 00 to 99, 00 is 2000 and 99 is 2099 * - uint32_t unix: * Seconds from 01.01.1970 00:00:00 */ typedef struct { uint8_t seconds; uint16_t subseconds; uint8_t minutes; uint8_t hours; uint8_t day; uint8_t date; uint8_t month; uint8_t year; uint32_t unix; } TM_RTC_Time_t; |
Datetime format structure
These are options which you use when set or get data from RTC.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/** * Set format of date and time * * - TM_RTC_Format_BIN * binary format * - TM_RTC_Format_BCD * BCD format * */ typedef enum { TM_RTC_Format_BIN, TM_RTC_Format_BCD } TM_RTC_Format_t; |
Clock source structure
RTC can work with internal or external oscillator.
Remember: Internal RC oscillator (it says that is calibrated) is not accurate at all. It failed about 2 seconds per minute on my both discovery boards. Use external clock source.
1 2 3 4 5 6 7 8 9 |
/** * Use internal or external clock * * Note: Internal clock is not so accurate */ typedef enum { TM_RTC_ClockSource_Internal, //Internal TM_RTC_ClockSource_External //External } TM_RTC_ClockSource_t; |
Interrupt structure
Wakeup with RTC is also possible. Below is structure where you can set every x seconds interrupt will happen.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/** * Select interrupt each x time * */ typedef enum { TM_RTC_Int_Disable, //Disable wakeup interrupt TM_RTC_Int_60s, //Interrupt every 60sec TM_RTC_Int_30s, //Interrupt every 30s TM_RTC_Int_15s, //Interrupt every 15s TM_RTC_Int_10s, //Interrupt every 10s TM_RTC_Int_5s, //Interrupt every 5s TM_RTC_Int_2s, //Interrupt every 2s TM_RTC_Int_1s, //Interrupt every 1s TM_RTC_Int_500ms, //Interrupt every 500ms TM_RTC_Int_250ms, //Interrupt every 250ms TM_RTC_Int_125ms //Interrupt every 125ms } TM_RTC_Int_t; |
Initialize RTC peripheral
On power on, you have to initialize RTC peripheral. This function enables clock for RTC, synchronize registers and prepare RTC to work.
1 2 3 4 5 6 7 8 9 |
/** * Initialize RTC * * Parameters: * - TM_RTC_ClockSource_t source: * Select RTC clock source * Returns 0 if RTC was initialized first time (power up), so you know when to set date and time */ extern uint32_t TM_RTC_Init(TM_RTC_ClockSource_t source); |
Set date and time
RTC is set to 01.01.01 00:00:00 on power up, if it is not configured before. If time is configured and reset happen, time should stay as it was before reset.
To set data, you have to fill time structure. When you do this, use
1 2 3 4 5 6 7 8 9 10 |
/** * Set date and time * * Parameters: * - TM_RTC_Time_t* data: * Struct to time data * - TM_RTC_Format_t format: * Member of struct TM_RTC_Format_t */ extern void TM_RTC_SetDateTime(TM_RTC_Time_t* data, TM_RTC_Format_t format); |
Get date and time
To get data from RTC you also need to have initialized time struct. Then use it like when you set date and time.
1 2 3 4 5 6 7 8 9 10 |
/** * Get date and time * * Parameters: * - TM_RTC_Time_t* data: * Struct to time data where to save data * - TM_RTC_Format_t format: * Member of struct TM_RTC_Format_t */ extern void TM_RTC_GetDateTime(TM_RTC_Time_t* data, TM_RTC_Format_t format); |
Seconds from 01.01.1970 00:00:00
I also made a function which calculates seconds from 01.01.1970 00:00:00.
Note: When you call function to get time from RTC, this function is also used to calculate time and store it in unix member of time structure.
1 2 3 4 5 6 7 8 9 10 |
/** * Get seconds from 01.01.1970 00:00:00 * * Parameters: * - TM_RTC_Time_t* data: * Struct to time data * * Returns Unix seconds */ extern uint32_t TM_RTC_GetUnixTimeStamp(TM_RTC_Time_t* data); |
Get date and time from 01.01.1970 00:00:00
Let’s say, that you are working on project where you have to save your time somehow. If you save date, month, year… separatelly, this will take a lot of bytes. If you convert time to seconds offset from 01.01.1970 00:00:00, and save it, that takes 4bytes, which is smaller. But than you have to get time back from this seconds. This is doing function below. It converts seconds from 1970 to a human readable time.
1 2 3 4 5 6 7 8 9 10 11 |
/** * Get formatted time from seconds till 01.01.1970 00:00:00 * It fills struct with valid data. Valid if year is greater or equal (>=) than 2000 * * Parameters: * - TM_RTC_Time_t* data: * Pointer to TM_RTC_Time_t struct to store formatted data in * - uint32_t unix: * Seconds from 01.01.1970 00:00:00 */ extern void TM_RTC_GetDateTimeFromUnix(TM_RTC_Time_t* data, uint32_t unix); |
RTC wake up interrupt
Because RTC support wakeup interrupt, you can use it if you need it. For that purpose, I wrote a function. As parameter you have to pass it Interrupt time struct
1 2 3 4 5 6 7 8 |
/** * Select RTC interrupt * * Parameters: * - TM_RTC_Int_t int_value: * Choose struct member from struct TM_RTC_Int_t */ extern void TM_RTC_Interrupts(TM_RTC_Int_t int_value); |
Also, when you activate wakeup interrupt, if you want to handle it, you have to make another function somewhere in your project (let’s say in main.c)
1 2 3 4 5 6 7 8 |
/** * Custom Request handler for wakeup * * Called from main handler */ void TM_RTC_RequestHandler(void) { } |
RTC Alarms
RTC peripheral on STM32F4 has capability to trigger 2 different alarms, Alarm A and Alarm B. They can be set, to trigger interrupt one time in a week or one time in a month. Also, they are completely independent between each, but they have interrupt handler in common. You are of course able to set one alarm to be triggered each month and one to be triggered each week.
RTC subsecond
RTC in STM32F4 supports also subsecond accuracy. But here is one problem. Subsecond depends on values you set for your “prescaler”. STM32F4’s RTC has 2 predivision values:
- ASYNC: 7-bit asynchronous prescaler
- Max value can be 0x7F
- SYNC: 15-bit synchronous prescaler
- Max value can be 0x7FFF
These 2 values are used to “digital” fix inaccuracy for RTC. Equation for RTC 1Hz clock is:
rtc_1hz_clock = RTC_IN_CLK(LSI or LSE) / ((ASYNC + 1) * (SYNC + 1))
By changing these two values, you can set your custom prescaler for RTC, but result should always be one.
You are maybe asking, why I’m posting this in subsecond section. The point is also, that subsecond “resolution” depends on SYNC value. Bigger than this value is, better resolution you will get for subsecond, max of max SYNC value. Subsecond is a downcounter. When it reaches zero (0), seconds are updated and it’s reload value is the same as SYNC value. For that purpose I’ve also edit this two constants:
- Before
- ASYNC: 0x7F
- SYNC: 0x00FF
- Now
- ASYNC: 0x1F
- SYNC: 0x03FF
This give us now 1/1024 of a second resolution. Subseconds are read from RTC registers when you read clock. I have also made some modification to library, which now allows you to custom set values for ASYNC and SYNC. If you need to calibrate your RTC crystal, you can set them to match your crystal frequency.
To get proper milliseconds from last second, you can do some easy math:
- General
- milliseconds = 1000ms – (subseconds * 1000 / SYNC)
- Our case
- milliseconds = 1000ms – (subseconds * 1000 / 1023)
- Subsecond register is downcounter, but we probably want milliseconds to raise with time, so 1000ms – is at the beginning of equation
Open defines.h file and edit settings:
1 2 3 4 5 6 |
/* RTC clock is: f_clk = RTCCLK(LSI or LSE) / ((RTC_SYNC_PREDIV + 1) * (RTC_ASYNC_PREDIV + 1)) */ /* Result of equation above should be "1" or your clock will not be accurate */ /* Sync pre division for clock */ #define RTC_SYNC_PREDIV 0x3FF /* Async pre division for clock */ #define RTC_ASYNC_PREDIV 0x1F |
- When you set this new library, you have to set date and time again, or your settings will not be valid!
- You can not set subseconds, you can just read it
- When you update your clock, subsecond is set to reload value = SYNC value
Set date and time with string
Sometimes, useful function will be to set date and time using string format. For that purpose (in version 1.4) I’ve added this option. Function accepts pointer to string in format below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
/** * Set date and time using string format * * Valid string format is: * * dd.mm.YY.x;HH:ii:ss * * - dd: date, 2 digits, decimal * - mm: month, 2 digits, decimal * - YY: year, last 2 digits, decimal * - x: day in a week: 1 digit, * - HH: hours, 24-hour mode, 2 digits, decimal * - ii: minutes, 2 digits, decimal * - ss: seconds, 2 ditigs, decimal * * Example: * - For date&time Thursday, December 25, 2014 22:05:12, you have to use string below: * 25.12.14.4;22:05:12 * * Parameters: * - char* str: * Date in string format. * * If date and time are valid, function will return TM_RTC_Result_Ok */ extern TM_RTC_Result_t TM_RTC_SetDateTimeString(char* str); |
Example 4 shows you how to use this.
RTC backup registers
RTC features 20 internal backup registers where you can store anything and will be available whole time RTC is active and has power.
My library uses backup register 19 (last one) to store informations about library, so you are not allowed to store data there. This is also prevent using my functions for that.
I’ve made 2 functions for this.
Look at example 5 how to use this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
/** * RTC has 20 backup registers where you can store data which will * be available all the time RTC is running and has power. * * Note: * My library uses register 19 to store info about RTC settings * and is not available for USER to store data there. * * This method allows you to write 32bit value to backup register 0 - 18 * * Note: * RTC HAVE to be initializated first before you can use this method * * Parameters: * - uint8_t location: * RTC backup register location, 0 to 18 allowed * - uint32_t value: * - Value to store to backup register * * No return */ void TM_RTC_WriteBackupRegister(uint8_t location, uint32_t value); /** * RTC has 20 backup registers where you can store data which will * be available all the time RTC is running and has power. * * Note: * My library uses register 19 to store info about RTC settings * and is not available for USER to store data there. * * Note: * RTC HAVE to be initializated first before you can use this method * * This method allows you to read 32bit value from backup register 0 - 18 * * Parameters: * - uint8_t location: * RTC backup register location, 0 to 18 allowed * - uint32_t value: * - Value to store to backup register * * No return */ uint32_t TM_RTC_ReadBackupRegister(uint8_t location); |
Functions and enumerations
Below are listed all functions and enumerations used in this library. In new version 1.1, alarm is supported. How to properly use it, look at example 2.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
/** * Struct for date/time * * Parameters: * - uint8_t seconds: * Seconds parameter, from 00 to 59 * - uint16_t subseconds: * Subsecond downcounter. When it reaches zero, it's reload value is the same as * RTC_SYNC_PREDIV, so in our case 0x3FF = 1023, 1024 steps in one second * - uint8_t minutes: * Minutes parameter, from 00 to 59 * - uint8_t hours: * Hours parameter, 24Hour mode, 00 to 23 * - uint8_t day: * Day in a week, from 1 to 7 * - uint8_t date: * Date in a month, 1 to 31 * - uint8_t month: * Month in a year, 1 to 12 * - uint8_t year: * Year parameter, 00 to 99, 00 is 2000 and 99 is 2099 * - uint32_t unix: * Seconds from 01.01.1970 00:00:00 */ typedef struct { uint8_t seconds; uint16_t subseconds; uint8_t minutes; uint8_t hours; uint8_t day; uint8_t date; uint8_t month; uint8_t year; uint32_t unix; } TM_RTC_t; /* Backward compatibility */ typedef TM_RTC_t TM_RTC_Time_t; /** * Result enumeration * * Parameters: * - TM_RTC_Result_Ok: * Everything OK * - TM_RTC_Result_Error: * An error occured */ typedef enum { TM_RTC_Result_Ok, TM_RTC_Result_Error } TM_RTC_Result_t; /** * Set format of date and time * * Parameters: * - TM_RTC_Format_BIN: * Binary format * - TM_RTC_Format_BCD: * BCD format */ typedef enum { TM_RTC_Format_BIN = 0x00, TM_RTC_Format_BCD } TM_RTC_Format_t; /** * Select RTC to wakeup MCU every X seconds * * Parameters: * - TM_RTC_Int_Disable: * Disable wakeup interrupt * - TM_RTC_Int_60s: * Interrupt every 60 seconds * - TM_RTC_Int_30s: * Interrupt every 30 seconds * - TM_RTC_Int_15s: * Interrupt every 15 seconds * - TM_RTC_Int_10s: * Interrupt every 10 seconds * - TM_RTC_Int_5s: * Interrupt every 5 seconds * - TM_RTC_Int_2s: * Interrupt every 2 seconds * - TM_RTC_Int_1s: * Interrupt every 1 seconds * - TM_RTC_Int_500ms: * Interrupt every 500 milliseconds * - TM_RTC_Int_250ms: * Interrupt every 250 milliseconds * - TM_RTC_Int_125ms: * Interrupt every 125 milliseconds */ typedef enum { TM_RTC_Int_Disable = 0x00, TM_RTC_Int_60s, TM_RTC_Int_30s, TM_RTC_Int_15s, TM_RTC_Int_10s, TM_RTC_Int_5s, TM_RTC_Int_2s, TM_RTC_Int_1s, TM_RTC_Int_500ms, TM_RTC_Int_250ms, TM_RTC_Int_125ms } TM_RTC_Int_t; /** * Use internal or external clock * * Parameters: * - TM_RTC_ClockSource_Internal: * Use internal clock for RTC, LSI oscillator * - TM_RTC_ClockSource_External * Use external, more accurate, clock for RTC, 32768Hz * * Note: Internal clock is not so accurate */ typedef enum { TM_RTC_ClockSource_Internal = 0x00, TM_RTC_ClockSource_External } TM_RTC_ClockSource_t; /** * Use alarm type you will use * * Parameters: * - TM_RTC_AlarmType_DayInWeek * Trigger alarm every day in a week, * days from 1 to 7 (Monday to Sunday) * - TM_RTC_AlarmType_DayInMonth * Trigger alarm every month */ typedef enum { TM_RTC_AlarmType_DayInWeek, TM_RTC_AlarmType_DayInMonth } TM_RTC_AlarmType_t; /** * Alarm identifier you will use * * Parameters: * - TM_RTC_Alarm_A * Work with alarm A * - TM_RTC_Alarm_B * Work with alarm B */ typedef enum { TM_RTC_Alarm_A = 0x00, TM_RTC_Alarm_B } TM_RTC_Alarm_t; /** * Struct for alarm time * * Parameters: * - TM_RTC_AlarmType_t alarmtype: * Alarm type setting. Read TM_RTC_AlarmType_t typedef comment for more info * - uint8_t seconds: * Seconds parameter, from 00 to 59 * - uint8_t minutes: * Minutes parameter, from 00 to 59 * - uint8_t hours: * Hours parameter, 24Hour mode, 00 to 23 * - uint8_t day: * If you select trigger for alarm every week, then this parameter has value between * 1 and 7, representing days in a week, Monday to Sunday * If you select trigger for alarm every month, then this parameter has value between * 1 - 31, representing days in a month. */ typedef struct { TM_RTC_AlarmType_t alarmtype; uint8_t seconds; uint8_t minutes; uint8_t hours; uint8_t day; } TM_RTC_AlarmTime_t; /** * Initialize RTC * * Parameters: * - TM_RTC_ClockSource_t source: * Select RTC clock source * * Returns 0 if RTC was initialized first time (power up), so you know when to set date and time */ uint32_t TM_RTC_Init(TM_RTC_ClockSource_t source); /** * Get number of seconds from 01.01.1970 00:00:00 * * Parameters: * - TM_RTC_Time_t* data: * Struct to time data * * Returns Unix seconds */ uint32_t TM_RTC_GetUnixTimeStamp(TM_RTC_t* data); /** * Get formatted time from seconds till 01.01.1970 00:00:00 * It fills struct with valid data. Valid if year is greater or equal (>=) than 2000 * * Parameters: * - TM_RTC_Time_t* data: * Pointer to TM_RTC_Time_t struct to store formatted data in * - uint32_t unix: * Seconds from 01.01.1970 00:00:00 */ void TM_RTC_GetDateTimeFromUnix(TM_RTC_t* data, uint32_t unix); /** * Select RTC interrupt * * Parameters: * - TM_RTC_Int_t int_value: * Choose struct member from struct TM_RTC_Int_t */ void TM_RTC_Interrupts(TM_RTC_Int_t int_value); /** * Set date and time * * Parameters: * - TM_RTC_Time_t* data: * Struct to time data * - TM_RTC_Format_t format: * Member of struct TM_RTC_Format_t */ TM_RTC_Result_t TM_RTC_SetDateTime(TM_RTC_t* data, TM_RTC_Format_t format); /** * Set date and time using string format * * Valid string format is: * * dd.mm.YY.x;HH:ii:ss * * - dd: date, 2 digits, decimal * - mm: month, 2 digits, decimal * - YY: year, last 2 digits, decimal * - x: day in a week: 1 digit, * - HH: hours, 24-hour mode, 2 digits, decimal * - ii: minutes, 2 digits, decimal * - ss: seconds, 2 ditigs, decimal * * Example: * - For date&time Thursday, December 25, 2014 22:05:12, you have to use string below: * 25.12.14.4;22:05:12 * * Parameters: * - char* str: * Date in string format. * * If date and time are valid, function will return TM_RTC_Result_Ok */ TM_RTC_Result_t TM_RTC_SetDateTimeString(char* str); /** * Get date and time * * Parameters: * - TM_RTC_Time_t* data: * Struct to time data where to save data * - TM_RTC_Format_t format: * Member of struct TM_RTC_Format_t */ void TM_RTC_GetDateTime(TM_RTC_t* data, TM_RTC_Format_t format); /** * Get number of days in month * * Parameters: * - uint8_t month: * Month, for which you need number of days (1 - 12) * - uint8_t year: * Specify year you want to know, last 2 digits only (00-99) */ uint8_t TM_RTC_GetDaysInMonth(uint8_t month, uint8_t year); /** * Get number of days in year * * Parameters: * - uint8_t year: * Specify year you want to know days in, last 2 digits only (00-99) */ uint16_t TM_RTC_GetDaysInYear(uint8_t year); /** * RTC has 20 backup registers where you can store data which will * be available all the time RTC is running and has power. * * Note: * My library uses register 19 to store info about RTC settings * and is not available for USER to store data there. * * This method allows you to write 32bit value to backup register 0 - 18 * * Note: * RTC HAVE to be initializated first before you can use this method * * Parameters: * - uint8_t location: * RTC backup register location, 0 to 18 allowed * - uint32_t value: * - Value to store to backup register * * No return */ void TM_RTC_WriteBackupRegister(uint8_t location, uint32_t value); /** * RTC has 20 backup registers where you can store data which will * be available all the time RTC is running and has power. * * Note: * My library uses register 19 to store info about RTC settings * and is not available for USER to store data there. * * Note: * RTC HAVE to be initializated first before you can use this method * * This method allows you to read 32bit value from backup register 0 - 18 * * Parameters: * - uint8_t location: * RTC backup register location, 0 to 18 allowed * - uint32_t value: * - Value to store to backup register * * No return */ uint32_t TM_RTC_ReadBackupRegister(uint8_t location); /** * Enable Alarm A or Alarm B for RTC * * Parameters: * - TM_RTC_Alarm_t Alarm: * Specify alarm A or B * - TM_RTC_AlarmTime_t* AlarmTime: * Pointer to alarm time struct * TM_RTC_Format_t format: * Stored date and time format, can be binary or "binary coded decimal" */ void TM_RTC_SetAlarm(TM_RTC_Alarm_t Alarm, TM_RTC_AlarmTime_t* AlarmTime, TM_RTC_Format_t format); /** * Disable specific alarm * * Parameters: * - TM_RTC_Alarm_t Alarm: * Select Alarm A or Alarm B to be disabled */ void TM_RTC_DisableAlarm(TM_RTC_Alarm_t Alarm); /** * Custom Request handler for RTC wakeup interrupt * * Called from main handler when wakeup interrupt occured * * With __weak parameter to prevent link errors if not defined by user */ void TM_RTC_RequestHandler(void); /** * Custom Alarm A interrupt handler * * Called when Alarm A interrupt is triggered * * With __weak parameter to prevent link errors if not defined by user */ void TM_RTC_AlarmAHandler(void); /** * Custom Alarm B interrupt handler * * Called when Alarm B interrupt is triggered * * With __weak parameter to prevent link errors if not defined by user */ void TM_RTC_AlarmBHandler(void); |
Common questions
Why is RTC not updated on read?
When i developed this library, I found somekind of a bug on STM32F4 RTC peripheral. If I first read date register and then time register, date was not correct updated on read. If I turn this arround, so first time, then date, everything was perfect.
Example 1
- Wakeup interrupt is triggered each second.
- After trigger, date & time is read from MCU and displayed over USART3 to user.
- If you press button on discovery boards, date & time is set to some other value.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
/** * Keil project example for internal RTC on STM32F4xx devices * * @author Tilen Majerle * @email tilen@majerle.eu * @website http://stm32f4-discovery.net * @ide Keil uVision 5 */ #include "defines.h" #include "stm32f4xx.h" #include "tm_stm32f4_rtc.h" #include "tm_stm32f4_usart.h" #include "tm_stm32f4_disco.h" #include "tm_stm32f4_delay.h" #include <stdio.h> char buf[50], buf2[50]; TM_RTC_Time_t datatime; int main(void) { //Initialize system SystemInit(); //Initialize delay TM_DELAY_Init(); //Initiaize button TM_DISCO_ButtonInit(); //Initialize Leds TM_DISCO_LedInit(); //Initialize USART, TX: PB10, RX: PB11 TM_USART_Init(USART3, TM_USART_PinsPack_1, 115200); //Initialize RTC with internal 32768Hz clock //It's not very accurate if (!TM_RTC_Init(TM_RTC_ClockSource_Internal)) { //RTC was first time initialized //Do your stuf here //eg. set default time } //Set wakeup interrupt every 1 second TM_RTC_Interrupts(TM_RTC_Int_1s); while (1) { //If button pressed if (TM_DISCO_ButtonPressed()) { datatime.hours = 0; datatime.minutes = 59; datatime.seconds = 55; datatime.year = 14; datatime.month = 6; datatime.date = 30; datatime.day = 6; //Set new time TM_RTC_SetDateTime(&datatime, TM_RTC_Format_BIN); } } } //Custom request handler function //Called on wakeup interrupt void TM_RTC_RequestHandler() { //Get time TM_RTC_GetDateTime(&datatime, TM_RTC_Format_BIN); //Format time sprintf(buf, "%02d.%02d.%04d %02d:%02d:%02d Unix: %u\n", datatime.date, datatime.month, datatime.year + 2000, datatime.hours, datatime.minutes, datatime.seconds, datatime.unix ); //Send to USART TM_USART_Puts(USART3, buf); //Toggle LED TM_DISCO_LedToggle(LED_RED | LED_GREEN); } |
Example 2
- Example below works the same as Example 1, except Alarms A and B are activated
- If you press the button, global date & time is set to 21:11:00
- Alarm A is triggered each week at day 1 (Monday is day 1, Sunday is day 7) at time 21:11:05
- After 5 seconds Alarm A is first time triggered
- Alarm B is triggered each month at day 20 at time 21:11:10
- After 10 seconds Alarm B is first time triggered
- Alarm A is triggered each week at day 1 (Monday is day 1, Sunday is day 7) at time 21:11:05
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
/** * Keil project for internal RTC Alarms * * Before you start, select your target, on the right of the "Load" button * * @author Tilen Majerle * @email tilen@majerle.eu * @website http://stm32f4-discovery.net * @ide Keil uVision 5 */ /* Include core modules */ #include "stm32f4xx.h" /* Include my libraries here */ #include "defines.h" #include "tm_stm32f4_rtc.h" #include "tm_stm32f4_usart.h" #include "tm_stm32f4_disco.h" #include "tm_stm32f4_delay.h" #include <stdio.h> char buf[50]; /* Struct for RTC tim */ TM_RTC_Time_t Time; /* Struct for alarm time */ TM_RTC_AlarmTime_t AlarmTime; int main(void) { /* Initialize system */ SystemInit(); /* Initialize delay */ TM_DELAY_Init(); /* Initiaize button */ TM_DISCO_ButtonInit(); /* Initialize Leds */ TM_DISCO_LedInit(); /* Initialize USART, TX: PB10, RX: PB11 */ TM_USART_Init(USART3, TM_USART_PinsPack_1, 115200); /* Initialize RTC with internal 32768Hz clock */ /* It's not very accurate */ if (!TM_RTC_Init(TM_RTC_ClockSource_Internal)) { /* RTC was first time initialized */ /* Do your stuff here */ /* eg. set default time */ } /* Set wakeup interrupt every 1 second */ TM_RTC_Interrupts(TM_RTC_Int_1s); while (1) { /* If button pressed */ if (TM_DISCO_ButtonPressed()) { /* Set new time */ Time.hours = 21; Time.minutes = 11; Time.seconds = 00; Time.year = 14; Time.month = 10; Time.date = 20; Time.day = 1; /* Set new RTC time */ TM_RTC_SetDateTime(&Time, TM_RTC_Format_BIN); /* Set alarm A each day 1 (Monday) in a week */ /* Alarm will be first triggered 5 seconds later as time is configured for RTC */ AlarmTime.hours = Time.hours; AlarmTime.minutes = Time.minutes; AlarmTime.seconds = Time.seconds + 5; AlarmTime.alarmtype = TM_RTC_AlarmType_DayInWeek; AlarmTime.day = 1; /* Set RTC alarm A, time in binary format */ TM_RTC_SetAlarm(TM_RTC_Alarm_A, &AlarmTime, TM_RTC_Format_BIN); /* Set alarm B each 20th day in a month */ /* Alarm will be first triggered 10 seconds later as time is configured for RTC */ AlarmTime.hours = Time.hours; AlarmTime.minutes = Time.minutes; AlarmTime.seconds = Time.seconds + 10; AlarmTime.day = 20; AlarmTime.alarmtype = TM_RTC_AlarmType_DayInMonth; /* Set RTC alarm B, time in binary format */ TM_RTC_SetAlarm(TM_RTC_Alarm_B, &AlarmTime, TM_RTC_Format_BIN); } } } /* Custom request handler function */ /* Called on wakeup interrupt */ void TM_RTC_RequestHandler() { /* Get time */ TM_RTC_GetDateTime(&Time, TM_RTC_Format_BIN); /* Format time */ sprintf(buf, "%02d.%02d.%04d %02d:%02d:%02d Unix: %u\n", Time.date, Time.month, Time.year + 2000, Time.hours, Time.minutes, Time.seconds, Time.unix ); /* Send to USART */ TM_USART_Puts(USART3, buf); /* Toggle LED */ TM_DISCO_LedToggle(LED_RED | LED_GREEN); } /* Custom request handler function */ /* Called on alarm A interrupt */ void TM_RTC_AlarmAHandler(void) { /* Show user to USART */ TM_USART_Puts(USART3, "Alarm A triggered\n"); /* Disable Alarm so it will not trigger next week at the same time */ //TM_RTC_DisableAlarm(TM_RTC_Alarm_A); } /* Custom request handler function */ /* Called on alarm B interrupt */ void TM_RTC_AlarmBHandler(void) { /* Show user to USART */ TM_USART_Puts(USART3, "Alarm B triggered\n"); /* Disable Alarm so it will not trigger next month at the same date and time */ //TM_RTC_DisableAlarm(TM_RTC_Alarm_A); } |
Example 3
- Example below works the same as example 1, just subsecond is added to USART output.
- Wakeup interrupt is set to 125ms, so you can see counter
- Important
- Subsecond register is downcounter, so do not be confused
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
/** * Keil project for internal RTC subseconds * * Before you start, select your target, on the right of the "Load" button * * @author Tilen Majerle * @email tilen@majerle.eu * @website http://stm32f4-discovery.net * @ide Keil uVision 5 */ /* Include core modules */ #include "stm32f4xx.h" /* Include my libraries here */ #include "defines.h" #include "tm_stm32f4_rtc.h" #include "tm_stm32f4_usart.h" #include "tm_stm32f4_disco.h" #include "tm_stm32f4_delay.h" #include <stdio.h> char buf[50]; TM_RTC_Time_t datatime; int main(void) { /* Initialize system */ SystemInit(); /* Initialize delay */ TM_DELAY_Init(); /* Initiaize button */ TM_DISCO_ButtonInit(); /* Initialize Leds */ TM_DISCO_LedInit(); /* Initialize USART, TX: PB10, RX: PB11 */ TM_USART_Init(USART3, TM_USART_PinsPack_1, 115200); /* Initialize RTC with internal 32768Hz clock */ /* It's not very accurate */ if (!TM_RTC_Init(TM_RTC_ClockSource_Internal)) { /* RTC was first time initialized */ /* Do your stuff here */ /* eg. set default time */ } /* Set wakeup interrupt every 125 ms */ TM_RTC_Interrupts(TM_RTC_Int_125ms); while (1) { /* If button pressed */ if (TM_DISCO_ButtonPressed()) { /* Subseconds are ignored when writing new time */ datatime.hours = 0; datatime.minutes = 59; datatime.seconds = 55; datatime.year = 14; datatime.month = 6; datatime.date = 30; datatime.day = 6; /* Set new time */ TM_RTC_SetDateTime(&datatime, TM_RTC_Format_BIN); } } } /* Custom request handler function */ /* Called on wakeup interrupt */ void TM_RTC_RequestHandler() { /* Get time */ TM_RTC_GetDateTime(&datatime, TM_RTC_Format_BIN); /* Format time */ sprintf(buf, "%02d.%02d.%04d %02d:%02d:%02d:%04d Unix: %u\n", datatime.date, datatime.month, datatime.year + 2000, datatime.hours, datatime.minutes, datatime.seconds, datatime.subseconds, /* Subseconds */ datatime.unix ); /* Send to USART */ TM_USART_Puts(USART3, buf); /* Toggle LED */ TM_DISCO_LedToggle(LED_RED | LED_GREEN); } |
Example 4
- USART1 (TX: PB6, RX: PB7, 115200baud) waits for any string (with \n at the end),
- received string is then analyzed with RTC internal checker,
- if date and time are valid, RTC is set
- For example, send from your USART:
- 11.12.14.4;10:45:50\n
- Date: 11
- Month: 12
- Year: 2014
- Day in a week: 4 (Thursday)
- Hours: 10
- Minutes: 45
- Seconds: 45
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
/** * Keil project for internal RTC * * Before you start, select your target, on the right of the "Load" button * * @author Tilen Majerle * @email tilen@majerle.eu * @website http://stm32f4-discovery.net * @ide Keil uVision 5 * @packs STM32F4xx Keil packs version 2.2.0 or greater required * @stdperiph STM32F4xx Standard peripheral drivers version 1.4.0 or greater required */ /* Include core modules */ #include "stm32f4xx.h" /* Include my libraries here */ #include "defines.h" #include "tm_stm32f4_rtc.h" #include "tm_stm32f4_usart.h" #include "tm_stm32f4_disco.h" #include "tm_stm32f4_delay.h" #include <stdio.h> char buf[50]; TM_RTC_Time_t datatime; int main(void) { char buff[100]; /* Initialize system */ SystemInit(); /* Initialize delay */ TM_DELAY_Init(); /* Initiaize button */ TM_DISCO_ButtonInit(); /* Initialize Leds */ TM_DISCO_LedInit(); /* Initialize USART, TX: PB6, RX: PB7 */ TM_USART_Init(USART1, TM_USART_PinsPack_2, 115200); /* Initialize RTC with internal 32768Hz clock */ /* It's not very accurate */ if (!TM_RTC_Init(TM_RTC_ClockSource_Internal)) { /* RTC was first time initialized */ /* Do your stuff here */ /* eg. set default time */ } while (1) { /* If string received from USART1 */ if (TM_USART_Gets(USART1, buff, sizeof(buff))) { /* Try to set date and time */ if (TM_RTC_SetDateTimeString(buff) == TM_RTC_Result_Ok) { /* Date and time written OK */ /* Send OK back */ TM_USART_Puts(USART1, "Written OK\n"); } else { /* Send error sring */ TM_USART_Puts(USART1, "Error\n"); } } } } |
Example 5
- If RTC is first time initialized, data is store to RTC backup register 4
- If you press button on discovery/nucleo board, board will reset
- After reset, RTC is still initialized and you will be able to test if written data is the same as read
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
/** * Keil project for internal RTC backup registers * * Before you start, select your target, on the right of the "Load" button * * @author Tilen Majerle * @email tilen@majerle.eu * @website http://stm32f4-discovery.net * @ide Keil uVision 5 * @packs STM32F4xx Keil packs version 2.2.0 or greater required * @stdperiph STM32F4xx Standard peripheral drivers version 1.4.0 or greater required */ /* Include core modules */ #include "stm32f4xx.h" /* Include my libraries here */ #include "defines.h" #include "tm_stm32f4_rtc.h" #include "tm_stm32f4_disco.h" #include "tm_stm32f4_delay.h" #include <stdio.h> int main(void) { /* Initialize system */ SystemInit(); /* Initialize delay */ TM_DELAY_Init(); /* Initiaize button */ TM_DISCO_ButtonInit(); /* Initialize Leds */ TM_DISCO_LedInit(); /* Initialize RTC with internal 32768Hz clock */ /* It's not very accurate */ if (!TM_RTC_Init(TM_RTC_ClockSource_Internal)) { /* RTC was first time initialized */ /* Do your stuff here */ /* eg. set default time */ /* Write data to backup register 4 */ TM_RTC_WriteBackupRegister(4, 0x1244); /* Turn on RED led = Write operation */ TM_DISCO_LedOn(LED_RED); } else { /* Try to read data back and check if it is OK */ if (TM_RTC_ReadBackupRegister(4) == 0x1244) { /* Read OK after reset, turn on GREEN led */ TM_DISCO_LedOn(LED_GREEN); } } /* If not leds ON, try to remove power from device first so RTC will need to initialize again */ /* If still no leds after that, then you have problems with your RTC clock */ while (1) { /* If button pressed */ if (TM_DISCO_ButtonPressed()) { /* After button press, system will reset */ NVIC_SystemReset(); } } } |
Projects are available at Github, download library below.
Internal RTC on STM32F4xx
Recent comments