Skip to main content
arnold_w
Senior II
March 14, 2019
Question

Advice on using RTC and WUTR on STM32F446 as a versatile timer and how to reset RTC->SSR?

  • March 14, 2019
  • 2 replies
  • 999 views

I am working with the STM32F446 microcontroller and I would like to create a versatile timer function using the RTC:

typedef void (*timerCallback_t)();

void scheduleOneTimerEvent(uint32_t timeoutMs, timerCallback_t timerCallback);

I am considering using one WUTR interrupt for this and I would let ck_spre provide clock for the WUTR. Since I only care about WUTR (I won't use the RTC as a regular calendar clock at all) I will have varying frequencies on ck_spre, depending on what timeoutMs is set to (and for large timeoutMs I will need several "hidden" interrupts that don't call the timerCallback). Does anybody foresee any problems with this or have any general suggestions for improvement? Also, to keep things simple, I was planning to reset time to default whenever scheduleOneTimerEvent is called, but I read in the reference manual that the subsecond register (RTC->SSR) is read only, so how would I go about to reset it?

This topic has been closed for replies.

2 replies

arnold_w
arnold_wAuthor
Senior II
March 14, 2019

I guess I can use a Backup Domain Reset (bit BDRST in the RCC->BDCR register) to reset the subsecond register (RTC->SSR), right?

arnold_w
arnold_wAuthor
Senior II
May 1, 2019

I did like this instead:

typedef enum {
 _0_ms = 256,
 _11_7_ms = 253,
 _15_6_ms = 252,
 .
 .
 .
 _984_4_ms = 4,
 _988_3_ms = 3,
 _992_2_ms = 2,
 _996_1_ms = 1,
} subSeconds_e;
 
#define DISABLE_ALL_INTS_IF_NECESSARY() uint32_t old_primask; \
 old_primask = __get_PRIMASK(); \
 __disable_irq()
 
/// Enable all interrupts if they were enable
#define ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED() if (!old_primask) \
 { \
 __enable_irq(); \
 }
 
#define HAL_RTC_WRITEPROTECTION_ENABLE() do { \
 RTC->WPR = 0xFF; \
 } while(0)
 
#define HAL_RTC_WRITEPROTECTION_DISABLE() do { \
 RTC->WPR = 0xCA; \
 RTC->WPR = 0x53; \
 } while(0)
 
#define HAL_RTC_ALARM_GET_IT(__INTERRUPT__) ((((RTC->ISR) & ((__INTERRUPT__) >> 4)) != 0) ? 1 : 0)
 
#define HAL_RTC_ALARM_CLEAR_FLAG(__FLAG__) do { \
 RTC->ISR = (~((__FLAG__) | RTC_ISR_INIT) | (RTC->ISR & RTC_ISR_INIT)); \
 } while(0)
 
static EventCallback_t RTCinterruptCallback_ = (EventCallback_t)NULL;
static uint32_t arg1_, arg2_;
static volatile Bool_t isRTCintScheduled_ = FALSE;
 
static HAL_StatusTypeDef RTC_EnterInitMode_() {
 if ((RTC->ISR & RTC_ISR_INITF) == 0) { // Check if the Initialization mode is set
 RTC->ISR = (uint32_t)RTC_INIT_MASK; // Set the Initialization mode
 uint32_t startTime = GET_TIMESTAMP_IN_CLK_CYCLES(); // Get tick
 
 // Wait until RTC is in INIT state and if Time out is reached exit
 while ((RTC->ISR & RTC_ISR_INITF) == 0) {
 if (RTC_TIMEOUT_VALUE < (clkCyclesToMilliseconds(getClkCyclesElapsed(startTime)))) {
 return HAL_TIMEOUT;
 }
 }
 }
 return HAL_OK;
}
 
Bool_t isRTCintScheduled() {
 return isRTCintScheduled_;
}
 
HAL_StatusTypeDef scheduleRTCinterrupt(uint8_t delaySecondsMax59, subSeconds_e delaySubSeconds, EventCallback_t RTCinterruptCallback, uint32_t arg1, uint32_t arg2) {
 DISABLE_ALL_INTS_IF_NECESSARY();
 if ((delaySecondsMax59 == 0) && (delaySubSeconds == _0_ms)) {
 issueDummyInt(RTCinterruptCallback, arg1, arg2);
 ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
 return HAL_OK;
 }
 isRTCintScheduled_ = TRUE;
 uint32_t RCC_BDCR = RCC->BDCR; // Make a copy of RCC->BDCR register
 RCC->BDCR |= ((uint32_t)RCC_BDCR_BDRST); // Hold Backup Domain in reset (will reset all RTC registers)
 RCC->BDCR &= ~((uint32_t)RCC_BDCR_BDRST); // Release Backup Domain from reset
 RCC->BDCR = RCC_BDCR; // Restore RCC->BDCR register values that were lost during the reset
 __HAL_RCC_RTC_ENABLE();
 HAL_RTC_WRITEPROTECTION_DISABLE(); // Disable the write protection for RTC registers
 if (RTC_EnterInitMode_() != HAL_OK) { // Set Initialization mode
 HAL_RTC_WRITEPROTECTION_ENABLE(); // Enable the write protection for RTC registers
 return HAL_ERROR;
 }
 
 RTC->CR = RTC_CR_BYPSHAD; // Set RTC_CR register
 RTC->PRER = 255; // Configure the RTC PRER, lower bits (7-bit asynchronous prescalar)
 RTC->PRER |= (((uint32_t)127) << 16); // Configure the RTC PRER, upper bits (16-bit synchronous prescalar)
 
#define ALARM_TIME_HOURS (0)
#define ALARM_TIME_MINUTES (0)
#define ALARM_DATE_WEEKDAY (0x1)
 
 RTC->ALRMAR = ((((uint32_t)ALARM_TIME_HOURS ) << 16) |
 (((uint32_t)ALARM_TIME_MINUTES ) << 8 ) |
 (((uint32_t)delaySecondsMax59 ) ) |
 (((uint32_t)RTC_HOURFORMAT12_AM) << 16 ) |
 (((uint32_t)ALARM_DATE_WEEKDAY ) << 24) |
 (((uint32_t)RTC_ALARMDATEWEEKDAYSEL_DATE) ) |
 (((uint32_t)RTC_ALARMMASK_NONE )));
 RTC->ALRMASSR = ((uint32_t)delaySubSeconds) | RTC_ALARMSUBSECONDMASK_NONE; // Configure the Alarm A Sub Second register
 RTC->ISR &= ~((uint32_t)RTC_ISR_INIT); // Exit Initialization mode
 
 uint32_t startTime = GET_TIMESTAMP_IN_CLK_CYCLES();
 while ((RTC->ISR & RTC_FLAG_ALRAWF) == 0) { // Wait until RTC ALRAWF flag is set and if Time out is reached exit
 if (RTC_TIMEOUT_VALUE < (clkCyclesToMilliseconds(getClkCyclesElapsed(startTime)))) {
 HAL_RTC_WRITEPROTECTION_ENABLE(); // Enable the write protection for RTC registers
 return HAL_TIMEOUT;
 }
 }
 
 RTC->CR |= RTC_IT_ALRA | RTC_CR_ALRAE; // Configure the Alarm interrupt and the Alarm state: Enable Alarm
 HAL_RTC_WRITEPROTECTION_ENABLE(); // Enable the write protection for RTC registers
 RTCinterruptCallback_ = RTCinterruptCallback;
 arg1_ = arg1;
 arg2_ = arg2;
 EXTI->RTSR |= RTC_EXTI_LINE_ALARM_EVENT; // Configure rising edge on alarm EXTI interrupt
 EXTI->IMR |= RTC_EXTI_LINE_ALARM_EVENT; // RTC Alarm Interrupt Configuration: EXTI configuration
 HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn); // Enable RTC alarm interrupt in NVIC
 ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
 return HAL_OK;
}
 
void cancelRTCandStopRTC() {
 DISABLE_ALL_INTS_IF_NECESSARY();
 RTCinterruptCallback_ = NULL;
 HAL_NVIC_DisableIRQ(RTC_Alarm_IRQn); // Enable RTC alarm interrupt in NVIC
 RTC->CR &= ~((uint32_t)(RTC_IT_ALRA | RTC_CR_ALRAE)); // Configure the Alarm interrupt and the Alarm state: Disable Alarm
 EXTI->RTSR &= ~RTC_EXTI_LINE_ALARM_EVENT; // Configure rising edge on alarm EXTI interrupt
 EXTI->IMR &= ~RTC_EXTI_LINE_ALARM_EVENT; // RTC Alarm Interrupt Configuration: EXTI configuration
 HAL_RTC_ALARM_CLEAR_FLAG(RTC_FLAG_ALRAF); // Clear the Alarm interrupt pending bit
 isRTCintScheduled_ = FALSE;
 ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
}
 
void RTC_Alarm_IRQHandler(void) {
 EXTI->PR = RTC_EXTI_LINE_ALARM_EVENT; // Clear the EXTI's line Flag for RTC Alarm
 if (HAL_RTC_ALARM_GET_IT(RTC_IT_ALRA)) {
 if (RTC->CR & RTC_IT_ALRA) { // Get the status of the Interrupt
 HAL_RTC_ALARM_CLEAR_FLAG(RTC_FLAG_ALRAF); // Clear the Alarm interrupt pending bit
 EventCallback_t RTCinterruptCallbackCopy = RTCinterruptCallback_;
 RTCinterruptCallback_ = NULL;
 isRTCintScheduled_ = FALSE;
 if (RTCinterruptCallbackCopy != NULL) {
 (void)RTCinterruptCallbackCopy(arg1_, arg2_);
 }
 }
 }
}