cancel
Showing results for 
Search instead for 
Did you mean: 

Implementing a Delay using TIMERs

Greusard Alexandre_2
Associate II
Posted on December 04, 2017 at 16:24

Hi!

I'm working on an custom board with stm32f107VCT6, and I'm using only standard stm32 library.

My problem is that I try to make a simple delay using timer and a variable, but the code stops at the instruction 'while' of my Delay wich is called in the TIM5_Handler....

If annyone have a clue, it would be very appreciated, I already tried a lot of workaround with no succes... Here is my code:

__IO uint16_t delay_counter;

int main()

{

       SetSysClockTo72();

      SetupLed();   // just Led to  see if the code is runing

   SetupTimerDelay();   // the timer used to decrement the delay value

   SetupTimerSecond();   // a one second timer wich toggle the led after a brief delay

   while(1)   // infinite loop

   {}

}

void TIM5_IRQHandler(void)

{

    if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)

    {

        TIM_ClearITPendingBit(TIM5, TIM_IT_Update);

       

Delay(20);   // the problematic delay function

        GPIOD->ODR ^= GPIO_Pin_2; //  toggle the 'working' led

    }

}

void Delay(uint32_t time)

{

    delay_counter=time;   // wich is decremented in the TIM3_Handler

    

while( delay_counter!=0);   // the program stops here, nothing happens then...

}

void TIM3_IRQHandler(void)

{

    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)

    {

        TIM_ClearITPendingBit(TIM3, TIM_IT_Update);

      

        if(delay_counter)

               delay_counter--;  //decrementing the counter

    }

}

void SetSysClockTo72(void)

{

    RCC_DeInit ();                        // RCC system reset(for debug purpose)

    RCC_HSEConfig (RCC_HSE_ON);           // Enable HSE

    // Wait till HSE is ready

    while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);

    RCC_HCLKConfig   (RCC_SYSCLK_Div1);   // HCLK   = SYSCLK

    RCC_PCLK2Config  (RCC_HCLK_Div1);     // PCLK2  = HCLK    (APB2)

    RCC_PCLK1Config  (RCC_HCLK_Div2);     // PCLK1  = HCLK/2  (APB1)

    RCC_ADCCLKConfig (RCC_PCLK2_Div4);    // ADCCLK = PCLK2/4

    *(vu32 *)0x40022000 = 0x01;           // Flash 2 wait state

    // PLLCLK = 8MHz * 9 = 72 MHz

    RCC_PLLConfig ((uint32_t)0x00010000, RCC_PLLMul_9);

    // Enable PLL

    RCC_PLLCmd (ENABLE);

    // Attente synchronisation PLL

    while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);

    // Select PLL as system clock source

    RCC_SYSCLKConfig (RCC_SYSCLKSource_PLLCLK);

    // Wait till PLL is used as system clock source

    while (RCC_GetSYSCLKSource() != 0x08);

}

void SetupTimerDelay()   //  1ms tick timer

{

    TIM_TimeBaseInitTypeDef TIMER_InitStructure;

      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

      TIM_TimeBaseStructInit(&TIMER_InitStructure);

    TIMER_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;

    TIMER_InitStructure.TIM_Prescaler = SystemCoreClock/10000;     // 0.1 ms

    TIMER_InitStructure.TIM_Period = 10;                        // 0.1 ms * 10 => p�riode = 1 ms

    TIM_TimeBaseInit(TIM3, &TIMER_InitStructure);

    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);

    TIM_Cmd(TIM3, ENABLE);

    NVIC_InitTypeDef NVIC_InitStructure;

    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);

    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure);

}

void SetupLed()

{

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);

  GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_2;

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

  GPIO_Init(GPIOD, &GPIO_InitStructure);

}

void SetupTimerSecond()

{

    TIM_TimeBaseInitTypeDef TIMER_InitStructure;

      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);

      TIM_TimeBaseStructInit(&TIMER_InitStructure);

    TIMER_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;

    TIMER_InitStructure.TIM_Prescaler = SystemCoreClock/2000;

    TIMER_InitStructure.TIM_Period = 2000;

    TIM_TimeBaseInit(TIM5, &TIMER_InitStructure);

    TIM_ITConfig(TIM5, TIM_IT_Update, ENABLE);

    TIM_Cmd(TIM5, ENABLE);

    NVIC_InitTypeDef NVIC_InitStructure;

    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);

    NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure);

}

Many thanks!

Alex.

#timer-interrupts #stop-work #counter #delays
6 REPLIES 6
Posted on December 04, 2017 at 16:29

Compiled as C++, resulting in name mangling?

Check in mapfile/disassembly if your ISRs' addresses are present in the vector table.

    NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);

Why?

JW

Posted on December 04, 2017 at 16:37

I've just noticed that you call Delay() from the TIM5 ISR.

This is a no-no.

It won't work unless you have set priorities so that TIM2 interrupt will nest into (pre-empt in ARM parlance) TIM5 interrupt.

Even if it would work, it's a bad practice.

JW

Posted on December 04, 2017 at 16:49

Many thanks for your quick answer.

It looks like my ISR adresses are correct cause my both timers handlers work fine (they can toggle my LED) ..?

I tried to set lower priority for TIM5 (=1) so TIM3 can decrement the counter but it doesn't work it still block at the while instruction.

Why is it a bad practise and what should be a good one?

Alex

Posted on December 04, 2017 at 19:55

I tried to set lower priority for TIM5 (=1) so TIM3 can decrement the counter but it doesn't work it still block at the while instruction.

You need to set the split point for group priority, read 2.3.6  Interrupt priority grouping in PM0056. Use NVIC_SetPriorityGrouping().

Why is it a bad practise and what should be a good one?

Dwelling in ISR longer than necessary prevents any other code from running. This is good only for a small group of programs - including rudimentary exercises such as this one. Good practice is to perform only the absolutely necessary work in ISR  - setting flags, storing data - and leaving anything lengthy for main() to perform.

Also, beware of changing ODR in ISR - this will be dangerous if ODR is changed in main() (or ISR with lower priority) too. Get used to using BSRR. I don't say it's a problem now but again once you write real programs it's better to follow the safe practices rather than be surprised later.

JW

Greusard Alexandre_2
Associate II
Posted on December 05, 2017 at 15:57

Ok, lots of thanks for all this advices! I'll read the PM0056 programing manual, it seems to be exactly what I need to go further. And I'll manage an other way to do this delay, taking care about what you said!

I thought it was better to work with ISR cause you can insure to work on real time (using timers).

What is the diference between ISR and ODR?

Alan Chambers
Associate II
Posted on December 05, 2017 at 21:48

I posted something earlier but it appears to have vanished.

You do not need two timers for this task. If you want to use a highish (1kHz, say) frequency timer as a ticker for low frequency events, implement software timers. Something like:

    typedef void (*Callback)(void);

    struct Timer

    {

        uint16_t counter;

        uint16_t period;

        bool     recurring;

        Callback callback;

    };

    Timer timer = {...};

    void TIM3_IRQHandler(void)

    {

        if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)

        {

            TIM_ClearITPendingBit(TIM3, TIM_IT_Update);

            if (timer.counter > 0)

            {

                if (--timer.counter == 0)

                {

                    timer.callback();

                    if (timer.recurring)

                    {

                        timer.counter = timer.period;

                    }

                }

            }

        }

    }    

    

It is quite straightforward to create a data structure which efficiently manages many concurrently running software timers (a priority queue or some such), all with one hardware timer (or, preferably, SysTick). This reserves all the TIM peripherals for more accurate timing, PWM and all the rest.

Al