2021-02-04 07:44 AM
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;
}
}
}
2021-02-04 07:48 AM
Timer middle-layer:
#define MAX_NUM_SCHEDULED_INTS (4) ///< Maximum number of scheduled interrupts
/// Super timer selector
typedef enum {
#if 1 <= MAX_NUM_SCHEDULED_INTS
SUPERTIMER_0 = 0,
#endif
#if 2 <= MAX_NUM_SCHEDULED_INTS
SUPERTIMER_1 = 1,
#endif
#if 3 <= MAX_NUM_SCHEDULED_INTS
SUPERTIMER_2 = 2,
#endif
#if 4 <= MAX_NUM_SCHEDULED_INTS
SUPERTIMER_3 = 3,
#endif
} superTimerSelector_e;
/// Timer states
struct timerState_s {
volatile uint8_t handle; ///< Handle
volatile Bool_t timerIntIsScheduled; ///< Timer interrupt is scheduled
volatile uint32_t numHiddenInts; ///< Number of interrupts from the low-level timer driver that we will need to reach the specified period
volatile uint16_t timerSubDelayLastIntTicks; ///< Timer sub delay last interrupt in ticks
volatile EventCallback_t callback; ///< Callback function
volatile uint32_t callbackArg1; ///< Callback argument 1
volatile uint32_t callbackArg2; ///< Callback argument 2
};
static struct timerState_s timerStates[] = {
#if 1 <= MAX_NUM_SCHEDULED_INTS
{0xFF, FALSE, 0, 0, NULL, 0, 0},
#endif
#if 2 <= MAX_NUM_SCHEDULED_INTS
{0xFF, FALSE, 0, 0, NULL, 0, 0},
#endif
#if 3 <= MAX_NUM_SCHEDULED_INTS
{0xFF, FALSE, 0, 0, NULL, 0, 0},
#endif
#if 4 <= MAX_NUM_SCHEDULED_INTS
{0xFF, FALSE, 0, 0, NULL, 0, 0},
#endif
};
void ML_superTimerInit() {
LL_superTimerInit();
}
void ML_scheduleSuperTimerInts(superTimerSelector_e superTimerSelector, uint32_t periodMs, EventCallback_t callback, uint32_t callbackArg1, uint32_t callbackArg2) {
DISABLE_ALL_INTS_IF_NECESSARY();
struct timerState_s* t = &timerStates[superTimerSelector];
if (t->timerIntIsScheduled) {
LL_deleteScheduledSuperTimerInt(t->handle);
t->timerIntIsScheduled = FALSE;
}
t->numHiddenInts = (uint16_t)((TIMER_TICK_FREQ_kHz * periodMs) / TIMx_ARR) + 1;
t->timerSubDelayLastIntTicks = (uint16_t)((TIMER_TICK_FREQ_kHz * periodMs) % TIMx_ARR);
t->callback = callback;
t->callbackArg1 = callbackArg1;
t->callbackArg2 = callbackArg2;
uint16_t firstDelayTicks = (t->numHiddenInts == 1) ? t->timerSubDelayLastIntTicks : TIMx_ARR;
t->handle = LL_scheduleSuperTimerInt(firstDelayTicks/TIMER_TICK_FREQ_kHz, timerInt, (uint32_t)superTimerSelector, 0);
t->timerIntIsScheduled = TRUE;
ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
}
void ML_deleteScheduledSuperTimerInts(superTimerSelector_e superTimerSelector) {
DISABLE_ALL_INTS_IF_NECESSARY();
struct timerState_s* t = &timerStates[superTimerSelector];
if (t->timerIntIsScheduled) {
LL_deleteScheduledSuperTimerInt(t->handle);
t->timerIntIsScheduled = FALSE;
}
ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
}
static uint32_t timerInt(uint32_t superTimerSelectorUint, uint32_t intCount) {
struct timerState_s* t = &timerStates[superTimerSelectorUint];
intCount++;
if (intCount == t->numHiddenInts) {
t->timerIntIsScheduled = FALSE;
if (t->callback != NULL) {
t->callback(t->callbackArg1, t->callbackArg2);
}
} else if ((intCount + 1) == t->numHiddenInts) {
// This was the interrupt right before the interrupt at which we will call the callback
t->handle = LL_scheduleSuperTimerInt(t->timerSubDelayLastIntTicks/TIMER_TICK_FREQ_kHz, timerInt, superTimerSelectorUint, intCount);
} else {
t->handle = LL_scheduleSuperTimerInt(TIMx_ARR/TIMER_TICK_FREQ_kHz, timerInt, superTimerSelectorUint, intCount);
}
return DONT_CARE;
}
2021-02-04 07:50 AM
Linked list:
struct timerIntStates_s {
volatile uint16_t startTickTime; ///< Start tick time
volatile uint16_t desiredDelayTicks; ///< Desired delay in timer ticks
volatile uint16_t timerThresholdForInt; ///< Timer threshold for interrupt
volatile Bool_t inUse; ///< In use
volatile EventCallback_t timerIntCallback; ///< Timer interrupt callback function
volatile uint32_t callbackArg1; ///< Callback argument 1
volatile uint32_t callbackArg2; ///< Callback argument 2
struct timerIntStates_s* prevElement; ///< Previous element
struct timerIntStates_s* nextElement; ///< Next Element
};
static struct timerIntStates_s linkedList[] = {
#if 1 <= MAX_NUM_SCHEDULED_INTS
{0, 0, 0, FALSE, NULL, 0, 0, NULL, NULL},
#endif
#if 2 <= MAX_NUM_SCHEDULED_INTS
{0, 0, 0, FALSE, NULL, 0, 0, NULL, NULL},
#endif
#if 3 <= MAX_NUM_SCHEDULED_INTS
{0, 0, 0, FALSE, NULL, 0, 0, NULL, NULL},
#endif
#if 4 <= MAX_NUM_SCHEDULED_INTS
{0, 0, 0, FALSE, NULL, 0, 0, NULL, NULL},
#endif
};
static volatile struct timerIntStates_s* firstElement = &linkedList[0];
inline static uint8_t getFirstUnusedElementIndex() {
struct timerIntStates_s* element = linkedList;
uint8_t i;
for (i = 0; i < (sizeof(linkedList)/sizeof(linkedList[0])); i++) {
if (!element->inUse) {
return i;
}
element++;
}
logCriticalErrorNullTermString(" \r\nNo unused element found in function getFirstUnusedElementIndex!! ");
return 0xFF;
}
struct timerIntStates_s* LL_getFirstElement() {
return (firstElement->inUse) ? (struct timerIntStates_s*)firstElement : NULL;
}
struct timerIntStates_s* LL_getSecondElement() {
return (firstElement->inUse) ? (struct timerIntStates_s*)firstElement->nextElement : NULL;
}
Bool_t LL_atLeastOneScheduledTimerInt() {
return firstElement->inUse;
}
uint8_t LL_insertElement(uint16_t startTickTime,
uint16_t desiredDelayTicks,
uint16_t timerThresholdForInt,
EventCallback_t timerIntCallback,
uint32_t callbackArg1,
uint32_t callbackArg2) {
DISABLE_ALL_INTS_IF_NECESSARY();
if (!firstElement->inUse) {
firstElement->startTickTime = startTickTime;
firstElement->desiredDelayTicks = desiredDelayTicks;
firstElement->timerThresholdForInt = timerThresholdForInt;
firstElement->inUse = TRUE;
firstElement->timerIntCallback = timerIntCallback;
firstElement->callbackArg1 = callbackArg1;
firstElement->callbackArg2 = callbackArg2;
firstElement->prevElement = NULL;
firstElement->nextElement = NULL;
ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
return 0;
}
uint8_t linkedListArrayIndex = getFirstUnusedElementIndex();
struct timerIntStates_s* runningElement = (struct timerIntStates_s*)firstElement;
struct timerIntStates_s* newElement = &linkedList[linkedListArrayIndex];
newElement->startTickTime = startTickTime;
newElement->desiredDelayTicks = desiredDelayTicks;
newElement->timerThresholdForInt = timerThresholdForInt;
newElement->inUse = TRUE;
newElement->timerIntCallback = timerIntCallback;
newElement->callbackArg1 = callbackArg1;
newElement->callbackArg2 = callbackArg2;
while (TRUE) {
uint16_t ticksUntilTimerInt = runningElement->timerThresholdForInt - startTickTime;
if (desiredDelayTicks < ticksUntilTimerInt) {
if (runningElement->prevElement == NULL) {
firstElement = newElement;
} else {
runningElement->prevElement->nextElement = newElement;
}
newElement->prevElement = runningElement->prevElement;
newElement->nextElement = runningElement;
runningElement->prevElement = newElement;
ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
return linkedListArrayIndex;
} else {
if (runningElement->nextElement == NULL) {
newElement->prevElement = runningElement;
newElement->nextElement = NULL;
runningElement->nextElement = newElement;
ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
return linkedListArrayIndex;
} else {
runningElement = runningElement->nextElement;
}
}
}
ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
return 0xFF;
}
void LL_deleteElement(struct timerIntStates_s* elementToDelete) {
if (elementToDelete == NULL) {
logCriticalErrorNullTermString(" \r\nAttempting to delete a NULL-element in function LL_deleteElement!! ");
return;
} else if (!elementToDelete->inUse) {
logCriticalErrorNullTermString(" \r\nAttempting to delete an unused element in function LL_deleteElement!! ");
return;
}
DISABLE_ALL_INTS_IF_NECESSARY();
if ((elementToDelete->prevElement == NULL) && (elementToDelete->nextElement == NULL)) {
// This must be the only element in the list
elementToDelete->inUse = FALSE;
} else if (elementToDelete->prevElement == NULL) {
// This must be the first element
elementToDelete->nextElement->prevElement = NULL;
firstElement = elementToDelete->nextElement;
elementToDelete->nextElement = NULL;
elementToDelete->inUse = FALSE;
} else if (elementToDelete->nextElement == NULL) {
// This must be the last element
elementToDelete->prevElement->nextElement = NULL;
elementToDelete->prevElement = NULL;
elementToDelete->inUse = FALSE;
} else {
// The element is somewhere in the middle
elementToDelete->prevElement->nextElement = elementToDelete->nextElement;
elementToDelete->nextElement->prevElement = elementToDelete->prevElement;
elementToDelete->prevElement = NULL;
elementToDelete->nextElement = NULL;
elementToDelete->inUse = FALSE;
}
ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
}
void LL_deleteElement_(uint8_t handle) {
if ((sizeof(linkedList)/sizeof(linkedList[0])) <= handle) {
logCriticalErrorNullTermString(" \r\nBad handle in function LL_deleteElement_: 0x");
logCriticalErrorHexNumber(handle, 2);
logCriticalErrorNullTermString("!! ");
}
LL_deleteElement(&linkedList[handle]);
}
void LL_clearLinkedList() {
DISABLE_ALL_INTS_IF_NECESSARY();
struct timerIntStates_s* element = linkedList;
uint8_t i;
for (i = 0; i < (sizeof(linkedList)/sizeof(linkedList[0])); i++) {
element->inUse = FALSE;
element++;
}
firstElement = linkedList;
ENABLE_ALL_INTS_IF_THEY_WERE_ENABLED();
}