Table of Contents
Introduction
This blog post is about a simple way to generate an adjustable clock signal, ideal for acting as a stimulus for testing circuits, for controlling digital circuits, or for communications projects such as radio transmitters or receivers. The output spans a wide range from 440 kHz, up to at least 149 MHz (possibly higher with software modifications), with approximately 1 Hz granularity.
Here is a 90-second video describing the project:
The total project cost is around $25, can be hand-soldered, and is coded entirely in Python, meaning that almost anyone can modify the code and program it with a USB cable, without needing any other tools or compilers. All parts used are currently easily available despite the semiconductor shortage.
The project uses the Pi Pico, which is a small processor board that runs MicroPython, which is almost indistinguishable from normal Python.
Some example uses for this project are:
* Local oscillator (LO) for radio circuits
* Clock generator for logic gate experiments
* Test or reference frequency generator for measuring radio receivers or calibrating radio receivers
* Test source for frequency response measurement for filters or amplifiers
The project came about because I worked on a Software Defined Radio circuit recently, however it needed an external signal generator to operate, and in particular, it required the generator to operate to far beyond the 25 MHz or so that low-cost function generators can offer.
Fortunately, there is a selection of integrated circuits (IC) that can generate clock signals across a wide range; they are intended for replacing specific-frequency crystal oscillators in digital communications systems. With the current semiconductor shortage, many clock generator ICs are in extremely short supply and so I had to seek out a suitable IC that was still available, and test it.
I deliberately coded this project in Python, since non-software-engineers require signal generation capability too, and by having the code in Python, they will have a smoother process to edit the code to perform their specific requirements and upload the code without compiling it. For instance, if a signal burst is required, alternating signals, or a frequency sweep, all these could be edited into the Python code and immediately tested on the board. You could also interactively control the output by typing in Python instructions in real-time.
Another reason to use Python was that I was curious if it was feasible to implement a user interface as well as perform more than a few double-precision floating-point operations to control a reasonably complex chip on-board, in real-time as the user interacts with the project. The project succeeded in this aim, and I learned the benefits and limitations, and how to work around some of the problems, when using Python instead of traditional languages targeted for microcontrollers.
The output signal is a crude square wave, and would need filtering if the harmonics are too high for your use case. There is no amplitude adjustment, the output is nominally 5V or 3.3V depending on if the jumper connection to the onboard buffer circuit is made or bypassed. The output does not have 50-ohm output impedance, and expects a lighter load, for instance, it should be able to drive at least two 5V logic gate inputs. External attenuation could be used if a lower output is needed, or an external amplifier could be connected for greater output.
The full schematic is shown below. Note that there is an error in the revision 1 schematic; resistors R1 and R2 are wrongly connected to +5V, and they should be connected to +3.3V instead. It is simple to make this change on the revision 1 PCB (see later photo).
Components
See further below for the full parts list. Due to the worldwide semiconductor shortage, I used parts that are easy to find and kept the component usage low so that there are hopefully fewer problems to solve if any parts do become unavailable.
The circuit board can be obtained by uploading the PCB files attached to this blog post, to any PCB factory website such as JLC PCB or Elecrow, or OSH Park.
The Pi Pico is available in hobby quantities, so it made sense to use that for this project. For the display, I originally used a 'starburst' segment display, however, I swapped out to a small low-cost, and easy-to-obtain TFT screen which could in the future be replaced with a different screen (there are several with the same chip on the LCD module, so the code would be compatible) if there were any shortages.
The clock generator IC can be swapped out, there are at least three manufacturers with pin- and software-compatible parts. Silicon Labs Si5351, Hangzhou Ruimeng Tech MS5351 and one other (I'll paste the part number here when I find it!) are all compatible with this project, and I tested with all three. The easiest to obtain is MS5351, available from lcsc.com.
The rotary encoder EC12E24104A6 is currently available from Farnell/Newark, but if it were to become unavailable, then there is an alternative EC12E2430401 part available from Aliexpress (however, the alternative is not preferred, because it requires more force to turn, which could get tiresome).
The crystal is easy to replace too, with any other 25 MHz 10pF load capacitance part. All other parts have jellybean status. The full parts list is below.
Building It
The PCB files are in a zip file attached below to this blog post, the zip file can be directly uploaded to PCB manufacturers such as JLC PCB or Elecrow. The clock generator IC, the 25 MHz crystal, and the LCD module require a bit more precision soldering than the other parts on the board, however, the PCB pads were created to be larger than normal, to simplify attaching these parts with a normal soldering iron, and it worked well. If there are solder bridges they can be removed with desoldering braid. The LCD module has a plastic flex cable that is easiest to solder by applying solder paste and then heating on top of the flex. If there is no solder paste then some normal solder can be applied to each pad, then put flux on, and then heat on top of the flex. All other parts are easier to solder; the resistors and ceramic capacitors are all large 0805-sized parts.
The photo below shows the flat flex soldered by applying solder paste and then heating the flex from the top side with an iron. It is very straightforward to do, and the LCD connections worked the first time.
The revision 1 board requires a modification to be made so that resistors R1 and R2 connect to the correct +3.3V voltage instead of 5V. To perform the modification, on the underside of the board, cut the trace shown in the photo below, and then solder a wire link as shown too.
Jumper Connections
The board requires five jumpers to be connected to successfully power up and pass the signals to the output connectors. The jumpers need to be plugged onto the board into the positions as shown in the diagram here.
Programming It
Once the board has been constructed, there are two steps to getting the software onto the Pi Pico, and you can do it with just a USB cable, you don't need to power the board from an external power supply.
For step 1 you'll need particular firmware (operating system/interpreter). For step 2, an application called Thonny is needed, plus the PicoSynthRF Python code.
Download and install Thonny, and also download the zip file containing the firmware and code from GitHub. The zip file can be downloaded by clicking on the green Code button on that page, and then selecting Download Zip File. Extract the contents of the file somewhere convenient on your PC. The extracted contents will contain a firmware file called rp2-pico-micropython-doubleprecision-xxx.uf2 which you'll be using for step 1.
Step 1
Hold down the white button on the Pi Pico, and while holding it down, attach the Pi Pico USB connection to your PC using a normal USB cable. Let go of the button, and you'll see a drive letter (such as D: drive) appear on your PC. Drag the MicroPython firmware onto that drive letter as if it were a USB memory stick. Step 1 is now complete!
Step 2
Run the Thonny application, and click on View->Files so that it is checked, and in the left side pane browse to the picosynthrf folder which contains the Python code files. Highlight everything in that folder in that pane, and right-click and select Upload to / as shown in the screenshot below.
Once you've done that, you'll see them all get transferred across to the Pi Pico. It will take about 30 seconds. Step 2 is done! At this point, you can disconnect and reconnect the USB cable and after a few seconds, you'll see the green LED on the Pi Pico start flashing, the display on the board will show a PicoSynthRF message and the board will be running.
Using It
Once the board has been programmed as discussed above, you can either use the USB cable to power the board, or you can disconnect and use a 7-12V power supply with a barrel plug. Alternatively, you could even use a 9V PP3 battery, connected to the pins labeled 7-12V at the top of the board (just use one option; don't connect these supplies simultaneously).
You'll see the frequency displayed on the TFT screen, and the rotary knob adjusts the frequency. Tap the Range button if you want to select a different speed for the rotary encoder frequency adjustment. The other two buttons on the board currently do nothing. If you connect an oscilloscope or other test equipment to the connection labeled BUF OUT 0 (connector J8), the signal can be observed. Although there are two outputs on the board, the current software only makes use of one output.
The 'scope traces here show the output when using a passive probe (not 50 ohm terminated).
Experimenting with the Code
All the main code is in a single file called app.py. The remainder files contain TFT driver code and fonts. If you ever need to modify the code to do what you want, you can open the app.py file in Thonny, edit it there and test it out by clicking the green run icon. Once you're happy with the new code, you can transfer it as before using the right-click on the file and selecting Upload to /, or you can click on File->Save and select the destination to be the Pi Pico when prompted.
Note that the firmware that was used in Step 1 above, is unmodified normal MicroPython firmware for the Pi Pico, however, it has been built for double-precision floating-point maths capabilities. If you use the current firmware available at the raspberrypi.org website instead, then the code will not work, because the code requires the higher precision maths capability. If you wish to build your own double-precision firmware image for the Pi Pico, there is a blog post about Pico Double-Precision here.
The current code is not pretty, because there are a lot of calculations that need to be done in order to configure the clock generator IC. The code could be modified to extend the frequency range or to enable the second output. If you achieve this, or implement other features, please publish the code to help others.
Enclosure
This project was intended to be used without an enclosure so that people could probe the signals and experiment with disconnecting the buffer circuit if needed, and so on. If the project is soldered as shown so far, then it's not ideal for an enclosure. However, if the LCD, buttons, RF connectors, and rotary encoder are soldered onto the other side of the circuit board, then it should be possible to fit a front panel on more easily, and the display can be raised slightly too in that case. If desired the rotary encoder could be directly mounted to a front panel, and have wires connecting it to the board.
Parts List
This project is very low-cost, and there are second or multiple sources for many of the components if any part is unavailable from a supplier. The SMD pad sizes are suitable for 0805 or 0603 components. Several different sizes of tact buttons can fit on the circuit board. The heatsink is not necessary unless additional circuits will be powered (the 5V supply is brought out on an expansion connector J11).
Item | Qty | Reference(s) | Value | Description |
1 | 8 | C1, C2, C6, C8, C11, C12, C13, C14 | 100n | 0805 Capacitor |
2 | 4 | C3, C4, C9, C10 | 10n | 0805 Capacitor |
3 | 1 | C5 | 22u 16V | Electrolytic Capacitor 5mm dia |
4 | 1 | C7 | 2.2u | Electrolytic Capacitor 5mm dia |
5 | 1 | D1 | LED | 3mm LED |
6 | 5 | FB1, FB2, FB3, FB4, FB5 | FBEAD | 0805 Ferrite, an example is HZ0805E601R-10 |
7 | 1 | J1 | DC_IN | DC Jack example is FC68148 |
8 | 1 | J2 | SIL2 | SIL header 2-way 2.54mm |
9 | 1 | J3 | SPST | Single pole switch 2.54mm example is EOZ 09.03201.02 |
10 | 5 | J4, J5, J6, J7, J10 | SIL3 | SIL header 3-way 2.54mm |
14 | 2 | J8, J9 | SMA | SMA socket |
17 | 1 | J11 | SIL6 | SIL header 6-way 2.54mm |
18 | 1 | LCD1 | LCD-1.14-ST7789 | 1.14" TFT Module with ST77898 driver |
19 | 1 | Q1 | Si2301 | MOSFET P-Channel SOT-23, an alternative is FDV304P or IRLML2246 |
20 | 2 | Q2, Q3 | BFR92P | Transistor BJT NPN RF |
21 | 2 | R1, R2 | 3.3k | 0805 Resistor |
22 | 10 | R3, R4, R5, R6, R19, R20, R21, R22, R23, R26 | 10k | 0805 Resistor |
23 | 3 | R7, R15, R17 | 100R | 0805 Resistor |
24 | 1 | R8 | 100k | 0805 Resistor |
25 | 1 | R9 | 1k | 0805 Resistor |
26 | 1 | R10 | 47R | 0805 Resistor |
27 | 2 | R11, R13 | 6.8k | 0805 Resistor |
28 | 2 | R12, R14 | 2.2k | 0805 Resistor |
29 | 2 | R16, R18 | 10R | 0805 Resistor |
30 | 2 | R24, R25 | DNF | 0805 Resistor - do not fit |
31 | 1 | SW1 | EC12E | Rotary encoder, EC12E24104A6 (or EC12E2430401 alternative) |
32 | 3 | SW2, SW3, SW4 | B3F-40xx | B3F-40xx 12x12mm Tact Switch |
35 | 1 | U1 | Si5351A | MSOP-10 Si5351A or MS5351M |
36 | 1 | U2 | Pi Pico | Pi Pico |
37 | 1 | U3 | LM7805 | LM7805 TO-220 |
38 | 1 | X1 | 25MHz | Crystal 25 MHz 10pF 3.2x2.5mm example is MCSJK-7U-25.00-10-10-60-B-10 |
39 | 1 | - | - | TO-220 Heatsink |
40 | 5 | - | - | Shorting jumpers |
41 | 1 | - | - | Encoder knob |
42 | 1 | - | - | Printed circuit board |
43 | 1 | - | - | M3 screw and nut |
Summary
This project provides a granular signal generation capability of up to 150 MHz. The project is very low cost (about $25), uses easily obtainable parts, and implements all functionality (user interface and configuring the clock generator) in Python so that non-engineers can also feel comfortable adapting this project for their needs, and interactively typing in Python instructions and immediately seeing the effect on the output signal.
Thanks for reading!
Top Comments