2010-03-12 01:33 AM
configure a general purpose TIMx (x=2,3,4) same as Systick on STM32F103B
2011-05-17 04:43 AM
Hi Slim,
What does mean ''we shouldn't use systick timer in user functions because that affects running accuracy'' ? Regards, low power2011-05-17 04:43 AM
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
2011-05-17 04:43 AM
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 worry2011-05-17 04:43 AM
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. -Clive2011-05-17 04:43 AM
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 * 100val_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() -Clive2011-05-17 04:43 AM
thanks a lot Clive1,
I'll do it, and tansmit you the results!!
Regards,
Slim
2011-05-17 04:43 AM
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);2011-05-17 04:43 AM
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
2011-05-17 04:43 AM
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