Skip to main content
slimbahri
Associate II
March 12, 2010
Question

configure a general purpose TIMx (x=2,3,4) same as Systick on STM32F103B

  • March 12, 2010
  • 14 replies
  • 2058 views
Posted on March 12, 2010 at 10:33

configure a general purpose TIMx (x=2,3,4) same as Systick on STM32F103B

    This topic has been closed for replies.

    14 replies

    slimbahri
    slimbahriAuthor
    Associate II
    May 17, 2011
    Posted on May 17, 2011 at 13:43

    Hi low power,

    considering the aim of the project, I'm working on stm32F103B, to develop a benchmark tool (IAR systems IDE), so, if i would use often ''SysTick_GetCounter()'' in source code, that will produce software and hardware jitter (Sw: many system calls, Hw: PLL jitter), so i'll introduce another timer with the same configuration as systick. please, i need the configuration code of  this general purpose Timer.

    Thanks in advance.

    Slim

    lowpowermcu
    Associate III
    May 17, 2011
    Posted on May 17, 2011 at 13:43

    Hi Slim,

    What does mean  ''we shouldn't use systick timer in user functions because that affects running accuracy'' ?

    Regards,

    low power

    Tesla DeLorean
    Guru
    May 17, 2011
    Posted on May 17, 2011 at 13:43

    Pretty sure you are not going to see PLL jitter at this level. Things like synchronization, bus arbitration, interrupts, DMA and flash line cacher are going to impede absolute repeatability of measurements. The clock drift of the HSI is quite substantial.

    The bigger problem is with quantization issues due to the granularity of the counters. You probably need to time multiple loops, and also wait on the counter to transition immediately before starting the test.

    Personally I would use the Cortex's cycle counter to benchmark code. It's about as fine grained as you can get, but is still in the 10s on nanoseconds for granularity. Cycles are probably a better unit of measurement, time implies you know the clock frequency accurately.

    -Clive
    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    lowpowermcu
    Associate III
    May 17, 2011
    Posted on May 17, 2011 at 13:43

    Hi Slim,

    As you can see in figure ''clock tree'' in datasheet, you should configure the APB1 prescaler so you have HCLK (core clock) = TIMxClk (timer clock)

    then you have to enable the timer counter.

    Regards,

    be happy, don't worry

    Tesla DeLorean
    Guru
    May 17, 2011
    Posted on May 17, 2011 at 13:43

    Here's some stuff I've pulled together with the cycle counter that I use to benchmark and optimize code on an STM32. This is like using RDTSC on x86.

    a) Do enough work that the timing will be useful, ie call overhead, and interrupt handling.

    b) Time empty calls if the call overhead is significant, or you want to remove it from estimates.

    c) Time multi-iterations, either use an average (general real world), or minimum (best case execution).

    d) Pick a suitable number of iterations. If something is complex you might only need to do it ten times, to get useful/repeatable numbers. Take care not to overrun the counters.

    e) Mimic expected utilization for optimization. For example performing a CRC on 128K of data, trying to shave a few cycles.

    -Clive

    // Using the trace unit within the STM32, a method more generally suggested by Joseph Yiu

    //  that works nicely on the STM32. For more course testing you could use a 1 ms SystemTick

    //  variable, this might also help on longer tests to catch situations were the cycle

    //  counter wraps.

    unsigned int cyc[2];

    // From http://forums.arm.com/index.php?showtopic=13949

    volatile unsigned int *DWT_CYCCNT     = (volatile unsigned int *)0xE0001004; //address of the register

    volatile unsigned int *DWT_CONTROL    = (volatile unsigned int *)0xE0001000; //address of the register

    volatile unsigned int *SCB_DEMCR     = (volatile unsigned int *)0xE000EDFC; //address of the register

    // Set up once

    *SCB_DEMCR = *SCB_DEMCR | 0x01000000;

    *DWT_CYCCNT = 0; // reset the counter

    *DWT_CONTROL = *DWT_CONTROL | 1 ; // enable the counter

    // Use macros around test code

    #define STOPWATCH_START { cyc[0] = *DWT_CYCCNT; }

    #define STOPWATCH_STOP { cyc[1] = *DWT_CYCCNT; cyc[1] = cyc[1] - cyc[0]; }

    STOPWATCH_START

     TestFunction();

    STOPWATCH_STOP

    printf(''TestFunction() %d Cycles\n'',cyc[1]);

    void EmptyFunction(void)

    {

    }

    STOPWATCH_START

    for(i=0; i<1000; i++)

      EmptyFunction();

    STOPWATCH_STOP

    printf(''1000x EmptyFunction() %d Cycles\n'',cyc[1]);

    empty = cyc[1];

    STOPWATCH_START

    for(i=0; i<1000; i++)

      TestFunction();

    STOPWATCH_STOP

    printf(''1000x TestFunction() %lf Cycles (Core average)\n'',(couble)(cyc[1] - empty) / 1000.0);

    quick = 0x7FFFFFFF;

    for(i=0; i<1000; i++)

    {

      STOPWATCH_START

      TestFunction();

      STOPWATCH_STOP

      if (quick > cyc[1]) quick = cyc[1];

    }

    printf(''TestFunction() %d Cycles (Best Case including call)\n'',quick);

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    slimbahri
    slimbahriAuthor
    Associate II
    May 17, 2011
    Posted on May 17, 2011 at 13:43

    thanks a lot Clive1,

    I'll do it, and tansmit you the results!!

    Regards,

    Slim

    Tesla DeLorean
    Guru
    May 17, 2011
    Posted on May 17, 2011 at 13:43

    This code is also pretty awful, a lot of integer divides stealing precision. Consider if portTICK_RATE_MS is 3 or 4

    val_tim_1=TIM_GetCounter(TIM3); 

    for (i=0;i<=99;i++)

    vTaskDelay( 10 / portTICK_RATE_MS ); // Delay 10 ms 

    }

    val_tim_2= TIM_GetCounter(TIM3); 

    val_tim_2=((val_tim_2- val_tim_1)/100);

    Try

    double deltatime;

    val_tim_1=TIM_GetCounter(TIM3); 

    vTaskDelay( (10 * 100) / portTICK_RATE_MS ); // Delay 10 ms * 100

    val_tim_2= TIM_GetCounter(TIM3); 

    deltatime = ((double)(val_tim_2- val_tim_1) / 100.0);

    It will give you a much better idea of the accuracy of vTaskDelay()

    -Clive

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    slimbahri
    slimbahriAuthor
    Associate II
    May 17, 2011
    Posted on May 17, 2011 at 13:43

    I have implemented this source code to benchmark the jitter of 10 ms delay:

    /* clcok config: HCLK = SYSCLK  */

    RCC_HCLKConfig(RCC_SYSCLK_Div1);

    /* clcok config: PCLK1 = HCLK/2  */

    RCC_PCLK1Config(RCC_HCLK_Div2);

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);

    /* TIM4config */

    /* Initialise data. */

    TIM_DeInit( TIM4 );

    TIM_TimeBaseStructure.TIM_Period = 0xffff;

    TIM_TimeBaseStructure.TIM_Prescaler = 15;/*  

     Prescalar 16 = 15+1  */

    TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;

    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure );

    //TIM_ARRPreloadConfig( TIM4, ENABLE);

            TIM4->EGR |= 0x01;

    for (i=0;i<=99;i++)

    {

      TIM4-> SR =0x0;

      TIM4->CR1 |= 0x0001;

      //GPIOB->BSRR = GPIO_Pin_9;

      vTaskDelay( 10 / portTICK_RATE_MS ); // Delay 10 ms 

      TIM4->CR1 &= 0x0000;

      TIM4->CNT = 0;

      

    }

    with this configuration and referring to clock tree in the datasheet of STM32 (pescalar = 16 = 15+1 and F= 72Mhz), the CNT register contains (+/-) AFC8 = 45000 (10), how can i convert it to display on LCD 10 ms or 10000 µs (in linear formula 

    proportionately 

    to CNT register and prescalar?)

    whereas, I have done this operations manually to know the granularity of the timer value to display after:  

    Using 72 Mhz on STM32  ---> T= 1.38 e(-8) 

    TIMx ->CNT :  0xFFFF---> 65535 

    65535 * 1.38 e(-8) = 0. 91 ms 

    CNT:  0xFFFF---> 65535 ---> 0.91 ms Prescalar  0

       

       1.82 ms Prescalar  2                            

                                ....

                                7.28 ms Prescalar  8

          14.56 ms Prascalar  16

                               

    7.28 <10 ms< 14.28    Prescalar 16 = 15+1

    How to display this please (with a generic formula)?

    Reagards, 

    Slim

    slimbahri
    slimbahriAuthor
    Associate II
    May 17, 2011
    Posted on May 17, 2011 at 13:43

    Hi clive, 

    with the method you suggested, we should using J-TRACE isn't it ? I have only J-LINK; with the firmware of ST v3. 2; I have configured a timer as follow (check the configuration please)

    #ifdef TIM_METHOD

    /* clcok config: HCLK = SYSCLK  */

    RCC_HCLKConfig(RCC_SYSCLK_Div1);

    /* clcok config: PCLK1 = HCLK/2  */

    RCC_PCLK1Config(RCC_HCLK_Div2);

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);

    /* TIM4config */

    /* Initialise data. */

    TIM_DeInit( TIM4 );

    TIM_TimeBaseStructure.TIM_Period = 0xffff;

    TIM_TimeBaseStructure.TIM_Prescaler = 15;

    TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;

    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure );

    //TIM_ARRPreloadConfig( TIM4, ENABLE);

            TIM4->EGR |= 0x01;

    #else

    /* GPIO config */

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init( GPIOB, &GPIO_InitStructure );

    #endif /* TIM_METHOD */

    for (i=0;i<=99;i++)

    {

      TIM4-> SR =0x0;

      TIM4->CR1 |= 0x0001;

      //GPIOB->BSRR = GPIO_Pin_9;

      vTaskDelay( 10 / portTICK_RATE_MS ); // Delay 10 ms 

      TIM4->CR1 &= 0x0000;

      TIM4->CNT = 0;

      //GPIOB->BRR = GPIO_Pin_9;//MeasuredDelay = TIM4->CNT;

      vTaskDelay( 1000 / portTICK_RATE_MS ); // Delay 10 ms 

    }

    with this configuration and referring to clock tree in the datasheet of STM32 (pescalar = 16 = 15+1 and F= 72Mhz), the CNT register contains (+/-) AFC8 = 45000 (10), how can i convert it to display on LCD 10 ms or 10000 µs (in linear formula 

    proportionately 

    to CNT register and prescalar?)

    regards,

    Slim

    Tesla DeLorean
    Guru
    May 17, 2011
    Posted on May 17, 2011 at 13:43

    >>with the method you suggested, we should using J-TRACE isn't it ? I have only J-LINK;

    The cycle counting works with the trace hardware inside the STM32 core, it *does not* require a J-TRACE or J-LINK. It works with stock STM32F103 devices, and probably the rest of the family.

    If the delay routine is using the SYSTICK at 1 ms, then the 10 ms delay could be between 9-11 ms depending on when you catch the counter.

    You could always get additional accuracy by using SysTick->VAL which counts down at 1/8000 of a millisecond (assuming 64 MHz, and a /8 prescale, and a 1 ms interrupt)

    -Clive

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..