Skip to content

harden prediv management and add parameter to set binary mode #98

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)`**
Expand Down
51 changes: 33 additions & 18 deletions examples/RTCReset/RTCReset.ino
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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();

Expand All @@ -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
Expand All @@ -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();
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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
Expand Down
82 changes: 43 additions & 39 deletions src/STM32RTC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -103,7 +104,7 @@ void STM32RTC::begin(bool resetTime, Hour_Format format)
*/
void STM32RTC::end(void)
{
RTC_DeInit();
RTC_DeInit(true);
_timeSet = false;
}

Expand All @@ -117,74 +118,77 @@ 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)
* @brief get the Binary Mode.
* @retval mode: MODE_BCD, MODE_BIN or MODE_MIX
*/
STM32RTC::Binary_Mode STM32RTC::getBinaryMode(void)
{
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::getPrediv(uint32_t *predivA, int16_t *dummy)
void STM32RTC::setBinaryMode(Binary_Mode mode)
{
UNUSED(dummy);
RTC_getPrediv(predivA);
_mode = mode;
}
#else

/**
* @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.
Expand Down Expand Up @@ -827,7 +831,7 @@ void STM32RTC::setAlarmSubSeconds(uint32_t subSeconds, Alarm name)
#ifdef RTC_ALARM_B
if (name == ALARM_B) {
_alarmBSubSeconds = subSeconds;
}
} else
#else
UNUSED(name);
#endif
Expand All @@ -850,7 +854,7 @@ void STM32RTC::setAlarmSeconds(uint8_t seconds, Alarm name)
#ifdef RTC_ALARM_B
if (name == ALARM_B) {
_alarmBSeconds = seconds;
}
} else
#else
UNUSED(name);
#endif
Expand All @@ -873,7 +877,7 @@ void STM32RTC::setAlarmMinutes(uint8_t minutes, Alarm name)
#ifdef RTC_ALARM_B
if (name == ALARM_B) {
_alarmBMinutes = minutes;
}
} else
#else
UNUSED(name);
#endif
Expand Down
26 changes: 17 additions & 9 deletions src/STM32RTC.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ class STM32RTC {
PM = HOUR_PM
};

enum Binary_Mode : uint8_t {
MODE_BCD = MODE_BINARY_NONE, /* not used */
MODE_BIN = MODE_BINARY_ONLY,
MODE_MIX = MODE_BINARY_MIX
};

enum Alarm_Match : uint8_t {
MATCH_OFF = OFF_MSK, // Never
MATCH_SS = SS_MSK, // Every Minute
Expand Down Expand Up @@ -126,7 +132,12 @@ class STM32RTC {
void end(void);

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);
Expand Down Expand Up @@ -212,13 +223,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();
Expand All @@ -232,11 +236,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;
Expand Down
Loading