Project tutorial
Signal Generator with Arduino Using DDS and Pico

Signal Generator with Arduino Using DDS and Pico © GPL3+

We'll create a signal generator using DDS techniques and evaluate its performance using a picoMeter, the world's smallest wireless DSO.

  • 10,078 views
  • 1 comment
  • 10 respects

Components and supplies

Apps and online services

About this project

Overview

The purpose of this project is to demonstrate the power of signal generation using a PWM signal and the DDS (Digital Direct Synthesis) method. We will validate the algorithm with a picoMeter, a wireless DSO for your keychain.

If you are more interested in the DDS you can read more about it on Wikipedia or any other website. There's plenty of info out there.

To experiment with signal generation we used Martin Nawrath's code and we modified it in order to generate dual tone signals. We'll be generating a signal that results from the addition of 2 sinusoid waves of different frequency. The resulting signal is very interesting in shape and looks something like this:

The signal looks pretty much like a DTMF signal, the ones used by the land line phones to dial a number. So DTMF generation is one of many direct application of this code.

The mathematical equation of such signal is:

Wiring

The PWM output from the Arduino needs to be low passed filtered in order to get rid of the high frequency components coming from the PWM fast transitions. The schematics for the low pass filter is very simple and its cut frequency was set to 2.1kHz:

Now, more important stuff, how do we play with this? The code reads two input pins in order to control the frequency of each of the sine waves. Those pins are the analog 0 and analog 1. We'll need two potentiometers (10K or 100K pots will both do the job) and connect them like this:

By turning the pots you can control the f1 and f2 variables of Equation 1.

Programming

The code for the Arduino 1 you can find it on the code section.

To extend the work on this area we could always add two different signal types like a sinusoid with a square signal, or a square signal with a triangular signal, all kids of different stuff. The outcome could be anything, and we could create any type of weird signals.

If you want to change that you just need to create your custom signal table like the one on sine256[] and run that with the phase accumulator.

The key line of code where you shape the signal you like is line 127, where the two sign waves are added.

OCR2A=(pgm_read_byte_near(sine256 + icntlow)+pgm_read_byte_near(sine256 + icnthigh))>>1;    

I invite you to play with this code and track your signals with picoMeter.

Cheers and good Hacks.

Code

Arduino Code for dual tone DDSArduino
/*
 *
 * DDS Sine Generator mit ATMEGS 168
 * Timer2 generates the  31250 KHz Clock Interrupt
 *
 * KHM 2009 /  Martin Nawrath
 * Kunsthochschule fuer Medien Koeln
 * Academy of Media Arts Cologne

 */

#include "avr/pgmspace.h"

// table of 256 sine values / one sine period / stored in flash memory
PROGMEM  prog_uchar 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))

int ledPin = 13;                 // LED pin 7
int testPin = 7;
int t2Pin = 6;
byte bb;

double dfreqlow;
double dfreqhigh;
// const double refclk=31372.549;  // =16MHz / 510
const double refclk=31376.6;      // measured

// variables used inside interrupt service declared as voilatile

volatile byte icnt1;             // var inside interrupt
volatile byte c4ms;              // counter incremented all 4ms
volatile byte icntlow;              // var inside interrupt
volatile unsigned long phacculow;   // pahse accumulator
volatile unsigned long tword_mlow;  // dds tuning word m

volatile byte icnthigh;              // var inside interrupt
volatile unsigned long phaccuhigh;   // pahse accumulator
volatile unsigned long tword_mhigh;  // dds tuning word m

void setup()
{
  pinMode(ledPin, OUTPUT);      // sets the digital pin as output
  Serial.begin(115200);        // connect to the serial port
  Serial.println("DDS Test");

  pinMode(6, OUTPUT);      // sets the digital pin as output
  pinMode(7, OUTPUT);      // sets the digital pin as output
  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

  dfreqlow=1000.0;                    // initial output frequency = 1000.o Hz
  dfreqhigh=1100.0;                    // initial output frequency = 1000.o Hz
  tword_mlow=pow(2,32)*dfreqlow/refclk;  // calulate DDS new tuning word 
  tword_mhigh=pow(2,32)*dfreqhigh/refclk;  // calulate DDS new tuning word 

}

void loop()
{
  while(1) {

     if (c4ms > 250) {                 // timer / wait fou a full second
      c4ms=0;
      dfreqlow=analogRead(0);             // read Poti on analog pin 0 to adjust output frequency from 0..1023 Hz
      dfreqhigh=analogRead(1);

      cbi (TIMSK2,TOIE2);              // disble Timer2 Interrupt
      tword_mlow=pow(2,32)*dfreqlow/refclk;  // calulate DDS new tuning word
      tword_mhigh=pow(2,32)*dfreqhigh/refclk;  // calulate DDS new tuning word
      sbi (TIMSK2,TOIE2);              // enable Timer2 Interrupt 

      Serial.print(dfreqlow);
      Serial.print("  ");

    }

   sbi(PORTD,6); // Test / set PORTD,7 high to observe timing with a scope
   cbi(PORTD,6); // Test /reset PORTD,7 high to observe timing with a scope
  }
 }
//******************************************************************
// timer2 setup
// set prscaler to 1, PWM mode to 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 (REFCLK)) / (2 exp 32)
// runtime : 8 microseconds ( inclusive push and pop)
ISR(TIMER2_OVF_vect) {

  sbi(PORTD,7);          // Test / set PORTD,7 high to observe timing with a oscope

  phacculow=phacculow+tword_mlow; // soft DDS, phase accu with 32 bits
  icntlow=phacculow >> 24;     // use upper 8 bits for phase accu as frequency information
                         // read value fron ROM sine table and send to PWM DAC
                         
  phaccuhigh=phaccuhigh+tword_mhigh; // soft DDS, phase accu with 32 bits
  icnthigh=phaccuhigh >> 24;     // use upper 8 bits for phase accu as frequency information
                         // read value fron ROM sine table and send to PWM DAC                       
  OCR2A=(pgm_read_byte_near(sine256 + icntlow)+pgm_read_byte_near(sine256 + icnthigh))>>1;    

  if(icnt1++ == 125) {  // increment variable c4ms all 4 milliseconds
    c4ms++;
    icnt1=0;
   }   

 cbi(PORTD,7);            // reset PORTD,7
}

Schematics

Output filter for the Arduino 1.
Screen shot 2017 02 20 at 11 14 25 am qtowlsqtcq
Input potentiometers for frequency selection.
Screen shot 2017 02 20 at 11 29 15 am hsb9sxvmr4

Comments

Similar projects you might like

Arduino based DDS Signal generator using AD9851

Project tutorial by Umar Sear

  • 22,144 views
  • 6 comments
  • 16 respects

A Tone Generator With LCD Display!

Project tutorial by cronenborg

  • 3,227 views
  • 0 comments
  • 8 respects

Arduino Due Arbitrary Waveform Generator

Project tutorial by BruceEvans

  • 7,275 views
  • 35 comments
  • 20 respects

LCD Thermometer With LEDs And Alarm Signal

Project showcase by GioMac

  • 4,872 views
  • 5 comments
  • 25 respects

The Magnetic Field and RGB Tester

Project tutorial by Kutluhan Aktar

  • 3,642 views
  • 0 comments
  • 14 respects

5vCircuitPowerMeter

Project tutorial by MicroBob

  • 1,695 views
  • 2 comments
  • 6 respects
Add projectSign up / Login