Skip to main content
dimf_91
Associate
September 19, 2016
Question

STM32F407VG Basic Timer 6 Magic value

  • September 19, 2016
  • 6 replies
  • 1352 views
Posted on September 19, 2016 at 10:54

Hello! I use a basic timer 6 to make a simple delay. Here is my code:

#define DELAY_TIME       10

#define PB2_ON()         GPIOB->BSRRL = GPIO_Pin_2

#define PB2_OFF()        GPIOB->BSRRH = GPIO_Pin_2

//Config PortB_Pin2 as output

void PortB_Init(void)

{

   RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

   GPIO_InitTypeDef PortB_Pin2_init;

   PortB_Pin2_init.GPIO_Mode = GPIO_Mode_OUT;

   PortB_Pin2_init.GPIO_Pin = GPIO_Pin_2;

   PortB_Pin2_init.GPIO_Speed = GPIO_Speed_100MHz;

   PortB_Pin2_init.GPIO_OType = GPIO_OType_PP;

   PortB_Pin2_init.GPIO_PuPd = GPIO_PuPd_NOPULL;

   GPIO_Init(GPIOB, &PortB_Pin2_init);

}

//Timer 6 config

void Timer6_Init(void)

{

   RCC->APB1ENR |= RCC_APB1Periph_TIM6; // Enable Timer 6 clock

   TIM6->PSC = 83;  // Clock frequency: 1MHz

   TIM6->ARR = 0xFFFF; // Auto-reload register value  

                       // After reaching this value appears Update Interrupt and Counter Register will be reset 

   TIM6->CR1 |= TIM_CR1_CEN; //TIM6 Enable

}

//Delay-func. Count µs

void ddelay_micros(uint16_t delay_time)

{

   TIM6->CNT = 0;

   while (TIM6->CNT != delay_time) ;

   return;

}

void main(void)

{

   SystemInit();  

   PortB_Init();

   Timer6_Init();

   RCC_ClocksTypeDef RCC_Clocks;

   RCC_GetClocksFreq(&RCC_Clocks);

    while(1)

    {

       PB2_ON();

       ddelay_micros(DELAY_TIME);

       PB2_OFF();

       ddelay_micros(DELAY_TIME);

    }

}

It works pretty well, the achieved delay is about 10,6 µs.

With DELAY_TIME values: 15, 20, 1000 i obtain the correct time intervals.

But! If i set 

DELAY_TIME to 12, the generated delay ist about 420 ns!

I do not understand what am i missing out.

Thanks for advice! :)

 
    This topic has been closed for replies.

    6 replies

    Tesla DeLorean
    Guru
    September 19, 2016
    Posted on September 19, 2016 at 18:16

    I wouldn't keep resetting the counter, it is not thread safe, and the exact match is also a bear-trap. Pull a local copy of the entry value, and compare against that until enough time has passed. Should be good to 65ms

    //Delay-func. Count µs
    void ddelay_micros(uint16_t delay_time)
    {
    uint16_t start = TIM6->CNT;
    while ((TIM6->CNT - start) < delay_time);
    return;
    }

    Not sure why your code fails, perhaps it's a tool/optimization issue, TIM6->CNT should be volatile.
    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    dimf_91
    dimf_91Author
    Associate
    September 21, 2016
    Posted on September 21, 2016 at 11:38

    Thanks for your help clive1!

    But i have one question more.

    If i use your code, and it will so happen, that i go into these delay_function and the value of TIM_CNT register is 65534. So i'll become a wrong time delay.

    What can i do to prevent it, if you mean that resetting the timer counter like ''TIM_CNT = 0'' is not such a good idea?

    Tesla DeLorean
    Guru
    September 21, 2016
    Posted on September 21, 2016 at 17:44

    If it does the math in the uint16t number space the fact it wraps will be handled by the subtraction.

    ie 0x0004 - 0xFFFC = 8

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    dimf_91
    dimf_91Author
    Associate
    September 22, 2016
    Posted on September 22, 2016 at 12:15

    Thanks again! You are right. 

    But i have one more idea. What if i use timer in one/pulse mode? Like that:

    //Timer 7 config

    void Timer7_Init(void)

    {

       RCC->APB1ENR |= RCC_APB1Periph_TIM7; // Enable Timer 7 clock

       TIM7->PSC = 83;  // Clock frequency: 1MHz

       TIM7->CR1 |= TIM_CR1_OPM; // Enable One-Pulse Mode

    }

    //Delay-func. Count µs

    void ddelay_micros(uint16_t delay_time)

    {

       TIM7

    ->SR = 0; //clear UIF Flag

       TIM7

    ->ARR = delay_time -1; // -1 clock cycle due to CEN set delay

       TIM7->CR1 |= TIM7_CR1_CEN; //Timer7 Enable

       while (!(TIM7

    ->SR & TIM_SR_UIF

    )); // wait till UIF is set

       return;

    }

    What do you think about this idea?

    With such implemantation i have an error about 0.5 µs from the disered delay and if i let pin toggle with these delay_func() i can see a very-very little phase jitter.

    Thank you for your time and help :)  

    Tesla DeLorean
    Guru
    September 22, 2016
    Posted on September 22, 2016 at 16:13

    Like I said, I prefer code that is thread/interrupt safe. The core has a 32-bit free-running counter, DWT_CYCCNT that clocks at the processor speed, depending on the core it has sub 10ns granularity.

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    dimf_91
    dimf_91Author
    Associate
    September 23, 2016
    Posted on September 23, 2016 at 09:35

    Hello clive1!

    I do not have a lot of experience in the µc programming, but what is the difference between using a free-runnig counter DWT and a basic timer such a Timers 6/7? Except their resolution and clock frequency?

    Thank you!