Author Topic: Black Pill Arduino low frequency (<350 kHz) high accuracy frequency measurement  (Read 2817 times)

0 Members and 1 Guest are viewing this topic.

Offline bb1Topic starter

  • Contributor
  • Posts: 38
  • Country: us
It is interesting to find what accuracy of frequency measurement can be achieved using only Black Pill STM32F411 internal counters.
For low frequencies stm32duino HardwareTimer library provides nice examples of frequency measurement based on Capture Interrupt.

I modified these examples to obtain as high accuracy as possible.
The resulting Arduino .ino program can be found in the attached zip file.

At low frequencies my program provides at least 7 correct frequency digits.
At the highest frequency of about 350 kHz 5 correct digits are observed using 2 sec measurement time,
and 6 correct frequency digits are obtained using 20 sec measurement time.

Input frequency signal is connected to pin PA1. No other signal connections are required.

The frequency measurement results are shown by Arduino Serial Monitor.
Measurement time can be set via Arduino Serial Monitor by sending numbers 0, 1 or 2,
which set the measurement time to 0.22 sec, 2.2 sec and 22 sec accordingly.

Capture interrupt used to measure frequency takes about 2.9 uS.
If such interrupts come faster than approximately 350 kHz, then they cannot be counted correctly.
Because of that if the input frequency exceeds 350 kHz, then the measured frequency is wrong.
For the same reason if the input frequency is close to the upper limit of 350 kHz,
setting of the measurement time via Arduino Serial Monitor becomes unresponsive,
as all processor power is spent inside Capture Interrupt.

Black Pill clock is not very accurate. In my case it is about 93 ppm lower than it should be.
To achieve high absolute accuracy frequency correction coefficient should be used.
It can be found using some high accuracy calibration clock, such as GPS 1PPS signal.
For my Black Pill freqCorrection = 1.000093675.
Of course, there is Black Pill clock drift, but it is not very high.

My Arduino program does not pretend to be perfect, but it was tested at many frequencies between 0.1 Hz and 350 kHz,
and had no problem.
Feel free to improve it.
 

Offline PCB.Wiz

  • Super Contributor
  • ***
  • Posts: 1477
  • Country: au
At low frequencies my program provides at least 7 correct frequency digits.
At the highest frequency of about 350 kHz 5 correct digits are observed using 2 sec measurement time,
and 6 correct frequency digits are obtained using 20 sec measurement time.

Capture interrupt used to measure frequency takes about 2.9 uS.
If such interrupts come faster than approximately 350 kHz, then they cannot be counted correctly.

On MCUs with external clock ability, you can also connect the FreqIn to the CLK of the counter and capture the counter on a fixed timebase.
That allows MHz region measurements without worrying about interrupt times.

If the HW allows, you can run two timing channels, one capturing time and one capturing cycles. Those are allowed to run, and  after some nominal aperture time (say ~100ms or ~1s)  you enable one-shot capture on the next Fi edge.
The HW then rounds-up to the next whole period of FreqIN, and the time-capture is the time for N whole cycles.

Now, you can use Reciprocal Counter maths to calculate WholeCycles/Time to get very high precisions over a wide range if FreqIn. 
100MHz timebases allow 8 digits per second of precision.

(Config Logic units can help here, not sure if STM32F411 has those)

 

Offline mino-fm

  • Regular Contributor
  • *
  • Posts: 142
  • Country: de
I guess F411 is running at 100 MHz. For any input frequency you should get 8 digits/s.
Counting pulses at PA0 should be possible up to 1 - 2 MHz. Setting IC1PSC to 3 (in TIM2_CCMR1) enables factor 8 of input-capture prescaler. Maximum frequency will reach >= 10 MHz; resolution and accuracy will not be affected.
For lower frequencies and faster reading prescaler can be reset to 1 again.

Maybe you try to change your code for better performance.
 

Offline AndrewBCN

  • Frequent Contributor
  • **
  • Posts: 571
  • Country: fr
There are two ways to measure the frequency of a repetitive input signal: one can measure the period (e.g. time elapsed between two rising edges) and then calculate the frequency, or one can directly count the cycles per second (or another "gate time", e.g. 0.1s, 10s, 100s, 1000s, etc).

On the STM32F411 you can configure some pins as an external clock, but the timer/counters are still clocked by an internal clock, so the external clock maximum frequency that can be measured on the Black Pill is 24MHz, according to the STM32F411 datasheet.

I have posted the code for this frequency measuring method on the stm32duino forum, a few months ago, and it is the method I use for the STM32 GPSDO.

The Hardware Timer library example code and the OP code both measure the period.
 

Offline wilhe_jo

  • Regular Contributor
  • *
  • Posts: 174
  • Country: at
Don't ever dare to use interrupts to measure frequencies!
That's way too inaccurate since you cannot predict the times needed to enter it (the memory bus is highly unpredictable).

Feed the signal to one counter and let it count to1 (or whatever pressing factor you need). A second timer is clocked at max clk in free running mode.
Use the first counter to generate a DMA event to copy the counter value from the freerunning one to memory.

Now configure the DMA to get you eg to 17 samples (the first one will be wrong for obvious reasons) and look on the values.
After enabling the first on the DMA, I got down to a few clk cycles of deviation (max difference between any 2 results) for a 1pps signal over 5minutes.

73
 

Offline mino-fm

  • Regular Contributor
  • *
  • Posts: 142
  • Country: de
the external clock maximum frequency that can be measured on the Black Pill is 24MHz, according to the STM32F411 datasheet.

F411 clocked at 100 MHz is capable to count nearly 50 MHz but 40 MHz is more realistically. Without additional hardware another timer of F411 could be used as a variable prescaler to measure frequencies from sub Hz range up to 40 MHz.

Don't ever dare to use interrupts to measure frequencies!
That's way too inaccurate since you cannot predict the times needed to enter it (the memory bus is highly unpredictable).

Why?
Interrupts are independet of memory bus cycles. As long as timer-capture ISR is handled faster than counting events arrive no interrupt is missing - counting is accurate as required.

Years ago I had a reciprocal counter project with F407 @ 168 MHz. Upper frequency has been 2.5 MHz counting directly and 20 MHz using internal /8 prescaler.
 

Offline PCB.Wiz

  • Super Contributor
  • ***
  • Posts: 1477
  • Country: au
There are two ways to measure the frequency of a repetitive input signal: one can measure the period (e.g. time elapsed between two rising edges) and then calculate the frequency, or one can directly count the cycles per second (or another "gate time", e.g. 0.1s, 10s, 100s, 1000s, etc).

Not quite, there is a 3rd variant, that uses two counters, running concurrently, called a Reciprocal frequency counter.
This has a nominal minimum gate time, but always captures time over a whole number of FreqIn cycles, so the actual capture time varies,
The two counters give you Cycles and Time in SysCLK units  and Cycles/Time is Hz

 A Reciprocal frequency counter has very wide dynamic range, and does not lose precision at low frequency (like a gated counter does) or lose precision at high frequency (like Period capture does)

#4 above uses two timers, so is very close to this but it need manual set of  N samples.
A system  that arms capture after some nominal minimum time, autoscales automatically so can read 1Hz ~ 25+MHz to 8 digits per second at 100Mhz sysclk.
 
« Last Edit: November 20, 2021, 09:55:16 am by PCB.Wiz »
 

Offline wilhe_jo

  • Regular Contributor
  • *
  • Posts: 174
  • Country: at
Don't ever dare to use interrupts to measure frequencies!
That's way too inaccurate since you cannot predict the times needed to enter it (the memory bus is highly unpredictable).

Why?
Interrupts are independet of memory bus cycles. As long as timer-capture ISR is handled faster than counting events arrive no interrupt is missing - counting is accurate as required.

Years ago I had a reciprocal counter project with F407 @ 168 MHz. Upper frequency has been 2.5 MHz counting directly and 20 MHz using internal /8 prescaler.

Ähm, data has to be pushed onto the stack and instructions need to be fetched.

If you need to fetch timer values with a minimum of jitter/uncertainty, there's really no way around DMA on a STM32.
But even there, you have some slight uncertainty due to the way bus abitration is done... just study the appnotes!

73
 

Offline mino-fm

  • Regular Contributor
  • *
  • Posts: 142
  • Country: de
If you need to fetch timer values with a minimum of jitter/uncertainty, there's really no way around DMA on a STM32.

I don't know what you are talking about. Everyone ist using capture registers to get timer values exactly.

In between I tried to test shown .ino-file on a Nucleo-G431 board and to do some optimizations. I had to change user led pin to PB8. TIM2 will not run, TIM1 does.
Looking at sourcecode there are serveral things you shouldn't do like stopping timers. Looking arround www you can find other sources looking exactly like this and AndrewBCN has used.
Don't mind, that's Arduino :-\
 

Offline wilhe_jo

  • Regular Contributor
  • *
  • Posts: 174
  • Country: at
never mind... I understood that first posting maybe the wrong way.

That sounded like the interrupt directly reads the "raw" counter (ie. the running value) and not the "latched" value the input-capture presents to you... that would be bad

Anyway, using the DMA to fetch the results from the input-capture registers would be the way to got to increase the upper frequency...

73
 

Offline AndrewBCN

  • Frequent Contributor
  • **
  • Posts: 571
  • Country: fr
the external clock maximum frequency that can be measured on the Black Pill is 24MHz, according to the STM32F411 datasheet.

F411 clocked at 100 MHz is capable to count nearly 50 MHz but 40 MHz is more realistically.

No. Check the datasheet. Max frequency for external clock must be < apb bus frequency / 3.5~4.  Realistically that means 24MHz on the Black Pill with the apb bus frequency of 96MHz.
 

Offline mino-fm

  • Regular Contributor
  • *
  • Posts: 142
  • Country: de
Take a look yourself.
 
The following users thanked this post: PCB.Wiz

Offline bb1Topic starter

  • Contributor
  • Posts: 38
  • Country: us
There are multiple ways to measure high frequencies using Black Pill STM32F411 internal counters, so I see no need to post any code for that.
I checked the highest frequencies which can be measured reliably.
The input signal was connected to TIM2_ETR pin PA0. External clock source mode 2 was used.
For async divider values of 1, 2, and 4 the highest frequencies were 47 MHz, 93 Mhz and 180 Mhz.
For the divider value of 8 I also got the highest reliably measured frequency of 180 Mhz, but I cannot exclude that higher frequencies could be measured with better setup.
 
The following users thanked this post: PCB.Wiz

Offline bb1Topic starter

  • Contributor
  • Posts: 38
  • Country: us
Failure to measure frequencies above 180 MHz may be due to insufficient amplitude of the input signal at such frequencies.
Indeed, it is easy to measure hysteresis of the input pin of STM32F411. Trivial Arduino code can be used for that:
#define LED_BUILTIN PC13
int val = 0;

void setup() {
  pinMode(LED_BUILTIN,OUTPUT);
  pinMode(PA0,INPUT);
}

void loop() {
  val=digitalRead(PA0);
  digitalWrite(LED_BUILTIN,val);
}
Scope Channel 1 is connected to the input signal on pin PA0, and scope Channel 2 is connected to the output pin PC13.
The signals and 400 mV hysteresis can be seen on the attached scope screenshot.
 
The following users thanked this post: PCB.Wiz

Offline bb1Topic starter

  • Contributor
  • Posts: 38
  • Country: us
I improved the setup, and now with async divider=4 highest reliably measured frequency is 185 MHz,
while with divider=8 I was able reliably measure PA0 Timer 2 input frequency of 260 MHz.
 
The following users thanked this post: PCB.Wiz

Offline mino-fm

  • Regular Contributor
  • *
  • Posts: 142
  • Country: de
I improved the setup, and now with async divider=4 highest reliably measured frequency is 185 MHz,
while with divider=8 I was able reliably measure PA0 Timer 2 input frequency of 260 MHz.

Very interesting. Thank you for testing.
So I tried ETR-input of TIM3 to realize an asynchronous divider 1:1024 for frequency measurement using STM32F429 disco-board. Upper limit was 415 MHz with unterminated flying connection (white = HF, black = GND).
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf