2019-03-14 07:39 AM
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?
2019-03-14 09:00 AM
I guess I can use a Backup Domain Reset (bit BDRST in the RCC->BDCR register) to reset the subsecond register (RTC->SSR), right?
2019-05-01 04:01 AM
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_);
}
}
}
}