DDS stands for Direct Digital Synthesizer, also called Numerically Controlled Oscillator(NCO), and it is a method for generating analog waveforms using digital techniques. In a DDS system, a digital value called a tuning word is used to control the frequency and phase of an output waveform. The tuning word is typically a large integer value that represents the desired frequency as a fraction of the DDS clock frequency. The DDS generates a waveform by rapidly changing the output frequency according to the tuning word, resulting in a continuous waveform with the desired frequency and phase characteristics.
DDS technology offers several advantages over traditional analog waveform generation methods. It provides high frequency resolution, allowing for precise control of waveform frequency with fine increments. DDS also offers excellent frequency stability and accuracy, making it suitable for applications that require precise frequency control. Additionally, DDS allows for easy waveform modulation and frequency sweeping, making it versatile for various signal processing and modulation techniques. . A basic DDS consist of phase accumulator, a phase to amplitude
converter(a sine table in ROM), a DAC(digital to analog converter) and a
filter. The following shows the block diagram of DDS.
\( f_o = \frac{M f_{clk}}{2^N} \) ------->(1)
therefore tuning word(M) is,
\(M= \frac{f_{clk} 2^N}{f_o}\) ------->(2)
The tuning word can be described using the phasor diagram shown below.
The tuning word is applied to the Phase accumulator where it is added with the previous accumulated phase. The phase accumulator consists of a j-bit frequency register which stores a tuning word followed by a j-bit full adder and a phase register. The digital input phase increment word or tuning word is entered in the frequency register. At each clock pulse this data is added to the data previously held in the phase register. The phase increment word represents a phase angle step that is added to the previous value at each 1/fclk seconds to produce a linearly increasing digital value. The upper k bits of N bits are used to phase information to extract amplitude information from the ROM which stores the sine wave amplitudes. The sine wave amplitude is sent to digital to analog converter and subsequently filtered to get sine wave.
DDS with Arduino
Arduino is a widely used microcontroller platform that provides a simple and affordable way to create DIY electronics projects. It offers a rich ecosystem of hardware and software libraries that make it easy to interface with various sensors, actuators, and peripherals. Arduino boards are based on the Atmel AVR microcontroller or other microcontroller architectures, and they are programmed using the Arduino IDE (Integrated Development Environment), which simplifies the development process.
To implement DDS using Arduino, we need a microcontroller with digital-to-analog converter (DAC) capabilities to generate the analog output waveform such as Arduino Due. But we can also implement DDS without inbuilt DAC like using Arduino Nano or Arduino Uno by using pulse width modulation(PWM) and applying filter to the PWM output.
DDS with Arduino Uno
Here it is illustrated how to generate Sine Wave with Arduino by programming Arduino Uno as DDS. The output will be PWM signal which is filtered to get sine wave. To implement DDS with Arduino, we use one of the timer and configure it either Fast PWM mode or Phase correct Mode. Here we will configure Timer 2 in Phase Correct Mode. We first calculate the tuning word and setup timer2 overflow interrupt. The Timer 2 is configured to trigger the interrupt whenever the compare match is reach and overflow flag is set. Once the interrupt is triggered, the interrupt service routine(ISR) is executed. When ISR is executed, the calculated tuning word is added the the phase accumulator. Then the MSB 8 bits of the phase accumulator is extracted by bit shifting and this is used as index to select the sine wave amplitude stored in program memory. The corresponding sine wave PWM signal appears on the pin 11(PB3) of Arduino Uno. This is because the compare match output of Timer 2 compare match module A is used and for this module PB3 pin configured as the output internally in the ATmega328p microcontroller chip.
Arduino DDS code
The following is the Arduino DDS code.
#include "avr/pgmspace.h"
// table of 256 sine values / one sine period / stored in flash memory
const unsigned char __ATTR_PROGMEM__ sine256[] = {
127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,217,219,221,223,225,227,229,231,233,234,236,238,239,240,
242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,233,231,229,227,225,223,
221,219,217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,115,111,108,105,102,99,96,93,90,87,84,81,78,
76,73,70,67,64,62,59,56,54,51,49,46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31,
33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124
};
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
byte bb;
int N = 32; //No. of bits of Phase Accumulator
double Fout;
//Fclk=16MHz/510=31372.549; formula for wave frequency in phase correct mode: Fcpu/5N N=1
const double Fclk=31376.6; // measured
// variables used inside interrupt service declared as voilatile
volatile byte i; // var inside interrupt
volatile byte j; // var inside interrupt
volatile byte c4ms; // counter incremented all 4ms
volatile unsigned long PhaseAcc; // phase accumulator
volatile unsigned long TuningWord; // dds tuning word M
void setup(){
Serial.begin(115200); // connect to the serial port
Serial.println("Arduino DDS");
pinMode(11, OUTPUT); // pin11= PWM output / frequency output
Setup_timer2();
// disable interrupts to avoid timing distortion
cbi (TIMSK0,TOIE0); // disable Timer0 !!! delay() is now not available
sbi (TIMSK2,TOIE2); // enable Timer2 Interrupt
Fout=1000.0; // initial output frequency 1000Hz
TuningWord = pow(2,N)*Fout/Fclk; // calulate DDS new tuning word
}
void loop(){
while(1){
if (c4ms > 250) { // timer / wait fou a full second
c4ms=0;
Fout = analogRead(0); // read Poti on analog pin 0 to adjust output frequency from 0..1023 Hz
cbi(TIMSK2,TOIE2); // disble Timer2 Interrupt
TuningWord = pow(2,32)*Fout/Fclk; // calulate DDS new tuning word
sbi(TIMSK2,TOIE2); // enable Timer2 Interrupt
Serial.print(Fout);
Serial.print(" ");
Serial.println(TuningWord);
}
}
}
//******************************************************************
// timer2 setup: set prescaler=1,Phase Correct PWM, 16000000/510 = 31372.55 Hz clock
void Setup_timer2() {
// Timer2 Clock Prescaler to 1
sbi (TCCR2B, CS20);
cbi (TCCR2B, CS21);
cbi (TCCR2B, CS22);
// Timer2 PWM Mode set to Phase Correct PWM
cbi (TCCR2A, COM2A0); // clear Compare Match
sbi (TCCR2A, COM2A1);
sbi (TCCR2A, WGM20); // Mode 1 Phase Correct PWM
cbi (TCCR2A, WGM21);
cbi (TCCR2B, WGM22);
}
//******************************************************************
// Timer2 Interrupt Service at 31372,550 KHz = 32uSec
// this is the timebase REFCLOCK for the DDS generator
// Fout = (M*Fclk) / (2 exp 32)
// runtime : 8 microseconds ( inclusive push and pop)
ISR(TIMER2_OVF_vect) {
PhaseAcc = PhaseAcc + TuningWord; // soft DDS, phase accu with 32 bits
i = PhaseAcc >> 24; // use upper 8 bits for phaseaccu as frequency information
// read value fron ROM sine table and send to PWM DAC
OCR2A=pgm_read_byte_near(sine256 + i);
// increment variable c4ms all 4 milliseconds
if(j++ == 125) {
c4ms++;
j=0;
}
}
This Arduino code implements a Direct Digital Synthesis (DDS) waveform generator using an Arduino board. DDS is a technique used to generate precise and stable analog waveforms using digital hardware. The code generates a sine wave output with a frequency that can be controlled using a potentiometer connected to analog input pin 0.
Here's a brief overview of the code:
The sine waveform values are stored in an array called
sine256
, which contains 256 values that represent one period of a sine wave. These values are stored in flash memory using the__ATTR_PROGMEM__
attribute to conserve RAM space.The code defines some macros
cbi
andsbi
which are used to clear and set bits in special function registers (SFRs) respectively. These macros are used to configure the hardware timers and pins on the Arduino board.The
Setup_timer2()
function sets up Timer2 of the Arduino board to generate a Phase Correct Pulse Width Modulated (PWM) signal with a frequency of approximately 31.4 kHz (calculated as 16 MHz divided by 510). This timer is used as the clock source for the DDS waveform generation.The
setup()
function is the setup routine that initializes the Arduino board. It configures the serial communication with a baud rate of 115200 and sets up the PWM output pin (pin 11) for DDS waveform generation.The
loop()
function is the main loop of the program. It reads the value of a potentiometer connected to analog input pin 0 using theanalogRead()
function, which gives a value ranging from 0 to 1023 representing the voltage on the analog pin. This value is used to adjust the output frequency of the DDS waveform.Inside the
loop()
function, a counterc4ms
is used to wait for 1 second before updating the output frequency. When the counter reaches 250, the potentiometer value is read and used to calculate a new tuning word for the DDS waveform generation. The tuning word is then updated in theTuningWord
variable, which determines the frequency of the output waveform.The
TuningWord
value is calculated using the formulaTuningWord = pow(2, 32) * Fout / Fclk
, whereFout
is the desired output frequency in Hertz andFclk
is the clock frequency of the DDS waveform generator (31.4 kHz in this case).The DDS waveform generation is done inside an interrupt service routine (ISR) that is triggered by Timer2 overflow. The ISR updates the phase accumulator
PhaseAcc
with theTuningWord
value, which determines the phase of the output waveform. The current value of the phase accumulator is used as an index to look up the corresponding value from thesine256
array, and this value is written to the PWM output pin (pin 11) using theanalogWrite()
function, which generates a Pulse Width Modulated (PWM) signal with a duty cycle proportional to the value written. This generates a sine waveform on the PWM output pin with a frequency determined by theTuningWord
value.
In the above code Phase Acc is declared as volatile unsigned long. The "unsigned long" data type typically represents an unsigned integer that is at least 32 bits in size, depending on the platform and compiler used. On most modern systems, "unsigned long" is typically 32 bits or 64 bits in size, with 32 bits being the most common. The "volatile" keyword is used to indicate that the value of the variable may change unexpectedly, possibly due to external factors outside the control of the program, such as hardware events or interrupts. This informs the compiler that the variable should not be optimized or cached in registers, and that every read or write operation on the variable should directly access its memory location.
Implementation and Test
Shown below is picture of Arduino DDS build with the help of breadboard.
And the following shows the circuit diagram for testing the Arduino DDS. The circuit diagram shows a 10KOhm potentiometer connected to analog pin A0 which is used to control the frequency of the output sine wave signal. At the sine wave PWM output pin 11 two stage RC low pass filter are used to convert the PWM signal to sine wave. The cutoff frequency is 1.59 KHz which was computer used the low pass filter online calculator.Next, upload the Arduino sketch to your Arduino board and observe the
generated waveform on your output channel, such as an oscilloscope or an
amplifier. You can adjust the potentiometer to change frequency of the sine wave.
Following shows the signal waveform at the output of the filter and the frequency spectrum.
Video demonstration of Arduino DDS
The video demonstrate how the Arduino works as DDS to generate sine wave signal, and how to adjust the potentiometer to change the output frequency in real-time.
Conclusion
In this blog post, we have explored how Direct Digital Synthesis (DDS) can be implemented using an Arduino microcontroller. DDS provides a powerful and flexible method for generating precise waveforms with high resolution and frequency accuracy. With the availability of DDS libraries for Arduino, it is easy to implement DDS in DIY electronics projects for various applications, such as communications, signal processing, audio synthesis, and instrumentation. Experimenting with DDS using Arduino opens up a world of possibilities for creating custom waveforms and exploring different signal processing techniques. Happy experimenting with DDS and Arduino!
References and Further Readings
[1] Programming Arduino Mega in CTC mode
[2] Arduino 8MHz Variable Frequency Generator
[3] LM358 based Voltage Controlled Oscillator(VCO)