Skip to content

feat: add second alarm (B) support #86

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
Jan 18, 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
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ _SubSeconds alarm management_
* **`void setAlarmTime(uint8_t hours, uint8_t minutes, uint8_t seconds, uint32_t subSeconds = 0, AM_PM period = AM)`**
* **`uint32_t getEpoch(uint32_t *subSeconds = nullptr)`**
* **`void setEpoch(uint32_t ts, uint32_t subSeconds = 0)`**
* **`void setAlarmEpoch(uint32_t ts, Alarm_Match match = MATCH_DHHMMSS, uint32_t subSeconds = 0)`**
* **`void setAlarmEpoch(uint32_t ts, Alarm_Match match = MATCH_DHHMMSS, uint32_t subSeconds = 0)`**

_Library version management_

Expand Down Expand Up @@ -105,7 +105,7 @@ _Library version management_
_One-Second interrupt_

STM32 RTC includes a one-second interrupt for generating a periodic interrupt signal.
- This feature is native on the stm32F1xx and mapped on the existing WakeUp interrupt on other stm32 mcus.
- This feature is native on the stm32F1xx and mapped on the existing WakeUp interrupt on other stm32 mcus.
- It is not available on some stm32F0 devices.

* **new API:**
Expand Down Expand Up @@ -138,6 +138,21 @@ To know if a time has already been set use:
}
```

### Since STM32RTC version higher than 1.3.4
_Second alarm (Alarm B)_

Some STM32 RTC have a second alarm named `RTC_ALARM_B`.
It is possible to use it thanks all alarm API with an extra argument:
- `STM32RTC::ALARM_A`
- `STM32RTC::ALARM_B`

```C++
rtc.attachInterrupt(myCallback, &myCallbackdata, STM32RTC::ALARM_B);
rtc.setAlarmDay(day, STM32RTC::ALARM_B);
rtc.setAlarmTime(hours, minutes, seconds + 5, 567, STM32RTC::ALARM_B);
rtc.enableAlarm(rtc.MATCH_DHHMMSS, STM32RTC::ALARM_B);
```

Refer to the Arduino RTC documentation for the other functions
http://arduino.cc/en/Reference/RTC

Expand Down
3 changes: 1 addition & 2 deletions examples/Epoch/Epoch.ino
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
*/

#include <STM32RTC.h>
#include <time.h>

/* Get the rtc object */
STM32RTC& rtc = STM32RTC::getInstance();
Expand Down Expand Up @@ -63,4 +62,4 @@ void print2digits(uint32_t number) {
Serial.print("0");
}
Serial.print(number);
}
}
221 changes: 221 additions & 0 deletions examples/RTCReset/RTCReset.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
/*
RTCReset

This sketch allows to test STM32RTC after a software reset or power off
with VBat. Including Alarm A and B management.

Creation 17 jan 2023
by Frederic Pillon for STMicroelectronics

This example code is in the public domain.

https://github.com/stm32duino/STM32RTC

*/
#include <STM32RTC.h>

/* Get the rtc object */
STM32RTC& rtc = STM32RTC::getInstance();

/* Change these values to set the current initial time

format: date: "Dec 31 2022" and time: "23:59:56"
by default use built date and time
*/
static const char* mydate = __DATE__;
static const char* mytime = __TIME__;
//static const char* mydate = "Dec 31 2022";
//static const char* mytime = "23:59:56";

/* Declare it volatile since it's incremented inside an interrupt */
volatile int alarmMatch_counter = 0;
volatile int alarmMatchB_counter = 0;

typedef struct {
uint32_t next;
bool alarm_a;
} cb_data_t;

static cb_data_t atime = { 2222, true};
#ifdef RTC_ALARM_B
static cb_data_t btime = { 3333, false};
#endif
static byte seconds = 0;
static byte minutes = 0;
static byte hours = 0;
static uint32_t subSeconds = 0;

static byte weekDay = 1;
static byte day = 0;
static byte month = 0;
static byte year = 0;
static STM32RTC::Hour_Format hourFormat = STM32RTC::HOUR_24;
static STM32RTC::AM_PM period = STM32RTC::AM;

#ifndef USER_BTN
#define USER_BTN PA0
#endif

static uint8_t conv2d(const char* p) {
uint8_t v = 0;
if ('0' <= *p && *p <= '9')
v = *p - '0';
return 10 * v + *++p - '0';
}

// sample input: date = "Dec 26 2009", time = "12:34:56"
void initDateTime (void) {
Serial.printf("Build date & time %s, %s\n", mydate, mytime);

year = conv2d(mydate + 9);
// Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
switch (mydate[0]) {
case 'J': month = (mydate[1] == 'a') ? 1 : ((mydate[2] == 'n') ? 6 : 7); break;
case 'F': month = 2; break;
case 'A': month = mydate[2] == 'r' ? 4 : 8; break;
case 'M': month = mydate[2] == 'r' ? 3 : 5; break;
case 'S': month = 9; break;
case 'O': month = 10; break;
case 'N': month = 11; break;
case 'D': month = 12; break;
}
day = conv2d(mydate + 4);
hours = conv2d(mytime);
if (hourFormat == rtc.HOUR_12) {
period = hours >= 12 ? rtc.PM : rtc.AM;
hours = hours >= 13 ? hours - 12 : (hours < 1 ? hours + 12 : hours);
}
minutes = conv2d(mytime + 3);
seconds = conv2d(mytime + 6);
}

void setup()
{
pinMode(USER_BTN, INPUT_PULLUP);
int32_t default_state = digitalRead(USER_BTN);

Serial.begin(9600);
while (!Serial);
// Wait user input to start
while (digitalRead(USER_BTN) == default_state);
// Convenient function to init date and time variables
initDateTime();

// Select RTC clock source: LSI_CLOCK, LSE_CLOCK or HSE_CLOCK.
// By default the LSI is selected as source.
// rtc.setClockSource(STM32RTC::LSE_CLOCK);

// In any case attach a callback to the RTC alarm interrupt.
rtc.attachInterrupt(alarmMatch, &atime);
#ifdef RTC_ALARM_B
rtc.attachInterrupt(alarmMatch, &btime, STM32RTC::ALARM_B);
#endif
rtc.begin(); // Initialize RTC 24H format
if (!rtc.isTimeSet()) {
Serial.printf("RTC time not set\n Set it.\n");
// Set the time
rtc.setTime(hours, minutes, seconds);
rtc.setDate(weekDay, day, month, year);
// ALARM_A (default argument)
rtc.setAlarmDay(day);
rtc.setAlarmTime(hours, minutes, seconds + 5, 567);
rtc.enableAlarm(rtc.MATCH_DHHMMSS);
#ifdef RTC_ALARM_B
// ALARM_B
rtc.setAlarmDay(day, STM32RTC::ALARM_B);
rtc.setAlarmTime(hours, minutes, seconds + 5, 567, STM32RTC::ALARM_B);
rtc.enableAlarm(rtc.MATCH_DHHMMSS, STM32RTC::ALARM_B);
#endif
} else {
// RTC already initialized
time_t epoc, alarm_epoc;
if (rtc.isAlarmEnabled()) {
rtc.enableAlarm(rtc.MATCH_DHHMMSS);
alarm_epoc = rtc.getAlarmEpoch();
epoc = rtc.getEpoch();
if (difftime(alarm_epoc, epoc) <= 0) {
Serial.printf("Alarm A was enabled and expired, force callback call\n");
alarmMatch(&atime);
} else {
Serial.printf("Alarm A was enabled and restored\n");
}
}
#ifdef RTC_ALARM_B
// ALARM_B
if (rtc.isAlarmEnabled(STM32RTC::ALARM_B)) {
rtc.enableAlarm(rtc.MATCH_DHHMMSS, STM32RTC::ALARM_B);
alarm_epoc = rtc.getAlarmEpoch(STM32RTC::ALARM_B);
epoc = rtc.getEpoch();
if (difftime(alarm_epoc, epoc) <= 0) {
Serial.printf("Alarm B was enabled and expired, force callback call\n");
alarmMatch(&btime);
} else {
Serial.printf("Alarm B was enabled and restored\n");
}
}
#endif
Serial.printf("RTC time already set\n");
}
Serial.printf("Alarm A enable status: %s\n", (rtc.isAlarmEnabled(STM32RTC::ALARM_A)) ? "True" : "False");
#ifdef RTC_ALARM_B
Serial.printf("Alarm B enable status: %s\n", (rtc.isAlarmEnabled(STM32RTC::ALARM_B)) ? "True" : "False");
#else
Serial.println("Alarm B not available.");
#endif
}

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);
// Print current alarm configuration
Serial.printf("Alarm A: %02d %02d:%02d:%02d.%03d\n", rtc.getAlarmDay(), rtc.getAlarmHours(), rtc.getAlarmMinutes(), rtc.getAlarmSeconds(), rtc.getAlarmSubSeconds());
#ifdef RTC_ALARM_B
Serial.printf("Alarm B: %02d %02d:%02d:%02d.%03d\n", rtc.getAlarmDay(STM32RTC::ALARM_B), rtc.getAlarmHours(STM32RTC::ALARM_B), rtc.getAlarmMinutes(STM32RTC::ALARM_B), rtc.getAlarmSeconds(STM32RTC::ALARM_B), rtc.getAlarmSubSeconds(STM32RTC::ALARM_B));
#endif
delay(1000);
}

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};
if (data != NULL) {
cbdata.next = ((cb_data_t*)data)->next;
cbdata.alarm_a = ((cb_data_t*)data)->alarm_a;
_millis = cbdata.next;
}

sec = _millis / 1000;
#if !defined(RTC_SSR_SS)
// Minimum is 1 second
if (sec == 0) {
sec = 1;
}
epoc = rtc.getEpoch(&epoc_ms);
#else
_millis = _millis % 1000;
epoc = rtc.getEpoch(&epoc_ms);

// Update epoch_ms - might need to add a second to epoch
epoc_ms += _millis;
if (epoc_ms >= 1000) {
sec ++;
epoc_ms -= 1000;
}
#endif
if (cbdata.alarm_a) {
Serial.printf("\t\t\tAlarm A Match %i\n", ++alarmMatch_counter);
rtc.setAlarmEpoch(epoc + sec, STM32RTC::MATCH_SS, epoc_ms);
}
#ifdef RTC_ALARM_B
else {
Serial.printf("\t\t\tAlarm B Match %i\n", ++alarmMatchB_counter);
rtc.setAlarmEpoch(epoc + sec, STM32RTC::MATCH_SS, epoc_ms, STM32RTC::ALARM_B);
}
#endif
}
35 changes: 28 additions & 7 deletions examples/advancedRTCAlarm/advancedRTCAlarm.ino
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
It uses the optional 'data' alarm callback parameters to
reload alarm with 'atime' offset indefinitely.

If a second alarm (B) is available, it is configured
to trigger each second.

Creation 25 May 2018
by Frederic Pillon for STMicroelectronics
Modified 03 Jul 2020
Expand All @@ -22,11 +25,17 @@ STM32RTC& rtc = STM32RTC::getInstance();

/* Declare it volatile since it's incremented inside an interrupt */
volatile int alarmMatch_counter = 0;
#ifdef RTC_ALARM_B
volatile int alarmBMatch_counter = 0;
#endif

/* Change this value to set alarm match offset in millisecond */
/* Note that STM32F1xx does not manage subsecond only second */
/* Note that only mcu with RTC_SSR_SS defined managed subsecond else only second */
#if defined(RTC_SSR_SS)
static uint32_t atime = 678;

#else
static uint32_t atime = 1000;
#endif
/* Change these values to set the current initial time */
const byte seconds = 0;
const byte minutes = 0;
Expand Down Expand Up @@ -54,6 +63,13 @@ void setup()
rtc.setAlarmDay(day);
rtc.setAlarmTime(16, 0, 10, 567);
rtc.enableAlarm(rtc.MATCH_DHHMMSS);

#ifdef RTC_ALARM_B
rtc.attachInterrupt(alarmBMatch, STM32RTC::ALARM_B);
rtc.setAlarmDay(day, STM32RTC::ALARM_B);
rtc.setAlarmTime(16, 0, 11, 567, STM32RTC::ALARM_B);
rtc.enableAlarm(rtc.MATCH_DHHMMSS, STM32RTC::ALARM_B);
#endif
}

void loop()
Expand All @@ -70,14 +86,10 @@ void alarmMatch(void *data)

if (data != NULL) {
_millis = *(uint32_t*)data;
// Minimum is 1 second
if (sec == 0) {
sec = 1;
}
}

sec = _millis / 1000;
#ifdef STM32F1xx
#if !defined(RTC_SSR_SS)
// Minimum is 1 second
if (sec == 0) {
sec = 1;
Expand All @@ -97,3 +109,12 @@ void alarmMatch(void *data)
Serial.printf("Alarm Match %i\n", ++alarmMatch_counter);
rtc.setAlarmEpoch(epoc + sec, STM32RTC::MATCH_SS, epoc_ms);
}

#ifdef RTC_ALARM_B
void alarmBMatch(void *data)
{
(void)data;
Serial.printf("Alarm B Match %i\n", ++alarmBMatch_counter);
rtc.setAlarmEpoch(rtc.getEpoch() + 2, STM32RTC::MATCH_SS, STM32RTC::ALARM_B);
}
#endif
4 changes: 4 additions & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ setTime KEYWORD2

getEpoch KEYWORD2
getY2kEpoch KEYWORD2
getAlarmEpoch KEYWORD2
setEpoch KEYWORD2
setY2kEpoch KEYWORD2
setAlarmEpoch KEYWORD2
Expand Down Expand Up @@ -70,6 +71,7 @@ detachSecondsInterrupt KEYWORD2

getClockSource KEYWORD2
setClockSource KEYWORD2
isAlarmEnabled KEYWORD2
isConfigured KEYWORD2
isTimeSet KEYWORD2

Expand All @@ -96,3 +98,5 @@ PM LITERAL1
LSE_CLOCK LITERAL1
LSI_CLOCK LITERAL1
HSE_CLOCK LITERAL1
ALARM_A LITERAL1
ALARM_B LITERAL1
Loading