diff --git a/.github/workflows/Continuous-Integration.yml b/.github/workflows/Continuous-Integration.yml index fa839df..abee14e 100644 --- a/.github/workflows/Continuous-Integration.yml +++ b/.github/workflows/Continuous-Integration.yml @@ -27,7 +27,6 @@ jobs: id: Compile uses: stm32duino/actions/compile-examples@main with: - board-pattern: ".*NUCLEO.*" custom-config: "./extras/rtc_cores_config.json" # Use the output from the `Compile` step diff --git a/README.md b/README.md index dc9fdd6..bc11b65 100644 --- a/README.md +++ b/README.md @@ -31,11 +31,11 @@ _RTC hours mode (12 or 24)_ _RTC clock source_ * **`Source_Clock getClockSource(void)`** : get current clock source. -* **`void setClockSource(Source_Clock source)`** : this function must be called before `begin()`. +* **`void setClockSource(Source_Clock source, uint32_t predivA, uint32_t predivS)`** : set the clock source (`LSI_CLOCK`, `LSE_CLOCK` or `HSE_CLOCK`) and (a)synchronous prescaler values. This function must be called before `begin()`. Use `(PREDIVA_MAX + 1)` and `(PREDIVS_MAX +1)` to reset value and use computed ones. Those values have to match the following conditions: **_1Hz = RTC CLK source / ((predivA + 1) * (predivS + 1))_** _RTC Asynchronous and Synchronous prescaler_ -* **`void getPrediv(int8_t *predivA, int16_t *predivS)`** : get (a)synchronous prescaler values if set else computed ones for the current clock source. -* **`void setPrediv(int8_t predivA, int16_t predivS)`** : set (a)synchronous prescaler values. This function must be called before `begin()`. Use -1 to reset value and use computed ones. Those values have to match the following conditions: **_1Hz = RTC CLK source / ((predivA + 1) * (predivS + 1))_** +* **`void getPrediv(uint32_t *predivA, uint32_t *predivS)`** : get (a)synchronous prescaler values if set else computed ones for the current clock source. +* **`void setPrediv(uint32_t predivA, uint32_t predivS)`** : set (a)synchronous prescaler values. This function must be called before `begin()`. Use `(PREDIVA_MAX + 1)` and `(PREDIVS_MAX +1)` to reset value and use computed ones. Those values have to match the following conditions: **_1Hz = RTC CLK source / ((predivA + 1) * (predivS + 1))_** _SubSeconds management_ * **`uint32_t getSubSeconds(void)`** @@ -153,6 +153,40 @@ It is possible to use it thanks all alarm API with an extra argument: rtc.enableAlarm(rtc.MATCH_DHHMMSS, STM32RTC::ALARM_B); ``` +### Since STM32RTC version higher than 1.3.7 +_Get the RTC handle_ + +* **`RTC_HandleTypeDef *RTC_GetHandle(void)`** + +_binary and mix modes_ + +Some STM32 RTC have a binary mode with 32-bit free-running counter +in addition to their BCD mode for calendar (for example stm32wl55). +Combined with BCD this is the MIX mode. Only using the binary counter is the BIN mode. +Three RTC functional modes are available: + - `STM32RTC::MODE_BCD` + - `STM32RTC::MODE_MIX` + - `STM32RTC::MODE_BIN` + +* **`Binary_Mode getBinaryMode(void);`** +* **`void setBinaryMode(Binary_Mode mode);`** + + +Any API using the Subsecond parameter is expressed in milliseconds +whatever the RTC input clock. This parameter is [0..999] in MIX or BCD mode +and [0..0xFFFFFFFF] in BIN mode. In BIN only mode, time and date registers are not used +by the RTC. Thus the getEpoch function is only to be called to get the subsecond [0..0xFFFFFFFF] +(returned time_t is not valid). The setAlarmEpoch only uses the sub-second [0..0xFFFFFFFF] +(time_t value is useless). + +_SubSeconds underflow_ + +Only dor STM32WLxx. Manage interrupt (SSRU) when SubSeconds register +underflow. Used by STM32LoRaWAN. + +* **`void attachSubSecondsUnderflowInterrupt(voidFuncPtr callback);`** +* **`void detachSubSecondsUnderflowInterrupt(void);`** + Refer to the Arduino RTC documentation for the other functions http://arduino.cc/en/Reference/RTC diff --git a/examples/RTCReset/RTCReset.ino b/examples/RTCReset/RTCReset.ino index ef55302..f05ea6e 100644 --- a/examples/RTCReset/RTCReset.ino +++ b/examples/RTCReset/RTCReset.ino @@ -36,9 +36,9 @@ typedef struct { bool alarm_a; } cb_data_t; -static cb_data_t atime = { 2222, true}; +static cb_data_t atime = { 2222, true }; #ifdef RTC_ALARM_B -static cb_data_t btime = { 3333, false}; +static cb_data_t btime = { 3333, false }; #endif static byte seconds = 0; static byte minutes = 0; @@ -64,7 +64,7 @@ static uint8_t conv2d(const char* p) { } // sample input: date = "Dec 26 2009", time = "12:34:56" -void initDateTime (void) { +void initDateTime(void) { Serial.printf("Build date & time %s, %s\n", mydate, mytime); year = conv2d(mydate + 9); @@ -89,15 +89,15 @@ void initDateTime (void) { seconds = conv2d(mytime + 6); } -void setup() -{ +void setup() { pinMode(USER_BTN, INPUT_PULLUP); int32_t default_state = digitalRead(USER_BTN); - - Serial.begin(9600); - while (!Serial); + Serial.begin(115200); + while (!Serial) + ; // Wait user input to start - while (digitalRead(USER_BTN) == default_state); + while (digitalRead(USER_BTN) == default_state) + ; // Convenient function to init date and time variables initDateTime(); @@ -110,7 +110,7 @@ void setup() #ifdef RTC_ALARM_B rtc.attachInterrupt(alarmMatch, &btime, STM32RTC::ALARM_B); #endif - rtc.begin(); // Initialize RTC 24H format + rtc.begin(); // Initialize RTC 24H format if (!rtc.isTimeSet()) { Serial.printf("RTC time not set\n Set it.\n"); // Set the time @@ -129,6 +129,10 @@ void setup() } else { // RTC already initialized time_t epoc, alarm_epoc; + rtc.getTime(&hours, &minutes, &seconds, &subSeconds, &period); + year = rtc.getYear(); + month = rtc.getMonth(); + day = rtc.getDay(); if (rtc.isAlarmEnabled()) { rtc.enableAlarm(rtc.MATCH_DHHMMSS); alarm_epoc = rtc.getAlarmEpoch(); @@ -156,16 +160,28 @@ void setup() #endif Serial.printf("RTC time already set\n"); } - Serial.printf("Alarm A enable status: %s\n", (rtc.isAlarmEnabled(STM32RTC::ALARM_A)) ? "True" : "False"); + // For STM32F1xx series, alarm is always disabled after a reset. + bool alarmA = rtc.isAlarmEnabled(STM32RTC::ALARM_A); + Serial.printf("Alarm A enable status: %s\n", (alarmA) ? "True" : "False"); + if (!alarmA) { + rtc.setAlarmDay(day); + rtc.setAlarmTime(hours, minutes, seconds + 5, 567); + rtc.enableAlarm(rtc.MATCH_DHHMMSS); + } #ifdef RTC_ALARM_B - Serial.printf("Alarm B enable status: %s\n", (rtc.isAlarmEnabled(STM32RTC::ALARM_B)) ? "True" : "False"); + bool alarmB = rtc.isAlarmEnabled(STM32RTC::ALARM_B); + Serial.printf("Alarm B enable status: %s\n", (alarmB) ? "True" : "False"); + if (!alarmB) { + rtc.setAlarmDay(day, STM32RTC::ALARM_B); + rtc.setAlarmTime(hours, minutes, seconds + 5, 567, STM32RTC::ALARM_B); + rtc.enableAlarm(rtc.MATCH_DHHMMSS, STM32RTC::ALARM_B); + } #else Serial.println("Alarm B not available."); #endif } -void loop() -{ +void loop() { rtc.getTime(&hours, &minutes, &seconds, &subSeconds, &period); // Print current date & time Serial.printf("\n%02d/%02d/%02d %02d:%02d:%02d.%03d\n", rtc.getDay(), rtc.getMonth(), rtc.getYear(), hours, minutes, seconds, subSeconds); @@ -177,13 +193,12 @@ void loop() delay(1000); } -void alarmMatch(void *data) -{ +void alarmMatch(void* data) { time_t epoc; uint32_t epoc_ms; uint32_t sec = 0; uint32_t _millis = 1000; - cb_data_t cbdata = {.next = 1000, .alarm_a = true}; + cb_data_t cbdata = { .next = 1000, .alarm_a = true }; if (data != NULL) { cbdata.next = ((cb_data_t*)data)->next; cbdata.alarm_a = ((cb_data_t*)data)->alarm_a; @@ -204,7 +219,7 @@ void alarmMatch(void *data) // Update epoch_ms - might need to add a second to epoch epoc_ms += _millis; if (epoc_ms >= 1000) { - sec ++; + sec++; epoc_ms -= 1000; } #endif diff --git a/examples/bin_onlyRTCAlarm/bin_onlyRTCAlarm.ino b/examples/bin_onlyRTCAlarm/bin_onlyRTCAlarm.ino new file mode 100644 index 0000000..e10d869 --- /dev/null +++ b/examples/bin_onlyRTCAlarm/bin_onlyRTCAlarm.ino @@ -0,0 +1,85 @@ +/* + mode BINary only RTC alarm + + This sketch shows how to configure the alarm A & B of the RTC in BIN mode + + Creation 12 Dec 2017 + by Wi6Labs + Modified 03 Jul 2020 + by Frederic Pillon for STMicroelectronics + Modified 03 sept 2023 + by Francois Ramu for STMicroelectronics + + This example code is in the public domain. + + https://github.com/stm32duino/STM32RTC +*/ + +#include + +/* Get the rtc object */ +STM32RTC& rtc = STM32RTC::getInstance(); + +uint32_t timeout; + +void setup() +{ + Serial.begin(115200); + + // Select RTC clock source: LSI_CLOCK, LSE_CLOCK or HSE_CLOCK. + rtc.setClockSource(STM32RTC::LSE_CLOCK); + + /* Configure the RTC mode */ + rtc.setBinaryMode(STM32RTC::MODE_BIN); + + /* in BIN mode time and Date register are not used, only the subsecond register for milisseconds */ + rtc.begin(true, STM32RTC::HOUR_24); + + /* wait for a while */ + delay(300); + + /* subsecond expressed in milliseconds */ + Serial.printf("Start at %d ms \r\n", rtc.getSubSeconds()); + + /* Attach the callback function before enabling Interrupt */ + rtc.attachInterrupt(alarmAMatch); + + /* Program the AlarmA in 12 seconds */ + rtc.setAlarmTime(0, 0, 0, 12000); + rtc.enableAlarm(rtc.MATCH_SUBSEC); + Serial.printf("Set Alarm A in 12s (at %d ms)\r\n", rtc.getAlarmSubSeconds()); + +#ifdef RTC_ALARM_B + /* Program ALARM B in 600ms ms from now (keep timeout < 1000ms) */ + timeout = rtc.getSubSeconds() + 600; + + rtc.attachInterrupt(alarmBMatch, STM32RTC::ALARM_B); + rtc.setAlarmSubSeconds(timeout, STM32RTC::ALARM_B); + rtc.enableAlarm(rtc.MATCH_SUBSEC, STM32RTC::ALARM_B); + Serial.printf("Set Alarm B (in %d ms) at %d ms\r\n", 600, + rtc.getAlarmSubSeconds(STM32RTC::ALARM_B)); +#endif /* RTC_ALARM_B */ + +} + +void loop() +{ + +} + +void alarmAMatch(void *data) +{ + UNUSED(data); + rtc.disableAlarm(STM32RTC::ALARM_A); + Serial.printf("Alarm A Match at %d ms \r\n", rtc.getSubSeconds()); +} + +#ifdef RTC_ALARM_B +void alarmBMatch(void *data) +{ + UNUSED(data); + rtc.disableAlarm(STM32RTC::ALARM_B); /* Else it will trig again */ + Serial.printf("Alarm B Match at %d ms\r\n", rtc.getSubSeconds()); +} +#endif /* RTC_ALARM_B */ + diff --git a/examples/mixRTCAlarm/mixRTCAlarm.ino b/examples/mixRTCAlarm/mixRTCAlarm.ino new file mode 100644 index 0000000..429181d --- /dev/null +++ b/examples/mixRTCAlarm/mixRTCAlarm.ino @@ -0,0 +1,99 @@ +/* + mode Mix RTC alarm + + This sketch shows how to configure the alarm A & B (if exists) + of the RTC in MIX or BCD (BINARY none) mode + + Creation 12 Dec 2017 + by Wi6Labs + Modified 03 Jul 2020 + by Frederic Pillon for STMicroelectronics + Modified 03 Jul 2023 + by Francois Ramu for STMicroelectronics + + This example code is in the public domain. + + https://github.com/stm32duino/STM32RTC +*/ + +#include + +/* Get the rtc object */ +STM32RTC& rtc = STM32RTC::getInstance(); + +/* Change these values to set the current initial time */ +const byte seconds = 06; +const byte minutes = 22; +const byte hours = 16; + +/* Change these values to set the current initial date */ +const byte day = 25; +const byte month = 6; +const byte year = 23; + +uint32_t timeout; + +void setup() +{ + Serial.begin(115200); + + // Select RTC clock source: LSI_CLOCK, LSE_CLOCK or HSE_CLOCK. + rtc.setClockSource(STM32RTC::LSE_CLOCK); + + /* Configure the RTC mode : STM32RTC::MODE_MIX or STM32RTC::MODE_BCD */ + rtc.setBinaryMode(STM32RTC::MODE_BCD); + + rtc.begin(true, STM32RTC::HOUR_24); + + rtc.setTime(hours, minutes, seconds); + rtc.setDate(day, month, year); + + /* wait for a while */ + delay(300); + + Serial.printf("Start at %02d:%02d:%02d.%03d\r\n", + rtc.getHours(), rtc.getMinutes(), rtc.getSeconds(), rtc.getSubSeconds()); + + /* Attach the callback function before enabling Interrupt */ + rtc.attachInterrupt(alarmAMatch); + + /* Program the AlarmA in 12 seconds */ + rtc.setAlarmDay(day); + rtc.setAlarmTime(hours, minutes, seconds + 12); + rtc.enableAlarm(rtc.MATCH_DHHMMSS); + Serial.printf("Set Alarm A in 12s (at %02d:%02d:%02d)\r\n", + rtc.getAlarmHours(), rtc.getAlarmMinutes(), rtc.getAlarmSeconds()); + +#ifdef RTC_ALARM_B + /* Program ALARM B in 600ms ms from now (keep timeout < 1000ms) */ + timeout = rtc.getSubSeconds() + 600; + + rtc.attachInterrupt(alarmBMatch, STM32RTC::ALARM_B); + rtc.setAlarmSubSeconds(timeout, STM32RTC::ALARM_B); + rtc.enableAlarm(rtc.MATCH_SUBSEC, STM32RTC::ALARM_B); + Serial.printf("Set Alarm B (in %d ms) at %d ms\r\n", 600, + rtc.getAlarmSubSeconds(STM32RTC::ALARM_B)); +#endif /* RTC_ALARM_B */ +} + +void loop() +{ + /* Just wait for Alarm */ +} + +void alarmAMatch(void *data) +{ + UNUSED(data); + rtc.disableAlarm(STM32RTC::ALARM_A); + Serial.printf("Alarm A Match at %02d:%02d:%02d\r\n", + rtc.getHours(), rtc.getMinutes(), rtc.getSeconds()); +} + +#ifdef RTC_ALARM_B +void alarmBMatch(void *data) +{ + UNUSED(data); + rtc.disableAlarm(STM32RTC::ALARM_B); + Serial.printf("Alarm B Match at %d ms\r\n", rtc.getSubSeconds()); +} +#endif /* RTC_ALARM_B */ diff --git a/extras/rtc_cores_config.json b/extras/rtc_cores_config.json index 8b3ed50..32a8293 100644 --- a/extras/rtc_cores_config.json +++ b/extras/rtc_cores_config.json @@ -28,175 +28,823 @@ "boards": [ "AFROFLIGHT_F103CB_12M", "BLACKPILL_F103CB", + "BLACKPILL_F401CC", "BLACK_F407VG", - "BLACK_F407ZE", "BLACK_F407ZG", - "BLUEBUTTON_F103R8T", "BLUEBUTTON_F103RCT", - "BLUEPILL_F103C6", "BLUEPILL_F103CB", - "DEMO_F030F4_16M", "DEMO_F030F4_HSI", - "GENERIC_F042C4TX", "GENERIC_F042C4UX", "GENERIC_F042C6TX", "GENERIC_F042K4TX", + "BLACK_F407ZE", + "BLACK_F407ZG", + "BLUEBUTTON_F103R8T", + "BLUEBUTTON_F103RCT", + "BLUEPILL_F103C6", + "BLUEPILL_F103CB", + "DEMO_F030F4_16M", + "DEMO_F030F4_HSI", + "GENERIC_C011D6YX", + "GENERIC_C011F4PX", + "GENERIC_C011F4UX", + "GENERIC_C011F6PX", + "GENERIC_C011F6UX", + "GENERIC_C031C4TX", + "GENERIC_C031C4UX", + "GENERIC_C031C6TX", + "GENERIC_C031F4PX", + "GENERIC_F031C4TX", + "GENERIC_F031E6YX", + "GENERIC_F031F4PX", + "GENERIC_F031G4UX", + "GENERIC_F031K4UX", + "GENERIC_F042C4TX", + "GENERIC_F042C4UX", + "GENERIC_F042C6TX", + "GENERIC_F042F4PX", + "GENERIC_F042G4UX", + "GENERIC_F042K4TX", + "GENERIC_F051C4TX", "GENERIC_F051K6UX", - "GENERIC_F072C8TX", "GENERIC_F072C8UX", "GENERIC_F072CBTX", "GENERIC_F072CBUX", - "GENERIC_F072R8TX", "GENERIC_F072RBHX", "GENERIC_F072RBIX", - "GENERIC_F091RCHX", "GENERIC_F091RCTX", "GENERIC_F091RCYX", - "GENERIC_F100C4TX", "GENERIC_F100C8TX", + "GENERIC_F058R8HX", + "GENERIC_F071C8TX", + "GENERIC_F071C8UX", + "GENERIC_F071CBTX", + "GENERIC_F071CBUX", + "GENERIC_F071V8HX", + "GENERIC_F071V8TX", + "GENERIC_F071VBHX", + "GENERIC_F072C8TX", + "GENERIC_F072C8UX", + "GENERIC_F072CBTX", + "GENERIC_F072CBUX", + "GENERIC_F072R8TX", + "GENERIC_F072RBHX", + "GENERIC_F072RBTX", + "GENERIC_F072V8HX", + "GENERIC_F072V8TX", + "GENERIC_F072VBHX", + "GENERIC_F078CBTX", + "GENERIC_F078CBUX", + "GENERIC_F078RBHX", + "GENERIC_F078VBHX", + "GENERIC_F091CBTX", + "GENERIC_F091CBUX", + "GENERIC_F091CCTX", + "GENERIC_F091RBTX", + "GENERIC_F091RCHX", + "GENERIC_F091RCTX", + "GENERIC_F091VBTX", + "GENERIC_F091VCTX", + "GENERIC_F098CCTX", + "GENERIC_F098RCHX", + "GENERIC_F098RCTX", + "GENERIC_F098VCHX", + "GENERIC_F100C4TX", + "GENERIC_F100C8TX", + "GENERIC_F100R4HX", "GENERIC_F100R8TX", - "GENERIC_F103C4TX", "GENERIC_F103C6UX", "GENERIC_F103C8TX", "GENERIC_F103CBUX", - "GENERIC_F103R4HX", "GENERIC_F103R4TX", "GENERIC_F103R8HX", "GENERIC_F103R8TX", - "GENERIC_F103RCTX", "GENERIC_F103RDTX", "GENERIC_F103RCYX", "GENERIC_F103RDYX", "GENERIC_F103RFTX", - "GENERIC_F103T4UX", "GENERIC_F103T8UX", - "GENERIC_F103V8HX", "GENERIC_F103V8TX", "GENERIC_F103VBHX","GENERIC_F103VBIX", - "GENERIC_F103VCHX", "GENERIC_F103VCTX", "GENERIC_F103VDHX", "GENERIC_F103VDTX", "GENERIC_F103VEHX", + "GENERIC_F100V8TX", + "GENERIC_F100ZCTX", + "GENERIC_F100ZDTX", + "GENERIC_F101C4TX", + "GENERIC_F101R4TX", + "GENERIC_F101T4UX", + "GENERIC_F101V8TX", + "GENERIC_F101ZCTX", + "GENERIC_F101ZDTX", + "GENERIC_F103C4TX", + "GENERIC_F103C6TX", + "GENERIC_F103C8TX", + "GENERIC_F103CBTX", + "GENERIC_F103R4HX", + "GENERIC_F103R4TX", + "GENERIC_F103R8HX", + "GENERIC_F103R8TX", + "GENERIC_F103RCTX", + "GENERIC_F103RCYX", + "GENERIC_F103RDTX", + "GENERIC_F103RDYX", + "GENERIC_F103RFTX", + "GENERIC_F103T4UX", + "GENERIC_F103T8UX", + "GENERIC_F103V8HX", + "GENERIC_F103V8TX", + "GENERIC_F103VBHX", + "GENERIC_F103VBTX", + "GENERIC_F103VCHX", + "GENERIC_F103VCTX", + "GENERIC_F103VDHX", + "GENERIC_F103VDTX", + "GENERIC_F103VEHX", "GENERIC_F103VFTX", - "GENERIC_F103ZCHX", "GENERIC_F103ZCTX", "GENERIC_F103ZDHX", "GENERIC_F103ZDTX", "GENERIC_F103ZEHX", - "GENERIC_F103ZFHX", "GENERIC_F103ZFTX", "GENERIC_F103ZGHX", - "GENERIC_F207ZCTX", "GENERIC_F207ZETX", "GENERIC_F207ZFTX", - "GENERIC_F217ZETX", "GENERIC_F217ZGTX", + "GENERIC_F103ZCHX", + "GENERIC_F103ZCTX", + "GENERIC_F103ZDHX", + "GENERIC_F103ZDTX", + "GENERIC_F103ZEHX", + "GENERIC_F103ZFHX", + "GENERIC_F103ZFTX", + "GENERIC_F103ZGHX", + "GENERIC_F205RBTX", + "GENERIC_F205RCTX", + "GENERIC_F205RETX", + "GENERIC_F205REYX", + "GENERIC_F205RFTX", + "GENERIC_F205RGEX", + "GENERIC_F205RGTX", + "GENERIC_F205RGYX", + "GENERIC_F205VBTX", + "GENERIC_F205VCTX", + "GENERIC_F205VETX", + "GENERIC_F205VFTX", + "GENERIC_F205VGTX", + "GENERIC_F205ZCTX", + "GENERIC_F205ZETX", + "GENERIC_F205ZFTX", + "GENERIC_F205ZGTX", + "GENERIC_F207ICHX", + "GENERIC_F207ICTX", + "GENERIC_F207IEHX", + "GENERIC_F207IETX", + "GENERIC_F207IFHX", + "GENERIC_F207IFTX", + "GENERIC_F207IGHX", + "GENERIC_F207IGTX", + "GENERIC_F207VCTX", + "GENERIC_F207VETX", + "GENERIC_F207VFTX", + "GENERIC_F207VGTX", + "GENERIC_F207ZCTX", + "GENERIC_F207ZETX", + "GENERIC_F207ZFTX", + "GENERIC_F207ZGTX", + "GENERIC_F215RETX", + "GENERIC_F215VETX", + "GENERIC_F215ZETX", + "GENERIC_F217IEHX", + "GENERIC_F217IETX", + "GENERIC_F217IGHX", + "GENERIC_F217VETX", + "GENERIC_F217ZETX", + "GENERIC_F301C6TX", + "GENERIC_F301C8TX", + "GENERIC_F301K6TX", + "GENERIC_F301R6TX", "GENERIC_F302R6TX", + "GENERIC_F303C6TX", + "GENERIC_F303C8TX", "GENERIC_F303CBTX", "GENERIC_F303K6TX", + "GENERIC_F303K8TX", + "GENERIC_F303R6TX", + "GENERIC_F303R8TX", "GENERIC_F303RBTX", "GENERIC_F303RDTX", - "GENERIC_F303VCTX", - "GENERIC_F334K4TX", "GENERIC_F334K6TX", "GENERIC_F334K8TX", - "GENERIC_F401CBUX", "GENERIC_F401CBYX", "GENERIC_F401CCFX", "GENERIC_F401CCYX", - "GENERIC_F401CDUX", "GENERIC_F401CDYX", "GENERIC_F401CEUX", "GENERIC_F401CEYX", - "GENERIC_F401RBTX", "GENERIC_F401RCTX", "GENERIC_F401RDTX", - "GENERIC_F401VBTX", "GENERIC_F401VCTX", "GENERIC_F401VDTX", + "GENERIC_F303VBTX", + "GENERIC_F318C8TX", + "GENERIC_F334C4TX", + "GENERIC_F334C6TX", + "GENERIC_F334K4TX", + "GENERIC_F334K6TX", + "GENERIC_F334R6TX", + "GENERIC_F378RCTX", + "GENERIC_F378VCHX", + "GENERIC_F401CBUX", + "GENERIC_F401CBYX", + "GENERIC_F401CCUX", + "GENERIC_F401CCYX", + "GENERIC_F401CDUX", + "GENERIC_F401CDYX", + "GENERIC_F401CEUX", + "GENERIC_F401CEYX", + "GENERIC_F401RBTX", + "GENERIC_F401RCTX", + "GENERIC_F401RDTX", + "GENERIC_F401VBTX", + "GENERIC_F401VCTX", + "GENERIC_F401VDTX", + "GENERIC_F405RGTX", "GENERIC_F407VETX", + "GENERIC_F407VGTX", + "GENERIC_F407ZETX", "GENERIC_F407ZGTX", - "GENERIC_F410C8TX", "GENERIC_F410C8UX", - "GENERIC_F410R8IX", "GENERIC_F410R8TX", "GENERIC_F410RBIX", - "GENERIC_F411CCUX", "GENERIC_F411CCYX", "GENERIC_F411CEYX", + "GENERIC_F410C8TX", + "GENERIC_F410C8UX", + "GENERIC_F410R8IX", + "GENERIC_F410R8TX", + "GENERIC_F410RBIX", + "GENERIC_F410T8YX", + "GENERIC_F411CCUX", + "GENERIC_F411CCYX", + "GENERIC_F411CEUX", "GENERIC_F411RCTX", - "GENERIC_F412CGUX", - "GENERIC_F412REYX", "GENERIC_F412REYXP", - "GENERIC_F412RGTX", "GENERIC_F412RGYX", "GENERIC_F412RGYXP", + "GENERIC_F412CEUX", + "GENERIC_F412RETX", + "GENERIC_F412REYX", + "GENERIC_F412REYXP", + "GENERIC_F412RGTX", + "GENERIC_F412RGYX", + "GENERIC_F412ZEJX", + "GENERIC_F412ZETX", + "GENERIC_F412ZGJX", + "GENERIC_F413CGUX", "GENERIC_F413CHUX", + "GENERIC_F413RGTX", "GENERIC_F413RHTX", - "GENERIC_F413ZGJX", "GENERIC_F413ZGTX", "GENERIC_F413ZHJX", - "GENERIC_F415RGTX", - "GENERIC_F417VETX", "GENERIC_F417VGTX", - "GENERIC_F417ZETX", "GENERIC_F417ZGTX", - "GENERIC_F423CHUX", - "GENERIC_F423RHTX", - "GENERIC_F423ZHJX", "GENERIC_F423ZHTX", - "GENERIC_F427ZGTX", "GENERIC_F427ZITX", - "GENERIC_F429ZETX", "GENERIC_F429ZGTX", "GENERIC_F429ZGYX", "GENERIC_F429ZIYX", - "GENERIC_F437ZGTX", "GENERIC_F437ZITX", - "GENERIC_F439ZGTX", "GENERIC_F439ZGYX", "GENERIC_F439ZITX", "GENERIC_F439ZIYX", + "GENERIC_F413ZGJX", + "GENERIC_F413ZGTX", + "GENERIC_F413ZHJX", + "GENERIC_F413ZHTX", + "GENERIC_F417VETX", + "GENERIC_F417ZETX", + "GENERIC_F423ZHJX", + "GENERIC_F427ZGTX", + "GENERIC_F427ZITX", + "GENERIC_F429ZETX", + "GENERIC_F429ZGTX", + "GENERIC_F429ZGYX", + "GENERIC_F429ZITX", + "GENERIC_F429ZIYX", + "GENERIC_F437ZGTX", + "GENERIC_F437ZITX", + "GENERIC_F439ZGTX", + "GENERIC_F439ZGYX", + "GENERIC_F439ZITX", "GENERIC_F446RCTX", - "GENERIC_F446VETX", - "GENERIC_F745ZETX", "GENERIC_F745ZGTX", - "GENERIC_F746BETX", "GENERIC_F746BGTX", "GENERIC_F746NEHX", - "GENERIC_F746ZETX", "GENERIC_F746ZEYX", "GENERIC_F746ZGYX", + "GENERIC_F446VCTX", + "GENERIC_F446ZCHX", + "GENERIC_F446ZCJX", + "GENERIC_F446ZCTX", + "GENERIC_F446ZEHX", + "GENERIC_F446ZEJX", + "GENERIC_F722RCTX", + "GENERIC_F722RETX", + "GENERIC_F722ZCTX", + "GENERIC_F722ZETX", + "GENERIC_F730R8TX", + "GENERIC_F745ZETX", + "GENERIC_F745ZGTX", + "GENERIC_F746BETX", + "GENERIC_F746BGTX", + "GENERIC_F746NEHX", + "GENERIC_F746NGHX", + "GENERIC_F746ZETX", + "GENERIC_F746ZEYX", + "GENERIC_F746ZGTX", + "GENERIC_F746ZGYX", "GENERIC_F750N8HX", "GENERIC_F750Z8TX", - "GENERIC_F756BGTX", "GENERIC_F756NGHX", - "GENERIC_F756ZGTX", "GENERIC_F756ZGYX", - "GENERIC_F765VGHX", "GENERIC_F765VIHX", "GENERIC_F765VITX", - "GENERIC_F767VGHX", "GENERIC_F767VGTX", "GENERIC_F767VIHX", "GENERIC_F767VITX", - "GENERIC_F765ZGTX", "GENERIC_F765ZITX", + "GENERIC_F756BGTX", + "GENERIC_F756ZGTX", + "GENERIC_F765IGKX", + "GENERIC_F765IGTX", + "GENERIC_F765IIKX", + "GENERIC_F765IITX", + "GENERIC_F765VGHX", + "GENERIC_F765VGTX", + "GENERIC_F765VIHX", + "GENERIC_F765VITX", + "GENERIC_F765ZGTX", + "GENERIC_F765ZITX", + "GENERIC_F767IGKX", + "GENERIC_F767IGTX", + "GENERIC_F767IIKX", + "GENERIC_F767IITX", + "GENERIC_F767VGHX", + "GENERIC_F767VGTX", + "GENERIC_F767VIHX", + "GENERIC_F767VITX", "GENERIC_F767ZGTX", - "GENERIC_F777VIHX", "GENERIC_F777VITX", - "GENERIC_F777ZITX", + "GENERIC_F767ZITX", + "GENERIC_F777IIKX", + "GENERIC_F777VIHX", + "GENERIC_G030C6TX", "GENERIC_G030K6TX", - "GENERIC_G031J4MX", "GENERIC_G031J6MX", - "GENERIC_G031K4TX", "GENERIC_G031K4UX", "GENERIC_G031K6TX", "GENERIC_G031K6UX", "GENERIC_G031K8TX", "GENERIC_G031K8UX", - "GENERIC_G041K6TX", "GENERIC_G041K6UX", "GENERIC_G041K8UX", - "GENERIC_G071R6TX", "GENERIC_G071R8TX", "GENERIC_G071RBIX", "GENERIC_G071RBTX", "GENERIC_G081RBIX", - "GENERIC_G0B1RBTX", "GENERIC_G0B1RCTX", "GENERIC_G0C1RCTX", "GENERIC_G0C1RETX", - "GENERIC_G431K6TX", "GENERIC_G431K6UX", "GENERIC_G431K8TX", "GENERIC_G431K8UX", "GENERIC_G431KBTX", "GENERIC_G431KBUX", + "GENERIC_G031C4TX", + "GENERIC_G031C4UX", + "GENERIC_G031C6TX", + "GENERIC_G031C6UX", + "GENERIC_G031C8TX", + "GENERIC_G031C8UX", + "GENERIC_G031F4PX", + "GENERIC_G031F6PX", + "GENERIC_G031F8PX", + "GENERIC_G031G4UX", + "GENERIC_G031G6UX", + "GENERIC_G031G8UX", + "GENERIC_G031J4MX", + "GENERIC_G031J6MX", + "GENERIC_G031K4TX", + "GENERIC_G031K4UX", + "GENERIC_G031K6TX", + "GENERIC_G031K6UX", + "GENERIC_G031K8TX", + "GENERIC_G031K8UX", + "GENERIC_G031Y8YX", + "GENERIC_G041C6TX", + "GENERIC_G041C6UX", + "GENERIC_G041C8TX", + "GENERIC_G041F6PX", + "GENERIC_G041F8PX", + "GENERIC_G041G6UX", + "GENERIC_G041K6TX", + "GENERIC_G041K6UX", + "GENERIC_G041K8TX", + "GENERIC_G050C6TX", + "GENERIC_G050K6TX", + "GENERIC_G051C6TX", + "GENERIC_G051C6UX", + "GENERIC_G051C8TX", + "GENERIC_G051C8UX", + "GENERIC_G051F6PX", + "GENERIC_G051F8PX", + "GENERIC_G051F8YX", + "GENERIC_G051G6UX", + "GENERIC_G051G8UX", + "GENERIC_G051K6TX", + "GENERIC_G051K6UX", + "GENERIC_G051K8TX", + "GENERIC_G051K8UX", + "GENERIC_G061C6TX", + "GENERIC_G061C6UX", + "GENERIC_G061C8TX", + "GENERIC_G061F6PX", + "GENERIC_G061F8PX", + "GENERIC_G061G6UX", + "GENERIC_G061K6TX", + "GENERIC_G061K6UX", + "GENERIC_G061K8TX", + "GENERIC_G071C6TX", + "GENERIC_G071C6UX", + "GENERIC_G071C8TX", + "GENERIC_G071C8UX", + "GENERIC_G071CBTX", + "GENERIC_G071CBUX", + "GENERIC_G071EBYX", + "GENERIC_G071G6UX", + "GENERIC_G071G8UX", + "GENERIC_G071G8UXN", + "GENERIC_G071GBUX", + "GENERIC_G071GBUXN", + "GENERIC_G071K6TX", + "GENERIC_G071K6UX", + "GENERIC_G071K8TX", + "GENERIC_G071K8UX", + "GENERIC_G071KBTX", + "GENERIC_G071KBUX", + "GENERIC_G071R6TX", + "GENERIC_G071R8TX", + "GENERIC_G071RBIX", + "GENERIC_G071RBTX", + "GENERIC_G081CBTX", + "GENERIC_G081KBTX", + "GENERIC_G081RBIX", + "GENERIC_G0B1CBTX", + "GENERIC_G0B1CBUX", + "GENERIC_G0B1CCTX", + "GENERIC_G0B1CCUX", + "GENERIC_G0B1CETX", + "GENERIC_G0B1CEUX", + "GENERIC_G0B1KBTX", + "GENERIC_G0B1KBUX", + "GENERIC_G0B1KCTX", + "GENERIC_G0B1KCUX", + "GENERIC_G0B1KETX", + "GENERIC_G0B1KEUX", + "GENERIC_G0B1MBTX", + "GENERIC_G0B1MCTX", + "GENERIC_G0B1METX", + "GENERIC_G0B1NEYX", + "GENERIC_G0B1RBTX", + "GENERIC_G0B1RCTX", + "GENERIC_G0B1RETX", + "GENERIC_G0B1VBIX", + "GENERIC_G0B1VBTX", + "GENERIC_G0B1VCIX", + "GENERIC_G0B1VCTX", + "GENERIC_G0B1VEIX", + "GENERIC_G0B1VETX", + "GENERIC_G0C1CCTX", + "GENERIC_G0C1CCUX", + "GENERIC_G0C1CETX", + "GENERIC_G0C1KCTX", + "GENERIC_G0C1KCUX", + "GENERIC_G0C1KETX", + "GENERIC_G0C1MCTX", + "GENERIC_G0C1RCTX", + "GENERIC_G0C1VCIX", + "GENERIC_G0C1VCTX", + "GENERIC_G0C1VEIX", + "GENERIC_G431C6TX", + "GENERIC_G431C6UX", + "GENERIC_G431C8TX", + "GENERIC_G431C8UX", + "GENERIC_G431CBTX", + "GENERIC_G431CBUX", + "GENERIC_G431K6TX", + "GENERIC_G431K6UX", + "GENERIC_G431K8TX", + "GENERIC_G431K8UX", + "GENERIC_G431KBTX", + "GENERIC_G431KBUX", + "GENERIC_G431M6TX", + "GENERIC_G431M8TX", + "GENERIC_G431MBTX", + "GENERIC_G431R6IX", + "GENERIC_G431R6TX", + "GENERIC_G431R8IX", + "GENERIC_G431R8TX", + "GENERIC_G431RBIX", + "GENERIC_G431RBTX", + "GENERIC_G431V6TX", + "GENERIC_G431V8TX", + "GENERIC_G431VBTX", "GENERIC_G441KBTX", - "GENERIC_G431R6IX", "GENERIC_G431R6TX", "GENERIC_G431R8IX", "GENERIC_G431R8TX", "GENERIC_G431RBIX", "GENERIC_G431RBTX", "GENERIC_G441RBIX", - "GENERIC_G473RBTX", "GENERIC_G473RCTX", "GENERIC_G473RETX", - "GENERIC_G474RBTX", "GENERIC_G474RCTX", "GENERIC_G474RETX", + "GENERIC_G471CCTX", + "GENERIC_G471MCTX", + "GENERIC_G471QCTX", + "GENERIC_G471RCTX", + "GENERIC_G471VCHX", + "GENERIC_G471VCIX", + "GENERIC_G471VCTX", + "GENERIC_G471VEHX", + "GENERIC_G471VEIX", + "GENERIC_G473CBTX", + "GENERIC_G473CCTX", + "GENERIC_G473CETX", + "GENERIC_G473MBTX", + "GENERIC_G473MCTX", + "GENERIC_G473METX", + "GENERIC_G473PBIX", + "GENERIC_G473PCIX", + "GENERIC_G473PEIX", + "GENERIC_G473QBTX", + "GENERIC_G473QCTX", + "GENERIC_G473QETX", + "GENERIC_G473RBTX", + "GENERIC_G473RCTX", + "GENERIC_G473RETX", + "GENERIC_G473VBHX", + "GENERIC_G473VBTX", + "GENERIC_G473VCHX", + "GENERIC_G473VCTX", + "GENERIC_G473VEHX", + "GENERIC_G473VETX", + "GENERIC_G474CBTX", + "GENERIC_G474CCTX", + "GENERIC_G474CETX", + "GENERIC_G474MBTX", + "GENERIC_G474MCTX", + "GENERIC_G474METX", + "GENERIC_G474PBIX", + "GENERIC_G474PCIX", + "GENERIC_G474PEIX", + "GENERIC_G474QBTX", + "GENERIC_G474QCTX", + "GENERIC_G474QETX", + "GENERIC_G474RBTX", + "GENERIC_G474RCTX", + "GENERIC_G474RETX", + "GENERIC_G474VBHX", + "GENERIC_G474VBTX", + "GENERIC_G474VCHX", + "GENERIC_G474VCTX", + "GENERIC_G474VEHX", + "GENERIC_G474VETX", + "GENERIC_G483CETX", + "GENERIC_G483METX", + "GENERIC_G483PEIX", + "GENERIC_G483QETX", "GENERIC_G483RETX", - "GENERIC_H742IGKX", "GENERIC_H742IIKX", "GENERIC_H742IGTX", "GENERIC_H742IITX", - "GENERIC_H742VGHX", "GENERIC_H742VIHX", "GENERIC_H742VGTX", "GENERIC_H742VITX", - "GENERIC_H742ZGTX", "GENERIC_H742ZITX", - "GENERIC_H743VGHX", "GENERIC_H743VIHX", "GENERIC_H743VITX", + "GENERIC_G483VEHX", + "GENERIC_G483VETX", + "GENERIC_G484VEHX", + "GENERIC_G491CCTX", + "GENERIC_G491CETX", + "GENERIC_G491KCUX", + "GENERIC_G491KEUX", + "GENERIC_G491MCSX", + "GENERIC_G491MCTX", + "GENERIC_G491MESX", + "GENERIC_G491METX", + "GENERIC_G491RCIX", + "GENERIC_G491RCTX", + "GENERIC_G491REIX", + "GENERIC_G491RETX", + "GENERIC_G491REYX", + "GENERIC_G491VCTX", + "GENERIC_G491VETX", + "GENERIC_G4A1MESX", + "GENERIC_G4A1REIX", + "GENERIC_G4A1RETX", + "GENERIC_H563ZGTX", + "GENERIC_H723ZETX", + "GENERIC_H723ZGTX", + "GENERIC_H730ZBTX", + "GENERIC_H742IGKX", + "GENERIC_H742IGTX", + "GENERIC_H742IIKX", + "GENERIC_H742IITX", + "GENERIC_H742VGHX", + "GENERIC_H742VGTX", + "GENERIC_H742VIHX", + "GENERIC_H742VITX", + "GENERIC_H742ZGTX", + "GENERIC_H742ZITX", + "GENERIC_H743IGKX", + "GENERIC_H743IGTX", + "GENERIC_H743IIKX", + "GENERIC_H743IITX", + "GENERIC_H743VGHX", + "GENERIC_H743VGTX", + "GENERIC_H743VIHX", + "GENERIC_H743VITX", "GENERIC_H743ZGTX", - "GENERIC_H747AGIX", "GENERIC_H747AIIX", "GENERIC_H747IGTX", "GENERIC_H747IITX", - "GENERIC_H750IBTX", "GENERIC_H750VBTX", "GENERIC_H750ZBTX", - "GENERIC_H753VIHX", "GENERIC_H753VITX", + "GENERIC_H743ZITX", + "GENERIC_H747AGIX", + "GENERIC_H747AIIX", + "GENERIC_H747IGTX", + "GENERIC_H747IITX", + "GENERIC_H750IBKX", + "GENERIC_H750IBTX", + "GENERIC_H750VBTX", + "GENERIC_H750ZBTX", + "GENERIC_H753IIKX", + "GENERIC_H753VIHX", "GENERIC_H753ZITX", - "GENERIC_H757AIIX", "GENERIC_H757IITX", - "GENERIC_H743IGKX", "GENERIC_H743IGTX", "GENERIC_H743IIKX", "GENERIC_H743IITX", "GENERIC_H753IIKX", "GENERIC_H753IITX", - "GENERIC_L010RBTX", + "GENERIC_H757AIIX", + "GENERIC_L010F4PX", + "GENERIC_L010K4TX", + "GENERIC_L011D3PX", + "GENERIC_L011D4PX", + "GENERIC_L011E3YX", + "GENERIC_L011F3PX", + "GENERIC_L011F3UX", + "GENERIC_L011F4PX", + "GENERIC_L011F4UX", + "GENERIC_L011G3UX", + "GENERIC_L011G4UX", + "GENERIC_L011K3TX", + "GENERIC_L011K3UX", + "GENERIC_L011K4TX", + "GENERIC_L011K4UX", + "GENERIC_L031C4TX", + "GENERIC_L031C4UX", + "GENERIC_L031C6TX", + "GENERIC_L031C6UX", + "GENERIC_L031E4YX", + "GENERIC_L031E6YX", + "GENERIC_L031F4PX", + "GENERIC_L031F6PX", + "GENERIC_L031G4UX", + "GENERIC_L031G6UX", "GENERIC_L031K4TX", - "GENERIC_L041K6TX", - "GENERIC_L051C6TX", "GENERIC_L051C6UX", "GENERIC_L051C8UX", - "GENERIC_L052R6TX", "GENERIC_L052R8TX", - "GENERIC_L053R6TX", "GENERIC_L063R8TX", - "GENERIC_L072CZEX", "GENERIC_L072CZYX", + "GENERIC_L031K6TX", + "GENERIC_L041C4TX", + "GENERIC_L041C6TX", + "GENERIC_L051C6TX", + "GENERIC_L051C6UX", + "GENERIC_L051C8TX", + "GENERIC_L052C6TX", + "GENERIC_L052C6UX", + "GENERIC_L052C8TX", + "GENERIC_L052C8UX", + "GENERIC_L052K6TX", + "GENERIC_L052K8TX", + "GENERIC_L052R6HX", + "GENERIC_L052R6TX", + "GENERIC_L052R8HX", + "GENERIC_L052R8TX", + "GENERIC_L052T6YX", + "GENERIC_L052T8YX", + "GENERIC_L053C6TX", + "GENERIC_L053C6UX", + "GENERIC_L053C8TX", + "GENERIC_L053C8UX", + "GENERIC_L053R6HX", + "GENERIC_L053R6TX", + "GENERIC_L053R8TX", + "GENERIC_L062C8UX", + "GENERIC_L063C8TX", + "GENERIC_L072CBTX", + "GENERIC_L072CBUX", + "GENERIC_L072CBYX", + "GENERIC_L072CZEX", + "GENERIC_L072CZTX", + "GENERIC_L072CZUX", + "GENERIC_L072CZYX", + "GENERIC_L072KBTX", + "GENERIC_L072KBUX", "GENERIC_L072KZTX", - "GENERIC_L072RBTX", "GENERIC_L072RZTX", + "GENERIC_L072KZUX", + "GENERIC_L072RBHX", + "GENERIC_L072RBIX", + "GENERIC_L072RBTX", + "GENERIC_L072RZHX", + "GENERIC_L072RZIX", + "GENERIC_L072RZTX", + "GENERIC_L072V8IX", + "GENERIC_L072V8TX", + "GENERIC_L072VBIX", + "GENERIC_L072VBTX", + "GENERIC_L072VZIX", + "GENERIC_L072VZTX", + "GENERIC_L073CBTX", + "GENERIC_L073CBUX", + "GENERIC_L073CZTX", + "GENERIC_L073CZUX", "GENERIC_L073CZYX", + "GENERIC_L073RBHX", "GENERIC_L073RBTX", - "GENERIC_L082CZYX", - "GENERIC_L082KBTX", "GENERIC_L082KZTX", - "GENERIC_L083RBTX", "GENERIC_L083RZTX", - "GENERIC_L100C6UX", "GENERIC_L100C6UXA", - "GENERIC_L151C6TX", "GENERIC_L151C6TXA", "GENERIC_L151C6UX", "GENERIC_L151C6UXA", "GENERIC_L151C8TX", "GENERIC_L151C8TXA", - "GENERIC_L151C8UX", "GENERIC_L151C8UXA", "GENERIC_L151CBTX", "GENERIC_L151CBUX", "GENERIC_L151CBUXA", - "GENERIC_L152C6TX", "GENERIC_L152C6TXA", "GENERIC_L152C6UX", "GENERIC_L152C6UXA", "GENERIC_L152C8TX", "GENERIC_L152C8TXA", - "GENERIC_L152C8UX", "GENERIC_L152C8UXA", "GENERIC_L152CBTX", "GENERIC_L152CBTXA", "GENERIC_L152CBUX", "GENERIC_L152CBUXA", - "GENERIC_L151RETX", "GENERIC_L162RETX", - "GENERIC_L412K8TX", "GENERIC_L412K8UX", "GENERIC_L412KBUX", - "GENERIC_L422KBTX", "GENERIC_L422KBUX", + "GENERIC_L073RZHX", + "GENERIC_L073RZIX", + "GENERIC_L073RZTX", + "GENERIC_L073V8IX", + "GENERIC_L073V8TX", + "GENERIC_L073VBIX", + "GENERIC_L073VBTX", + "GENERIC_L073VZIX", + "GENERIC_L073VZTX", + "GENERIC_L082CZUX", + "GENERIC_L082KBTX", + "GENERIC_L082KBUX", + "GENERIC_L083CBTX", + "GENERIC_L083CZTX", + "GENERIC_L083RBHX", + "GENERIC_L083RBTX", + "GENERIC_L083V8IX", + "GENERIC_L083VBIX", + "GENERIC_L083VBTX", + "GENERIC_L083VZIX", + "GENERIC_L083VZTX", + "GENERIC_L100C6UX", + "GENERIC_L100C6UXA", + "GENERIC_L151C6TX", + "GENERIC_L151C6TXA", + "GENERIC_L151C6UX", + "GENERIC_L151C6UXA", + "GENERIC_L151C8TX", + "GENERIC_L151C8TXA", + "GENERIC_L151C8UX", + "GENERIC_L151C8UXA", + "GENERIC_L151CBTX", + "GENERIC_L151CBTXA", + "GENERIC_L151CBUX", + "GENERIC_L151CBUXA", + "GENERIC_L151RETX", + "GENERIC_L151ZDTX", + "GENERIC_L152C6TX", + "GENERIC_L152C6TXA", + "GENERIC_L152C6UX", + "GENERIC_L152C6UXA", + "GENERIC_L152C8TX", + "GENERIC_L152C8TXA", + "GENERIC_L152C8UX", + "GENERIC_L152C8UXA", + "GENERIC_L152CBTX", + "GENERIC_L152CBTXA", + "GENERIC_L152CBUX", + "GENERIC_L152RETX", + "GENERIC_L152ZDTX", + "GENERIC_L412K8TX", + "GENERIC_L412K8UX", + "GENERIC_L412KBTX", + "GENERIC_L412KBUX", + "GENERIC_L422KBTX", + "GENERIC_L431RBIX", + "GENERIC_L431RBTX", + "GENERIC_L431RBYX", + "GENERIC_L431RCIX", + "GENERIC_L431RCTX", "GENERIC_L432KBUX", - "GENERIC_L433CBTX", "GENERIC_L433CCTX", "GENERIC_L433CBUX", - "GENERIC_L442KCUX", - "GENERIC_L443CCTX", "GENERIC_L443CCUX", - "GENERIC_L452RCIX", "GENERIC_L452RCTX", "GENERIC_L452RCYX", "GENERIC_L452REIX", "GENERIC_L452REYX", - "GENERIC_L462REIX", "GENERIC_L462RETX", "GENERIC_L462REYX", - "GENERIC_L475RCTX", "GENERIC_L475RETX", "GENERIC_L475RGTX", - "GENERIC_L476RCTX", "GENERIC_L476RETX", - "GENERIC_L486RGTX", - "GENERIC_L475VCTX", "GENERIC_L475VGTX", - "GENERIC_L476VCTX", "GENERIC_L476VETX", "GENERIC_L476VGTX", - "GENERIC_L486VGTX", + "GENERIC_L432KCUX", + "GENERIC_L433CBTX", + "GENERIC_L433CBUX", + "GENERIC_L433CCTX", + "GENERIC_L433CCUX", + "GENERIC_L433RBIX", + "GENERIC_L433RBTX", + "GENERIC_L433RBYX", + "GENERIC_L433RCIX", + "GENERIC_L433RCTX", + "GENERIC_L433RCYX", + "GENERIC_L443CCTX", + "GENERIC_L443RCIX", + "GENERIC_L443RCTX", + "GENERIC_L452RCIX", + "GENERIC_L452RCTX", + "GENERIC_L452RCYX", + "GENERIC_L452REIX", + "GENERIC_L452RETX", + "GENERIC_L452REYX", + "GENERIC_L452REYXP", + "GENERIC_L462REIX", + "GENERIC_L462RETX", + "GENERIC_L475RCTX", + "GENERIC_L475RETX", + "GENERIC_L475RGTX", + "GENERIC_L475VCTX", + "GENERIC_L475VETX", + "GENERIC_L475VGTX", + "GENERIC_L476RCTX", + "GENERIC_L476RETX", + "GENERIC_L476RGTX", + "GENERIC_L476VCTX", + "GENERIC_L476VETX", + "GENERIC_L476VGTX", "GENERIC_L496ZETX", - "GENERIC_L4A6ZGTX", - "GENERIC_L4A6ZGTXP", - "GENERIC_L4R5VGTX", "GENERIC_L4R5VITX", + "GENERIC_L496ZGTX", + "GENERIC_L496ZGTXP", + "GENERIC_L4R5VGTX", + "GENERIC_L4R5VITX", + "GENERIC_L4R5ZGTX", "GENERIC_L4R5ZGYX", + "GENERIC_L4R5ZITX", + "GENERIC_L4R5ZIYX", "GENERIC_L4R7VITX", - "GENERIC_L4S7VITX", - "GENERIC_L4R5ZGTX", "GENERIC_L4R5ZITX", "GENERIC_L4R7ZITX", - "GENERIC_L4R9ZGJX", "GENERIC_L4R9ZGYX", "GENERIC_L4R9ZIYX", + "GENERIC_L4R9ZGJX", + "GENERIC_L4R9ZGYX", + "GENERIC_L4R9ZIJX", + "GENERIC_L4R9ZIYX", + "GENERIC_L4S5VITX", + "GENERIC_L4S5ZITX", "GENERIC_L4S5ZIYX", - "GENERIC_L4S7ZITX", - "GENERIC_L4S9ZIJX", "GENERIC_L4S9ZIYX", "GENERIC_L552ZCTXQ", - "GENERIC_L562ZETXQ", - "GENERIC_U575AGIXQ", "GENERIC_U575AIIXQ", "GENERIC_U575ZGTXQ", "GENERIC_U585ZETXQ", - "GENERIC_WB55CCUX", "GENERIC_WB55CGUX", - "GENERIC_WB55RCVX", "GENERIC_WB55REVX", + "GENERIC_L552ZETXQ", + "GENERIC_MP153AACX", + "GENERIC_MP153CACX", + "GENERIC_MP153DACX", + "GENERIC_MP153FACX", + "GENERIC_MP157AACX", + "GENERIC_MP157CACX", + "GENERIC_MP157DACX", + "GENERIC_U575AGIXQ", + "GENERIC_U575AIIXQ", + "GENERIC_U575ZGTXQ", + "GENERIC_U575ZITXQ", + "GENERIC_WB35CCUXA", + "GENERIC_WB35CEUXA", + "GENERIC_WB55CCUX", + "GENERIC_WB55CEUX", + "GENERIC_WB55RCVX", + "GENERIC_WB55REVX", "GENERIC_WL54CCUX", - "GENERIC_WL54JCIX", "GENERIC_WLE4J8IX", "GENERIC_WLE4JBIX", - "GENERIC_WLE4C8UX", "GENERIC_WLE4CBUX", "GENERIC_WLE4CCUX", + "GENERIC_WL54JCIX", + "GENERIC_WL55CCUX", + "GENERIC_WL55JCIX", + "GENERIC_WLE4C8UX", + "GENERIC_WLE4CBUX", + "GENERIC_WLE4CCUX", + "GENERIC_WLE4J8IX", + "GENERIC_WLE4JBIX", "GENERIC_WLE4JCIX", - "GENERIC_WLE5C8UX", "GENERIC_WLE5CBUX", "GENERIC_WLE5CCUX", - "GENERIC_WLE5J8IX", "GENERIC_WLE5JBIX", "GENERIC_WLE5JCIX", + "GENERIC_WLE5C8UX", + "GENERIC_WLE5CBUX", + "GENERIC_WLE5J8IX", + "GENERIC_WLE5JBIX", "NUCLEO_H743ZI", "NUCLEO_L496ZG", "NUCLEO_L4R5ZI", - "PYBSTICK26_DUINO", "PYBSTICK26_LITE", "PYBSTICK26_STD", + "PYBSTICK26_DUINO", + "PYBSTICK26_LITE", + "PYBSTICK26_STD", "RAK811_TRACKERA" ] - } , + }, { - "pattern": "RTC_Seconds", - "applicable": false, - "boards": [ "NUCLEO_C031C6", "NUCLEO_F030R8", "NUCLEO_F031K6", "NUCLEO_F042K6"] + "pattern": "^[^F][^1].*", + "applicable": true, + "boards": [ + "NUCLEO_C031C6", + "NUCLEO_F091RC", + "NUCLEO_F103RB", + "NUCLEO_F207ZG", + "NUCLEO_F303RE", + "NUCLEO_F429ZI", + "NUCLEO_F767ZI", + "NUCLEO_G071RB", + "NUCLEO_G0B1RE", + "NUCLEO_G474RE", + "NUCLEO_H563ZI", + "NUCLEO_H743ZI2", + "NUCLEO_L073RZ", + "NUCLEO_L152RE", + "NUCLEO_L452REP", + "NUCLEO_L4R5ZI_P", + "NUCLEO_L552ZE_Q", + "NUCLEO_U575ZI_Q", + "NUCLEO_WB15CC", + "NUCLEO_WL55JC1" + ] }, { "pattern": "F1RTCDateRetention", "applicable": true, "boards": [ "NUCLEO_F103RB"] + }, + { + "pattern": "RTC_Seconds", + "applicable": false, + "boards": [ + "NUCLEO_C031C6" + ] } ] } diff --git a/keywords.txt b/keywords.txt index 5663a9e..37fcf3d 100644 --- a/keywords.txt +++ b/keywords.txt @@ -13,6 +13,10 @@ STM32RTC KEYWORD1 ####################################### getInstance KEYWORD2 +getHandle KEYWORD2 + +getBinaryMode KEYWORD2 +setBinaryMode KEYWORD2 getWeekDay KEYWORD2 getDay KEYWORD2 @@ -68,6 +72,8 @@ attachInterrupt KEYWORD2 detachInterrupt KEYWORD2 attachSecondsInterrupt KEYWORD2 detachSecondsInterrupt KEYWORD2 +attachSubSecondsUnderflowInterrupt KEYWORD2 +detachSubSecondsUnderflowInterrupt KEYWORD2 getClockSource KEYWORD2 setClockSource KEYWORD2 @@ -85,6 +91,7 @@ IS_HOUR_FORMAT KEYWORD2 # Constants (LITERAL1) ####################################### MATCH_OFF LITERAL1 +MATCH_SUBSEC LITERAL1 MATCH_SS LITERAL1 MATCH_MMSS LITERAL1 MATCH_HHMMSS LITERAL1 @@ -100,3 +107,6 @@ LSI_CLOCK LITERAL1 HSE_CLOCK LITERAL1 ALARM_A LITERAL1 ALARM_B LITERAL1 +MODE_BCD LITERAL1 +MODE_BIN LITERAL1 +MODE_MIX LITERAL1 diff --git a/library.json b/library.json index 80ec1cc..5c570df 100644 --- a/library.json +++ b/library.json @@ -7,7 +7,7 @@ "type": "git", "url": "https://github.com/stm32duino/STM32RTC" }, - "version": "1.3.7", + "version": "1.4.0", "frameworks": "arduino", "platforms": "ststm32", "build": { diff --git a/library.properties b/library.properties index bd8adde..4666e3a 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=STM32duino RTC -version=1.3.7 +version=1.4.0 author=STMicroelectronics maintainer=stm32duino sentence=Allows to use the RTC functionalities of STM32 based boards. diff --git a/src/STM32RTC.cpp b/src/STM32RTC.cpp index 10d8379..4a29928 100644 --- a/src/STM32RTC.cpp +++ b/src/STM32RTC.cpp @@ -63,6 +63,7 @@ void STM32RTC::begin(bool resetTime, Hour_Format format) _format = format; reinit = RTC_init((format == HOUR_12) ? HOUR_FORMAT_12 : HOUR_FORMAT_24, + (_mode == MODE_MIX) ? ::MODE_BINARY_MIX : ((_mode == MODE_BIN) ? ::MODE_BINARY_ONLY : ::MODE_BINARY_NONE), (_clockSource == LSE_CLOCK) ? ::LSE_CLOCK : (_clockSource == HSE_CLOCK) ? ::HSE_CLOCK : ::LSI_CLOCK , resetTime); @@ -103,7 +104,7 @@ void STM32RTC::begin(bool resetTime, Hour_Format format) */ void STM32RTC::end(void) { - RTC_DeInit(); + RTC_DeInit(true); _timeSet = false; } @@ -117,74 +118,83 @@ STM32RTC::Source_Clock STM32RTC::getClockSource(void) } /** - * @brief set the RTC clock source. By default LSI clock is selected. This - * method must be called before begin(). + * @brief set the RTC clock source and user (a)synchronous prescalers values. + * @note By default LSI clock is selected. This method must be called before begin(). * @param source: clock source: LSI_CLOCK, LSE_CLOCK or HSE_CLOCK + * @param predivA: Asynchronous prescaler value. + * @note Reset value: RTC_AUTO_1_SECOND for STM32F1xx series, else (PREDIVA_MAX + 1) + * @param predivS: Synchronous prescaler value. + * @note Reset value: (PREDIVS_MAX + 1), not used for STM32F1xx series. * @retval None */ -void STM32RTC::setClockSource(Source_Clock source) +void STM32RTC::setClockSource(Source_Clock source, uint32_t predivA, uint32_t predivS) { if (IS_CLOCK_SOURCE(source)) { _clockSource = source; RTC_SetClockSource((_clockSource == LSE_CLOCK) ? ::LSE_CLOCK : (_clockSource == HSE_CLOCK) ? ::HSE_CLOCK : ::LSI_CLOCK); } + RTC_setPrediv(predivA, predivS); } -#if defined(STM32F1xx) /** - * @brief get user asynchronous prescaler value for the current clock source. - * @param predivA: pointer to the current Asynchronous prescaler value - * @param dummy : not used (kept for compatibility reason) - * @retval None + * @brief get the Binary Mode. + * @retval mode: MODE_BCD, MODE_BIN or MODE_MIX */ -void STM32RTC::getPrediv(uint32_t *predivA, int16_t *dummy) +STM32RTC::Binary_Mode STM32RTC::getBinaryMode(void) { - UNUSED(dummy); - RTC_getPrediv(predivA); + return _mode; } + +/** + * @brief set the Binary Mode. By default MODE_BCD is selected. This + * method must be called before begin(). + * @param mode: the RTC mode: MODE_BCD, MODE_BIN or MODE_MIX + * @retval None + */ +void STM32RTC::setBinaryMode(Binary_Mode mode) +{ +#if defined(RTC_BINARY_NONE) + _mode = mode; #else +#warning "only BCD mode is supported" + UNUSED(mode); + _mode = MODE_BCD; +#endif /* RTC_BINARY_NONE */ +} + /** * @brief get user (a)synchronous prescaler values if set else computed * ones for the current clock source. * @param predivA: pointer to the current Asynchronous prescaler value - * @param predivS: pointer to the current Synchronous prescaler value + * @param predivS: pointer to the current Synchronous prescaler value, + * not used for STM32F1xx series. * @retval None */ -void STM32RTC::getPrediv(int8_t *predivA, int16_t *predivS) +void STM32RTC::getPrediv(uint32_t *predivA, uint32_t *predivS) { - if ((predivA != nullptr) && (predivS != nullptr)) { + if ((predivA != nullptr) +#if !defined(STM32F1xx) + && (predivS != nullptr) +#endif /* STM32F1xx */ + ) { RTC_getPrediv(predivA, predivS); } } -#endif /* STM32F1xx */ -#if defined(STM32F1xx) /** - * @brief set user asynchronous prescalers value. + * @brief set user (a)synchronous prescalers values. * @note This method must be called before begin(). - * @param predivA: Asynchronous prescaler value. Reset value: RTC_AUTO_1_SECOND - * @param dummy : not used (kept for compatibility reason) + * @param predivA: Asynchronous prescaler value. + * @note Reset value: RTC_AUTO_1_SECOND for STM32F1xx series, else (PREDIVA_MAX + 1) + * @param predivS: Synchronous prescaler value. + * @note Reset value: (PREDIVS_MAX + 1), not used for STM32F1xx series. * @retval None */ -void STM32RTC::setPrediv(uint32_t predivA, int16_t dummy) +void STM32RTC::setPrediv(uint32_t predivA, uint32_t predivS) { - UNUSED(dummy); - RTC_setPrediv(predivA); + setClockSource(_clockSource, predivA, predivS); } -#else -/** - * @brief set user (a)synchronous prescalers value. - * @note This method must be called before begin(). - * @param predivA: Asynchronous prescaler value. Reset value: -1 - * @param predivS: Synchronous prescaler value. Reset value: -1 - * @retval None - */ -void STM32RTC::setPrediv(int8_t predivA, int16_t predivS) -{ - RTC_setPrediv(predivA, predivS); -} -#endif /* STM32F1xx */ /** * @brief enable the RTC alarm. @@ -216,6 +226,21 @@ void STM32RTC::enableAlarm(Alarm_Match match, Alarm name) RTC_StopAlarm(::ALARM_A); } break; + case MATCH_SUBSEC: + /* force _alarmday to 0 to go to the right alarm config in MIX mode */ +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + RTC_StartAlarm(::ALARM_B, 0, 0, 0, 0, + _alarmBSubSeconds, (_alarmBPeriod == AM) ? HOUR_AM : HOUR_PM, + static_cast(31UL)); + } else +#endif + { + RTC_StartAlarm(::ALARM_A, 0, 0, 0, 0, + _alarmSubSeconds, (_alarmPeriod == AM) ? HOUR_AM : HOUR_PM, + static_cast(31UL)); + } + break; case MATCH_YYMMDDHHMMSS://kept for compatibility case MATCH_MMDDHHMMSS: //kept for compatibility case MATCH_DHHMMSS: @@ -309,6 +334,31 @@ void STM32RTC::detachSecondsInterrupt(void) } #endif /* ONESECOND_IRQn */ + +#ifdef STM32WLxx +/** + * @brief attach a callback to the RTC SubSeconds underflow interrupt. + * @note only for STM32WLxx + * @param callback: pointer to the callback + * @retval None + */ +void STM32RTC::attachSubSecondsUnderflowInterrupt(voidFuncPtr callback) +{ + attachSubSecondsUnderflowIrqCallback(callback); +} + +/** + * @brief detach the RTC SubSeconds underflow callback. + * @retval None + */ +void STM32RTC::detachSubSecondsUnderflowInterrupt(void) +{ + detachSubSecondsUnderflowIrqCallback(); +} + +#endif /* STM32WLxx */ + + // Kept for compatibility. Use STM32LowPower library. void STM32RTC::standbyMode(void) { @@ -321,7 +371,7 @@ void STM32RTC::standbyMode(void) /** * @brief get RTC subseconds. - * @retval return the current subseconds from the RTC. + * @retval return the current milliseconds from the RTC. */ uint32_t STM32RTC::getSubSeconds(void) { @@ -369,7 +419,7 @@ uint8_t STM32RTC::getHours(AM_PM *period) * @param hours: pointer to the current hours * @param minutes: pointer to the current minutes * @param seconds: pointer to the current seconds - * @param subSeconds: pointer to the current subSeconds + * @param subSeconds: pointer to the current subSeconds (in milliseconds) * @param period: optional (default: nullptr) * pointer to the current hour period set in the RTC: AM or PM * @retval none @@ -611,7 +661,7 @@ uint8_t STM32RTC::getAlarmYear(void) /** * @brief set RTC subseconds. - * @param subseconds: 0-999 + * @param subseconds: 0-999 milliseconds * @retval none */ void STM32RTC::setSubSeconds(uint32_t subSeconds) @@ -816,20 +866,21 @@ void STM32RTC::setDate(uint8_t weekDay, uint8_t day, uint8_t month, uint8_t year /** * @brief set RTC alarm subseconds. - * @param subseconds: 0-999 (in ms) + * @param subseconds: 0-999 (in ms) or 32bit nb of milliseconds in BIN mode * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists * @retval none */ void STM32RTC::setAlarmSubSeconds(uint32_t subSeconds, Alarm name) { - if (subSeconds < 1000) { +#ifndef RTC_ALARM_B + UNUSED(name); +#endif + if ((_mode == MODE_BIN) || (subSeconds < 1000)) { #ifdef RTC_ALARM_B if (name == ALARM_B) { _alarmBSubSeconds = subSeconds; - } -#else - UNUSED(name); + } else #endif { _alarmSubSeconds = subSeconds; @@ -850,7 +901,7 @@ void STM32RTC::setAlarmSeconds(uint8_t seconds, Alarm name) #ifdef RTC_ALARM_B if (name == ALARM_B) { _alarmBSeconds = seconds; - } + } else #else UNUSED(name); #endif @@ -873,7 +924,7 @@ void STM32RTC::setAlarmMinutes(uint8_t minutes, Alarm name) #ifdef RTC_ALARM_B if (name == ALARM_B) { _alarmBMinutes = minutes; - } + } else #else UNUSED(name); #endif @@ -912,7 +963,7 @@ void STM32RTC::setAlarmHours(uint8_t hours, AM_PM period, Alarm name) if (_format == HOUR_12) { _alarmBPeriod = period; } - } + } else #else UNUSED(name); #endif @@ -925,6 +976,18 @@ void STM32RTC::setAlarmHours(uint8_t hours, AM_PM period, Alarm name) } } + +/** + * @brief set RTC alarm time. + * @param subSeconds: 0-999 ms or 32bit nb of milliseconds in BIN mode + * @param name: ALARM_A or ALARM_B if exists + * @retval none + */ +void STM32RTC::setAlarmTime(uint32_t subSeconds, Alarm name) +{ + setAlarmTime(0, 0, 0, subSeconds, AM, name); +} + /** * @brief set RTC alarm time. * @param hours: 0-23 @@ -943,7 +1006,7 @@ void STM32RTC::setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, Ala * @param hours: 0-23 * @param minutes: 0-59 * @param seconds: 0-59 - * @param subSeconds: 0-999 + * @param subSeconds: 0-999 ms or 32bit nb of milliseconds in BIN mode * @param name: ALARM_A or ALARM_B if exists * @retval none */ @@ -954,10 +1017,10 @@ void STM32RTC::setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uin /** * @brief set RTC alarm time. - * @param hours: 0-23 - * @param minutes: 0-59 - * @param seconds: 0-59 - * @param subSeconds: 0-999 (optional) + * @param hours: 0-23 (not used in BIN mode) + * @param minutes: 0-59 (not used in BIN mode) + * @param seconds: 0-59 (not used in BIN mode) + * @param subSeconds: 0-999 ms (optional) or 32bit nb of milliseconds in BIN mode * @param period: hour format AM or PM (optional) * @param name: optional (default: ALARM_A) * ALARM_A or ALARM_B if exists @@ -1158,10 +1221,13 @@ void STM32RTC::setAlarmEpoch(time_t ts, Alarm_Match match, uint32_t subSeconds, time_t t = ts; struct tm *tmp = gmtime(&t); - setAlarmDay(tmp->tm_mday, name); - setAlarmHours(tmp->tm_hour, name); - setAlarmMinutes(tmp->tm_min, name); - setAlarmSeconds(tmp->tm_sec, name); + /* in BIN only mode, the time_t is not relevant, but only the subSeconds in ms */ + if (_mode != MODE_BIN) { + setAlarmDay(tmp->tm_mday, name); + setAlarmHours(tmp->tm_hour, name); + setAlarmMinutes(tmp->tm_min, name); + setAlarmSeconds(tmp->tm_sec, name); + } setAlarmSubSeconds(subSeconds, name); enableAlarm(match, name); } @@ -1246,6 +1312,7 @@ bool STM32RTC::isAlarmEnabled(Alarm name) void STM32RTC::syncTime(void) { hourAM_PM_t p = HOUR_AM; + RTC_GetTime(&_hours, &_minutes, &_seconds, &_subSeconds, &p); _hoursPeriod = (p == HOUR_AM) ? AM : PM; } @@ -1276,7 +1343,7 @@ void STM32RTC::syncAlarmTime(Alarm name) RTC_GetAlarm(::ALARM_B, &_alarmBDay, &_alarmBHours, &_alarmBMinutes, &_alarmBSeconds, &_alarmBSubSeconds, &p, &match); _alarmBPeriod = (p == HOUR_AM) ? AM : PM; - } + } else #else UNUSED(name); #endif diff --git a/src/STM32RTC.h b/src/STM32RTC.h index b8be12b..fe0374c 100644 --- a/src/STM32RTC.h +++ b/src/STM32RTC.h @@ -52,8 +52,8 @@ * @brief STM32 RTC library version number */ #define STM32_RTC_VERSION_MAJOR (0x01U) /*!< [31:24] major version */ -#define STM32_RTC_VERSION_MINOR (0x03U) /*!< [23:16] minor version */ -#define STM32_RTC_VERSION_PATCH (0x06U) /*!< [15:8] patch version */ +#define STM32_RTC_VERSION_MINOR (0x04U) /*!< [23:16] minor version */ +#define STM32_RTC_VERSION_PATCH (0x00U) /*!< [15:8] patch version */ /* * Extra label for development: * 0: official release @@ -85,8 +85,15 @@ class STM32RTC { PM = HOUR_PM }; + enum Binary_Mode : uint8_t { + MODE_BCD = MODE_BINARY_NONE, + MODE_BIN = MODE_BINARY_ONLY, + MODE_MIX = MODE_BINARY_MIX + }; + enum Alarm_Match : uint8_t { MATCH_OFF = OFF_MSK, // Never + MATCH_SUBSEC = SUBSEC_MSK, // Every Subsecond MATCH_SS = SS_MSK, // Every Minute MATCH_MMSS = SS_MSK | MM_MSK, // Every Hour MATCH_HHMMSS = SS_MSK | MM_MSK | HH_MSK, // Every Day @@ -125,8 +132,19 @@ class STM32RTC { void end(void); + // Could be used to mix Arduino API and STM32Cube HAL API (ex: DMA). Use at your own risk. + RTC_HandleTypeDef *getHandle(void) + { + return RTC_GetHandle(); + } + Source_Clock getClockSource(void); - void setClockSource(Source_Clock source); + void setClockSource(Source_Clock source, uint32_t predivA = (PREDIVA_MAX + 1), uint32_t predivS = (PREDIVS_MAX + 1)); + void getPrediv(uint32_t *predivA, uint32_t *predivS); + void setPrediv(uint32_t predivA, uint32_t predivS); + + Binary_Mode getBinaryMode(void); + void setBinaryMode(Binary_Mode mode); void enableAlarm(Alarm_Match match, Alarm name = ALARM_A); void disableAlarm(Alarm name = ALARM_A); @@ -141,6 +159,11 @@ class STM32RTC { void detachSecondsInterrupt(void); #endif /* ONESECOND_IRQn */ +#ifdef STM32WLxx + // STM32WLxx has a dedicated IRQ + void attachSubSecondsUnderflowInterrupt(voidFuncPtr callback); + void detachSubSecondsUnderflowInterrupt(void); +#endif /* STM32WLxx */ // Kept for compatibility: use STM32LowPower library. void standbyMode(); @@ -190,6 +213,7 @@ class STM32RTC { void setAlarmMinutes(uint8_t minutes, Alarm name = ALARM_A); void setAlarmHours(uint8_t hours, Alarm name); void setAlarmHours(uint8_t hours, AM_PM period = AM, Alarm name = ALARM_A); + void setAlarmTime(uint32_t subSeconds, Alarm name = ALARM_A); void setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, Alarm name); void setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds, Alarm name); void setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds = 0, AM_PM period = AM, Alarm name = ALARM_A); @@ -212,13 +236,6 @@ class STM32RTC { void setAlarmEpoch(time_t ts, Alarm_Match match, Alarm name); void setAlarmEpoch(time_t ts, Alarm_Match match = MATCH_DHHMMSS, uint32_t subSeconds = 0, Alarm name = ALARM_A); -#if defined(STM32F1xx) - void getPrediv(uint32_t *predivA, int16_t *dummy = nullptr); - void setPrediv(uint32_t predivA, int16_t dummy = 0); -#else - void getPrediv(int8_t *predivA, int16_t *predivS); - void setPrediv(int8_t predivA, int16_t predivS); -#endif /* STM32F1xx */ bool isConfigured(void) { return RTC_IsConfigured(); @@ -232,11 +249,15 @@ class STM32RTC { friend class STM32LowPower; private: - STM32RTC(void): _clockSource(LSI_CLOCK) {} + STM32RTC(void): _mode(MODE_BCD), _clockSource(LSI_CLOCK) + { + setClockSource(_clockSource); + } static bool _timeSet; Hour_Format _format; + Binary_Mode _mode; AM_PM _hoursPeriod; uint8_t _hours; uint8_t _minutes; diff --git a/src/rtc.c b/src/rtc.c index 903ca7c..73c911f 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -56,33 +56,44 @@ extern "C" { /* Private define ------------------------------------------------------------*/ /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ -static RTC_HandleTypeDef RtcHandle = {0}; +static RTC_HandleTypeDef RtcHandle = {.Instance = RTC}; static voidCallbackPtr RTCUserCallback = NULL; static void *callbackUserData = NULL; #ifdef RTC_ALARM_B static voidCallbackPtr RTCUserCallbackB = NULL; static void *callbackUserDataB = NULL; #endif +#ifdef ONESECOND_IRQn static voidCallbackPtr RTCSecondsIrqCallback = NULL; - +#endif +#ifdef STM32WLxx +static voidCallbackPtr RTCSubSecondsUnderflowIrqCallback = NULL; +#endif static sourceClock_t clkSrc = LSI_CLOCK; +static uint32_t clkVal = LSI_VALUE; static uint8_t HSEDiv = 0; #if !defined(STM32F1xx) /* predividers values */ static uint8_t predivSync_bits = 0xFF; -static int8_t predivAsync = -1; -static int16_t predivSync = -1; +static uint32_t predivAsync = (PREDIVA_MAX + 1); +static uint32_t predivSync = (PREDIVS_MAX + 1); +static uint32_t fqce_apre; #else -static uint32_t prediv = RTC_AUTO_1_SECOND; +/* Default, let HAL calculate the prescaler*/ +static uint32_t predivAsync = RTC_AUTO_1_SECOND; #endif /* !STM32F1xx */ static hourFormat_t initFormat = HOUR_FORMAT_12; +static binaryMode_t initMode = MODE_BINARY_NONE; /* Private function prototypes -----------------------------------------------*/ static void RTC_initClock(sourceClock_t source); #if !defined(STM32F1xx) -static void RTC_computePrediv(int8_t *asynch, int16_t *synch); +static void RTC_computePrediv(uint32_t *asynch, uint32_t *synch); #endif /* !STM32F1xx */ +#if defined(RTC_BINARY_NONE) +static void RTC_BinaryConf(binaryMode_t mode); +#endif static inline int _log2(int x) { @@ -90,6 +101,15 @@ static inline int _log2(int x) } /* Exported functions --------------------------------------------------------*/ +/** + * @brief Get pointer to RTC_HandleTypeDef + * @param None + * @retval pointer to RTC_HandleTypeDef + */ +RTC_HandleTypeDef *RTC_GetHandle(void) +{ + return &RtcHandle; +} /** * @brief Set RTC clock source @@ -98,15 +118,54 @@ static inline int _log2(int x) */ void RTC_SetClockSource(sourceClock_t source) { - switch (source) { - case LSI_CLOCK: - case LSE_CLOCK: - case HSE_CLOCK: - clkSrc = source; - break; - default: - clkSrc = LSI_CLOCK; - break; + clkSrc = source; + if (source == LSE_CLOCK) { + clkVal = LSE_VALUE; + } else if (source == HSE_CLOCK) { + /* HSE division factor for RTC clock must be define to ensure that + * the clock supplied to the RTC is less than or equal to 1 MHz + */ +#if defined(STM32F1xx) + /* HSE max is 16 MHZ divided by 128 --> 125 KHz */ + HSEDiv = 128; +#elif defined(RCC_RTCCLKSOURCE_HSE_DIV32) && !defined(RCC_RTCCLKSOURCE_HSE_DIV31) + HSEDiv = 32; +#elif !defined(RCC_RTCCLKSOURCE_HSE_DIV31) + if ((HSE_VALUE / 2) <= HSE_RTC_MAX) { + HSEDiv = 2; + } else if ((HSE_VALUE / 4) <= HSE_RTC_MAX) { + HSEDiv = 4; + } else if ((HSE_VALUE / 8) <= HSE_RTC_MAX) { + HSEDiv = 8; + } else if ((HSE_VALUE / 16) <= HSE_RTC_MAX) { + HSEDiv = 16; + } +#elif defined(RCC_RTCCLKSOURCE_HSE_DIV31) + /* Not defined for STM32F2xx */ +#ifndef RCC_RTCCLKSOURCE_HSE_DIVX +#define RCC_RTCCLKSOURCE_HSE_DIVX 0x00000300U +#endif /* RCC_RTCCLKSOURCE_HSE_DIVX */ +#if defined(RCC_RTCCLKSOURCE_HSE_DIV63) +#define HSEDIV_MAX 64 +#else +#define HSEDIV_MAX 32 +#endif + for (HSEDiv = 2; HSEDiv < HSEDIV_MAX; HSEDiv++) { + if ((HSE_VALUE / HSEDiv) <= HSE_RTC_MAX) { + break; + } + } +#else +#error "Could not define HSE div" +#endif /* STM32F1xx */ + if ((HSE_VALUE / HSEDiv) > HSE_RTC_MAX) { + Error_Handler(); + } + clkVal = HSE_VALUE / HSEDiv; + } else if (source == LSI_CLOCK) { + clkVal = LSI_VALUE; + } else { + Error_Handler(); } } @@ -123,7 +182,7 @@ void RTC_SetClockSource(sourceClock_t source) static void RTC_initClock(sourceClock_t source) { RCC_PeriphCLKInitTypeDef PeriphClkInit; - + RTC_SetClockSource(source); if (source == LSE_CLOCK) { /* Enable the clock if not already set by user */ enableClock(LSE_CLOCK); @@ -133,7 +192,6 @@ static void RTC_initClock(sourceClock_t source) if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } - clkSrc = LSE_CLOCK; } else if (source == HSE_CLOCK) { /* Enable the clock if not already set by user */ enableClock(HSE_CLOCK); @@ -145,23 +203,17 @@ static void RTC_initClock(sourceClock_t source) #if defined(STM32F1xx) /* HSE max is 16 MHZ divided by 128 --> 125 KHz */ PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_HSE_DIV128; - HSEDiv = 128; #elif defined(RCC_RTCCLKSOURCE_HSE_DIV32) && !defined(RCC_RTCCLKSOURCE_HSE_DIV31) PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_HSE_DIV32; - HSEDiv = 32; #elif !defined(RCC_RTCCLKSOURCE_HSE_DIV31) if ((HSE_VALUE / 2) <= HSE_RTC_MAX) { PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_HSE_DIV2; - HSEDiv = 2; } else if ((HSE_VALUE / 4) <= HSE_RTC_MAX) { PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_HSE_DIV4; - HSEDiv = 4; } else if ((HSE_VALUE / 8) <= HSE_RTC_MAX) { PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_HSE_DIV8; - HSEDiv = 8; } else if ((HSE_VALUE / 16) <= HSE_RTC_MAX) { PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_HSE_DIV16; - HSEDiv = 16; } #elif defined(RCC_RTCCLKSOURCE_HSE_DIV31) /* Not defined for STM32F2xx */ @@ -169,29 +221,17 @@ static void RTC_initClock(sourceClock_t source) #define RCC_RTCCLKSOURCE_HSE_DIVX 0x00000300U #endif /* RCC_RTCCLKSOURCE_HSE_DIVX */ #if defined(RCC_RTCCLKSOURCE_HSE_DIV63) -#define HSEDIV_MAX 64 #define HSESHIFT 12 #else -#define HSEDIV_MAX 32 #define HSESHIFT 16 #endif - for (HSEDiv = 2; HSEDiv < HSEDIV_MAX; HSEDiv++) { - if ((HSE_VALUE / HSEDiv) <= HSE_RTC_MAX) { - PeriphClkInit.RTCClockSelection = (HSEDiv << HSESHIFT) | RCC_RTCCLKSOURCE_HSE_DIVX; - break; - } - } + PeriphClkInit.RTCClockSelection = (HSEDiv << HSESHIFT) | RCC_RTCCLKSOURCE_HSE_DIVX; #else #error "Could not define RTCClockSelection" #endif /* STM32F1xx */ - if ((HSE_VALUE / HSEDiv) > HSE_RTC_MAX) { - Error_Handler(); - } - if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } - clkSrc = HSE_CLOCK; } else if (source == LSI_CLOCK) { /* Enable the clock if not already set by user */ enableClock(LSI_CLOCK); @@ -201,78 +241,72 @@ static void RTC_initClock(sourceClock_t source) if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } - clkSrc = LSI_CLOCK; } else { Error_Handler(); } } -#if defined(STM32F1xx) -/** - * @brief set user asynchronous prescaler value. - * @note use RTC_AUTO_1_SECOND to reset value - * @param asynch: asynchronous prescaler value in range 0 - PREDIVA_MAX - * @retval None - */ -void RTC_setPrediv(uint32_t asynch) -{ - /* set the prescaler for a stm32F1 (value is hold by one param) */ - prediv = asynch; - LL_RTC_SetAsynchPrescaler(RTC, asynch); -} -#else /** * @brief set user (a)synchronous prescaler values. - * @note use -1 to reset value and use computed ones * @param asynch: asynchronous prescaler value in range 0 - PREDIVA_MAX + * @note Reset value: RTC_AUTO_1_SECOND for STM32F1xx series, else (PREDIVA_MAX + 1) * @param synch: synchronous prescaler value in range 0 - PREDIVS_MAX + * @note Reset value: (PREDIVS_MAX + 1), not used for STM32F1xx series. * @retval None */ -void RTC_setPrediv(int8_t asynch, int16_t synch) +void RTC_setPrediv(uint32_t asynch, uint32_t synch) { - if ((asynch >= -1) && ((uint32_t)asynch <= PREDIVA_MAX) && \ - (synch >= -1) && ((uint32_t)synch <= PREDIVS_MAX)) { +#if defined(STM32F1xx) + UNUSED(synch); + /* set the prescaler for a stm32F1 (value is hold by one param) */ + predivAsync = asynch; + if (!IS_RTC_ASYNCH_PREDIV(predivAsync)) { + predivAsync = RTC_AUTO_1_SECOND; + } + LL_RTC_SetAsynchPrescaler(RTC, predivAsync); +#else + if ((asynch <= PREDIVA_MAX) && (synch <= PREDIVS_MAX)) { predivAsync = asynch; predivSync = synch; } else { RTC_computePrediv(&predivAsync, &predivSync); } predivSync_bits = (uint8_t)_log2(predivSync) + 1; -} #endif /* STM32F1xx */ - -#if defined(STM32F1xx) -/** - * @brief get user asynchronous prescaler value for the current clock source. - * @param asynch: pointer where return asynchronous prescaler value. - * @retval None - */ -void RTC_getPrediv(uint32_t *asynch) -{ - /* get the prescaler for a stm32F1 (value is hold by one param) */ - prediv = LL_RTC_GetDivider(RTC); - *asynch = prediv; } -#else + + /** * @brief get user (a)synchronous prescaler values if set else computed ones * for the current clock source. * @param asynch: pointer where return asynchronous prescaler value. - * @param synch: pointer where return synchronous prescaler value. + * @param synch: pointer where return synchronous prescaler value, + * not used for STM32F1xx series. * @retval None */ -void RTC_getPrediv(int8_t *asynch, int16_t *synch) +void RTC_getPrediv(uint32_t *asynch, uint32_t *synch) { - if ((predivAsync == -1) || (predivSync == -1)) { - RTC_computePrediv(&predivAsync, &predivSync); +#if defined(STM32F1xx) + UNUSED(synch); + /* get the prescaler for a stm32F1 (value is hold by one param) */ + predivAsync = LL_RTC_GetDivider(RTC); + *asynch = predivAsync; +#else + if ((!IS_RTC_SYNCH_PREDIV(predivSync)) || (!IS_RTC_ASYNCH_PREDIV(predivAsync))) { + if (!LL_RTC_IsActiveFlag_INITS(RtcHandle.Instance)) { + RTC_computePrediv(&predivAsync, &predivSync); + } else { + predivAsync = LL_RTC_GetAsynchPrescaler(RtcHandle.Instance); + predivSync = LL_RTC_GetSynchPrescaler(RtcHandle.Instance); + } } if ((asynch != NULL) && (synch != NULL)) { *asynch = predivAsync; *synch = predivSync; } predivSync_bits = (uint8_t)_log2(predivSync) + 1; -} #endif /* STM32F1xx */ +} #if !defined(STM32F1xx) /** @@ -282,63 +316,87 @@ void RTC_getPrediv(int8_t *asynch, int16_t *synch) * @param synch: pointer where return synchronous prescaler value. * @retval None */ -static void RTC_computePrediv(int8_t *asynch, int16_t *synch) +static void RTC_computePrediv(uint32_t *asynch, uint32_t *synch) { uint32_t predivS = PREDIVS_MAX + 1; - uint32_t clk = 0; + *asynch = PREDIVA_MAX + 1; /* Get user predividers if manually configured */ if ((asynch == NULL) || (synch == NULL)) { return; } - /* Get clock frequency */ - if (clkSrc == LSE_CLOCK) { - clk = LSE_VALUE; - } else if (clkSrc == LSI_CLOCK) { - clk = LSI_VALUE; - } else if (clkSrc == HSE_CLOCK) { - clk = HSE_VALUE / HSEDiv; - } else { - Error_Handler(); - } - /* Find (a)synchronous prescalers to obtain the 1Hz calendar clock */ - for (*asynch = PREDIVA_MAX; *asynch >= 0; (*asynch)--) { - predivS = (clk / (*asynch + 1)) - 1; + do { + (*asynch)--; + predivS = (clkVal / (*asynch + 1)) - 1; - if (((predivS + 1) * (*asynch + 1)) == clk) { + if (((predivS + 1) * (*asynch + 1)) == clkVal) { break; } - } + } while (*asynch != 0); /* * Can't find a 1Hz, so give priority to RTC power consumption * by choosing the higher possible value for predivA */ - if ((predivS > PREDIVS_MAX) || (*asynch < 0)) { + if ((!IS_RTC_SYNCH_PREDIV(predivS)) || (!IS_RTC_ASYNCH_PREDIV(*asynch))) { *asynch = PREDIVA_MAX; - predivS = (clk / (*asynch + 1)) - 1; + predivS = (clkVal / (*asynch + 1)) - 1; } - if (predivS > PREDIVS_MAX) { + if (!IS_RTC_SYNCH_PREDIV(predivS)) { Error_Handler(); } - *synch = (int16_t)predivS; + *synch = predivS; + + fqce_apre = clkVal / (*asynch + 1); } #endif /* !STM32F1xx */ +#if defined(RTC_BINARY_NONE) +static void RTC_BinaryConf(binaryMode_t mode) +{ + RtcHandle.Init.BinMode = (mode == MODE_BINARY_MIX) ? RTC_BINARY_MIX : ((mode == MODE_BINARY_ONLY) ? RTC_BINARY_ONLY : RTC_BINARY_NONE); + if (RtcHandle.Init.BinMode == RTC_BINARY_MIX) { + /* Configure the 1s BCD calendar increment */ + + uint32_t inc = 1 / (1.0 / ((float)clkVal / (float)(predivAsync + 1.0))); + if (inc <= 256) { + RtcHandle.Init.BinMixBcdU = RTC_BINARY_MIX_BCDU_0; + } else if (inc < (256 << 1)) { + RtcHandle.Init.BinMixBcdU = RTC_BINARY_MIX_BCDU_1; + } else if (inc < (256 << 2)) { + RtcHandle.Init.BinMixBcdU = RTC_BINARY_MIX_BCDU_2; + } else if (inc < (256 << 3)) { + RtcHandle.Init.BinMixBcdU = RTC_BINARY_MIX_BCDU_3; + } else if (inc < (256 << 4)) { + RtcHandle.Init.BinMixBcdU = RTC_BINARY_MIX_BCDU_4; + } else if (inc < (256 << 5)) { + RtcHandle.Init.BinMixBcdU = RTC_BINARY_MIX_BCDU_5; + } else if (inc < (256 << 6)) { + RtcHandle.Init.BinMixBcdU = RTC_BINARY_MIX_BCDU_6; + } else if (inc < (256 << 7)) { + RtcHandle.Init.BinMixBcdU = RTC_BINARY_MIX_BCDU_7; + } else { + Error_Handler(); + } + } +} +#endif /* RTC_BINARY_NONE */ + /** * @brief RTC Initialization * This function configures the RTC time and calendar. By default, the * RTC is set to the 1st January 2001 * Note: year 2000 is invalid as it is the hardware reset value and doesn't raise INITS flag * @param format: enable the RTC in 12 or 24 hours mode + * @param mode: enable the RTC in BCD or Mix or Binary mode * @param source: RTC clock source: LSE, LSI or HSE * @param reset: force RTC reset, even if previously configured * @retval True if RTC is reinitialized, else false */ -bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) +bool RTC_init(hourFormat_t format, binaryMode_t mode, sourceClock_t source, bool reset) { bool reinit = false; hourAM_PM_t period = HOUR_AM, alarmPeriod = HOUR_AM; @@ -352,15 +410,26 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) uint32_t alarmBSubseconds = 0; bool isAlarmBSet = false; #endif -#if defined(STM32F1xx) - uint32_t asynch; -#else - int8_t asynch; - int16_t sync; -#endif initFormat = format; + initMode = mode; + /* Ensure all RtcHandle properly set */ RtcHandle.Instance = RTC; +#if defined(STM32F1xx) + RtcHandle.Init.AsynchPrediv = predivAsync; + RtcHandle.Init.OutPut = RTC_OUTPUTSOURCE_NONE; +#else + RtcHandle.Init.HourFormat = (format == HOUR_FORMAT_12) ? RTC_HOURFORMAT_12 : RTC_HOURFORMAT_24; + RtcHandle.Init.OutPut = RTC_OUTPUT_DISABLE; + RtcHandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH; + RtcHandle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN; +#if defined(RTC_OUTPUT_PULLUP_NONE) + RtcHandle.Init.OutPutPullUp = RTC_OUTPUT_PULLUP_NONE; +#endif +#if defined(RTC_OUTPUT_REMAP_NONE) + RtcHandle.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE; +#endif /* RTC_OUTPUT_REMAP_NONE */ +#endif /* STM32F1xx */ /* Ensure backup domain is enabled before we init the RTC so we can use the backup registers for date retention on stm32f1xx boards */ enableBackupDomain(); @@ -384,29 +453,18 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) BackupDate |= getBackupRegister(RTC_BKP_DATE + 1) & 0xFFFF; if ((BackupDate == 0) || reset) { // RTC needs initialization - /* Let HAL calculate the prescaler */ - RtcHandle.Init.AsynchPrediv = prediv; - RtcHandle.Init.OutPut = RTC_OUTPUTSOURCE_NONE; + // Init RTC clock + RTC_initClock(source); #else if (!LL_RTC_IsActiveFlag_INITS(RtcHandle.Instance) || reset) { // RTC needs initialization - RtcHandle.Init.HourFormat = format == HOUR_FORMAT_12 ? RTC_HOURFORMAT_12 : RTC_HOURFORMAT_24; - RtcHandle.Init.OutPut = RTC_OUTPUT_DISABLE; - RtcHandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH; - RtcHandle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN; -#if defined(RTC_OUTPUT_PULLUP_NONE) - RtcHandle.Init.OutPutPullUp = RTC_OUTPUT_PULLUP_NONE; -#endif -#if defined(RTC_OUTPUT_REMAP_NONE) - RtcHandle.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE; -#endif /* RTC_OUTPUT_REMAP_NONE */ -#if defined(RTC_BINARY_NONE) - RtcHandle.Init.BinMode = RTC_BINARY_NONE; -#endif - RTC_getPrediv((int8_t *) & (RtcHandle.Init.AsynchPrediv), (int16_t *) & (RtcHandle.Init.SynchPrediv)); -#endif // STM32F1xx // Init RTC clock RTC_initClock(source); + RTC_getPrediv(&(RtcHandle.Init.AsynchPrediv), &(RtcHandle.Init.SynchPrediv)); +#if defined(RTC_BINARY_NONE) + RTC_BinaryConf(mode); +#endif /* RTC_BINARY_NONE */ +#endif // STM32F1xx HAL_RTC_Init(&RtcHandle); // Default: saturday 1st of January 2001 @@ -443,14 +501,15 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) if (source != oldRtcClockSource) { // RTC is already initialized, but RTC clock source is changed // In case of RTC source clock change, Backup Domain is reset by RTC_initClock() - // Save current config before call to RTC_initClock() + // Save current config before reinit RTC_GetDate(&years, &month, &days, &weekDay); RTC_GetTime(&hours, &minutes, &seconds, &subSeconds, &period); + // As clock source changed, force update prediv with user or computef ones #if defined(STM32F1xx) - RTC_getPrediv(&asynch); + RTC_setPrediv(predivAsync, 0); #else - RTC_getPrediv(&asynch, &sync); -#endif // STM32F1xx + RTC_setPrediv(predivAsync, predivSync); +#endif if (isAlarmASet) { RTC_GetAlarm(ALARM_A, &alarmDay, &alarmHours, &alarmMinutes, &alarmSeconds, &alarmSubseconds, &alarmPeriod, &alarmMask); } @@ -459,17 +518,25 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) RTC_GetAlarm(ALARM_B, &alarmBDay, &alarmBHours, &alarmBMinutes, &alarmBSeconds, &alarmBSubseconds, &alarmBPeriod, &alarmBMask); } #endif + RTC_DeInit(false); // Init RTC clock RTC_initClock(source); - +#if defined(STM32F1xx) + RTC_getPrediv(&(RtcHandle.Init.AsynchPrediv), NULL); +#else + RTC_getPrediv(&(RtcHandle.Init.AsynchPrediv), &(RtcHandle.Init.SynchPrediv)); +#endif + /* + * TODO: RTC is already initialized, but RTC BIN mode is changed + * force the update of the BIN register in the RTC_ICSR + */ +#if defined(RTC_BINARY_NONE) + RTC_BinaryConf(mode); +#endif /* RTC_BINARY_NONE */ + HAL_RTC_Init(&RtcHandle); // Restore config RTC_SetTime(hours, minutes, seconds, subSeconds, period); RTC_SetDate(years, month, days, weekDay); -#if defined(STM32F1xx) - RTC_setPrediv(asynch); -#else - RTC_setPrediv(asynch, sync); -#endif // STM32F1xx if (isAlarmASet) { RTC_StartAlarm(ALARM_A, alarmDay, alarmHours, alarmMinutes, alarmSeconds, alarmSubseconds, alarmPeriod, alarmMask); } @@ -480,9 +547,17 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) #endif } else { // RTC is already initialized, and RTC stays on the same clock source - // Init RTC clock RTC_initClock(source); + // This initialize variables: predivAsync, predivSync and predivSync_bits +#if defined(STM32F1xx) + RTC_getPrediv(&(RtcHandle.Init.AsynchPrediv), NULL); +#else + RTC_getPrediv(&(RtcHandle.Init.AsynchPrediv), &(RtcHandle.Init.SynchPrediv)); +#endif +#if defined(RTC_BINARY_NONE) + RTC_BinaryConf(mode); +#endif /* RTC_BINARY_NONE */ #if defined(STM32F1xx) memcpy(&RtcHandle.DateToUpdate, &BackupDate, 4); /* Update date automatically by calling HAL_RTC_GetDate */ @@ -490,9 +565,6 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) /* and fill the new RTC Date value */ RTC_SetDate(RtcHandle.DateToUpdate.Year, RtcHandle.DateToUpdate.Month, RtcHandle.DateToUpdate.Date, RtcHandle.DateToUpdate.WeekDay); -#else - // This initialize variables: predivAsync, predivSync and predivSync_bits - RTC_getPrediv(NULL, NULL); #endif // STM32F1xx } } @@ -502,23 +574,48 @@ bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset) HAL_RTCEx_EnableBypassShadow(&RtcHandle); #endif + /* + * NOTE: freezing the RTC during stop mode (lowPower deepSleep) + * could inhibit the alarm interrupt and prevent the system to wakeUp + * from stop mode even if the RTC alarm flag is set. + */ return reinit; } /** * @brief RTC deinitialization. Stop the RTC. + * @param reset_cb: reset user callback * @retval None */ -void RTC_DeInit(void) +void RTC_DeInit(bool reset_cb) { HAL_RTC_DeInit(&RtcHandle); - RTCUserCallback = NULL; - callbackUserData = NULL; + /* Peripheral clock disable */ + __HAL_RCC_RTC_DISABLE(); +#ifdef __HAL_RCC_RTCAPB_CLK_DISABLE + __HAL_RCC_RTCAPB_CLK_DISABLE(); +#endif + HAL_NVIC_DisableIRQ(RTC_Alarm_IRQn); +#ifdef ONESECOND_IRQn + HAL_NVIC_DisableIRQ(ONESECOND_IRQn); +#endif +#ifdef STM32WLxx + HAL_NVIC_DisableIRQ(TAMP_STAMP_LSECSS_SSRU_IRQn); +#endif + if (reset_cb) { + RTCUserCallback = NULL; + callbackUserData = NULL; #ifdef RTC_ALARM_B - RTCUserCallbackB = NULL; - callbackUserDataB = NULL; + RTCUserCallbackB = NULL; + callbackUserDataB = NULL; #endif - RTCSecondsIrqCallback = NULL; +#ifdef ONESECOND_IRQn + RTCSecondsIrqCallback = NULL; +#endif +#ifdef STM32WLxx + RTCSubSecondsUnderflowIrqCallback = NULL; +#endif + } } /** @@ -542,14 +639,14 @@ bool RTC_IsConfigured(void) * @param hours: 0-12 or 0-23. Depends on the format used. * @param minutes: 0-59 * @param seconds: 0-59 - * @param subSeconds: 0-999 + * @param subSeconds: 0-999 (not used) * @param period: select HOUR_AM or HOUR_PM period in case RTC is set in 12 hours mode. Else ignored. * @retval None */ void RTC_SetTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds, hourAM_PM_t period) { RTC_TimeTypeDef RTC_TimeStruct; - UNUSED(subSeconds); + UNUSED(subSeconds); /* not used (read-only register) */ /* Ignore time AM PM configuration if in 24 hours format */ if (initFormat == HOUR_FORMAT_24) { period = HOUR_AM; @@ -592,7 +689,7 @@ void RTC_SetTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSe */ void RTC_GetTime(uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint32_t *subSeconds, hourAM_PM_t *period) { - RTC_TimeTypeDef RTC_TimeStruct; + RTC_TimeTypeDef RTC_TimeStruct = {0}; /* in BIN mode, only the subsecond is used */ if ((hours != NULL) && (minutes != NULL) && (seconds != NULL)) { #if defined(STM32F1xx) @@ -615,7 +712,19 @@ void RTC_GetTime(uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint32_t *s } #if defined(RTC_SSR_SS) if (subSeconds != NULL) { - *subSeconds = ((predivSync - RTC_TimeStruct.SubSeconds) * 1000) / (predivSync + 1); + /* + * The subsecond is the free-running downcounter, to be converted in milliseconds. + */ + if (initMode == MODE_BINARY_ONLY) { + *subSeconds = (((UINT32_MAX - RTC_TimeStruct.SubSeconds + 1) & UINT32_MAX) + * 1000) / fqce_apre; + } else if (initMode == MODE_BINARY_MIX) { + *subSeconds = (((UINT32_MAX - RTC_TimeStruct.SubSeconds) & predivSync) + * 1000) / fqce_apre; + } else { + /* the subsecond register value is converted in millisec on 32bit */ + *subSeconds = ((predivSync - RTC_TimeStruct.SubSeconds) * 1000) / (predivSync + 1); + } } #else UNUSED(subSeconds); @@ -665,7 +774,7 @@ void RTC_SetDate(uint8_t year, uint8_t month, uint8_t day, uint8_t wday) */ void RTC_GetDate(uint8_t *year, uint8_t *month, uint8_t *day, uint8_t *wday) { - RTC_DateTypeDef RTC_DateStruct; + RTC_DateTypeDef RTC_DateStruct = {0}; /* in BIN mode, the date is not used */ if ((year != NULL) && (month != NULL) && (day != NULL) && (wday != NULL)) { HAL_RTC_GetDate(&RtcHandle, &RTC_DateStruct, RTC_FORMAT_BIN); @@ -683,7 +792,7 @@ void RTC_GetDate(uint8_t *year, uint8_t *month, uint8_t *day, uint8_t *wday) * @param hours: 0-12 or 0-23 depends on the hours mode. * @param minutes: 0-59 * @param seconds: 0-59 - * @param subSeconds: 0-999 + * @param subSeconds: 0-999 milliseconds * @param period: HOUR_AM or HOUR_PM if in 12 hours mode else ignored. * @param mask: configure alarm behavior using alarmMask_t combination. * See AN4579 Table 5 for possible values. @@ -691,6 +800,9 @@ void RTC_GetDate(uint8_t *year, uint8_t *month, uint8_t *day, uint8_t *wday) */ void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds, hourAM_PM_t period, uint8_t mask) { +#if !defined(RTC_SSR_SS) + UNUSED(subSeconds); +#endif RTC_AlarmTypeDef RTC_AlarmStructure; /* Ignore time AM PM configuration if in 24 hours format */ @@ -698,11 +810,12 @@ void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, u period = HOUR_AM; } + /* Use alarm A by default because it is common to all STM32 HAL */ + RTC_AlarmStructure.Alarm = name; + if ((((initFormat == HOUR_FORMAT_24) && IS_RTC_HOUR24(hours)) || IS_RTC_HOUR12(hours)) && IS_RTC_DATE(day) && IS_RTC_MINUTES(minutes) && IS_RTC_SECONDS(seconds)) { /* Set RTC_AlarmStructure with calculated values*/ - /* Use alarm A by default because it is common to all STM32 HAL */ - RTC_AlarmStructure.Alarm = name; RTC_AlarmStructure.AlarmTime.Seconds = seconds; RTC_AlarmStructure.AlarmTime.Minutes = minutes; RTC_AlarmStructure.AlarmTime.Hours = hours; @@ -717,12 +830,19 @@ void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, u { RTC_AlarmStructure.AlarmSubSecondMask = predivSync_bits << RTC_ALRMASSR_MASKSS_Pos; } - RTC_AlarmStructure.AlarmTime.SubSeconds = predivSync - (subSeconds * (predivSync + 1)) / 1000; + /* + * The subsecond param is a nb of milliseconds to be converted in a subsecond + * downcounter value and to be compared to the SubSecond register + */ + if ((initMode == MODE_BINARY_ONLY) || (initMode == MODE_BINARY_MIX)) { + /* the subsecond is the millisecond to be converted in a subsecond downcounter value */ + RTC_AlarmStructure.AlarmTime.SubSeconds = UINT32_MAX - (subSeconds * (predivSync + 1)) / 1000; + } else { + RTC_AlarmStructure.AlarmTime.SubSeconds = predivSync - (subSeconds * (predivSync + 1)) / 1000; + } } else { RTC_AlarmStructure.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL; } -#else - UNUSED(subSeconds); #endif /* RTC_SSR_SS */ if (period == HOUR_PM) { RTC_AlarmStructure.AlarmTime.TimeFormat = RTC_HOURFORMAT12_PM; @@ -752,7 +872,6 @@ void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, u } } #else - UNUSED(subSeconds); UNUSED(period); UNUSED(day); UNUSED(mask); @@ -763,6 +882,39 @@ void RTC_StartAlarm(alarm_t name, uint8_t day, uint8_t hours, uint8_t minutes, u HAL_NVIC_SetPriority(RTC_Alarm_IRQn, RTC_IRQ_PRIO, RTC_IRQ_SUBPRIO); HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn); } +#if defined(RTC_SSR_SS) + else { + /* SS have to be managed*/ +#if defined(RTC_ALRMASSR_SSCLR) + RTC_AlarmStructure.BinaryAutoClr = RTC_ALARMSUBSECONDBIN_AUTOCLR_NO; +#endif /* RTC_ALRMASSR_SSCLR */ + RTC_AlarmStructure.AlarmMask = RTC_ALARMMASK_ALL; +#ifdef RTC_ALARM_B + if (name == ALARM_B) { + /* Expecting RTC_ALARMSUBSECONDBINMASK_NONE for the subsecond mask on ALARM B */ + RTC_AlarmStructure.AlarmSubSecondMask = mask << RTC_ALRMBSSR_MASKSS_Pos; + } else +#endif + { + /* Expecting RTC_ALARMSUBSECONDBINMASK_NONE for the subsecond mask on ALARM A */ + RTC_AlarmStructure.AlarmSubSecondMask = mask << RTC_ALRMASSR_MASKSS_Pos; + } +#if defined(RTC_ICSR_BIN) + if ((initMode == MODE_BINARY_ONLY) || (initMode == MODE_BINARY_MIX)) { + /* We have an SubSecond alarm to set in RTC_BINARY_MIX or RTC_BINARY_ONLY mode */ + /* The subsecond in ms is converted in ticks unit 1 tick is 1000 / fqce_apre */ + RTC_AlarmStructure.AlarmTime.SubSeconds = UINT32_MAX - (subSeconds * (predivSync + 1)) / 1000; + } else +#endif /* RTC_ICSR_BIN */ + { + RTC_AlarmStructure.AlarmTime.SubSeconds = predivSync - subSeconds * (predivSync + 1) / 1000; + } + /* Set RTC_Alarm */ + HAL_RTC_SetAlarm_IT(&RtcHandle, &RTC_AlarmStructure, RTC_FORMAT_BIN); + HAL_NVIC_SetPriority(RTC_Alarm_IRQn, RTC_IRQ_PRIO, RTC_IRQ_SUBPRIO); + HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn); + } +#endif /* RTC_SSR_SS */ } /** @@ -848,7 +1000,16 @@ void RTC_GetAlarm(alarm_t name, uint8_t *day, uint8_t *hours, uint8_t *minutes, } #if defined(RTC_SSR_SS) if (subSeconds != NULL) { - *subSeconds = ((predivSync - RTC_AlarmStructure.AlarmTime.SubSeconds) * 1000) / (predivSync + 1); + /* + * The subsecond is the bit SS[14:0] of the ALARM SSR register (not ALARMxINR) + * to be converted in milliseconds + */ + if ((initMode == MODE_BINARY_ONLY) || (initMode == MODE_BINARY_MIX)) { + /* read the ALARM SSR register on SS[14:0] bits --> 0x7FFF */ + *subSeconds = (((0x7fff - RTC_AlarmStructure.AlarmTime.SubSeconds + 1) & 0x7fff) * 1000) / fqce_apre; + } else { + *subSeconds = ((predivSync - RTC_AlarmStructure.AlarmTime.SubSeconds) * 1000) / (predivSync + 1); + } } #else UNUSED(subSeconds); @@ -1071,6 +1232,48 @@ void RTC_WKUP_IRQHandler(void) #endif /* STM32F1xx */ #endif /* ONESECOND_IRQn */ +#ifdef STM32WLxx +/** + * @brief Attach SubSeconds underflow interrupt callback. + * @param func: pointer to the callback + * @retval None + */ +void attachSubSecondsUnderflowIrqCallback(voidCallbackPtr func) +{ + /* Callback called on SSRU interrupt */ + RTCSubSecondsUnderflowIrqCallback = func; + + /* Enable the IRQ that will trig the one-second interrupt */ + if (HAL_RTCEx_SetSSRU_IT(&RtcHandle) != HAL_OK) { + Error_Handler(); + } + HAL_NVIC_SetPriority(TAMP_STAMP_LSECSS_SSRU_IRQn, RTC_IRQ_SSRU_PRIO, RTC_IRQ_SSRU_SUBPRIO); + HAL_NVIC_EnableIRQ(TAMP_STAMP_LSECSS_SSRU_IRQn); +} + +/** + * @brief Detach SubSeconds underflow interrupt callback. + * @param None + * @retval None + */ +void detachSubSecondsUnderflowIrqCallback(void) +{ + RTCSubSecondsUnderflowIrqCallback = NULL; + if (HAL_RTCEx_DeactivateSSRU(&RtcHandle) != HAL_OK) { + Error_Handler(); + } + HAL_NVIC_DisableIRQ(TAMP_STAMP_LSECSS_SSRU_IRQn); +} + +void HAL_RTCEx_SSRUEventCallback(RTC_HandleTypeDef *hrtc) +{ + (void)hrtc; + if (RTCSubSecondsUnderflowIrqCallback != NULL) { + RTCSubSecondsUnderflowIrqCallback(NULL); + } +} +#endif /* STM32WLxx */ + #if defined(STM32F1xx) void RTC_StoreDate(void) { diff --git a/src/rtc.h b/src/rtc.h index bad868b..8d0bcd8 100644 --- a/src/rtc.h +++ b/src/rtc.h @@ -55,6 +55,12 @@ typedef enum { HOUR_FORMAT_24 } hourFormat_t; +typedef enum { + MODE_BINARY_NONE, /* BCD only */ + MODE_BINARY_ONLY, + MODE_BINARY_MIX +} binaryMode_t; + typedef enum { HOUR_AM, HOUR_PM @@ -70,7 +76,9 @@ typedef enum { /* NOTE: STM32 RTC can't assign a month or a year to an alarm. Those enum are kept for compatibility but are ignored inside enableAlarm(). */ M_MSK = 16, - Y_MSK = 32 + Y_MSK = 32, + SUBSEC_MSK = 48, + ALL_MSK = 0xFF } alarmMask_t; typedef enum { @@ -100,8 +108,12 @@ typedef void(*voidCallbackPtr)(void *); #ifndef RTC_IRQ_SUBPRIO #define RTC_IRQ_SUBPRIO 0 #endif - - +#ifndef RTC_IRQ_SSRU_PRIO +#define RTC_IRQ_SSRU_PRIO 0 +#endif +#ifndef RTC_IRQ_SSRU_SUBPRIO +#define RTC_IRQ_SSRU_SUBPRIO 0 +#endif #define HSE_RTC_MAX 1000000U #if !defined(STM32F1xx) @@ -113,6 +125,7 @@ typedef void(*voidCallbackPtr)(void *); #else /* for stm32F1 the MAX value is combining PREDIV low & high registers */ #define PREDIVA_MAX 0xFFFFFU +#define PREDIVS_MAX 0xFFFFFFFFU /* Unused for STM32F1xx series */ #endif /* !STM32F1xx */ #if defined(STM32C0xx) || defined(STM32F0xx) || defined(STM32H5xx) || \ @@ -164,17 +177,13 @@ typedef void(*voidCallbackPtr)(void *); /* Exported macro ------------------------------------------------------------*/ /* Exported functions ------------------------------------------------------- */ +RTC_HandleTypeDef *RTC_GetHandle(void); void RTC_SetClockSource(sourceClock_t source); -#if defined(STM32F1xx) -void RTC_getPrediv(uint32_t *asynch); -void RTC_setPrediv(uint32_t asynch); -#else -void RTC_getPrediv(int8_t *asynch, int16_t *synch); -void RTC_setPrediv(int8_t asynch, int16_t synch); -#endif /* STM32F1xx */ +void RTC_getPrediv(uint32_t *asynch, uint32_t *synch); +void RTC_setPrediv(uint32_t asynch, uint32_t synch); -bool RTC_init(hourFormat_t format, sourceClock_t source, bool reset); -void RTC_DeInit(void); +bool RTC_init(hourFormat_t format, binaryMode_t mode, sourceClock_t source, bool reset); +void RTC_DeInit(bool reset_cb); bool RTC_IsConfigured(void); void RTC_SetTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds, hourAM_PM_t period); @@ -193,7 +202,10 @@ void detachAlarmCallback(alarm_t name); void attachSecondsIrqCallback(voidCallbackPtr func); void detachSecondsIrqCallback(void); #endif /* ONESECOND_IRQn */ - +#ifdef STM32WLxx +void attachSubSecondsUnderflowIrqCallback(voidCallbackPtr func); +void detachSubSecondsUnderflowIrqCallback(void); +#endif /* STM32WLxx */ #if defined(STM32F1xx) void RTC_StoreDate(void); #endif