cancel
Showing results for 
Search instead for 
Did you mean: 

Reconfiguring a Timer within its interrupt handler

rushikeshsheth
Associate II
Posted on August 18, 2015 at 20:59

On an STM32F4 Discovery (STM32F407)

I'm trying to set up a timer and then reconfigure it within its own interrupt handler. The idea is that I should be able to repeatedly trigger tasks from the ISR in an aperiodic fashion. When I try to simply set up a periodic interrupt, I have no trouble, but when I try to change the period within the interrupt handler, the handler gets triggered immediately back to back until I disable the timer. I used the DWT_CYCCNT register to get some cycle count information, so I could have a better understanding of what was happening with my timing: (gdb) p cycleCountDebugBuf $1 = {3999991120, 1672, 1672, 1672, 1672, 0 <repeats 15 times>} I write the cycle count between enabling the timer and entering the ISR for each value. As you can see, after the first reconfiguration, the handler is repeatedly triggered without any real delay. I have confirmed that the delay that I provide when reconfiguring is a valid number. I've played around with a few different variations of the handler, trying to disable and re-enable the NVIC, using TIM_DeInit() and then reenabling the clocks, and simply providing a TIM_CMD to disable and re-enable the timer, but nothing has worked. I also tried pulling the reconfiguration out of the ISR and placing it in a queue that is triggered by the ISR, but that didnt work either. I'm sure I'm missing something simple here - any advice? Code for the timer initialization and ISR below. Initialization:

// NEED TO SET UP THE TIMER FOR THE FIRST TIME
TIM_TimeBaseInitTypeDef Timer_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
//Get the first delay value, in ms
uint32_t delay = delaySequence->duration;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM9, ENABLE);
/*Set prescaler such that each timer tick takes 8400 clock ticks. 
84000000/8400 = 10000 timer ticks per second - our timer has a resolution of 100 us*/
Timer_InitStruct.TIM_Prescaler = 8400;
//Timer_InitStruct.TIM_Prescaler = 40000;
Timer_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
//Set the timer period to the delay, in units of 100 us
Timer_InitStruct.TIM_Period = delay * 10;
//Timer_InitStruct.TIM_Period = 50;
Timer_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
Timer_InitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM9, &Timer_InitStruct);
TIM_Cmd(TIM9, ENABLE);
TIM_ITConfig(TIM9, TIM_IT_Update, ENABLE);
NVIC_InitStruct.NVIC_IRQChannel = TIM1_BRK_TIM9_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0xff;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0xff;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
startCycleCount();

Interrupt Handler:

void
TIM1_BRK_TIM9_IRQHandler(){
long
lHigherPriorityTaskWoken = pdFALSE;
if
(TIM_GetITStatus(TIM9, TIM_IT_Update) != RESET)
{
my_queue_msg_t msg;
//TIM_DeInit(TIM9);
TIM_Cmd(TIM9, DISABLE);
TIM_ClearITPendingBit(TIM9, TIM_IT_Update);
returnCycleCount();
startCycleCount();
/*
Removed msg population for simplicity's sake.
*/
//Send the msg to queue
xQueueSendToBackFromISR(myQueue, &msg, &lHigherPriorityTaskWoken);
interruptCount++;
if
(interruptCount < maxInterruptCount){ 
TIM_TimeBaseInitTypeDef Timer_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
//Pull the next delay value
uint8_t delay = delayArray[interruptCount];
//re-enable the clock after doing the deinit
//RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM9, ENABLE);
/*Set prescaler such that each timer tick takes 8400 clock ticks. 
84000000/8400 = 10000 timer ticks per second - our timer has a resolution of 100 us*/
Timer_InitStruct.TIM_Prescaler = 8400;
//Timer_InitStruct.TIM_Prescaler = 40000;
Timer_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
//Set the timer period to the delay, in units of 100 us
Timer_InitStruct.TIM_Period = delay * 10;
//Timer_InitStruct.TIM_Period = 50;
Timer_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
Timer_InitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM9, &Timer_InitStruct);
TIM_Cmd(TIM9, ENABLE);
TIM_ITConfig(TIM9, TIM_IT_Update, ENABLE);
/*
NVIC_InitStruct.NVIC_IRQChannel = TIM1_BRK_TIM9_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0xff;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0xff;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
*/
}
else
{
//We should never reach here.
//do something useless so we can break here
msg.msgType = 0xDEADBEEF;
TIM_Cmd(TIM9, DISABLE);
TIM_ITConfig(TIM9, TIM_IT_Update, DISABLE);
}
}
portEND_SWITCHING_ISR( lHigherPriorityTaskWoken );
}

Thanks! #interrupt #timer #stm32f407
7 REPLIES 7
Posted on August 18, 2015 at 22:11

Remember, the Prescaler and Period are N-1 values, so to divide 84 MHz to 10 KHz

Prescaler = 8400 - 1;

I think you're massively over-complicating this.

Change the TIM9->ARR to the period you want, have it preload if you want it too action within the current cycle of the timer.

If you use a 32-bit TIM, with a maximal setting, you can advance CCRx values along the timeline to get the intervals you want, and have 4 channels to play with.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on August 18, 2015 at 22:26

> I think you're massively over-complicating this.

+1

And, this is a well-deserved punishment for using the ''library''.

stm32f4xx_tim.c

TIM_TimeBaseStructInit()

[...]

  /* Generate an update event to reload the Prescaler

     and the repetition counter(only for TIM1 and TIM8) value immediatly */

  TIMx->EGR = TIM_PSCReloadMode_Immediate; 

JW

rushikeshsheth
Associate II
Posted on August 19, 2015 at 00:14

Thanks Jan & Clive for your input - I figured it should be fairly straightforward.

I went ahead and simplified the handler so it all it does is update the auto reload register with the delay I'd like and then set the UG bit in the EGR register, but I'm still seeing the same issue. I took a look at the Reference manual and saw that setting EGR should not affect my existing prescaler config etc, and that it should be cleared by hardware after forcing the register refresh & zeroing the counter. What else am I missing here? Updated handler below:

void TIM1_BRK_TIM9_IRQHandler(){
long lHigherPriorityTaskWoken = pdFALSE;
if (TIM_GetITStatus(TIM9, TIM_IT_Update) != RESET)
{
my_queue_msg_t msg;
TIM_ClearITPendingBit(TIM9, TIM_IT_Update);
returnCycleCount();
startCycleCount();
/*
Removed msg population for simplicity's sake.
*/
//Send the msg to queue
xQueueSendToBackFromISR(myQueue, &msg, &lHigherPriorityTaskWoken);
interruptCount++;
if(interruptCount < 
maxInterruptCount
){
uint32_t 
delay
= 
delayArray
[interruptCount];
TIM9->ARR = (delay * 10) - 1;
TIM9->EGR = TIM_PSCReloadMode_Immediate; 
}
else{
TIM_ITConfig(TIM9, TIM_IT_Update, DISABLE);
}
}
portEND_SWITCHING_ISR( lHigherPriorityTaskWoken );
}

Posted on August 19, 2015 at 07:35

Yes, the EGR bits are self-clearing, but by setting the EGR.UG bit you force an update event thus set SR.UIF flag and fire the interrupt (unless CR1.URS or CR1.UDIS is set, which I doubt you have). 

But you don't need to set EGR.UG at all, unless you set shadowing of ARR register (by setting CR1.ARPE bit), which again I doubt you have.

You need to think about your ISR's execution length with regard to the set time, though, not to miss the interrupt by setting ARR below the current CNT.

JW

rushikeshsheth
Associate II
Posted on August 19, 2015 at 23:54

I commented out the EGR update and see that the interrupt timing behaves as I wanted it to.

However, I was a little confused as to what was happening and would like to clarify my understanding. Based on your and Clive's comments above, my understanding was that in order to have the period update be effective for the next tick, I had to write EGR as you mentioned above:

TIM9->EGR = TIM_PSCReloadMode_Immediate;

My previous understanding was that if I didn't, the next tick would be based on the previous period. So if I configured the timer for a delay of 100 ms, and then of 300 ms, I would end up getting interrupts at 100 ms, 100ms, 300ms. I can see how writing UG within EGR and forcing an interrupt immediately would cause the behavior I was seeing previously. The time base init function I was using in my first implementation also writes UG, so this is in line with all the behavior I have seen. My deduction is that the TIMx->ARR update is immediately effective, and as long as I do that update before the counter hits the previously populated value I should get what I want (correct me if I'm wrong here.) This seems like it could get sketchy if my delays are very small and the # of cycles that the ISR takes is non-trivial in comparison. As always, I appreciate your input. Thanks!
Posted on August 20, 2015 at 10:55

Look at the schematic drawing of timer module at the beginning of timer chapter in RM. Note that there are shadows under some of the boxes representing registers. Those registers *may* be be written directly or indirectly, depending on respective ''shadow-enable'' bit. Re-read my previous post and in the RM read carefully the description of each bit mentioned there.

JW
dooz
Associate II
Posted on August 20, 2015 at 10:56

I'm using TIM->ARR to apply some various frequencies to my step motor and as far as I know as soon as the compiler reads the line where you apply ARR value it becomes effective immediately. So it must be the same within your timer interrupt.