cancel
Showing results for 
Search instead for 
Did you mean: 

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

arnold_w
Senior II

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?

2 REPLIES 2
arnold_w
Senior II

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
Senior II

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_);
            }
        }
    }
}