Skip to main content
SJant.1
Associate III
March 2, 2021
Question

Nano second Precision using a hardware timer on STM32L4S5ZI

  • March 2, 2021
  • 10 replies
  • 4153 views

Hi,

I am working with STM32L4S5ZI MCU and I have a custom board with the same.

I am evaluating the hardware timer and as part of my project I need time in nano seconds for some time constrained work. Right now I am using Timer3 and below is my sample code with which I am able to get the counter value between a start and stop of the timer.

Can I derive the time with nano second precision using these timers on the chip? And I need to do this specifically with a timer and not using any other means like a DWT.

Please let me know if there is a way to do this. Thanks a lot.

int i =1;
regValue = TIM3->CNT; 
printf("Timer value %d\n", regValue);
HAL_TIM_Base_Start(&htim3);
 
printf("Timer cycle %d\n", i);
i++; 
 
HAL_TIM_Base_Stop(&htim3);
regValue = TIM3->CNT;
printf("Timer value %d\n\n", regValue);

This topic has been closed for replies.

10 replies

Uwe Bonnes
Chief
March 2, 2021

Timer resolution is limited by the timer clock Maximum clock is 120 MHz, so you get 8.33 ms resolution.

SJant.1
SJant.1Author
Associate III
March 2, 2021

Hi Uwe Bonnes,

Thank you for your response. I figured out the max clock as 120MHz just after posting the above question.

As per your response, the resolution for a 120MHz clock should be 8.33ns rather than 8.33ms right?

Thanks!

Uwe Bonnes
Chief
March 2, 2021

Off by one typing...

SJant.1
SJant.1Author
Associate III
March 2, 2021

Just to confirm what you mean by "Off by one typing" is that you pressed m instead of n right? :)

Javier1
Principal
March 2, 2021

If what you want to do is measure the time in nanoseconds in between events (for example width of a digital pulse)

you should take a look at the INPUT CAPTURE funcionality

and maybe use the timer with 0 preescaler and max clock speed

hit me up in https://www.linkedin.com/in/javiermuñoz/
SJant.1
SJant.1Author
Associate III
March 2, 2021

Just wanted to add to the question,

so the max Period that can be set to TIM3 is 65535 since it is a 16bit timer. So once the period is completed I can see that the counter restarts from 0.

Now how can I keep track of the number of times that the period got completed so that at the final calculation I can calculate the entire time?

Sharing something on this would be really helpful. Thanks!

Uwe Bonnes
Chief
March 2, 2021

Use Timer overflow interrupt and count overflows. Be carefully when combining overflow count and timer count to a number > 16 Bit!

Nikita91
Lead II
March 3, 2021

You want a nanosecond precision and you use the HAL and callback?

You will get more precision using timer registers directly, and put your code for timer overflow in the interrupt handler (and not use the callback).

What is the maximum time you want to measure? If it is less than 35 sec you can use TIM2 which is 32 bits.

Don't use start/stop time base. Use a free running counter: reading the current counter value is much faster, and you only have to subtract the start value from the stop value to get the duration (only with unsigned ints).

For your callback question: what did you put in the TIM interrupt handler?

SJant.1
SJant.1Author
Associate III
March 3, 2021

Hi Nikita,

As you can see in the code snippet attached above, I registered HAL_GetTimerOverflow_count as the callback function for period elapsed interrupt. I did not make any changes in interrupt handler function.

Regarding writing to registers, I can do that and yeah I am aware of the delay that the HAL functions can create.

Instead of callback function are you suggesting to use TIMx_IRQ​Handler() function directly?

To start and stop the timer can I set some bits in registers which should do the same thing as the HAL functions but with much less consumption of time?

Please explain how this can be done directly with writing to registers.

Thanks a lot ​

Nikita91
Lead II
March 3, 2021

Yes put your code in the TIMx_IRQ​Handler() function directly.

You can start the timer, then never stop it. use something like:

uint32_t start, stop, delay ;
 
 
start = TIMx->CNT ;
 
...
 
stop = TIMx->CNT ;
delay = stop - start ;
// Then add the overflow counter * 65536

This is OK if the delay is less than 65536 on a 16 bit counter.

Else it's a bit more complicated: on start set CNT to 0, and reset your overflow counter, ideally this must be atomic.

Another thought : what happens if the overflow interrupt comes right after the 'stop = TIMx->CNT ;'? The value will be off by +1 overflow count.

For the stop value you must get the timer counter and the overflow counter atomically (Disabling interrupts for this very short time ?).

Uwe Bonnes
Chief
March 3, 2021

Better use the capture unit to capture start and stop events.

Nikita91
Lead II
March 3, 2021

How to manage overflow counter?

Can this be done reliably with timer chaining?

Nikita91
Lead II
March 3, 2021

Start the time base and never stop it. You can use use something like:

uint32_t start, stop, delay ;
 
start = TIMx->CNT ;
....
stop = TIMx->CNT ;
delay = stpo - cnt ; 
// Then add de overflow count * 65536

This is ok if the delay is less then 65536

Else it's a bit more complicated: On start clear the CNT and the overflow counter.

On stop read the CNT and the overflow counter

Beware: what happens if the overflow interrupt is between the CNT reading and the overflow counter reading ? The delay will be off by 1 overflow count.

So the two reads must be atomic (disable the interrupts for this very short time?)

To use a critical section on bare metal Cortex (Very rude, probably better to use BASEPRI on M3, M4, M7):

uint32_t	status ;
 
status = __get_PRIMASK() ;
__set_PRIMASK (1) ;
__ASM volatile ("" : : : "memory") ; // Compiler barrier : avoid the compiler to move instructions across this barrier
 
... Critical section
 
__ASM volatile ("" : : : "memory") ; // Compiler barrier : avoid the compiler to move instructions across this barrier
__set_PRIMASK (status) ;

Ideally the same thing for start: You must clear the 2 counters atomically

Yes put your code directly in TIMx_IRQ​Handler() 

Nikita91
Lead II
March 4, 2021

In reality a free counteris too complicated.

You were right, starting and stopping the counter is easier and more reliable.

Perhaps use only the timer CR1_CEN bit to start/stop the timer.

SJant.1
SJant.1Author
Associate III
March 5, 2021

Thank you Nikita for you detailed explanations and your time.