DIY a 10MHz Reference Signal Generator with Arduino Nano

DIY a 10MHz Reference Signal Generator with Arduino Nano

I recently conducted experiments for a new design that required a function generator. Despite the new function generator having quite good frequency tolerance specifications, I needed to achieve higher precision. One way to obtain higher precision with frequency generators, arbitrary waveform generators, and frequency counters is to use the 10MHz reference input provided on many of these test instruments. As there was no such reference available, and I have been playing with the Arduino Nano (hereinafter referred to as Nano) recently, I decided to see if I could build such a design around the Nano.
Figure 1 shows the main components of this simple and inexpensive “no calibration” design. The frequency is generated by a 10MHz voltage-controlled crystal oscillator (VCXO). This oscillator produces a frequency of approximately 10MHz and has reasonable tolerance, but also has a frequency adjustment pin that supports adjustments upwards or downwards. The trick to obtaining a more precise 10MHz signal is to set the correct adjustment for the VCXO and to readjust it when it drifts due to various factors. Let’s take a look at how it is implemented.

DIY a 10MHz Reference Signal Generator with Arduino NanoFigure 1:The main components of a simple frequency generator that requires no calibration.

The GPS module can provide a very accurate pulse-per-second (1PPS) signal. This pulse looks like a pulse of approximately 100ms per second. Even with cheap modules, this signal is usually accurate within tens of nanoseconds. This serves as the external reference for generating an accurate 10MHz signal. The 1PPS signal is connected to the Nano through an I/O line that can generate interrupts. The design also includes a 32-bit counter that can be read and reset by the Nano. By connecting the 32-bit counter to the output of the VCXO, it can be used to calculate the number of VCXO cycles since the last reset. The last main part is how to adjust the VCXO.
The Nano does not have a DAC, but it turns out it has a simple function that allows it to control its pulse-width modulation (PWM) output, thus obtaining an adjustable DC output voltage (after filtering). A simple call can output a 5V PWM signal at a fixed frequency. The PWM has an 8-bit adjustment range, so if you want a 2.5V output, you can set the PWM to 128 (50% duty cycle). (Note that for this set of calls, the Nano uses a frequency of about 500Hz, but this is only important for the filter design.) So, the first thought was to use the Nano’s PWM output, filter it, and obtain a relatively ripple-free DC level, then apply it to the adjustment pin of the VCXO. This can work, but the problem is that there are only 255 discrete levels that can be set, which may not be enough. Let’s delve deeper into this.
The VCXO I chose for this project is the TSEAACSANF-10.000000 from Taitien. It has a 2ppm initial frequency tolerance and a maximum adjustment range of about ±12ppm obtained by using a voltage of 0.5V to 2.5V on the adjustment pin, which is also known as the “pulling range.” Therefore, if using the full 255 steps of PWM, it can adjust the VCXO with a resolution of about (2×12/255≈0.1ppm). This does not seem to offer much improvement in ppm accuracy, so I came up with the idea of using two PWMs: one for coarse adjustment and one for fine adjustment. To keep the design simple, I chose to use only resistors without operational amplifiers. This part of the circuit can be seen in Figure 2 (which also shows two filter capacitors that we will discuss later).

DIY a 10MHz Reference Signal Generator with Arduino NanoFigure 2:Coarse and fine tuning of the PWM output designed to be used as an adjustable DC output voltage.

Essentially, what we see is an averaging circuit, with a resistance of 2kΩ for the coarse adjustment part of the circuit and a resistance of 270kΩ for the fine adjustment part. The last 2kΩ resistor is used to divide the voltage to the adjustment pin, reminding that a maximum of 2.5V is needed here. The VCXO adjustment voltage can be easily solved using superposition, with a value approximately equal to formula 1.
VCXO adjustment voltage = 9.76×10-3×PWMCOARSE + 144×10-6×PWMFINE (Formula 1)
Where PWMCOARSE and PWMFINE range from 0 to 255. This circuit is sometimes referred to as a passive averaging circuit, as described by the Miller theorem. Now, using two PWMs to make the output voltage to the VCXO adjustment pin monotonic is impractical because the precision of the resistors needs to be very high. This can be solved by choosing resistor values so that the adjustment range of the PWM fine tuning overlaps with the subsequent coarse tuning range.
Let me give an example: The resistor values shown in Figure 2 support a step movement of approximately 9.8mV for the PWM coarse tuning signal. The PWM fine tuning signal moves about 0.14mV per step, which means it moves about 36mV across its entire range. Since the fine tuning range is greater than the coarse tuning step, there will always be a significant overlap.
Figure 3 shows several coarse tuning ranges and how to configure the fine tuning values to overlap with them. In the subsequent discussion about the adjustment algorithm, I will explain how to use this overlap. (Note that the two 100μF capacitors in Figure 2 are chosen to keep the ripple of the PWM waveform below the 0.14mV fine-tuning step. To help with filtering, the code can increase the PWM frequency to about 31kHz.)

DIY a 10MHz Reference Signal Generator with Arduino NanoFigure 3:Graphical illustration of the fine tuning range and coarse tuning step overlap.

Putting all this together, the operation is as follows: After powering on, the Nano sets the PWM coarse and fine tuning to initial values (some intermediate value) and then waits until it detects the 1PPS signal from the GPS module. Then, at the next 1PPS interrupt, the Nano resets the 32-bit counter. The counter then starts counting the number of cycles from the VCXO. After receiving the subsequent 1PPS interrupt, the Nano captures and reads the value of the 32-bit counter, which should be 10,000,000. If the count value is less than 10,000,000, the PWM fine tuning value is adjusted upwards. If the fine tuning value before adjustment is 255, the PWM coarse tuning value is incremented and the PWM fine tuning value is set to 127 (the middle value). If the count value is greater than 10,000,000, the PWM fine tuning value is adjusted downwards. If the fine tuning value before adjustment is 0, the PWM coarse tuning value is decremented and the PWM fine tuning value is set to 127 (the middle value). As you can see, overlapping the fine tuning process with multiple coarse tuning values allows the algorithm to safely search for suitable values, but this is somewhat inefficient in terms of search speed.
This is actually the first stage of the adjustment. If you only measure the count value for one second, you cannot achieve better than 0.1ppm precision. Therefore, the second stage of the code supports the 32-bit counter to accumulate 10 seconds by waiting for 10 interrupts. Now the count value can rise to 100,000,000, allowing adjustments to the range of 0.01ppm (i.e., 10ppb). The first two stages are used to quickly approach the 10MHz target value. The third continuous operation stage counts for 100 seconds, waiting for 100 interrupts, and then reads the counter again. This achieves the expected count of 1,000,000,000, supporting adjustments to 1ppb. But how much can PWM actually adjust? As mentioned earlier, the PWM fine tuning moves about 0.14mV per step, and the VCXO can achieve about ±12ppm adjustment in the range of 0.5V to 2.5V. Therefore, each fine tuning step will shift the frequency by about (2×12ppm×(0.14mV/2.0V))≈0.0017ppm, or 1.7ppb. So, it seems we have enough adjustment to achieve about ±1ppb. More can be seen in the code, but this is the essence of searching for a precise 10MHz. (At this point, it should be noted that the Arduino Nano actually has a 16-bit PWM, but the maximum frequency is too low, resulting in a long settling time for the required filter—too long for this application.)
As for performance, the Nano can output various statistics on an LCD, and my average frequency is usually 10,000,000.00 with an average error of 0.01Hz, and the standard deviation of this average is 0.1ppb. Thus, it can maintain the frequency close to a few ppb. This is two orders of magnitude better than what my function generator achieves with an external reference.
Now let’s take a look at the schematic. You can see that the PWM averaging circuit feeds the adjustment voltage to the VCXO (the only device powered by the Nano’s 3.3V supply). The circuit after the VCXO is used to square the clipped sine wave output from the VCXO. This shaped and buffered 10MHz square wave signal is sent not only to the counter but also to an external BNC connector. Two of the BNC connectors are used to provide TTL signals. Another is configured with two inverter gates and a 33Ω resistor to produce an output of about 50Ω. The fourth BNC connector is responsible for providing the buffered 1PPS signal for external devices.
Below the Nano is the GPS module. The module and antenna can be purchased online for about $10 to $12. The PCB is shown in Figure 4 and Figure 5.

DIY a 10MHz Reference Signal Generator with Arduino NanoFigure 4:PCB of the 10MHz signal source using GPS specifications.

DIY a 10MHz Reference Signal Generator with Arduino NanoFigure 5:PCB connected to the packaged GPS specification oscillator.

In the middle of the schematic is the 32-bit counter (Figure 1). This 74LV8154 (about $1) actually has two 16-bit counters that can be configured as a single 32-bit counter. This counter design supports reading the 32-bit counter by addressing four separate bytes. This requires four address lines, and I did not have enough I/O on the Nano, so I used a 74LS139 to convert two lines from the Nano into the four lines needed by the counter.
Figure 6 shows the LCD, which is a standard I2C 20 character × 4 line LCD. At the bottom of the schematic is a simple linear power supply that uses a 12V AC/DC adapter to regulate to 8V for the Nano and 5V for the various parts of the circuit. I use a linear power supply to ensure a quiet Vcc plane. Finally, there are two LEDs. One green LED indicates the 1PPS signal. The second two-color LED turns green when the system is tightly locked to the desired 10MHz; if it is not locked, it is red; if the system is in hold state, it alternates between red and green. The hold state is used to maintain the current settings during a loss of the 1PPS signal. To detect the loss of the 1PPS signal, I used a watchdog timer (WDT). I did not use the WDT to detect erroneous code execution issues but set the WDT timeout to 2 seconds and reset it in the 1PPS interrupt routine. If the system does not receive a 1PPS interrupt, the WDT will not be reset.

DIY a 10MHz Reference Signal Generator with Arduino NanoFigure 6:The completed GPS specification oscillator package with LCD.

The Nano’s code is written in C language using the Arduino IDE. The code is primarily driven by the 1PPS interrupt, which responds quickly by locking the 32-bit counter value (the function of the 74LV8154), clearing the counter, and setting the reset flag. You may have noticed that some parts of the next count are missing because we are capturing the counter register and then clearing it to start counting from 0 again. (There will be a finite time between capture and clear.) This is true, so the firmware compensates for this delay by adding an offset value to the original counter value. It turns out that there are 16 missing counts. This has been verified in two ways. First, by measuring the time between the capture register signal and the clear register signal on an oscilloscope. Second, I ran many tests using 1-second and 100-second captures (these captures were adjusted with a delay offset) without adjusting the VCXO. The total value of the 1-second capture (now containing 100 delay offsets) should equal the reading of the 100-second capture (which contains one delay offset). In the operational code, each time the counter is read, the lost 16 counts are re-added.
The main loop in the code (the Arduino development system always runs the classic C language “Main” function as a loop) waits for the interrupt flag to be set and reads the value of the lock register of the 74LV8154 when it is set. Next, it checks if this is a “reasonable” reading and not corrupted (with a difference from 10MHz exceeding 12ppm). If it is not good, it is discarded. If it is good, it processes that value in a small boxcar averager. It then uses that average to adjust the PWM up or down, as described above. It then decides which stage it is in. The first stage runs a 1-second count and adjusts the VCXO. This quickly informs us of the PWM settings. This stage runs for 20 seconds. The second stage requires a 10-second count reading to fine-tune the PWM settings. It is set to run 5 times or 50 seconds. The final stage runs a 100-second reading and then runs continuously, adjusting to maintain frequency as temperature or other parameters change.
In the final stage, the LEDs are set as needed, and statistics are kept after each reading. These statistics can be viewed on the LCD and can be viewed on multiple pages in the main loop using a debounced button (Figure 7). Statistics include instantaneous frequency, average frequency, current ppb error, ppb average error, ppb error standard deviation, number of good readings, number of bad readings, continuous lock time, maximum frequency observed, minimum frequency observed, and PWM coarse and fine tuning settings.

DIY a 10MHz Reference Signal Generator with Arduino NanoFigure 7:Statistics performed after each reading on the LCD.

Complete project information can be found on the open-source website https://www.thingiverse.com/thing:5188330 (or you can also search for “DamianB2” on thingiverse.com).
Project information includes a complete KiCad project with schematics, PCB, and PCBA BOM. It also includes a complete assembly BOM, Arduino source code, links to 3D printing files for the enclosure, 3D printing files for the GPS module enclosure, nameplate images, and various notes, etc.
One last thought: Some may have noticed that in designing the GPS disciplined oscillator (GPSDO), we inadvertently created the essence of a very good frequency counter. We will explore this further in the future.

Authors:Damian Bonicatto and Phoenix Bonicatto

Source: EDN USA Edition

Original reference: Simple GPS Disciplined 10MHz Reference uses Dual PWMs, compiled by Franklin Zhao.

Copyright notice: This article is from the September 2022 issue of “Electronic Technology Design”, all rights reserved, and reproduction is prohibited.

END
Click 👇 the business card to follow me
Recommended past articles
1. Classic MOS tube level conversion circuit, a must-have remedy for hardware engineers on home trips and guarding the house!
2. About LDO and DC-DC, this article is enough!
3. What is capacitor bulging? Does it need to be replaced?

Leave a Comment

×