Introduction: Poor Man's Waveform Generator Based on RP2040 Raspberry-pi-Pico Board

As many projects do, i guess, this project started with a fried asking for help. - He needed some support to repair his tube-amplifier he uses for his e-guitar.- To do so I needed to have a little tool which creates a signal to feed the amplifiers input, so I started to search the internet... and found two amazing projects, which sparked the idea that I just would need to bring together both projects and I'd have a very nice new tool on my bench.

Rgco has written a great instructable for an arbitrary wave form generator using a Raspberrypi-Pico with the RP2040 CPU, which should become the heart of my tool. Rgco provides basic software to calculate waves and run the generator. I quickly built the hardware on a bread board, loaded the software and it was working perfectly fine.

- Repair of the tube amplifier done - one more happy friend :), but I was just starting. I made a plan for a tool, which can operate without a connection to a PC.

Peter Hinch has developed the micro-python package - micro-gui - , which is the perfect base to build a user interface for small embedded systems.

An output stage was quickly designed and with approx. 60Euro spend on additional parts, I built an Arbitrary Wave form Generator tool (AWG) with a simple user interface. A user can choose a basic wave form, set up frequency, amplitude, offset and few more parameters.

Some basic specifications:

  • Frequency range: 20Hz - 15MHz sine wave (not all wave forms work nicely up to 15MHz)
  • DAC resolution 8-bit or 10-bit
  • Output switches for AC/DC coupling and output resistance Zout low/50Ohm
  • Output Voltage 6Vpp no load, 2.8Vpp with Zout switch to 50Ohm and 50 Ohm termination
  • Wave forms: sine, pulse (saw tooth, triangle, square wave), Gaussian, sinc, exponential, noise (all re-using rgco's software)

I was building on a 6.5cm x 13cm prototype board with copper stripes.

In this instructable I will focus on how to build a standalone AWG, add here and there a remark, so you can avoid running in the same traps. For details of the two projects by rcgo and Peter, please refer to their web-sites. However I will point out where I added to or deviated from the original projects.

If you follow the instructable to build your own AWG tool, you should have some basic skills in hand soldering electronic components. If debugging is needed, the use of a (simple) oscilloscope is handy. I recommend you read rgco's instructable and the readme file of Peter Hinch on his github site.


Remote control of the AWG: If you plan to build an AWG and want to control it with a PC or a Raspberry Pi, then please see following instructable.


Updates:

Member feedback and developments of the micro-gui package and arbitrary-wave-form-generater instructable led to several improvements of the original AWG design. Now the AWG has a nicer user interface and enjoys a significant noise reduction of the output signal. This instructable is now updated to refelct these improvements:

#1: Hardware versions

This instructable covers two hardware versions of the AWG, a version with 8-bit DAC and a version with 10-bit DAC. The 8-bit version is better suited for higher frequencies, the 10-bit version has higher resolution of the amplitude.

#2: Software versions

For the 8-bit AWG: ui.py, wavegen.py and main.py files are available for download in step 8

For the 10-bit AWG: ui10.py, wavegen10.py and main.py files are available for download in step 9

#3: AWG quick reference guide

The user interface is described in step 6. The quick reference guide is updated to reflect the recent changes.

#4: AWG 10-bit software update Dec-9

(1) Some users asked to keep the parameter values, when stopping the generator, instead of initializing them. This makes perfect sense if you want to tune/adjust the wave while in use. (2) For Gaussian, sinc and exponential parameters are now "bracketed", so that they cannot be set to values, which lead to a unusable waveform. Both changes are now implemented in the Dec-9 revision of the 10-bit AWG. (downloadable from Step 9). If requested, I will do the changes to the 8-bit AWG as well. Please reach out through the comment section.

#5: Added Step10 - Contributions of the community

Step 'Contributions of the Community' is added to publish or add links to contributions of users to the AWG project.

#6: AWG 8-bit software update Feb-18

(1) Keep the parameter values, when stopping the generator, instead of initializing them. This makes perfect sense if you want to tune/adjust the wave while in use. (2) For Gaussian, sinc and exponential parameters are now "bracketed", so that they cannot be set to values, which lead to a unusable waveform. Both changes are implemented in the Feb18 revision of the 8-bit AWG. (downloadable from Step 8).

Updated the directory tree picture in Step 3 to reflect the recent changes.

#7: In description add link to Remote User Interface for the AWG

Supplies

The main parts to build the AWG are:

  • 1 Raspberry-pi Pico board
  • 1 2.8" color LCD TFT display with SPI interface
  • 19 precision resistors 10x 2k .1% and 9 x 1k .1%
  • 1 AD8055 (300MHz voltage feedback amplifier)
  • 1 each NTE2633 and NTE2634 transistor. As an alternative I tested 2N2219 and 2N2905 transistors, which work well, too. Upper frequency limit is then ~5MHz
  • 2 heat sinks for the transistors
  • 1 each 79L05 and 78L05 voltage regulators to supply the AD8055
  • a +-12V power supply
  • 1 6.5cm x 13cm prototype board
  • bread board wires to connect the display, switches and encoders
  • and some small parts resistors, capacitors, pin headers, wires...

Please refer to the attached bill of materials for all electronic components.

I am using through hole components, as they are easier to solder and most of them I had in my "component stock". Eventually I opted to put all parts in a nice housing.

A remark on the power supply: I was starting with a Meanwell 5V, +-12V switching power supply just to learn that the signal of the generator was heavily distorted by the power supply's switching noise. I ended up building a simple linear power supply using a 2x15V transformer two bridge rectifiers with two 1000uF electrolytic capacitors and voltage regulators 7812,7912 (+-12V for the output buffer) and an L7805CV for the Pico. I put a heat sink on all three regulators.

Step 1: Conceptual Overview

The AWG consists of three main functional blocks:

  1. The User interface, where the wave form is selected and all parameters of the wave form can be entered / adjusted. When a wave form is selected the respective parameters are set to default values.
  2. The AWG core, where the samples for the wave form are calculated and stored in memory. From memory the wave output is started, using the RP2040's PIO and DMA functions.
  3. The output buffer stage, where the signal is amplified by 2x and a class AB output stage will provide a low output impedance and short circuit protection. With switches the output impedance can be switched between low impedance and 50 Ohm and AC or DC coupling can be selected as well. For low frequencies DC coupling is recommended.

The user interface and AWG core are implemented in micro-python running on the Pico's RP2040 CPU.

Remark: I was experimenting using both cores of the RP2040, one for the generator and one for the user interface. This is working fine, but had no advantage. So, for simplicity reasons, I decided to run the whole program on one core.

Step 2: Building the Hardware

On the schematic drawing you can find the three functional blocks surrounded by "blue boxes". Use the schematic of either the 8-bit or the 10-bit version of the AWG to build your own.

All parts are soldered to the prototype board. Follow the schematic left to right. Make sure you have one central ground supply point where you connect the ground line of the power supply to the ground pins of the Pico board, the two voltage regulators and the output connector. I created ground loops in the beginning which induced additional noise on the output signal.

Some considerations / learning:

  • I opted to provide power to the AD8055 from the same +-12V power used for the output buffer Transistors Q1,Q2 using to small 5V voltage regulators. This resulted in less digital noise on the output signal.
  • The RP2040 DAC has a remarkable rise time of 14ns, which resulted in signal overshoot, esp with square waves, so I added 3.3pF C14 to limit the slew rate. This problem can likely be eliminated with proper layout of the traces on a printed circuit board.
  • The output voltage swing of the DAC is 3.2Vpp, which results in AD8055 clipping the wave. Reducing R3 from 51k to 20k reduces the DAC output by ~10%, which solved that problem. Remark: As an alternative leave R3 51k and reduce output amplitude in the UI instead.
  • During testing the CPU locked up ever now and then when I pushed a button or turned the encoder. Measurements showed that the cables to the switches and encoder picked up noise. I added a de-bounce network, which eliminated this problem (R30...R41 and C12...C17).update: The de-bounce network can be eliminated if you use a liner power supply or a really well filtered switch mode power supply.
  • Add heat sinks to the transistors with less then 25K/W. With R 11 set to 47Ohm the buffer stage output can withstand a short circuit to ground.

Variants for simplification:

  • If you do not need the full 20MHz bandwidth of the output buffer stage, replace Q1 and Q2 with 2N2219 and 2N2905. These transistors are less expensive and have better availability. Tests show upper bandwidth limit is then ~5MHz. Add heat sinks to those transistors as well.
  • In case you do not need low output impedance, you can simplify the output stage and just use the AD8055. Take the signal after R6 (see "TP 1" in the schematic). The AWG can then drive loads down to ~50 Ohm.

Step 3: Installing the Software Packages

The AWG is implemented using micro-python. As a first step please load micropython to the Pico. Use version 1.17 ( rp2-pico-20210902-v1.17.uf2 ) or newer. Older versions do not work as they miss functionality needed for micro-gui.

This instructable assumes you have some basic knowledge on how to use a Pico board, load micro-python and python modules to the Pico. In case you need help, please find the step by step guide getting started with micropython on raspberry pi pico.

As I cannot attach archives or zipped files here, please install following packages:

I. Install micro-gui. If you are using Linux, create a project directory and use git to copy micro-gui to the project directory. E.g. with: git clone https://github.com/peterhinch/micropython-micro-gui.

Use mpfshell, rshell or your preferred micropython IDE to copy the micro-gui modules to the Pico.

If you are using Windows, go to the micro-gui github site, click on the button 'code' and download the zip file (see picture). You can use your preferred tool to download the files to the Pico.

II. Install the hardware drivers. Copy the AWG driver files to the Pico:

  • hardware_setup.py - copy to root directory, contains the info for the buttons, encoder and display driver
  • colors.py - replace the original module as AWG uses more colors, copy attached file to gui/core directory on the Pico

III. Install the AWG files:

The AWG files differ for the 8-bit vs. 10-bit version. As I cannot attaches packages, I have splitted the packages into two Steps of this instructable.

  • Download software for the 8-bit version from Step 8
  • Download software for the10-bit version from Step 9

Then copy the files to the pico. The file names of the 10-bit version are shown in brackets [ ] in text below.

  • ui.py [ui10.py] - copy to the root directory, this is the AWG main program
  • wave_gen.py [wave_gen10.py] - copy to root directory, this module calculates the wave and programs the Pico's PIO and DMA
  • main.py - copy to root directory, module is called after boot, imports ui.py [ui10.py] to start the AWG

I added a picture to show the graphical representation of files and directory structure on the Pico. The picture shows only the files needed for the AWG. If you download micro-gui as a whole, you get more files, e.g. demos of the micro-gui or additional fonts, which are not used by the AWG. You can just copy the micro-gui files needed, as shown in the picture.

Remark: I have cleaned up this instructable, only the newest revisions of files are attached. If you already built an AWG, it is recommended that you upgrade to the newest revision for the version with either 8-bit DAC or 10-bit DAC. Should you need older files, please reach out to me using the comments.

Step 4: The Heart of the AWG

The python module wave_gen.py [wave_gen10.py] contains the code for calculating the waves and to program the Pico's PIO and DMA. I re-use the code created by Rgco. The modules attached support six basic wave forms. The AWG can do much more, such as summing or multiplying two waves, see Rcgo's instructable if you need more functionality.

Changes to the original code:

  1. There are two versions of the code one for an 8-bit DAC one for a 10-bit DAC.
  2. The Pico is overclocked, the CPU runs at 250MHz instead of 125MHz
  3. I am using DMA channels 2 and 3 instead of 0 and 1. DMA channels 0 and 1 are used by other functions, e.g. the SPI interface for the TFT display
  4. While testing I observed "out of memory errors", so I am calling the garbage collector (gc) module after the calculations are completed and the wave buffer is filled to free up unused but still allocated memory.

Remark: The Pico's CPU and the USB connection runs very reliable at 250MHz

Step 5: The User Interface

The User Interface (UI) consists of two push buttons, a rotary encoder with a push button, the 2.8" TFT display and the python module ui.py [ui10.py]. The module defines the screen layout and the AWG controls. In short, what happens when a button is pressed or the encoder is rotated. In the following UI elements are shown in italics letters, e.g. setup points to the setup button on the screen. You can find a quick reference user guide for the UI in the next Step.

Principles for the UI:

  • All user input is collected and stored in a the dictionary called "wave". Upon pressing the setup generator button, the data stored in the dictionary is used by module wave_gen [wave_gen10] to calculate and output the wave.
  • The AWG shows its current status on screen. Status is one of the following -- init --, calc wave, running or stopped. Each status is pretty self explaining and is briefly described in the module ui.py [ui10.py].
  • The UI allows to select a wave via the function drop down menu. When a wave is selected the parameters of the wave are set to default values, which are "mid points" of the parameters. If you press the setup generator button with default values, you get a wave which is working fine. Typically you want to adjust the frequency to your needs.
  • The frequency is not changed if you select a different wave, but all other parameters are set to default values.
  • In the UI only the parameters relevant for the wave selected are active and can be changed. Parameters not needed are „greyed out“.
  • The number of samples used and the resulting output frequency of the wave are displayed.

Remark: The AWG is a digital wave generator. This implies that e.g. frequency, amplitude and offset changes in steps, which results in some limitations:

  • The output frequency can deviate from the frequency setup in the UI, e.g. if the setup frequency is "in-between" two digital frequency steps.

Step 6: AWG Quick Reference Guide

The user interface hardware consists of two push buttons and a rotary encoder in hardware. AWG controls and a setup /stop generator button are implemented in the UI software.

  • The push button Next moves the focus to the next control, which becomes active. The focus is indicated by a white border. E.g. in the picture you can see the function drop down list having focus.
  • The previous button moves the focus back to the previous control.
  • The rotary encoder has to functions. (a) Pushing the encoder serves as the enter button, (b) turning the encoder increases or decreases a value or moves up and down a drop down list.

Please see attached Quick reference Guide for more details.

Remark: If the output signal is clipped or distorted, try adjusting the amplitude, offset or parameters. Doing so often reduces the clipping or distortion. Fine tune parameters to optimize the output, e.g. for pulse: instead of setting rise and fall times to 0 set it to a small value, such as 0.05. This reduces overshoot of the signal by limiting the slew rate.

Step 7: Limitations and Improvements

The AWG was designed to be easy to use, easy to reproduce and low cost.

  1. Six basic waves are implemented. If the Pico board is connected to a PC, basically any wave form can be produced by adding the formula of the wave to module wave_gen.py. If the wave should be persistent in the AWG, add the wave names and controls to the ui.py module.
  2. The output stage of the AWG is a basic class AB design. It is short circuit prove and provides a 6V pp swing. An output filter could be added to reduce the higher frequency digital noise.
  3. As the parameter values can be adjusted over the full range, the user must be aware of what the impact to the wave form is, when adjusting the parameters. With some intense testing parameters could be bracketed, so that the user cannot set up parameters which do not work. ----- solved: Bracketing implemented for AWG 10-bit. On request can be ported to AWG 8-bit.
  4. The Pico board produces the output signal and runs the user interface and SPI interface for display as well. This leads to all kind of digital noise on the generator’s output signal, such as e.g. noise resulting from the SPI interface. -----solved: In the recent software the display update (SPI interface) is turned off when the AWG is started. This significantly reduces digital noise on the output signal (see pictures). The noise can be further reduced by taking some caution to avoid cross talk between traces or cables when building on a prototype board or making a PCB layout.
  5. To take the noise reduction a step further, using two Pico boards could as well result in much lower output noise. One Pico board for the generator and one for the user interface, connecting them with e.g. a serial interface.

Any improvements and comments are very welcome.

Step 8: Software for AWG With 8-bit DAC

Attached the three files to be uploaded to the Pico when implementing the AWG with the 8-bit DAC. (see Step 3 as well)

Change log: (remark, the revision of the software in use is displayed in the top right corner of the AWG display)

revision Feb-18, ui.py updated:

  • Keep the parameter values, when stopping the generator, instead of initializing them each time.
  • For Gaussian, sinc and exponential parameters are now "bracketed", so that they cannot be set to values, which lead to a unusable waveform.


Step 9: AWG With 10-bit DAC

Attached the three files to be uploaded to the Pico when implementing the AWG with the 10-bit DAC. (see Step 3 as well)

Change log: (remark, the revision of the software in use is displayed in the top right corner of the AWG display)

revision Dec-9, ui10.py updated:

  • Some users asked to keep the parameter values, when stopping the generator, instead of initializing them.
  • For Gaussian, sinc and exponential parameters are now "bracketed", so that they cannot be set to values, which lead to a unusable waveform.

Step 10: Contributions of the Community

#1 User rasberrymero has built this project and created a pcb layout. See attached picture of the layout and pictures of the project on his member page.

Rasberrymero's comments: A few words about the pcb design. The resistors are 0,4W except for the analog stage. I use single row pin header connectors. (I had the parts in fins, so the resistors are not the same ). Single sided pcb, needed three jumpers. Using Sprint layout, which is free.I f any questions arise I will be happy to answer.

https://www.softpedia.com/get/Science-CAD/Sprint-Layout-Viewer.shtml


#2 User zenographie built a pcb with mainly SMD components for this project. For interested users, who want to build this pcb, here is the github link to the kicad files. Please find pictures in zenographie's comment below.