cancel
Showing results for 
Search instead for 
Did you mean: 

Measuring elapsed time in nanoseconds

Qwyntex
Associate III

I need to measure the time it takes for a component to reach a certain voltage, read out with the ADC. So far I have been measuring in microseconds but it turned out that that is not precise enough.

I found the following code to measure time on Stackoverflow and it works well, but I only superficially understand it and I can't really modify it.

// setup
__HAL_RCC_TIM2_CLK_ENABLE();
TIM2->PSC = HAL_RCC_GetPCLK1Freq()/1000000 - 1;
TIM2->CR1 = TIM_CR1_CEN;

// read time in microseconds
time_start = TIM2->CNT;

Can someone explain to me how it works and if I can modify it to measure nanoseconds? There might a super obvious way to do it but, again, I don't understand the code (This is what I get for copying code from the internet I guess)
Is there maybe a better solution than this?

 

Some additional info:

I don't need the measurement to be super accurate, whatever the chip itself can provide is probably enough for my needs.

I've set up the ADC in continuous conversion mode and DMA circular which I think is the fastest it can measure but if there is an even faster method, please let me know.

1 ACCEPTED SOLUTION

Accepted Solutions

It's the reciprocal you'd divide a 100 MHz by 10 to get to 10 MHz, the PSC = 10 - 1;

TIM2 and TIM5 are 32-bit.

To get the best resolution you'd use the 96 MHz (PSC=0), each tick would be 10.42 ns

You can't interrupt at high rates, but you'd set ARR=0xFFFFFFFF, and then CNT would cycle 0 thru 0xFFFFFFFF repetitively.

uint32_t start, stop, elapsed;

double elapsedtime;

start =TIM2->CNT;

// Stuff to time

stop = TIM2->CNT;

elapsed = stop - start;

elapsedtime = (1.0 / (double)SystemCoreClock) * (double)elapsed; // time in seconds

 

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

View solution in original post

13 REPLIES 13
Pavel A.
Evangelist III

Well, let's ask the Copilot AI that sits right in the web browser, a click away.

Me: Dear Copilot, I have following C code for STM32 microcontroller. It gets time in microseconds. Can you explain how it works? Can you advise a better way to get time in microseconds?

__HAL_RCC_TIM2_CLK_ENABLE();
TIM2->PSC = HAL_RCC_GetPCLK1Freq()/1000000 - 1;
TIM2->CR1 = TIM_CR1_CEN;

time_start = TIM2->CNT;

Copilot:

--- quote begin ---

This code is used to get the time in microseconds using the TIM2 timer peripheral of the STM32 microcontroller. Here’s how it works:

  1. The first line enables the clock for the TIM2 peripheral.
  2. The second line sets the prescaler value of the TIM2 peripheral. The prescaler value is calculated by dividing the APB1 clock frequency by 1,000,000 and subtracting 1 from the result. The APB1 clock frequency is obtained using the HAL_RCC_GetPCLK1Freq() function.
  3. The third line enables the TIM2 peripheral by setting the CEN bit of the CR1 register.
  4. The fourth line reads the current value of the CNT register of the TIM2 peripheral and stores it in the time_start variable.

This code is a simple and effective way to get the time in microseconds using the TIM2 peripheral. However, there are other ways to get the time in microseconds on the STM32 microcontroller. One such method is to use the SysTick timer peripheral, which is a 24-bit timer that can be used to generate interrupts at regular intervals. You can configure the SysTick timer to generate an interrupt every microsecond and increment a counter in the interrupt handler. This counter can then be used to get the time in microseconds.

Another method is to use the TIM5 timer peripheral, which is a 32-bit timer that can be used to measure time intervals up to 2$^{32}$ microseconds. You can configure the TIM5 timer to count up to its maximum value and generate an interrupt when it overflows. You can then use the overflow counter and the current value of the TIM5 counter to get the time in microseconds.

--- quote end ---

Me: In your last response the idea to generate an interrupt every microsecond is not good at all.

Copilot:

--- quote begin ---

I apologize for any confusion caused by my previous response. I understand that generating an interrupt every microsecond can be a resource-intensive operation and may not be the best approach for all use cases.

... Here’s an example code snippet that demonstrates how to use the TIM5 timer to get the time in microseconds:

 

 

 

__HAL_RCC_TIM5_CLK_ENABLE();
TIM5->PSC = HAL_RCC_GetPCLK1Freq() / 1000000 - 1;
TIM5->ARR = 0xFFFFFFFF;
TIM5->CR1 = TIM_CR1_CEN;

time_start = (uint64_t)TIM5->CNT << 32;
time_start |= TIM5->CNT;

 

 

 

 

In this code snippet, we enable the clock for the TIM5 peripheral and set the prescaler value to obtain a timer frequency of 1 MHz. We also set the ARR register to its maximum value to allow the timer to count up to 2$^{32}$ microseconds. We then enable the TIM5 peripheral and read the current value of the CNT register twice to get the time in microseconds.

I hope this helps!

--- quote end ---

Hmm... I still don't understand what the poor thing decided to do with TIM5->CNT in lines 6-7...  at least CubeIDE looks less buggy in comparison )

Divide a Hz clock by 1,000,000, you'll get a MHz,and the prescaler will make TIM tick at 1 MHz (units of time, micro seconds)

With PSC=0 the TIM will clock at the TIMCLK bus rate. Each tick will be several nano-seconds, but you can count them, or watch them elapse.

DWT CYCCNT will allow you to measure time in processor cycles, so perhaps 168 MHz (5.9 ns) or 84 MHz (11.9 ns) depending on the model of F4

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Mikk Leini
Senior

First: 1ns tick = 1GHz frequency. It is beyond any todays STM32 internal clocks. 

Second: ADC has certain minimum sampling time. On STM32's it is a one-digit fraction of a microsecond. You just can't get the reading fast enough to even measure it.

You should explain what do you really want to achieve or build. I guess you do something that requires deep electronics knowledge instead.

Qwyntex
Associate III

I am trying to measure the capacitance of a material but the problem is that for one of the materials it appears to be in the picofarad range.
I measure the capacitance by measuring the time it takes for the capacitor to reach a specific percentage while charging it over a known resistance. (The ADC is connected behind the resistor)

I have tried a larger resistor so that it takes longer to charge but because it is so big, it takes time for the applied 3.3v to be measured by the ADC, even with no capacitor/material connected, which then appears as measured capacitance.

If I could measure time more precise I could use a smaller resistor which has this problem to a lesser degree while at the same time gaining the same amount of precision.
Microseconds are not far off, I think 100ns range should be enough.

 

Is it possible for me to know how long a tick or the average tick takes? Do they always take the same amount of time? I am using an F411CEU6 with HCLK and SYSCLK configured at 96MHz using an external clock.

Am I right in my understanding that I could divide the clock by 10,000,000 to get TIM to tick at 10Mhz allowing me to measure in 100ns? Is the TIMCLK bus rate the limit and anything lower will make it tick at the bus frequency?

Mikk Leini
Senior

At tiny capacitances the ADC internal sample and hold capacitor starts to affect the measurement. I am not an electronics expert to suggest all the options, but one method I know is using time-to-digital converter (TDC). There's a company called PMT (previously Acam) who has designed TDC's just for tiny capacitance measurement: https://www.pmt-fl.com/products/picocap/
I used one of their general TDC and it was quite simple to interface with MCU over SPI. Depending on what's your budget and timeline, maybe worth checking.

Mikk Leini
Senior

Interesting topic... so I'll throw in one more idea:

Skip ADC, it's too slow. Use STM32 with comparator and DAC. Make the comparator reference voltage with DAC and route comparator output to some timer (I'm pretty sure there is some option like that). Then slowly sweep the DAC voltage and perform thousands of measurements. You should get a point cloud from which you should find the first order RC curve equation with some clever approximation functions.

PS. You need to sweep DAC because otherwise you always get the same quantized results. 
PPS. You could also check Monte Carlo method. If you could add randomness to charging start time or noise to charging voltage, then you can work with fixed reference voltage or maybe even get it done with timer input pin... just need enough noise and samples.

If you really want to measure some pF , explain, what you have - and what and why you want to do this.

I made a simple circuit, with a 20MHz cpu, easy can get +/- some 0,01 pF .

If you feel a post has answered your question, please click "Accept as Solution".

It's the reciprocal you'd divide a 100 MHz by 10 to get to 10 MHz, the PSC = 10 - 1;

TIM2 and TIM5 are 32-bit.

To get the best resolution you'd use the 96 MHz (PSC=0), each tick would be 10.42 ns

You can't interrupt at high rates, but you'd set ARR=0xFFFFFFFF, and then CNT would cycle 0 thru 0xFFFFFFFF repetitively.

uint32_t start, stop, elapsed;

double elapsedtime;

start =TIM2->CNT;

// Stuff to time

stop = TIM2->CNT;

elapsed = stop - start;

elapsedtime = (1.0 / (double)SystemCoreClock) * (double)elapsed; // time in seconds

 

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