Why does my multi-timer generate interrupts at the wrong time?
I am working with the STM32F769 microcontroller and I need several timers. I think it's too risky to use the hardware timers extensively so I decided to use ONE hardware timer in its simplest form and then do more processing in software. However, there is something wrong with my implementation, I can't get it to work properly, does anybody know what's wrong? My code consists of 3 parts, a low-level timer driver, a timer middle-layer and a linked list implementation. None of the timers can be prescaled down to 1kHz (I realize that it would make it a little easier) so I have chosen 2 kHz instead.
Low-level timer driver:
#define TIMER_TICK_FREQ_kHz (2)
#define TIMx_ARR (0xFFFF) ///< The value where the timer wraps around
#define GET_NUM_ELAPSED_TICKS(__TIMx_CNT__, __START_TIME_TICK__) ((uint16_t)((uint16_t)(__TIMx_CNT__) - (uint16_t)(__START_TIME_TICK__)))
#define DISABLE_ALL_INTS_IF_NECESSARY() uint32_t old_primask; \
old_primask = __get_PRIMASK(); \
__disable_irq()
#define ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED() if (!old_primask) \
{ \
__enable_irq(); \
}
static uint16_t getPrescalar(uint32_t timerFreqHz, uint16_t desiredTickFreqHz);
static void setPreScalarAndPeriod(uint16_t preScalar, uint16_t timerPeriod);
void LL_superTimerInit() {
__HAL_RCC_TIM14_CLK_ENABLE();
__TIM14_FORCE_RESET();
__TIM14_RELEASE_RESET();
uint32_t timerFreqHz = getTimerFreq(TIM14);
uint16_t prescalar = getPrescalar(timerFreqHz, 1000 * TIMER_TICK_FREQ_kHz);
setPreScalarAndPeriod(prescalar, TIMx_ARR);
NVIC_SetPriority(TIM8_TRG_COM_TIM14_IRQn, SPI_INT_PRIORITY);
NVIC_EnableIRQ(TIM8_TRG_COM_TIM14_IRQn);
}
static void setPreScalarAndPeriod(uint16_t preScalar, uint16_t timerPeriod) {
if (((uint16_t) TIM14->PSC) != preScalar) {
TIM14->PSC = preScalar;
TIM14->EGR = TIM_EGR_UG;
TIM14->SR = 0xFFFFFFFE; // Clear update interrupt flag
}
TIM14->ARR = timerPeriod;
}
static uint16_t getPrescalar(uint32_t timerFreqHz, uint16_t desiredTickFreqHz) {
uint32_t timerFreqHzDiv = timerFreqHz/desiredTickFreqHz;
uint32_t divisor = timerFreqHzDiv / (uint32_t)(TIMx_ARR + 1);
uint16_t prescalar = (uint16_t)(timerFreqHzDiv/(divisor + 1) - 1);
return prescalar;
}
uint8_t LL_scheduleSuperTimerInt(uint16_t delayMsMax32767, EventCallback_t callback, uint32_t callbackArg1, uint32_t callbackArg2) {
DISABLE_ALL_INTS_IF_NECESSARY();
uint16_t desiredDelayTicks = TIMER_TICK_FREQ_kHz * delayMsMax32767;
uint16_t TIMx_CNT = TIM14->CNT;
uint8_t handle = LL_insertElement(
TIMx_CNT, // startTickTime
desiredDelayTicks, // desiredDelayTicks
(uint16_t)(TIMx_CNT + desiredDelayTicks), // timerThresholdForInt
callback, // timerIntCallback
callbackArg1, // callbackArgs1
callbackArg2); // callbackArgs2
struct timerIntStates_s* firstElement = LL_getFirstElement();
TIM14->CCR1 = firstElement->timerThresholdForInt;
TIM14->CR1 = 1; // Start timer
TIM14->DIER = 2; // Enable CCxIE (Capture/Compare interrupt enable)
if ((desiredDelayTicks == 0) || (firstElement->desiredDelayTicks <= GET_NUM_ELAPSED_TICKS(TIM14->CNT, TIMx_CNT))) {
TIM14->EGR = 1; // Generate Capture/Compare interrupt from software immediately
}
ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
return handle;
}
void LL_deleteScheduledSuperTimerInt(uint8_t handle) {
DISABLE_ALL_INTS_IF_NECESSARY();
LL_deleteElement_(handle);
if (LL_atLeastOneScheduledTimerInt()) {
struct timerIntStates_s* firstElement = LL_getFirstElement();
TIM14->CCR1 = firstElement->timerThresholdForInt;
if (firstElement->desiredDelayTicks <= GET_NUM_ELAPSED_TICKS(TIM14->CNT, firstElement->startTickTime)) {
TIM14->EGR = 1; // Generate Capture/Compare interrupt from software immediately
}
}
ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
}
void TIM8_TRG_COM_TIM14_IRQHandler(void) {
TIM14->SR = 0; // Clear all interrupt flags
while (TRUE) {
if (!LL_atLeastOneScheduledTimerInt()) {
TIM14->DIER = 0; // Disable Capture/Compare 1 and Update interrupt enable interrupts
return;
}
struct timerIntStates_s* firstElement = LL_getFirstElement();
uint16_t numTicksElapsed = GET_NUM_ELAPSED_TICKS(TIM14->CNT, firstElement->startTickTime);
if (firstElement->desiredDelayTicks <= numTicksElapsed) {
struct timerIntStates_s* secondElement = LL_getSecondElement();
if (secondElement != NULL) {
TIM14->CCR1 = secondElement->timerThresholdForInt; // Update the value at which the timer will generate an interrupt
}
EventCallback_t timerIntCallbackCopy = firstElement->timerIntCallback;
uint32_t callbackArg1 = firstElement->callbackArg1;
uint32_t callbackArg2 = firstElement->callbackArg2;
LL_deleteElement(firstElement);
if (timerIntCallbackCopy != NULL) {
timerIntCallbackCopy(callbackArg1, callbackArg2);
}
} else {
break;
}
}
}