Zbotic Logo Zbotic Logo
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

  • Shop
  • About Us
  • Contact Us
  • Reseller
  • Blogs
020 69134444
1800 209 0998
[email protected]
Help Desk
Facebook Twitter Instagram Linkedin YouTube
Zbotic Logo Zbotic Logo
0 0

View Wishlist Add all to cart

0 0
0 Shopping Cart
Shopping cart (0)
Subtotal: ₹0.00

View cartCheckout

All departments
  • 3D Print Service
  • 3D Printer
  • Batteries & Chargers
  • Development Boards
  • Drone Parts
  • EBike parts
  • Sensor Modules
  • Electronic Components
  • Electronic Modules
  • IoT and Wireless
  • Mechanical Parts and Workbench Tools
  • Motors & Drivers & Pumps & Actuators
  • DIY and Robot Kits
  • Show more
  • Home
  • Shop
  • Sale
  • 3D Print Service
  • PCB Service
  • B2B
  • Blogs
  • Contact Us
Return to previous page
Home Arduino & Microcontrollers

Arduino Waveform Generator: Build a Function Generator with DDS

Arduino Waveform Generator: Build a Function Generator with DDS

March 11, 2026 /Posted byJayesh Jain / 0

An Arduino function generator using DDS (Direct Digital Synthesis) is one of the most rewarding electronics projects you can build — it produces sine, square, triangle, and sawtooth waveforms that you can use for testing circuits, driving speakers, sweeping filters, and dozens of other lab tasks. Unlike commercial bench function generators that cost thousands of rupees, an Arduino-based DDS waveform generator can be built for under ₹500 using parts you likely already own. In this guide, we’ll walk through two approaches — a pure Arduino DDS solution and an AD9833 module build — with full code and wiring diagrams.

Table of Contents

  • How Direct Digital Synthesis Works
  • Pure Arduino DDS: PWM + RC Filter Method
  • Using the AD9833 DDS Module with Arduino
  • Wiring and Circuit Diagrams
  • Arduino Code Walkthrough
  • Adding a Display and Frequency Control
  • Applications and Use Cases
  • Frequently Asked Questions

How Direct Digital Synthesis Works

Direct Digital Synthesis is a technique for generating precise waveforms by stepping through a pre-computed lookup table stored in memory. The core idea is simple: a phase accumulator increments by a tuning word on every clock cycle. The upper bits of the accumulator address a sine lookup table, and the output value feeds a DAC (Digital-to-Analog Converter).

The output frequency is determined by:

f_out = (tuning_word × f_clock) / 2^N

Where N is the number of bits in the phase accumulator (commonly 32). This formula means you can achieve very fine frequency resolution — often sub-Hz — with a fast enough system clock.

On an Arduino Uno (ATmega328P at 16 MHz), a pure software DDS implementation can generate sine waves up to about 20–30 kHz before the stepped waveform becomes too distorted. For higher frequencies up to 12.5 MHz, a dedicated DDS chip like the AD9833 is needed.

Recommended: Arduino Uno R3 Beginners Kit — The ATmega328P on this board provides the 16 MHz clock and PWM hardware needed for a software DDS waveform generator, plus all the jumper wires and breadboard to prototype quickly.

Pure Arduino DDS: PWM + RC Filter Method

The ATmega328P doesn’t have a true DAC, but you can simulate one using Fast PWM mode at a high frequency combined with an RC low-pass filter. This is the most accessible approach requiring zero additional ICs.

Timer1 Setup for 62.5 kHz PWM

Configure Timer1 in Fast PWM mode with no prescaler (16 MHz / 256 = 62.5 kHz PWM frequency). This is fast enough to reconstruct waveforms up to about 20 kHz with a proper filter.

// DDS Sine Wave Generator – ATmega328P
// Output: Pin 9 (OC1A)

#include <avr/pgmspace.h>
#include <avr/interrupt.h>

// 256-point sine table (8-bit, 0-255)
const uint8_t sine_table[256] PROGMEM = {
  128,131,134,137,140,143,146,149,152,155,158,162,165,167,170,173,
  176,179,182,185,188,190,193,196,198,201,203,206,208,211,213,215,
  218,220,222,224,226,228,230,232,234,235,237,238,240,241,243,244,
  245,246,247,248,249,250,251,252,252,253,253,254,254,254,255,255,
  255,255,255,254,254,254,253,253,252,252,251,250,249,248,247,246,
  245,244,243,241,240,238,237,235,234,232,230,228,226,224,222,220,
  218,215,213,211,208,206,203,201,198,196,193,190,188,185,182,179,
  176,173,170,167,165,162,158,155,152,149,146,143,140,137,134,131,
  128,124,121,118,115,112,109,106,103,100, 97, 93, 90, 88, 85, 82,
   79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40,
   37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11,
   10,  9,  8,  7,  6,  5,  4,  3,  3,  2,  2,  1,  1,  1,  0,  0,
    0,  0,  0,  1,  1,  1,  2,  2,  3,  3,  4,  5,  6,  7,  8,  9,
   10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35,
   37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76,
   79, 82, 85, 88, 90, 93, 97,100,103,106,109,112,115,118,121,124
};

volatile uint32_t phase_accumulator = 0;
volatile uint32_t tuning_word = 0;
volatile uint8_t wave_type = 0; // 0=sine, 1=square, 2=triangle

void setup() {
  pinMode(9, OUTPUT);
  // Fast PWM, 8-bit, no prescaler → 62.5 kHz
  TCCR1A = _BV(COM1A1) | _BV(WGM10);
  TCCR1B = _BV(WGM12) | _BV(CS10);
  // Timer2 for DDS interrupt
  TCCR2A = _BV(WGM21);              // CTC mode
  TCCR2B = _BV(CS20);               // No prescaler
  OCR2A  = 63;                       // 16MHz/64 = 250kHz sample rate
  TIMSK2 = _BV(OCIE2A);
  sei();
  setFrequency(1000); // Start at 1 kHz
}

void setFrequency(uint32_t freq_hz) {
  // sample_rate = 250000 Hz, accumulator = 32-bit
  tuning_word = (uint32_t)((float)freq_hz * 4294967296.0 / 250000.0);
}

ISR(TIMER2_COMPA_vect) {
  phase_accumulator += tuning_word;
  uint8_t index = phase_accumulator >> 24; // Top 8 bits
  uint8_t val;
  switch(wave_type) {
    case 0: val = pgm_read_byte(&sine_table[index]); break;
    case 1: val = (index < 128) ? 255 : 0; break; // Square
    case 2: val = (index < 128) ? (index*2) : (255-(index-128)*2); break; // Triangle
    default: val = index; break; // Sawtooth
  }
  OCR1AL = val;
}

void loop() {
  // Encoder or serial control goes here
}

The RC Filter

Connect a two-pole RC low-pass filter on pin 9 to smooth the PWM into a clean sine wave:

  • R1 = 1 kΩ from Pin 9 to node A
  • C1 = 10 nF from node A to GND
  • R2 = 1 kΩ from node A to output
  • C2 = 10 nF from output to GND

This 2-pole RC filter with ~16 kHz cutoff suppresses the 62.5 kHz PWM carrier while passing audio-range frequencies with minimal phase distortion.

Using the AD9833 DDS Module with Arduino

For a serious bench tool with clean waveforms up to 12.5 MHz, the AD9833 programmable waveform generator IC is the gold standard. This chip uses a 25 MHz reference clock and 28-bit phase accumulator to produce sine, triangle, and square waves with 0.1 Hz resolution.

AD9833 breakout modules are available for ₹80–₹150 and interface to Arduino via SPI using just three wires (FSYNC, SCLK, SDATA) plus power. The output is a true analog signal — no RC filter needed.

AD9833 Key Specs

  • Output frequency: 0–12.5 MHz
  • Frequency resolution: 0.1 Hz (at 25 MHz MCLK)
  • Phase resolution: 0.1° (12-bit phase register)
  • Two independent frequency and phase registers
  • Output amplitude: ~0.65 Vpp (use op-amp to scale up)
  • Supply: 2.3–5.5 V
Recommended: Arduino Frequency Counter Kit with 16×2 LCD Display — Use this kit to measure and verify the exact frequency output of your DDS waveform generator. Essential for calibration and lab work.

Wiring and Circuit Diagrams

AD9833 to Arduino Uno Wiring

AD9833 Pin Arduino Pin Notes
VCC 5V Module has onboard regulator
GND GND Common ground
SDATA Pin 11 (MOSI) SPI data
SCLK Pin 13 (SCK) SPI clock (max 40 MHz)
FSYNC Pin 10 (SS) Active-low chip select

Rotary Encoder for Frequency Control

  • CLK → Pin 2 (interrupt-capable)
  • DT → Pin 3
  • SW → Pin 4 (push button, changes wave type)
  • + → 5V, GND → GND

Arduino Code Walkthrough

Below is a complete Arduino sketch for the AD9833-based function generator with rotary encoder frequency control:

#include <SPI.h>
#include <Wire.h>

#define FSYNC_PIN 10
#define MCLK 25000000UL  // 25 MHz AD9833 reference clock

// Wave type register bits
#define SINE     0x2000
#define TRIANGLE 0x2002
#define SQUARE   0x2028

uint32_t frequency = 1000;  // Start at 1 kHz
int waveType = SINE;

void AD9833_init() {
  SPI.begin();
  SPI.setDataMode(SPI_MODE2);
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV8); // 2 MHz SPI
  digitalWrite(FSYNC_PIN, HIGH);
}

void AD9833_write(uint16_t data) {
  digitalWrite(FSYNC_PIN, LOW);
  SPI.transfer16(data);
  digitalWrite(FSYNC_PIN, HIGH);
}

void AD9833_setFrequency(uint32_t freq) {
  uint32_t freqReg = (uint32_t)((float)freq * 268435456.0 / MCLK);
  uint16_t lsb = (uint16_t)(freqReg & 0x3FFF) | 0x4000;
  uint16_t msb = (uint16_t)((freqReg >> 14) & 0x3FFF) | 0x4000;
  AD9833_write(0x2100); // Reset + B28 mode
  AD9833_write(lsb);
  AD9833_write(msb);
  AD9833_write(0xC000); // Phase = 0
  AD9833_write((uint16_t)waveType);
}

void setup() {
  pinMode(FSYNC_PIN, OUTPUT);
  Serial.begin(9600);
  AD9833_init();
  AD9833_setFrequency(frequency);
  Serial.println("AD9833 DDS Ready. Send: f1000 for 1kHz, w0/w1/w2 for sine/triangle/square");
}

void loop() {
  if (Serial.available()) {
    String cmd = Serial.readStringUntil('n');
    cmd.trim();
    if (cmd.startsWith("f")) {
      frequency = cmd.substring(1).toInt();
      AD9833_setFrequency(frequency);
      Serial.print("Frequency set to: "); Serial.print(frequency); Serial.println(" Hz");
    } else if (cmd == "w0") {
      waveType = SINE; AD9833_setFrequency(frequency); Serial.println("Sine wave");
    } else if (cmd == "w1") {
      waveType = TRIANGLE; AD9833_setFrequency(frequency); Serial.println("Triangle wave");
    } else if (cmd == "w2") {
      waveType = SQUARE; AD9833_setFrequency(frequency); Serial.println("Square wave");
    }
  }
}

Adding a Display and Frequency Control

A bare-bones serial interface works for the lab, but a proper standalone function generator needs a display and manual frequency control. The 2.4-inch TFT display shield or a 16×2 LCD with rotary encoder provides an excellent user interface.

For the rotary encoder frequency adjustment, use exponential frequency steps: when turning slowly, increment by 1 Hz; moderate speed → 10 Hz; fast turning → 1 kHz per step. This gives you fine control at low frequencies and fast sweep at high frequencies.

A suggested menu structure:

  • Line 1: Wave type + current frequency (e.g., “SINE 1000.00 Hz”)
  • Line 2: Adjust mode indicator + step size (e.g., “[FREQ] Step: 10 Hz”)
  • Short press: cycle wave type (Sine → Triangle → Square → Sawtooth)
  • Long press: cycle frequency step size (1 / 10 / 100 / 1k / 10k Hz)
Recommended: 2.4″ Inch Touch Screen TFT Display Shield for Arduino UNO MEGA — Mount directly on your Arduino Uno for a touch-controlled frequency generator interface with no extra wiring.
Recommended: Arduino Mega 2560 R3 Board — Use the Mega for an advanced DDS generator with the TFT shield — the Mega’s extra memory and pins let you add a spectrum display, signal history, and dual-channel output simultaneously.

Applications and Use Cases

A DIY Arduino function generator is genuinely useful, not just a learning exercise. Here’s where it proves its value:

  • Audio amplifier testing: Sweep 20 Hz–20 kHz sine wave through your amplifier and measure output with a multimeter or oscilloscope
  • RC filter characterization: Find the -3 dB cutoff frequency by sweeping and measuring output amplitude
  • Servo and motor testing: Generate PWM signals at specific frequencies to test servo control without a microcontroller
  • Crystal oscillator testing: Drive a crystal at its rated frequency to verify operation
  • Lock-in amplifier reference: Provide a stable reference signal for synchronous detection experiments
  • Sound synthesis: Use audio-range output to drive a speaker directly (through an amplifier) for tone generation
  • Electromagnetic compatibility (EMC) pre-testing: Generate harmonics to identify interference sources in your PCB design

Frequently Asked Questions

What is the maximum frequency an Arduino Uno can generate with DDS?

Using pure software DDS with Timer2 interrupts, an Arduino Uno can generate recognizable sine waves up to about 20–30 kHz. Above that, the stepped nature of the waveform causes significant distortion. With an AD9833 module over SPI, you can reach 12.5 MHz with a clean, hardware-generated output.

Do I need an oscilloscope to use this function generator?

Not necessarily. The serial terminal provides frequency readout, and an Arduino Frequency Counter Kit can measure the output frequency. However, an oscilloscope is the best way to verify waveform shape and amplitude, especially when characterizing filters.

Can I generate two frequencies simultaneously?

Yes, the AD9833 has two frequency registers (FREQ0 and FREQ1) and two phase registers. Switching between them is done by toggling the FSEL bit — you can switch frequencies rapidly enough to approximate two-tone generation. True simultaneous dual-frequency output requires two AD9833 modules or a more advanced DDS chip like the AD9958.

Is the output voltage adjustable?

The AD9833 outputs approximately 0.65 Vpp. To scale up to standard ±5V bench levels, add an inverting op-amp stage (TL072 or MCP6002) with a gain of about 15×. The pure PWM approach on Arduino can be scaled with the same op-amp circuit.

Can I use this as an audio signal source?

Yes. The output quality is excellent for audio testing. Use the AD9833 module for the cleanest sine waves. You will need a simple op-amp output stage to drive a 1 kΩ load impedance typical of audio input stages.

Conclusion

An Arduino DDS function generator — whether using pure software PWM for audio frequencies or an AD9833 module for RF-range signals — is an indispensable workshop tool that costs a fraction of commercial alternatives. The pure Arduino approach teaches you how DDS actually works; the AD9833 build gives you a proper lab instrument. Build both, learn from both, and upgrade your workbench toolkit.

Browse our full selection of Arduino boards, shields, and kits at Zbotic.in to get everything you need for your DDS function generator project.

Tags: AD9833, Arduino DDS, Arduino project, function generator, signal generator, Waveform Generator
Share Post
  • Facebook
  • Linkedin
  • Whatsapp
Watchdog Timer in Arduino: Pre...
blog watchdog timer in arduino prevent system lockups 594820
blog arduino sleep modes reduce power consumption to microamps 594824
Arduino Sleep Modes: Reduce Po...

Related posts

Svg%3E
Read more

Arduino Batch Programming: Flash Multiple Boards Quickly

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Based Radar System with Ultrasonic Sensor

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Automatic Plant Monitor: Sunlight, Moisture, Temperature

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Lie Detector: GSR Sensor Polygraph Project

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading
Svg%3E
Read more

Arduino Metal Detector: Build a Treasure Finder

April 1, 2026 0
Table of Contents Introduction Components and Hardware Setup Wiring Diagram and Connections Complete Code with Explanation Customization and Improvements Troubleshooting... Continue reading

Add comment Cancel reply

Your email address will not be published. Required fields are marked

Facebook Twitter Instagram Pinterest Linkedin Youtube

Get the latest deals and more.

Download on Google Play Download on the App Store

Call us: 020 69134444 / 1800 209 0998

Monday - Saturday 09:30 AM - 06:00 PM
For Technical Supports Email: [email protected]
For Sales / Enquiries Email: [email protected]

  • My Account

    • Cart

    • Wishlist

    • Checkout

    • My Orders

    • Track Order

    • My Account

  • Information

    • FAQs

    • Blogs

    • Career

    • About Us

    • Contact Us

    • Payment Options

  • Policies

    • Privacy Policy

    • Terms & Conditions

    • GST Input Tax Credit

    • Shipping Return Policy

    • E-Waste Collection Points

    • Our Sitemap

© Zbotic.in is registered trademark of Moxie Supply Pvt Ltd – All Rights Reserved
Login
Use Phone Number
Use Email Address
Not a member yet? Register Now
Reset Password
Use Phone Number
Use Email Address
Register
Already a member? Login Now