cancel
Showing results for 
Search instead for 
Did you mean: 

delay function with timer interrupt

Benjamin Brammer
Senior II
Posted on April 25, 2018 at 16:03

Hey Guys,

I don't know what I am doing wrong here and really would appreciate some help:

I created a function called

void DELAY_1_5us(uint16_t period)
{
delay = true;
__HAL_TIM_SET_AUTORELOAD(&htim7, period);
LCD_CS_LOW;
HAL_TIM_Base_Start_IT(&htim7);
while(delay);
}�?�?�?�?�?�?�?�?

this function uses TIM7 of the STM32F429ZI-DISC1 discovery board as a simple programmable timer for approx. x time 1,5µs delay. with the 16-Bit ARR value I get maximum 2^16 x 1,5µs delays because of the following init routine (84MHz/128 = 656,250kHz = 1,5238 µs counter tick):

void MX_TIM7_Init(void)
{
 TIM_MasterConfigTypeDef sMasterConfig;
 htim7.Instance = TIM7;
 htim7.Init.Prescaler = 128;
 htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
 htim7.Init.Period = 1;
 if (HAL_TIM_Base_Init(&htim7) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
 sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
 sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
 if (HAL_TIMEx_MasterConfigSynchronization(&htim7, &sMasterConfig) != HAL_OK)
 {
 _Error_Handler(__FILE__, __LINE__);
 }
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

In the ISR I just set the delay value so the while loop in the dely function is interrupted. The LCD_CS pin is just to measure the elapsed time on an oscilloscope.

void TIM7_IRQHandler(void)
{
 /* USER CODE BEGIN TIM7_IRQn 0 */
HAL_TIM_Base_Stop_IT(&htim7);
LCD_CS_HIGH;
delay = false;
 /* USER CODE END TIM7_IRQn 0 */
 HAL_TIM_IRQHandler(&htim7);
 /* USER CODE BEGIN TIM7_IRQn 1 */
 /* USER CODE END TIM7_IRQn 1 */
}�?�?�?�?�?�?�?�?�?�?�?�?

The problem now is, that the function -regardless which value for the delay period of the DELAY_1_5_us functionI choose - the timer shows only approx. 1µs delay on the oscilloscope. I checked the register entry for TIM7_ARR and it changes with each function call. I also thought this might have to do something with the UG bit to generate an UE so that the timer loads the new ARR value.

But even with htim7.instance->EGR = 0x1 nothing is happening. Also when I try to activate the preload buffer with htim7.instance->CR1 |= 0x0080; nothing is changing in the appropriate reigisters.

I would really appreciate if someone could help me. I tried the code with ATOLLIC TrueStudio and SW4STM32 all set with optimize for debug.

best regads

Benjamin

#delay-function #stm32f4 #tim7 #changing-arr-in-run
17 REPLIES 17
Posted on April 30, 2018 at 15:01

I often use a free running timer and count the cycles for delay. That free running timer could be the systic or any other timer. Or even dwt.

In your case, you may want to convert the duration to the number of cycles and then set the timer to expire at end of that cycle.

I can post something later if this isn't clear to you.

Posted on April 30, 2018 at 15:28

Yes I tried to do this with my last post and the rewrite of the delay function. I have read another 

https://www.carminenoviello.com/2015/09/04/precisely-measure-microseconds-stm32/

 where the problem is explained and also the variant you suggested with the DWT is used. I think I will try that DWT solution..
Posted on April 30, 2018 at 16:26

The basic construct is quite simple:

1. Read the current timer counter.

2. Loop around until the current timer counter minus the counter value read in 1 above is bigger than your desired cycles.

3. Exit.

If you are mostly married about small delays, no need to use any interrupt and simply read systick->Val or dwt->cyccnt.

A couple of lines will do. Note that systick- is a down counter.

Posted on May 01, 2018 at 01:06

Interrupting at 1 MHz is a fools errand.

If you clock a free running TIM at 1 MHz you can read the TIM->CNT register until it advances the desired number of ticks.

start = TIM->CNT;

while((TIM->CNT - start) < 10); // Wait 10 us

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on May 01, 2018 at 01:09

Here is what I would do.

1. define your timer tick read-out:

&sharpdefine systick() (-SysTick->VAL) //24-bit downcounter

&sharpdefine coretick() (DWT->CYCCNT) //32-bit upcounter

&sharpdefine tim7tick() (TIM7->CNT) //using TIM7

they are for SysTick, DWT, and TIM7 timers, or whatever timing mechanism you want to use.

2. write a timing routine based on those mechanisms

/delay a few cycles using systick()

void delay_systick(uint32_t cnt) {

uint32_t tmp = systick(); //time stamp the beginning

while (systick() - tmp < cnt) continue; //wait until time expires

}

//delay a few cycles using coretick()

void delay_coretick(uint32_t cnt) {

uint32_t tmp = coretick(); //time stamp the beginning

while (coretick() - tmp < cnt) continue; //wait until time expires

}

I provided two examples above, using SysTick or DWT. If you want, you can easily write your own using TIM7 or whatever timer you want.

3. perform a delay:

IO_FLP(LEDG_PORT, LEDG); //flip ledg

//delay using systick

delay_systick(SystemCoreClock / 10); //waste sometime = 1/10th a second

//alternatively, delay using coretick

//delay_coretick(SystemCoreClock / 10); //waste sometime = 1/10th a second

Here, I simply blinked an LED and used systick to generate a delay. the commented out section used DWT to generate a section. You can use TIM7 for that, as well, if you write your own timing routine.

Obviously you will need to initialize SysTick / DWT / TIM7 or whatever you want to use before hand. No interrupt required as you just need short delays.

I have heard numerous times here people thought using SysTick is difficult as it is a 24-bit downcounter but as you can see, it is every bit as easy as using a regular counter.

hope it helps.

PS: written those lines on tested so you may need to debug them a little.

Posted on May 01, 2018 at 01:14

I would hesitate using that, for two reasons:

1) you want to have a generic version that takes cycles to be delayed as a parameter and then you can write a us-based version using the cycle delay routine.

2) I would put the GPIO operation outside of your routine so the delay routine is generic and can be reused. In your case, I would do this:

  IO_CLR(GPIOC, TEST_PIN); //clear test pin

  delay_DWT(cycles_1us5); //delay a few units of 1us5

  IO_SET(GPIOC, TEST_PIN); //set test pin

With that, you can use delay_DWT() sometime in the future, in other projects.

Remember that the only reason you are writing a piece of code is so that you don't have to write it again in the future. With re-usability, your time spent writing a piece of code is an investment, not an expense.

Posted on May 01, 2018 at 01:23

I wrote the tim7 delay routines like this:

//delay a few cycles using tim7

void delay_tim7tick(uint32_t cnt) {

uint32_t tmp = tim7tick(); //time stamp the beginning

while (tim7tick() - tmp < cnt) continue; //wait until time expires

}

coupled with the following:

IO_FLP(LEDG_PORT, LEDG); //flip ledg

delay_tim7tick(SystemCoreClock / 10 / 256); //256:1 prescaler picked to form an effective 24-bit timebase

I could replicate the same 24-bit timebase used earlier.

and I just tested all the code pieces above and they all work as expected.

Posted on May 01, 2018 at 12:15

Hey dhenry,

thanks again for your advise and code snippets. What I don't understand is why use so much different counter clocks, like systick or DWT or a timer. As far as I have understood the routine I found in the Link, which I posted above, I just need one function that counts with the system core clk as a counter tick. After scaling the counts to be precise one us I can achieve every delay I want. And it is very precise. Off course I can put the system clock scaling outside the routine but would nevertheless only need one version. Or did I miss something here?

best regards

Benjamin