DDS

SwordfishUser.DDS History

Show minor edits - Show changes to output

Added lines 1-108:
DDS is an acronym standing for "direct digital synthesis." It's sometimes called a "numerically controller oscillator," but DDS seems to be the more popular term.

I devoted most of a chapter in my book to using a 16F877 PIC to build an audio DDS generator, and I don't propose to duplicate that effort here. In brief, a DDS generates its output by computing the value of the desired sine wave (or other waveform, as DDS is not limited to sinusoidal waveforms) at specific time intervals and then sending the value to a digital-to-analog converter, where the numerical data is converted to a corresponding voltage level.
 
The upper trace in the oscilloscope capture shows a typical DDS output. Note that the desired sine wave is approximated by discrete voltage steps.  The PIC calculates the appropriate voltage level as a byte-range number (0...255) and sends it to the DAC where it is converted to a voltage. In this case, the output is a 730 Hz sine wave.

http://www.cliftonlaboratories.com/userimages/swordf3.gif

The lower trace is the DAC's output after it is run through a low pass filter, which cleans up most of the digital step artifacts seen in the raw DAC's output.

To show you that the DDS is not restricted to sine wave outputs, the lower trace in the following oscilloscope capture is a DDS-generated sawtooth or ramp waveform. The upper trace is a synchronization waveform.

http://www.cliftonlaboratories.com/userimages/swordf4.gif

!!!DAC - Digital to Analog Converter

Let's look at how a DAC works. We'll start with a simple 4-bit R-RL ladder type DAC. The two oscilloscope plots above were taken using this circuit, breadboarded up with five discrete resistors.

%lfloat% http://www.cliftonlaboratories.com/userimages/swordf5.gif
The four switches, DB0...DB3 switch between ground and +5V. Of course, our circuit will not use real SPDT switches, but rather the PIC's output pins will switch between logic high (+5V, more or less) and logic low (0.2V or so). How does this simple circuit work? The easiest way to understand how it works is to regard the four resistors as current sources. Into a low impedance load (such as Rload, the 47 ohm resistor), when DB3 is switched to the +5V source, it will force 5 mA (5V/1000 ohms) through Rload. In this case, the output voltage is 235 mV, calculated as 0.005 A * 47 ohms = 0.235V

Likewise, when DB2 is switched to +5V, Rload has 2.5 mA through it, DB1 controls a 1.25 mA source and DB0 controls a 0.63 mA source.

With the switches set as shown in the diagram, the total current through Rload is 2.5 mA (from DB2, through R2) and 1.25 mA (from DB1, through R3), totaling 3.75 mA, for an output voltage of 176 mV.

What about the shunting effect of R1 and R4, you may be thinking. These clearly drain current away from Rload. And, how can you just sum the currents?

The answer is yes, these are all real effects. However, we've chosen R1...R4 and Rload carefully. A 1K resistor shunting 47 ohms is negligible, at least at the resolution we expect from a crude 4-bit DAC. Likewise, a couple hundred millivolts across Rload won't make our assumption that R1...R4 are current sources that bad. Hence, we can simply sum the currents and be "close enough" for our purposes.

Don't believe me? Dig out your calculator and work through Ohm's law and send me an E-mail with the exact answer. It'll be within 10% of our simplistic analysis.

Oh yes, why is this called an R-2R ladder? Looking at the resistor values and diagram should answer this question for you. (I used real values at 3.9K and 8.2K; in theory these should be 4.0K and 8.0K).

And, why make the ratios 2:1? It has something to do with the binary system doesn't it? Yes, indeed it does. If we look at a 4-bit binary number (usually called a nibble) defined by DB3...DB0, we see that the DC output of our R-2R ladder DAC automatically tracks the binary value of the nibble. The binary value to voltage multiplier is the current generated by the least significant bit (R4, or 0.625 mA) times Rload, or 29.4 mV.

Let's see if this works. The nibble data value represented by these switches is 0110 binary, or in decimal 6. Hence, the output voltage should be 6 * 29.4 mV = 176 mV, exactly what we calculated before.

Nice when it all fits together, isn't it?

!!!Simple Swordfish DDS Code--Program DDS-1

OK, time for some code. I'll first list the complete program and then explain what is behind it. The program assumes we have two of the simple 4-bit resistive DAC circuits connected to the PIC's Port B.

The following schematic fragment shows the resistive DAC connection.

http://www.cliftonlaboratories.com/userimages/swordf6.gif

Our first code generates two 4-bit sine waves, one on Chan_A_Out and one on Chan_B_Out, at different frequencies.

=code [=
{
****************************************************************
* Name    : Toner.BAS                                          *
* Author  : Jack R. Smith                                      *
* Notice  : Copyright (c) 2006 Clifton Laboratories            *
*        : All Rights Reserved                                *
* Date    : 7/22/2006                                          *
* Version : 1.0                                                *
* Notes  :                                                    *
*        :                                                    *
****************************************************************
}

'Program to output two simultaneous DDS-generated sine waves
'into 4-bit DACs on Port B. Channel A is lower nibble
'Channel B is upper nibble.

Device = 18F4620
Clock = 40 'remember to enable HSPLL in configuration
Config
  OSC = HSPLL, //turn 4x PLL ON
  LVP = OFF,
  DEBUG = OFF,
  PWRT = ON,
  BOREN = ON,
  WDT = OFF,
  PBADEN = OFF, 'for 18F4620 - makes port B digital
  XINST=OFF

Const
  SinTable(16) As Byte = (8,10,13,14,15,14,13,10,8,5,2,1,0,1,2,5)
Dim
  aStep, bStep As Word,
  PhaseAccumA As Word, 'use these as a 4.12 (15 bit total)
  PhaseAccumB As Word, 'phase accumulators
  Temp As Byte
 
// program start...
TRISB = $00 'make output

'adjust these values when loop code is finished as the
'output frequency depends on Repeat / Until loop timing
aStep = 935
bStep = 267 'generates 780 Hz with current timing
PhaseAccumA = 0
PhaseAccumB = 0

Repeat
  High(PORTC.0)
  PhaseAccumA = PhaseAccumA + i
  PhaseAccumB = PhaseAccumB + j
  Temp = SinTable(PhaseAccumA >> 12)
  Temp = Temp + 16 * (SinTable(PhaseAccumB >> 12))
  PORTB = Temp
  Low(PORTC.0)
Until False
=]