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
}
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.
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"?
I use ESP32 and alll waves exit good (oscilloscope side), but i want also a square modifiable with duty cycle that AD9833 cannot made, so i use also this sketch that work only on ESP32 and use LedC command with high speed mode so 19000 Hz with 12 bit resoltuion but you can made more Hz if you reduce resolution how show this table:
// BIT Hz With a LEDC_APB_CLK == 80MHz, these are the following maximum values, in Hz:
// 1 40000000
// 2 20000000
// 3 10000000
// 4 5000000
// 5 2500000
// 6 1250000
// 7 625000
// 8 312500
// 9 156250
// 10 78125
// 11 39062
// 12 19531
// 13 9765
// 14 4882
// 15 2441
// 16 1220
// 17 610
// 18 305
// 19 152
// 20 76
#include "stdio.h"
#include "driver/ledc.h"
#include "esp_err.h"
//#include "driver/pcnt.h" // Library ESP32 PCNT
//#include "soc/pcnt_struct.h"
int output_pin = 4;
float frequency = 10000; // frequency value
uint32_t resolution = 12; // Resolution value
uint32_t osc_freq = 0; // Oscillator frequency - 0-3
int percento = 25; //DUTY 5-95 %
//Risoluzione = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
//const float Duty50[16] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 };
uint32_t mDuty = 0; // Duty value
void init_osc_freq() {
ledc_timer_config_t ledc_timer = {}; // LEDC timer config instance
ledc_timer.duty_resolution = ledc_timer_bit_t(resolution); // Set resolution
ledc_timer.freq_hz = osc_freq; // Set Oscillator frequency
ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE; // Set high speed mode
ledc_timer.timer_num = LEDC_TIMER_0; // Set LEDC timer index - 0
ledc_timer_config(&ledc_timer); // Set LEDC Timer config
ledc_channel_config_t ledc_channel = {}; // LEDC Channel config instance
ledc_channel.channel = LEDC_CHANNEL_0; // Set HS Channel - 0
ledc_channel.duty = mDuty; // Set Duty Cycle 50%
ledc_channel.gpio_num = output_pin; // LEDC Oscillator output GPIO 33
ledc_channel.intr_type = LEDC_INTR_DISABLE; // LEDC Fade interrupt disable
ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE; // Set LEDC high speed mode
ledc_channel.timer_sel = LEDC_TIMER_0; // Set timer source of channel - 0
ledc_channel_config(&ledc_channel); // Config LEDC channel
}
void Delay(unsigned long timeToWait) {// delay no block CPU
unsigned long now = millis();
while ((millis() - now) < timeToWait)
;
}
void setup() {
Serial.begin(115200);
Delay(1000); // wait for serial
mDuty = (pow(2, resolution));
mDuty = mDuty * ((float)percento / 100.0);
init_osc_freq();
Delay(200);
ledcAttach(output_pin, frequency, resolution);
//ledcWrite(output_pin, mDuty);
}
void loop() {
for (int i = 5; i <= 95; i += 5) {
percento = i;
frequency=i*200;
ledc_set_freq(LEDC_HIGH_SPEED_MODE,LEDC_TIMER_0,frequency);
mDuty = (pow(2, resolution));
mDuty = mDuty * ((float)percento / 100.0);
//Serial.print("Duty ");
//Serial.println((String)percento + "% " + (String)mDuty);
ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, mDuty);
ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0);
Delay(3000);
}
}
// LEDC_CLKx PWM Frequency Highest Resolution (bit) 1 Lowest Resolution (bit) 2
// APB_CLK (80 MHz) 1 kHz 16 7
// APB_CLK (80 MHz) 5 kHz 13 4
// APB_CLK (80 MHz) 10 kHz 12 3
// RC_FAST_CLK (8 MHz) 1 kHz 12 3
// RC_FAST_CLK (8 MHz) 2 kHz 11 2
// REF_TICK (1 MHz) 1 kHz 9 1
// 1 The highest resolution is calculated when the clock divisor LEDC_CLK_DIVx is 1 and rounded down.
// If the highest resolution calculated by the formula is higher than the counter’s width 20 bits, then
// the highest resolution should be 20 bits.
// 2 The lowest resolution is calculated when the clock divisor LEDC_CLK_DIVx is 1023 + 255
// 256 and
// rounded up. If the lowest resolution calculated by the formula is lower than 0, then the lowest
// resolution should be 1.
// The low-speed timers l_timerx on the low-speed channel differ from the high-speed timers h_timerx in two
i am using your library and AD9833 for my project in oscilloscope im getting square wave perfrctly fine by using gen.setWave(AD9833_SQUARE1); but if i change it to sine and triangular wave form the waveform is distorted and only i get noise as output
and this code i wrote for a project using RobT's library as a foundation. It allows you via button control to switch between waveform type and adjust fq x multiples of 10 and store last setting. works great. Maybe this will help you...
#include <Button.h>
#include "AD9833.h"
#include <FastLED.h>
#include <EEPROM.h>
AD9833 AD(10, 11, 13); // SW SPI over the HW SPI pins (UNO);
// AD(10); // HW SPI
int WaveArray[5] = { AD9833_OFF, AD9833_SINE, AD9833_SQUARE1, AD9833_SQUARE2, AD9833_TRIANGLE };
int i = 0; // Index For The WaveTable Array
Button WaveArraySelectButton(2); // Connect your button between pin x and GND
Button FreqIncreaseButton10(3); // Connect your button between pin x and GND
Button FreqIncreaseButton100(4);
Button FreqIncreaseButton1000(5);
Button FreqDecreaseButton10(6); // Connect your button between pin x and GND
Button FreqDecreaseButton100(7);
Button FreqDecreaseButton1000(8);
#define LEDPIN 20
int LEDSTATE = LOW;
//EEPROM variables, change these every few thousand writes.....
#define addressfrequencyVal 0 //where to store freq value
//#define addressWaveformValInitial 10 //where to store waveform value
//float freqValInitial = 0;
int waveformValInitial = 3;
//unsigned long Freq = 0; // Hz
double Freq = 0;
//unsigned long Freq0 = 0;
void EEPROMREADSETUP() { //read once to udate variables from memory
Freq = EEPROM.read(addressfrequencyVal); // read a byte from 'x' address
AD.setFrequency(Freq, 0);
}
void writeEEPROM() {
EEPROM.write(addressfrequencyVal, Freq);
}
void clearEEPROM() {
EEPROM.write(addressfrequencyVal, 0); //write 0 to address 'x'
}
void setup() {
Serial.begin(115200);
SPI.begin();
AD.begin();
//delay(2000);
AD.setFrequencyChannel(0);
AD.setPhaseChannel(0);
WaveArraySelectButton.begin();
FreqIncreaseButton10.begin();
FreqIncreaseButton100.begin();
FreqIncreaseButton1000.begin();
FreqDecreaseButton10.begin();
FreqDecreaseButton100.begin();
FreqDecreaseButton1000.begin();
pinMode(LEDPIN, OUTPUT);
EEPROMREADSETUP();
AD.setWave(WaveArray[waveformValInitial]);
//Serial.println(AD.getWave());
Serial.print("WaveArray: ");
Serial.println(WaveArray[waveformValInitial]);
//AD.setFrequencyChannel(0); //default
//AD.setFrequency(Freq, 0);
Serial.print("Frequency: ");
Serial.println(Freq);
//Serial.println(AD.getFrequency(0));
delay(1000);
// Serial.println(analogRead(A3));
}
void loop() {
if (WaveArraySelectButton.toggled()) {
if (WaveArraySelectButton.read() == Button::PRESSED) {
Serial.println("WaveArraySelectButton has been pressed");
LEDSTATE = HIGH;
AD.setWave(WaveArray[i++]);
if (i >= 5) {
i = 0;
}
} else {
Serial.println("WaveArraySelectButton has been released");
LEDSTATE = LOW;
Serial.print("WaveArray: ");
Serial.println(WaveArray[i]);
}
}
if (FreqIncreaseButton10.toggled()) {
if (FreqIncreaseButton10.read() == Button::PRESSED) {
Serial.println("FreqIncreaseButton10 has been pressed");
LEDSTATE = HIGH;
Freq += 10;
AD.setFrequency(Freq, 0);
AD.setPhase(180, 0); //////////////////
clearEEPROM();
writeEEPROM();
} else {
Serial.println("FreqIncreaseButton10 has been released");
LEDSTATE = LOW;
// AD.setPhase( 45, 0);
Serial.print("Frequency: ");
Serial.println(AD.getFrequency(0));
}
}
if (FreqIncreaseButton100.toggled()) {
if (FreqIncreaseButton100.read() == Button::PRESSED) {
Serial.println("FreqIncreaseButton100 has been pressed");
LEDSTATE = HIGH;
Freq += 100;
AD.setFrequency(Freq, 0);
AD.setPhase(45, 0);
} else {
Serial.println("FreqIncreaseButton100 has been released");
LEDSTATE = LOW;
clearEEPROM();
writeEEPROM();
Serial.print("Frequency: ");
Serial.println(AD.getFrequency(0));
Serial.print("EEPROMFrequency: ");
Serial.println(EEPROM.read(addressfrequencyVal));
}
}
if (FreqIncreaseButton1000.toggled()) {
if (FreqIncreaseButton1000.read() == Button::PRESSED) {
Serial.println("FreqIncreaseButton1000 has been pressed");
LEDSTATE = HIGH;
Freq += 1000;
AD.setFrequency(Freq, 0);
} else {
Serial.println("FreqIncreaseButton1000 has been released");
LEDSTATE = LOW;
clearEEPROM();
writeEEPROM();
//Serial.print("Frequency: ");
Serial.println(AD.getFrequency(0));
//EEPROM.read(addressfrequencyVal);
Serial.print("EEPROMFrequency: ");
Serial.println(EEPROM.read(addressfrequencyVal));
}
}
if (FreqDecreaseButton10.toggled()) {
if (FreqDecreaseButton10.read() == Button::PRESSED) {
Serial.println("FreqDecreaseButton10 has been pressed");
LEDSTATE = HIGH;
if (Freq > 10) {
Freq -= 10;
}
AD.setFrequency(Freq, 0);
} else {
Serial.println("FreqDecreaseButton10 has been released");
LEDSTATE = LOW;
clearEEPROM();
writeEEPROM();
Serial.print("Frequency: ");
Serial.println(AD.getFrequency(0));
}
}
if (FreqDecreaseButton100.toggled()) {
if (FreqDecreaseButton100.read() == Button::PRESSED) {
Serial.println("FreqDecreaseButton100 has been pressed");
LEDSTATE = HIGH;
if (Freq > 100) {
Freq -= 100;
}
AD.setFrequency(Freq, 0);
} else {
Serial.println("FreqDecreaseButton100 has been released");
LEDSTATE = LOW;
clearEEPROM();
writeEEPROM();
Serial.print("Frequency: ");
Serial.println(AD.getFrequency(0));
}
}
if (FreqDecreaseButton1000.toggled()) {
if (FreqDecreaseButton1000.read() == Button::PRESSED) {
Serial.println("FreqDecreaseButton1000 has been pressed");
LEDSTATE = HIGH;
if (Freq > 1000) {
Freq -= 1000;
}
AD.setFrequency(Freq, 0);
} else {
Serial.println("FreqDecreaseButton1000 has been released");
LEDSTATE = LOW;
clearEEPROM();
writeEEPROM();
Serial.print("Frequency: ");
Serial.println(AD.getFrequency(0));
}
}
digitalWrite(LEDPIN, LEDSTATE);
}
// ======END========
and BTW, you can't change the duty cycle of AD9833 by code (AFAIK). Maybe you can get two of them and build a comparator circuit? I was able to control output wave amplitude by adjusting pwm to a transistor in an amplifier circuit. This amplifier was fed the AD9833 waveform output.
The square wave has an amplitud of 5V, while the sine's amplitud (or properly said, the DAC's amplitud) is only 0.6V.
The signal is there, but your oscilloscope scale might be too high to see it as you expect (for example, you've set it to 5 V/Div; lower it to 1 V/Div).
I came here because of the same "mistake", but adjusting the oscilloscope's scale showed me the sine signal.