Snippets

Peter Scargill AD9833 Signal Generator

Created by Peter Scargill last modified
// Simple, low cost tone generator based on the AD9833 module. 4(6)-button control
// Memory 1        (long press set)
// Memory 2        (long press set)
// Frequency Down  (long press tone on/off)
// Frequency Up    (long press frequency reset to 1Khz)
// Range Change    1hz 10hz 100hz 1k, 10k, 100k (long press reset to 1k)
// Output Type     SIN TRIANGLE SQUARE

#include <SPI.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiAvrI2c.h"
#include "OneButton.h"
#include <EEPROM.h>

// 0X3C+SA0 - 0x3C or 0x3D
#define I2C_ADDRESS 0x3C
SSD1306AsciiAvrI2c oled;

// spi code for the AD9833 from here  http://www.vwlowen.co.uk/arduino/AD9833-waveform-generator/AD9833-waveform-generator.htm

#define SINE 0x2000                    // Define AD9833's waveform register value.
#define SQUARE 0x2028                  // When we update the frequency, we need to
#define TRIANGLE 0x2002                // define the waveform as well.
#define REF__FREQ 25000000.0           // On-board crystal reference frequency

#define BEEPER 2

#define M1_PIN 3
#define M2_PIN 4
#define FREQ_DOWN_PIN 5
#define FREQ_UP_PIN 6     // long press=reset to 1000hz
#define RANGE_PIN 7     // long press=reset to 1khz range
#define WAVE_PIN 8
#define FSYNC 10

#define UPDATE_DELAY 3000 // 3 seconds to any storage of changes

uint32_t myMillis;
uint8_t updateValues=0;

struct
{
  uint16_t check;
  uint8_t beepOn;
  uint8_t waveType;
  unsigned long freq;
  unsigned long range;
  uint8_t m1_waveType;
  unsigned long m1_freq;
  uint8_t m2_waveType;
  unsigned long m2_freq;
} eeSave;

OneButton m1Button(M1_PIN, true);
OneButton m2Button(M2_PIN, true);
OneButton upButton(FREQ_UP_PIN, true);
OneButton rangeButton(RANGE_PIN, true);
OneButton downButton(FREQ_DOWN_PIN, true);
OneButton waveButton(WAVE_PIN, true);

void armUpdate()
{
  myMillis=millis()+UPDATE_DELAY;
  updateValues=1;
}

void processUpdate()
{
  if ((updateValues==1) && (myMillis<millis()))
  {
    updateValues=0;
    EEPROM.put(0, eeSave);
  }
}

void setup(void)
{
  EEPROM.get(0, eeSave);
  if (eeSave.check != 0x52c3)
  {
    eeSave.check = 0x52c3;
    eeSave.beepOn = 1;
    eeSave.waveType = SINE;
    eeSave.freq = 1000L;
    eeSave.range = 1000L;
    eeSave.m1_waveType = SINE;
    eeSave.m1_freq = 1000L;
    eeSave.m2_waveType = SINE;
    eeSave.m2_freq = 1000L;
    EEPROM.put(0, eeSave);
  }

  oled.begin(&Adafruit128x32, I2C_ADDRESS);
  oled.setFont(Adafruit5x7);
  delay(100);
  oled.clear();
  oled.print("Signal Generator 1.0");

  SPI.begin();
  SPI.setDataMode(SPI_MODE2);
  //delay(50);
  AD9833reset();
  pinMode(FREQ_UP_PIN, INPUT_PULLUP);      // Set pins for rotary encoders as INPUTS and enable
  pinMode(FREQ_DOWN_PIN, INPUT_PULLUP);    // internal pullup resistors.
  pinMode(RANGE_PIN, INPUT_PULLUP);
  pinMode(WAVE_PIN, INPUT_PULLUP);
  AD9833setFrequency(eeSave.freq, eeSave.waveType);

  m1Button.attachClick(useM1);
  m1Button.attachLongPressStart(setM1);
  m2Button.attachClick(useM2);
  m2Button.attachLongPressStart(setM2);
  upButton.attachClick(frequencyUp);
  upButton.attachLongPressStart(frequencyReset);
  downButton.attachClick(frequencyDown);
  downButton.attachLongPressStart(beepToggle);
  waveButton.attachClick(waveToggle);
  rangeButton.attachClick(rangeSet);
  rangeButton.attachLongPressStart(rangeReset);
  if (eeSave.beepOn) {
    tone(BEEPER, 4400, 100);
    delay(160);
    tone(BEEPER, 4600, 400);
  }
}

void beepToggle()
{
  if (eeSave.beepOn)
  {
    eeSave.beepOn = 0;
    tone(BEEPER, 4000, 80);
  }
  else
  {
    eeSave.beepOn = 1;
    tone(BEEPER, 5000, 80);
  }
  armUpdate();
}
void waveToggle()
{
  switch (eeSave.waveType)
  {
    case SINE: eeSave.waveType = SQUARE; break;
    case SQUARE: eeSave.waveType = TRIANGLE; break;
    case TRIANGLE: eeSave.waveType = SINE; break;
    default: eeSave.waveType = SINE; break;
  }
  AD9833setFrequency(eeSave.freq, eeSave.waveType);
  if (eeSave.beepOn) tone(BEEPER, 5000, 80);
  armUpdate();
}

void useM1()
{
    eeSave.freq=eeSave.m1_freq;
    eeSave.waveType=eeSave.m1_waveType;
    AD9833setFrequency(eeSave.freq, eeSave.waveType);
    if (eeSave.beepOn) tone(BEEPER, 5000, 80);
    armUpdate();
}

void setM1()
{
    eeSave.m1_freq=eeSave.freq;
    eeSave.m1_waveType=eeSave.waveType;
    AD9833setFrequency(eeSave.freq, eeSave.waveType);
    if (eeSave.beepOn) tone(BEEPER, 4000, 80);
    armUpdate();
}

void useM2()
{
    eeSave.freq=eeSave.m2_freq;
    eeSave.waveType=eeSave.m2_waveType;
    AD9833setFrequency(eeSave.freq, eeSave.waveType);
    if (eeSave.beepOn) tone(BEEPER, 5000, 80);
    armUpdate();
}

void setM2()
{
    eeSave.m2_freq=eeSave.freq;
    eeSave.m2_waveType=eeSave.waveType;
    AD9833setFrequency(eeSave.freq, eeSave.waveType);
    if (eeSave.beepOn) tone(BEEPER, 4000, 80);
    armUpdate();
}

void frequencyUp()
{
  if (eeSave.freq < 4000000L) eeSave.freq += eeSave.range; else eeSave.freq = 4000000L;
  AD9833setFrequency(eeSave.freq, eeSave.waveType);
  if (eeSave.beepOn) tone(BEEPER, 5000, 80);
  armUpdate();
}

void frequencyDown()
{
  if (eeSave.freq >= eeSave.range) eeSave.freq -= eeSave.range; else eeSave.freq = 1L;
  AD9833setFrequency(eeSave.freq, eeSave.waveType);
  if (eeSave.beepOn) tone(BEEPER, 5000, 80);
  armUpdate();
}

void rangeReset()
{
  eeSave.range = 1000L;
  AD9833setFrequency(eeSave.freq, eeSave.waveType);
  if (eeSave.beepOn) tone(BEEPER, 4500, 80);
  armUpdate();
}

void rangeSet()
{
  switch (eeSave.range)
  {
    case 1: eeSave.range = 10L; break;
    case 10: eeSave.range = 100L; break;
    case 100: eeSave.range = 1000L; break;
    case 1000: eeSave.range = 10000L; break;
    case 10000: eeSave.range = 100000L; break;
    case 100000: eeSave.range = 1L; break;
    default: eeSave.range = 1000L; break;
  }
  AD9833setFrequency(eeSave.freq, eeSave.waveType);
  if (eeSave.beepOn) tone(BEEPER, 5000, 80);
  armUpdate();
}


void frequencyReset()
{
  eeSave.freq = 1000L;
  AD9833setFrequency(eeSave.freq, eeSave.waveType);
  if (eeSave.beepOn) tone(BEEPER, 4400, 600);
  armUpdate();
}


void loop() {
  // put your main code here, to run repeatedly:
  m1Button.tick();
  m2Button.tick();
  upButton.tick();
  downButton.tick();
  waveButton.tick();
  rangeButton.tick();
  processUpdate();
}

// AD9833 documentation advises a 'Reset' on first applying power.
void AD9833reset() {
  WriteRegister(0x100);   // Write '1' to AD9833 Control register bit D8.
  delay(10);
}

// Set the frequency and waveform registers in the AD9833.
void AD9833setFrequency(long frequency, int Waveform) {

  long FreqWord = (frequency * pow(2, 28)) / REF__FREQ;

  int MSB = (int)((FreqWord & 0xFFFC000) >> 14);    //Only lower 14 bits are used for data
  int LSB = (int)(FreqWord & 0x3FFF);

  //Set control bits 15 ande 14 to 0 and 1, respectively, for frequency register 0
  LSB |= 0x4000;
  MSB |= 0x4000;

  WriteRegister(0x2100);
  WriteRegister(LSB);                  // Write lower 16 bits to AD9833 registers
  WriteRegister(MSB);                  // Write upper 16 bits to AD9833 registers.
  WriteRegister(0xC000);               // Phase register
  WriteRegister(Waveform);             // Exit & Reset to SINE, SQUARE or TRIANGLE

  oled.setCursor(0, 2);
  char txt[40];
  sprintf(txt, "Freq: %d,%03d,%03dHz", uint16_t(eeSave.freq / 1000000), uint16_t((eeSave.freq % 1000000) / 1000), uint16_t(eeSave.freq % 1000));
  oled.print(txt);
  oled.clearToEOL();
  oled.setCursor(0, 3); oled.print("Range: ");
  switch (eeSave.range)
  {
    case 1: oled.print("1Hz"); break;
    case 10: oled.print("10Hz"); break;
    case 100: oled.print("100Hz"); break;
    case 1000: oled.print("1KHz"); break;
    case 10000: oled.print("10KHz"); break;
    case 100000: oled.print("100KHz"); break;
  }
  oled.clearToEOL();
  oled.setCursor(108, 2);
  switch (eeSave.waveType)
  {
    case SINE: oled.print("SIN"); break;
    case TRIANGLE: oled.print("TRI"); break;
    case SQUARE: oled.print("SQR"); break;
  }
  oled.clearToEOL();

}


void WriteRegister(int dat) {
  digitalWrite(FSYNC, LOW);           // Set FSYNC low before writing to AD9833 registers
  delayMicroseconds(10);              // Give AD9833 time to get ready to receive data.
  SPI.transfer(highByte(dat));        // Each AD9833 register is 32 bits wide and each 16
  SPI.transfer(lowByte(dat));         // bits has to be transferred as 2 x 8-bit bytes.
  digitalWrite(FSYNC, HIGH);          //Write done. Set FSYNC high
}

Comments (1)

  1. Peter Scargill

    Happy new year to everyone except the idiot who keeps spamming my snippets… “Linda Melson” - If I can trace you - you’ll regret the spam for the rest of your life.

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.