Simple frequency counter, high precision/accuracy/resolution


In my previous design idea, a design for a simple 10 MHz GPS Disciplined Oscillator (GPSDO) reference was presented. A note at the end of this article describes the realization that a frequency counter could be derived by simple circuit modifications. This article goes on to present a simple circuit for a frequency counter derived from the GPSDO scheme.

As shown on the Figure 1, we reuse much of the previous design, including the power supply, Arduino Nano, 32-bit counter, LCD, and LEDs. However, we remove the VCXO circuit. We add a second push button, input signal conditioning and create different outputs. Many parts of the software will also be retained or slightly modified.

Figure 1 Basic block diagram of the GPS-enabled frequency counter.

Figure 2 shows the detailed design of the frequency counter. By comparing this diagram to the previous GPSDO diagram, you can see the changes. Along with removing the VCXO circuit, you will see that the 74LS139 has been removed. The design of the frequency counter freed up some I/O ports, so I didn’t need to add an extension to address the counter (74LV8154).

Figure 2 Diagram of the frequency counter.

You will also notice the addition of an analog conditioning circuit feeding the counter input. To keep the design simple, only a Schmitt trigger inverter, a cap and some resistors are used. This is a well-known circuit dating back to at least the 1975 Fairchild application note 140. The circuit uses 2 resistors (R6 and R7) to bias the Schmitt trigger inverter to its midpoint (it works because the input is AC coupled via C17). Biasing towards the center positions means that the input signal is centered on the hysteresis of the Schmitt trigger. This gives the input signal maximum sensitivity. The capacitor and resistors are selected to allow signals from just over 10 MHz to 80 MHz to pass to the Schmitt inverter. The maximum of 80 MHz was identified through preliminary testing of the circuit. The limit is imposed by the 74LV8154 counter as that appears to be its maximum count rate (the datasheet is somewhat fuzzy on this number). The 10 mHz minimum is due to a design decision to have a maximum gate time of 100 seconds. With a gate time of 100 seconds, the minimum of 1 count will occur if the input signal has a frequency of 1/100 or 10 mHz. (The 1 second and 10 second gate times are also available via the “Gate Time” push button.)

The GPSDO used a push button and was read by an ADC on the Arduino Nano (picture 3). This frequency counter design uses 2 push buttons which are connected to analog comparators to read their states.

picture 3 Frequency counter completed in a 3D printed case with LCD display.

So, once the signal is squared through the Schmitt trigger, the squared signal is sent to the 32-bit counter. To measure the frequency of this signal, the Arduino Nano waits for a 1 pulse per second (1PPS) signal interruption from the GPS module (~$10). Even a cheap GPS module can provide a very accurate 1PPS signal. This pulse looks like a pulse of about 100 ms per second. After receiving this interrupt, the code signals the 74LV8154 to clear the counter. The code then waits for the next 1PPS interrupt and, when received, the Nano signals the counter to latch the current count in its on-chip register and then clears the counter. The code now reads data from the locked register. This is a description of a 1 second gate time operation, so the register value is the frequency of the input signal. If a gate time of 10 seconds or 100 seconds is selected, using the “Gate Time” pushbutton, the code waits 10 or 100 1PPS interrupts before locking the count. The number is then scaled by 10 or 100 to get the frequency.

Learn more about firmware; the two projects are actually quite similar and share a lot of code. We still drive the LCD and LEDs, watch for loss of the 1PPS signal via a 2 second watchdog timer (WDT), count the pulses, and on a 1PPS interrupt read the register in the 74LV8154, etc. A big change is that we no longer need to adjust the PWMs as they are not used in this design. This reduces code complexity and saves RAM.

As the code for the Nano is derived from the GPSDO code, it is again written in C using the Arduino IDE. The frequency counter code is essentially driven by the 1PPS interrupt. When a 1PPS interrupt occurs, the interrupt service routine responds by latching the 32-bit counter, clearing the counter, and setting a reset flag. The piece of code that locks the current count to a register built into the 74LC8154 and then clears the count to zero takes some time between locking and clearing. This means that we are missing some accounts. I call this time latency. The firmware compensates for this latency by adding an offset value to the raw counter value. Turns out there are 16 missing accounts. (The previous GPSDO article explains how the latency offset of 16 counts was checked.) In the run code, the missing count of 16 is added back to each counter read.

Let’s take a look at the performance. First, Figure 4 shows the minimum signal as a function of frequency.

Figure 4 Graph of minimum signal versus frequency.

The minimum is usually less than 1 Vdp except at frequencies below 1 Hz where the minimum of the sine wave climbs. Maximum input voltage is 5Vdp. As for accuracy, it typically reads frequency with a standard deviation of 0.01 Hz (with a gate time of 100 seconds). Figure 5 displays LCD pages for a gate time of 100 seconds with a 10 MHz input signal.

Figure 5 Statistics on LCD with a gate time of 100 seconds with a 10 MHz input signal.

On the GPSDO project, there was a page showing a histogram of readings. It worked for this project because the frequency and frequency range were known a priori, whereas on this frequency meter project there is no information on the frequency or frequency range to display. Given this, the amount of RAM needed to store previous readings is limited, so this feature has been removed.

(A note on standard deviation: Engineers often assume that standard deviation implies a normal distribution. This is not true. A standard deviation calculation does not assume any particular distribution. In this particular case, the deviation I typically see is a one-sided skewed distribution — not a normal distribution.)

To avoid incurring the wrath of my former Physics 101 teacher, care has been taken to display the data using mathematically correct significant digits. Freq, 1/f (period), Fmean and Fstd (mostly) follow standard mathematical rules for significant figures, based on measured frequency and gate time. For example, after taking 100 readings, Fmean will display an additional digit after the decimal. There is one exception to significant digit compliance. That is, the 1/f (period) displayed when measuring low frequencies…below 10 Hz. An exceptional example: when using a 6 Hz input signal and d ‘a gate time of 1 second, the period (1/f) will show 167ms and not 100ms as dictated by the rules of significant digits. This gives the impression that it would be less confusing for the user.

Vigilant readers may have spotted a seemingly inconsistent design issue. I previously stated that there are gate times of 1, 10, and 100 seconds. I also stated that the design is capable of reading up to 80 MHz using the 32 bit counter. So what happens when we take a reading of an 80 MHz signal using a gate time of 100 seconds? The counter should show 8,000,000,000 after 100 seconds, but we’re using a 32-bit counter. A 32-bit counter will only count up to 4,294,967,295 (232-1). To fix this, the code takes a snapshot of the counter reading (without resetting the counter) at 50 seconds and saves it as an intermediate reading. Then, when the final meter reading is taken at 100 seconds, it is compared to the intermediate reading. If the final reading is lower than the intermediate reading, we know there has been a roll in the meter. In this case, the code will add 232 to the intermediate reading to get a true meter reading. This method gives us the greatest accuracy because we don’t need to take 50 seconds worth of readings and add them up, thus avoiding a counter clear operation and a second offset correction.

Overall this frequency counter works quite well, is simple, requires no calibration, gives good information about the connected signal, and fits well on my bench.

Full project information for this can be found on the open-source site: (or you can search for “DamianB2”).

Project information includes complete KiCad project with schematic, PCB and PCBA BOM. Also included is a full assembly BOM, Arduino source code, 3D print files for the enclosure, link to 3D print files for the GPS module enclosure, nameplate artwork, various notes , etc.

Damian Bonicatto is a consulting engineer with decades of experience in embedded hardware, firmware, and system design. He holds 30 patents.

Phoenix Bonicatto is a freelance writer.

Related Content


About Author

Comments are closed.