DDS-based Rasp Pi function generator
Pass the Hat
A touch display, a case, and a custom add-on board transform the humble Rasp Pi into a high-performance function generator that rivals expensive commercial offerings.
Modern laboratory instruments are a marvel of integration, combining hardware, software, and often mechanical components to produce versatile and highly functional units that bring real value to the engineers and technologists who use them. In this article, I aim to show how such an instrument might be put together by combining the Raspberry Pi with several elements to provide a low-cost and flexible function generator that can rival its more expensive commercial cousins on price and possibly beat them in terms of flexibility.
Function Generator
A signal or function generator is a versatile frequency source able to output a signal in a number of output waveshapes and at an adjustable amplitude and frequency from direct current (DC) up into the high-frequency electromagnetic radio frequency (RF) region, depending on the application. Sophisticated instruments can also include frequency, phase, or amplitude modulation useful for testing radio transmitters and receivers.
No engineer's workbench is complete without such an instrument. Together with an oscilloscope, it allows an engineer to characterize the frequency and phase response of an amplifier, to test and calibrate counters and frequency meters, and to replace built-in oscillators in devices under test or during development.
A variable frequency and amplitude signal is often required for uses beyond the purely electronic, such as for applications in mechanical and acoustic engineering for testing loudspeakers and driving vibration platforms and a plethora of other electronically excited mechanical devices. In the field of medicine, variable-frequency sources are used to test the response of the human ear, as well as to drive ultrasound devices used in imaging. The list goes on and on.
Many of the commercially available instruments cost upward of several hundred dollars. Because of their self-contained nature, modifying their function is not generally possible, and embedding them into a larger system can be difficult because of their size and power requirements. Modern analog integrated circuit technology makes available to the instrument designer a vast array of building blocks, many of them programmable, to help build a sophisticated instrument with a minimum of components. The addition of a microcontroller or other embedded computer enables the designer to add extra features and produce a fully featured, flexible instrument that is easy to use.
The instrument presented here is based on a Raspberry Pi, an excellent basis for personalized instrumentation, the official Pi 7-inch LCD screen and case, and a direct digital synthesis (DDS) function generator on a Hardware Attached on Top (HAT) board of my design (see the "Specifications" box).
Specifications
- 0.01Hz to 10MHz, adjustable in 0.01Hz steps
- Sine, square, and triangle outputs
- 50-ohm or low-impedance output with jumper option
- Output adjustable from +/-1mV to +/-5V, peak-to-peak
- Transistor-transistor logic (TTL) sync output
- Ninth-order filter for sine purity
- Linux drivers or access through the serial peripheral interface (SPI) bus
- Current limit warning LED
- Onboard supplies for analog section
- SMA connector for output
Hardware
Practically all modern signal or function generators are implemented by DDS (see the Direct Digital Synthesis box). Because DDS chips are readily available for a few dollars, you can easily build an instrument with similar performance and greater flexibility for a few tens of dollars. The maximum output frequency is limited by the semiconductor technology used to build the chip and the master or "clock" frequency (usually derived from a crystal oscillator) used to drive the chip. Because the generation is purely digital, the frequencies produced have no lower bound. For this project, I have chosen the AD9833BRMZ [1] DDS chip from Analog Devices, which has a range up to 12.5MHz. As a side benefit, this chip can also produce triangular and square wave outputs.
Direct Digital Synthesis
Direct digital synthesis is a well-known technique for generating repetitive waveforms from a single, high-frequency source such as a crystal oscillator. The concept is fairly simple. Samples of the required waveform are stored in RAM or ROM (in the case of a sine wave, only a quarter of a full cycle need be stored, and the rest inferred by symmetry). A counter is then used to access the stored samples sequentially, and the samples are passed to a digital-to-analog converter (DAC), which then outputs the desired waveform.
By varying the frequency fed to the counter, the frequency of the output may be varied. The resulting waveform is a stepped approximation of the desired waveform; an output filter is required to reconstruct the exact waveform. This filter is generally a high-order passive filter with a corner frequency set as half the reference clock frequency. You can find more information about DDS in an online tutorial [2].
A useful generator must also have a means to adjust output amplitude. Amplitude control can be achieved many ways, from simple, mechanical variable potentiometers (pots) to their semiconductor equivalents. Many of these approaches have limitations because of excessive stray capacitance, linearity, stability, and the ability to control the amplitude with software. An optimum solution is to use a multiplying DAC, wherein the reference signal is replaced by the signal to be controlled. These so-called multiplying DACs are available and can control amplitudes of frequencies into the tens of megahertz and beyond. The AD5443YRMZ [3] from Analog Devices is such a chip, providing 10-bit resolution of amplitude control.
Both of the chips selected have SPI interfaces, so they are ideal for interfacing to the Raspberry Pi. Additionally, both are supported by Analog Devices' Linux Industrial I/O Subsystem drivers (see the "IIO" box).
IIO
The Linux Industrial I/O Subsystem (IIO) is an attempt at a unified device driver interface for chips such as data converters, sensors, and frequency sources. The wiki [4] gives an excellent in-depth description. From the perspective of this article, the main thing to note is that, once loaded, a device driver for a particular device exposes readable and writable parameters as entries in the /sys
tree of the filesystem that allow user programs to sample and modify the device's function by file semantics, with no low-level (I2C/SPI/serial/etc.) programming required. High-speed devices also support a streaming interface.
Because the generated signal is a stepwise approximation to the ideal sine wave output, the output must be filtered. The filter chosen for this design is a ninth-order elliptic low-pass filter. Filters of this sort are a complex topic beyond the scope of this article, but you can find a detailed explanation online [5]. A passive filter comprises only resistors, capacitors, and inductors.
A number of online resources (e.g., RF Tools [6]) offer simple design tools that allow you to dial in the required parameter and then deliver a schematic of the filter, the component values, and a graphical phase and frequency response. The filter for the current design has a corner cut-off frequency of around 12.5MHz, or half of the chosen crystal drive frequency of 25MHz. This filter is an anti-alias filter, and by attenuating the higher frequencies (greater than the Nyquist frequency), it prevents the aliased components from being output.
A wide-band power amplifier is employed to produce output that can be used to drive other instruments at useful power levels. The OPA564AIDWP from Texas Instruments [7] provides an output of +/-10V peak-to-peak at more than 1A across the 10MHz frequency range, so it is ideal for this application. It has built-in overcurrent protection and a thermal shutdown mode, so it is immune to the type of abuse that a bench instrument might encounter.
The remaining components that make up a practical design are connectors, op-amps for level shifting and buffering, potentiometers for calibration, power supplies, and filters. The power supply used to power the instrument must be of sufficient capacity to supply both the Rasp Pi and the DDS board. The official universal supply [8] is recommended. The block diagram in Figure 1 shows how the essential elements are connected to form the complete instrument.
The excellent open source schematic capture and printed circuit board (PCB) design tool KiCad [9] was used to capture the design and layout of the two-layer PCB. The schematic (Figure 2) is a complete implementation of a practical DDS function generator, with power supplies, filtering, synchronization output, level shifters between stages, and calibration potentiometers to set null the output offset in the power amplifier and to set the full scale gain to the value indicated in the GUI.
Along with the schematic, KiCAD presents a 3D render of the fully laid out PCB (Figure 3). The KiCAD 3D models are invaluable, allowing designs to be incorporated into larger mechanical models to ensure the mechanical positions of connectors and other elements are correct and that no component fouls any nearby mechanical element, such as features of an enclosure.
Mounted on a Raspberry Pi, the DDS board fits within the official 7-inch Rasp Pi display case [10]. A small modification to the case is required to allow the output connector to protrude through the case wall (Figure 4). Two vertical saw cuts toward the HDMI connector remove a small rectangle of plastic and do not in any way affect the structural integrity of the case.
The Software
Today's instruments have generally moved away from front panels loaded with knobs and switches to a more generic user interface with an LED or LCD screen and possibly a single multifunction control knob. The interface designed for the current instrument is intended to run on the standard Rasp Pi 7-inch touch LCD [11], so the user interface will be entirely touch-based, allowing a compact instrument to be built into the standard case. The whole setup is packaged as an instrument, with a Python-based GUI allowing the full control of frequency, amplitude, and waveshape that you would expect from a commercial instrument.
As already stated, many instruments use a multipurpose spinner, or control dial, to change operating parameters that are displayed on an LED or LCD screen. The current GUI uses the same paradigm (Figure 5), with a circular GUI dial that controls frequency and amplitude. Two buttons associated with the dial select the decade [12] currently being edited, and another two allow you to jog the setting up or down for precise control. The frequency of voltage being edited is displayed in the central area of the screen, with the voltage displayed below. A series of buttons selects the function of the dial, and other buttons control output waveform and frequency range.
Table 1
GUI Functions
Position | Function |
---|---|
Top left |
Range selector |
Top center |
Frequency output indicator with cursor and units. |
Top right |
Left and right arrows select decade (the cursor under the '1' character in the frequency output indicator). |
Plus and minus jog up or down 1 unit. |
|
Dial scrolls frequency up or down. If you scroll past 9, the cursor moves to the left and continues to increment. |
|
Bottom left |
Waveform selector. |
Bottom center |
Voltage output indicator with cursor and units. |
Bottom right |
Selector for dial function. |
The IIO subsystem drivers hide the details of the SPI interface that are used to communicate with the DDS and DAC chips. The parameters that can be varied, such as frequency, level, and waveshape appear as files in the /sys
tree. Simply writing valid values to those files causes SPI messages to be sent to the chip. (Although it would be possible to bypass the IIO drivers, i.e., not load them at all, and generate SPI messages directly, it would require a detailed understanding of the SPI protocol used by each chip and, in my opinion, is a lot of hard work for no appreciable gain.)
The IIO driver takes care of scaling and all the low-level communication details, which makes interfacing with the chip from a high-level language such as Python extremely convenient. The core methods required to communicate with the DDS board are shown in Listing 1. The iio.py
routines control the hardware through the IIO device drivers and their entries in the /sys
tree and represent the least amount of code required to control the generator.
Listing 1
iio.py
01 import sys 02 03 # 04 # This file provides routines to control the hardware via the iio 05 # device drivers and their entries in the /sys tree. 06 # This is the least amount of code required to control the generator; 07 # all the other files simply provide a GUI front end. 08 # If you want to design your own GUI or embed the generator in another system, 09 # this is all you need. 10 # 11 12 ## dds registers 13 frequency_register='/sys/bus/iio/devices/iio:device1/out_altvoltage0_frequency0' 14 wavetype_register='/sys/bus/iio/devices/iio:device1/out_altvoltage0_out0_wavetype' 15 enable_register='/sys/bus/iio/devices/iio:device1/out_altvoltage0_out_enable' 16 17 # dac registers 18 voltage_register='/sys/bus/iio/devices/iio:device0/out_voltage0_raw' 19 20 # disable calls to hardware in test mode 21 test=(len(sys.argv)>1) 22 23 # enable output 24 def enableOutput(enable): 25 if test==False: 26 fo=open(enable_register,'w') 27 if enable==True: 28 fo.write('1') 29 else: 30 fo.write('0') 31 32 # write frequency in Hz to DDS 33 def setFrequency(frequency): 34 output=str(int(frequency)) 35 #print('F='+output) 36 if test==False: 37 fo=open(frequency_register,'w') 38 fo.write(output) 39 40 # write to DDS one of 'sine', 'square', 'triangle' 41 def setWavetype(wavetype): 42 #print('T='+wavetype) 43 if test==False: 44 fo=open(wavetype_register,"w") 45 fo.write(wavetype) 46 47 # DAC full scale is 4096 (2^12), so scale from 48 # 5000 millivolts 1.23=4095/10000 49 def setVoltage(voltage): 50 output=str(int(voltage*4095/10000)) 51 #print('V='+output) 52 if test==False: 53 fo=open(voltage_register,'w') 54 fo.write(output)
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Gnome OS Transitioning Toward a General-Purpose Distro
If you're looking for the perfectly vanilla take on the Gnome desktop, Gnome OS might be for you.
-
Fedora 41 Released with New Features
If you're a Fedora fan or just looking for a Linux distribution to help you migrate from Windows, Fedora 41 might be just the ticket.
-
AlmaLinux OS Kitten 10 Gives Power Users a Sneak Preview
If you're looking to kick the tires of AlmaLinux's upstream version, the developers have a purrfect solution.
-
Gnome 47.1 Released with a Few Fixes
The latest release of the Gnome desktop is all about fixing a few nagging issues and not about bringing new features into the mix.
-
System76 Unveils an Ampere-Powered Thelio Desktop
If you're looking for a new desktop system for developing autonomous driving and software-defined vehicle solutions. System76 has you covered.
-
VirtualBox 7.1.4 Includes Initial Support for Linux kernel 6.12
The latest version of VirtualBox has arrived and it not only adds initial support for kernel 6.12 but another feature that will make using the virtual machine tool much easier.
-
New Slimbook EVO with Raw AMD Ryzen Power
If you're looking for serious power in a 14" ultrabook that is powered by Linux, Slimbook has just the thing for you.
-
The Gnome Foundation Struggling to Stay Afloat
The foundation behind the Gnome desktop environment is having to go through some serious belt-tightening due to continued financial problems.
-
Thousands of Linux Servers Infected with Stealth Malware Since 2021
Perfctl is capable of remaining undetected, which makes it dangerous and hard to mitigate.
-
Halcyon Creates Anti-Ransomware Protection for Linux
As more Linux systems are targeted by ransomware, Halcyon is stepping up its protection.