AD9833 function generator

I am facing issue in my project of function generator which can generate sine, square and triangular wave using AD9833 module and Arduino uno.
Code is works properly and it generates square wave but its not generating sine and triangular wave form.

Here i am using this libraries:

Here is the code:


#include <AD9833.h>               // Library for AD9833 Module   
#include <Wire.h>                 // Wire Library for OLED
#include <Adafruit_GFX.h>         // Support Library for OLED
#include <Adafruit_SSD1306.h>     // OLED library
#include <math.h>                 // Math Library

#define SCREEN_WIDTH 128           // OLED display Width, in pixels
#define SCREEN_HEIGHT 64           // OLED display height, in pixels

#define SET_FREQUENCY_HZ 5         // Pushbutton To Set Frequency In Hz (D5)
#define SET_FREQUENCY_KHZ 6        // Pushbutton To Set Frequency In KHz (D6)
#define SET_FREQUENCY_MHZ 7        // Pushbutton To Set Frequency In MHz (D7)

#define SCL_PIN A5
#define SDA_PIN A4

#define FNC_PIN 10                 // Fsync Required by the AD9833 Module
#define CLK_PIN 2                  // Clock Pin of the Encoder
#define DATA_PIN 3                 // Data Pin of the Encoder
#define BTN_PIN 4                  // Internal Push Button on the Encoder

int counter = 1;                    // Counter value for the rotary encoder
int clockPin;                       // Current state of CLK_PIN
int clockPinState;                  // Previous state of CLK_PIN

unsigned long timeDebounce = 0;     // Used for debouncing
unsigned long moduleFrequency = 0;  // Frequency to set output

const long debounce = 220;          // Debounce delay in milliseconds

bool set_frequency_hz = true;       // Default frequency unit
bool set_frequency_khz = false;
bool set_frequency_mhz = false;

String waveSelect = "SIN";          // Startup waveform of the module

int encoder_btn_count = 0;          // Encoder button press count

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
AD9833 gen(FNC_PIN);

void setup() {
  // Initialize Serial Communication
  Serial.begin(9600);
  Serial.println(F("=== AD9833 Function Generator Initialized ==="));
  
  // Initialize AD9833 module
  gen.Begin();
  Serial.println(F("AD9833 module initialized."));

  // Configure Encoder Pins
  pinMode(CLK_PIN, INPUT);
  pinMode(DATA_PIN, INPUT);
  pinMode(BTN_PIN, INPUT_PULLUP);
  Serial.println(F("Encoder pins configured."));

  // Read initial state of CLK_PIN
  clockPinState = digitalRead(CLK_PIN);
  Serial.print(F("Initial CLK_PIN state: "));
  Serial.println(clockPinState);

  // Configure Frequency Selection Pushbuttons
  pinMode(SET_FREQUENCY_HZ, INPUT);
  pinMode(SET_FREQUENCY_KHZ, INPUT);
  pinMode(SET_FREQUENCY_MHZ, INPUT);
  Serial.println(F("Frequency selection pushbuttons configured."));

  // Initialize OLED Display
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x64
    Serial.println(F("Error: SSD1306 allocation failed."));
    for (;;); // Loop forever if display initialization fails
  }
  Serial.println(F("OLED display initialized successfully."));

  // Display Startup Message
  display.clearDisplay();               // Clear the Screen
  display.setTextSize(2);              // Set text size
  display.setTextColor(WHITE);         // Set text color
  
  display.setCursor(30, 0);            // Set cursor position
  display.println("AD9833");
  
  display.setCursor(17, 20);           // Set cursor position
  display.println("Function");
  
  display.setCursor(13, 40);           // Set cursor position
  display.println("Generator");
  
  display.display();                    // Update the Display
  Serial.println(F("Startup message displayed on OLED."));
  delay(2000);                          // Delay of 2 seconds

  // Enable output by default
  gen.EnableOutput(true);
  Serial.println(F("AD9833 output enabled by default."));

  update_display();                     // Call update_display Function
}

void loop() {
  // Handle rotary encoder rotation
  clockPin = digitalRead(CLK_PIN);
  
  if (clockPin != clockPinState && clockPin == HIGH) {
    if (digitalRead(DATA_PIN) != clockPin) {
      counter--;
      Serial.print(F("Encoder rotated CCW. Counter decremented to: "));
    } else {
      counter++;
      Serial.print(F("Encoder rotated CW. Counter incremented to: "));
    }
    
    // Ensure counter does not go below 1
    if (counter < 1) {
      counter = 1;
      Serial.println(counter);
      Serial.println(F("Counter cannot go below 1."));
    }
    
    // Update display based on counter value
    update_display();
  }
  
  clockPinState = clockPin; // Remember last CLK_PIN state

  // Handle encoder button press to change waveform
  if (digitalRead(BTN_PIN) == LOW && millis() - timeDebounce > debounce) {
    encoder_btn_count++; // Increment the count
    Serial.print(F("Encoder button pressed. Waveform count: "));
    Serial.println(encoder_btn_count);
    
    if (encoder_btn_count > 2) { // Reset count if greater than 2
      encoder_btn_count = 0;
      Serial.println(F("Waveform count reset to 0."));
    }
    
    switch (encoder_btn_count) {
      case 0:
        waveSelect = "SIN";
        Serial.println(F("Waveform selected: SIN"));
        break;
      case 1:
        waveSelect = "SQR";
        Serial.println(F("Waveform selected: SQR"));
        break;
      case 2:
        waveSelect = "TRI";
        Serial.println(F("Waveform selected: TRI"));
        break;
    }
    
    update_display(); // Update the display
    timeDebounce = millis(); // Update debounce time
  }
  
  // Handle frequency unit selection via pushbuttons
  if (digitalRead(SET_FREQUENCY_HZ) == LOW && millis() - timeDebounce > debounce) {
    set_frequency_hz = true;
    set_frequency_khz = false;
    set_frequency_mhz = false;
    Serial.println(F("Frequency unit set to Hz."));
    moduleFrequency = (unsigned long)counter; // Recalculate moduleFrequency
    update_display();
    timeDebounce = millis();
  }
  
  if (digitalRead(SET_FREQUENCY_KHZ) == LOW && millis() - timeDebounce > debounce) {
    set_frequency_hz = false;
    set_frequency_khz = true;
    set_frequency_mhz = false;
    
    // Calculate frequency in Hz
    moduleFrequency = (unsigned long)counter * 1000; // Cast to unsigned long
    Serial.print(F("Frequency unit set to KHz. Module frequency: "));
    Serial.println(moduleFrequency);
    update_display();
    timeDebounce = millis();
  }
  
  if (digitalRead(SET_FREQUENCY_MHZ) == LOW && millis() - timeDebounce > debounce) {
    set_frequency_hz = false;
    set_frequency_khz = false;
    set_frequency_mhz = true;

    // Calculate frequency in Hz and ensure it's within limits
    moduleFrequency = (unsigned long)counter * 1000000; // Cast to unsigned long
    if (moduleFrequency > 12000000) {
      moduleFrequency = 12000000; // Cap frequency at 12 MHz
      counter = 12; // Set counter to a value that matches the capped frequency
      Serial.println(F("Frequency capped at 12 MHz."));
    }
    
    Serial.print(F("Frequency unit set to MHz. Module frequency: "));
    Serial.println(moduleFrequency);
    update_display();
    timeDebounce = millis();
  }
  
  // Add a small delay to improve stability
  delay(10);
}

void update_display() {
  display.clearDisplay(); // Clear the display
  display.setTextSize(1); // Set text size
  display.setCursor(10, 0); // Set cursor position
  display.println("Function Generator");
  
  display.setTextSize(2); // Set text size
  display.setCursor(0, 20); // Set cursor position
  
  // Update moduleFrequency based on selected frequency unit
  if (set_frequency_hz) {
    moduleFrequency = (unsigned long)counter; // Set frequency in Hz
  } else if (set_frequency_khz) {
    moduleFrequency = (unsigned long)counter * 1000; // Set frequency in KHz
  } else if (set_frequency_mhz) {
    moduleFrequency = (unsigned long)counter * 1000000; // Set frequency in MHz
  }

  // Display the selected waveform and apply it
  if (waveSelect == "SIN") {
    display.println("SIN");
    gen.ApplySignal(SINE_WAVE, REG0, moduleFrequency);
    Serial.print(F("Applied SINE_WAVE at "));
    Serial.print(moduleFrequency);
    Serial.println(F(" Hz."));
  } else if (waveSelect == "SQR") {
    display.println("SQR");
    gen.ApplySignal(SQUARE_WAVE, REG0, moduleFrequency);
    Serial.print(F("Applied SQUARE_WAVE at "));
    Serial.print(moduleFrequency);
    Serial.println(F(" Hz."));
  } else if (waveSelect == "TRI") {
    display.println("TRI");
    gen.ApplySignal(TRIANGLE_WAVE, REG0, moduleFrequency);
    Serial.print(F("Applied TRIANGLE_WAVE at "));
    Serial.print(moduleFrequency);
    Serial.println(F(" Hz."));
  }
  
  // Display the counter value
  display.setCursor(45, 20);
  display.println(counter);
  
  // Display the frequency unit
  display.setCursor(90, 20);
  if (set_frequency_hz) {
    display.println("Hz");
    Serial.println(F("Current frequency unit: Hz."));
  } else if (set_frequency_khz) {
    display.println("KHz");
    Serial.println(F("Current frequency unit: KHz."));
  } else if (set_frequency_mhz) {
    display.println("MHz");
    Serial.println(F("Current frequency unit: MHz."));
  }
  
  display.display(); // Update the display
}

Here is the link of circuit testing video which gives idea
https://drive.google.com/file/d/1R3gvzJiKtvYJPjrsShcqQZHU0daT0JZn/view?usp=drive_link

Please describe what you expected to happen, what happened instead, and what you have done to debug the problem.

Note that on the Uno, the display buffer takes up half of RAM, so you may be having memory problems.

To save RAM, use the F macro on these .print statements as well:

  display.println("Function Generator");

Problem is the circuit not generates the sine and triangular wave form but its generates square waveform

Do you see the correct output on the serial monitor for the lines with blue arrows?

What does it do instead?

Yes serial monitor prints the output but but waveform not displaying on oscilloscope

Do you know whether you are communicating with the AD9833 okay? For example, can you change the frequency of the square wave?

yes it works and change the frequency also

I've never used an AD9833. You say the communication to it works okay for changing the frequency, but not for changing the waveform type. It looks like gen.ApplySignal() sends both the waveform type and the frequency, but only the frequency parameter has any effect and the waveform type is ignored.
Hopefully somone else will be able to help you with that.

1 Like

Can you try my library?
If that library fails too, the problem might be in the hardware.

Please note that there exists at least more devices in the AD98xx family e.g. AD9832 - AD9837 which might be partially compatible. Never tested those others, these might support only a subset (or not at all).

Can you please verify you do have a AD9833 and not one of the "cousins"?

also have a look at MD_AD9833 library
used it to generate signals for a number of projects - see creating-a-modulated-signal-using-timers-with-a-variable-duty-cycle