20

I am looking at ways to use the Raspberry Pi as a signal generator. Currently I am working on hooking up an inexpensive AD9850 module from ebay. This will generate fairly accurate frequencies up to 40MHz (70MHz with the AD9851).

enter image description here

I guess the audio output could also be used for arbitrary waveforms up to a very limited frequency.

Are there any other possibilities?

Piotr Kula
  • 17,307
  • 6
  • 65
  • 104
John La Rooy
  • 11,947
  • 9
  • 47
  • 75

4 Answers4

13

Here's how I've connected the AD9850 module up and a simple Python program to show how to set the frequency to 1000Hz.

The AD9850 needs to run at 5V to work properly with the 125MHz crystal. It would probably be ok to wire 4 GPIO pins directly to the module since they are just inputs, but connecting via a MCP23017 effectively turns the module into another I²C peripheral and makes sure the RPi inputs are safe.

Important Note About +5V
The +5V pin on the GPIO can't really supply enough current to power the AD9850. You should use an external 5V supply.

RPi connected to AD9850 module via MCP23017

from functools import partial

import smbus

def main():
    addr = 0x20
    bus = smbus.SMBus(0) # or SMBus(1) on newer pis

    # Helper functions    
    wr_dir = partial(bus.write_byte_data, addr, 0x01)
    wr = partial(bus.write_byte_data, addr, 0x13)

    # Set Pins B0-B4 of the MCP23017 to output
    wr_dir(0xF0)

    # Names of the Pins
    RST = 1 << 0
    DATA = 1 << 1
    FQ = 1 << 2
    CLK = 1 << 3

    def send_bit(bit):
        # send a single bit
        wr(DATA * bit)
        wr(CLK | DATA * bit)

    def fq():
        wr(FQ)
        wr(0)

    def init():
        wr(RST)
        wr(0)
        wr(CLK)
        wr(0)
        wr(FQ)
        wr(0) 

    freq = 1000  
    init()
    dphase = int(0.5 + (freq << 32) / 125000000.0)
    for x in range(32):
        send_bit((dphase >> x) & 1)

    # Phase bits can all be 0
for x in range(8):
    send_bit(0)

    fq()

if __name__ == "__main__":
    main()
John La Rooy
  • 11,947
  • 9
  • 47
  • 75
11

You could in theory hook up some D/A converter to GPIO pins but this is not suitable for signal generation because you will not be able to drive it with precise timing, mainly because Linux is not a real-time OS.

Also, there is no way this could operate at such high frequencies.

If 44 kHz or so is enough, I think audio jack may be the simplest way to do it.

4

John La Rooy has a good solution but the circuit may be more complicated than some would prefer. This describes a similar solution designed by Tom Herbison using only the AD9850, although it uses 4 GPIO signal pins instead of 2 like John's solution.

Tom connects to GPIO like this: connection diagram

Notice he runs the AD9850 on 3.3V instead of 5V. According to this discussion, the AD9850 is rated to run at 3.3V or 5V, but some boards may use components not able to handle 5V for long, so running at 3.3V may actually be a better solution, depending on your flavor of AD9850 board.

My particular AD9850 board had most of the pin labels only underneath the board, so I took a picture of the underside before pressing it down into a prototyping board. The pin locations ended up being identical to the ones on Tom's board anyway. On my board, FQ is labeled FU_UQ, CLK is W_CLK, and RST is RESET.

Tom provides this Python 3 script for controlling the function generator. Here's a copy of v1.0 in case the download link ever breaks:

# RPi RF Signal Generator v1.0

# Copyright (C) 2013 Tom Herbison MI0IOU
# Email (hidden to discourage spammers - see original rpi_rfsiggen.py file)
# Web <http://www.asliceofraspberrypi.co.uk>

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

# import GUI module
from tkinter import *

# import GPIO module
import RPi.GPIO as GPIO

# setup GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)

# Define GPIO pins
W_CLK = 15
FQ_UD = 16
DATA = 18
RESET = 22

# setup IO bits
GPIO.setup(W_CLK, GPIO.OUT)
GPIO.setup(FQ_UD, GPIO.OUT)
GPIO.setup(DATA, GPIO.OUT)
GPIO.setup(RESET, GPIO.OUT)

# initialize everything to zero
GPIO.output(W_CLK, False)
GPIO.output(FQ_UD, False)
GPIO.output(DATA, False)
GPIO.output(RESET, False)

# Function to send a pulse to GPIO pin
def pulseHigh(pin):
    GPIO.output(pin, True)
    GPIO.output(pin, True)
    GPIO.output(pin, False)
    return

# Function to send a byte to AD9850 module
def tfr_byte(data):
    for i in range (0,8):
        GPIO.output(DATA, data & 0x01)
        pulseHigh(W_CLK)
        data=data>>1
    return

# Function to send frequency (assumes 125MHz xtal) to AD9850 module
def sendFrequency(frequency):
    freq=int(frequency*4294967296/125000000)
    for b in range (0,4):
        tfr_byte(freq & 0xFF)
        freq=freq>>8
    tfr_byte(0x00)
    pulseHigh(FQ_UD)
    return


# Class definition for RPiRFSigGen application
class RPiRFSigGen:
        # Build Graphical User Interface
        def __init__(self, master):
                frame = Frame(master, bd=10)
                frame.pack(fill=BOTH,expand=1)
                # set output frequency
                frequencylabel = Label(frame, text='Frequency (Hz)', pady=10)
                frequencylabel.grid(row=0, column=0)
                self.frequency = StringVar()
                frequencyentry = Entry(frame, textvariable=self.frequency, width=10)
                frequencyentry.grid(row=0, column=1)
                # Start button
                startbutton = Button(frame, text='Start', command=self.start)
                startbutton.grid(row=1, column=0)
                # Stop button
                stopbutton = Button(frame, text='Stop', command=self.stop)
                stopbutton.grid(row=1, column=1)


        # start the DDS module
        def start(self):
                frequency = int(self.frequency.get())
                pulseHigh(RESET)
                pulseHigh(W_CLK)
                pulseHigh(FQ_UD)
                sendFrequency(frequency)

        # stop the DDS module
        def stop(self):
                pulseHigh(RESET)

# Assign TK to root
root = Tk()

# Set main window title
root.wm_title('RPi RFSigGen')

# Create instance of class RPiRFSigGen
app = RPiRFSigGen(root)

# Start main loop and wait for input from GUI
root.mainloop()

Since any use of the GPIO pins on the pi requires running as root, Tom describes two methods of launching his python code with root privileges. His first method is to modify the Python IDE desktop icon to always run as root, but that strikes me as unsafe - you don't want to run all python GUI programs as root if you don't have to. The second method is to run sudo idle3_ from a command prompt to launch the Python 3 Integrated Development Environment with root privileges whenever it needs root privileges.

Tom doesn't mention installing the RPi.GPIO python 3 library so it may already be available on some Pi OS versions, but it was not available on the Occidentalis v0.2 I was using so I ran sudo apt-get install python3-rpi.gpio. Note that Python 3 uses a different location for RPi.GPIO so sudo apt-get install python-rpi.gpio will only make the library accessible to Python 2.

Once the Python 3 IDE is open with root privileges, open the file rpi_rfsiggen.py, then choose Run -> Run Module from the menu or press F5.

I was able to get a nice stable 18kHZ sine wave at 1Vpp from the SinB output pin (labeled ZOUT2 on my board) on my first try.

3

If you just want to implement a function generator for audio and Lf rf then pick up a cheap AD9833 module from EBAY. This will give you sine, square and triangle waves plus variable phase. Apparently not much good past 7 MHz....

enter image description here

John La Rooy
  • 11,947
  • 9
  • 47
  • 75
Steve
  • 31
  • 1