Yeat another AD9833 Function Generator

1. Features

The function generator is based on Direct Digital Synthesis (DDS) realized with an AD9833 module which also incorporates an output amplifier AD8051 whose output amplitude is controlled by a digital potentiometer MCP41010.

An ESP32 microcontroller controls all the functions of the DDS module and also realizes the user interface comprising an I2C LCD and a rotary encoder with axial pushbutton.

The various functions of the generator are choosen by clockwise (cw) or counterclockwise (ccw) rotating the rotary encoders shaft and by clicking (click, or longclick) its pusbutton.

2. Specification

Frequency Range
0.1 Hz - 9'999'999.9 Hz
Minimum Frequency Increment
0.1 Hz
Sine, Triangle, Square
Output Voltage Range
0.0 - 3.2 Vpp (Sine, Triangle)
adjustable with Digital Potentiometer in 255 Steps
3.2 Vpp (Square)

min. Start Frequency
0.1 Hz
max Stop Frequency
9‘999‘999.9 Hz
min. Frequency Step
0.1 Hz
max. Frequency Step
99‘999.9 Hz
min. Frequency Step Duration
1 ms
max Frequency Step Duration
99‘999 ms

3. Parts

  • AD9833 DDS Module 25 MHz Clock
  • with AD8051 Output Amplifier and MCP41010 Digital Potentiometer
  • ESP32 Microcontroller DoIt DevKit V1
  • LC Display I2C 4 x 20
  • Rotary Encoder with axial Pushbutton
Parts needed for the AD9833 Function Generator (except breadboard)

4. Wiring

FSY (Chip Select AD9833)
CLK (SPI Clock)
DAT (SPI Data)
CS (Chip Select MCP41010)

LCD I2C 4x20
ESP32 (5V)

Rotary Encoder with Pushbutton

5. User Interface and Operation

Initial Display

On power up Screen-0 is displayed. The settings of both channels are shown. The active channel is marked, here as ⟨Chan 0⟩. We see the selected wave forms, the settings of the digital potentiometer and and the actual frequencies of each channel.

Channel 0 is active
Screen-0 : Channel 0 is active and its signal is fed to output
LongClick toggles output signal on/off.
Click switchs to other channel and back.
Channel 1 is active
Screen-0 : Channel 1 is active and its signal is fed to output
Channel 1 is set to triangle wave form. The output amplitude is set to 65 units of the digital potentiometer corresponding to aproximatly 1000 mVpp and the frequency is set to 554.4 Hz.
Rotating CW/CCW navigates to Screen-1, Screen-2, Screen-3 or backwards.


The values of both channels can be set independently. The set values are not remembered between power ups. That is left for further enhacement and could be done by storing the settings in ESP32 flash memory.

Settings chan0
Screen-1 : Settings of channel 0
Longclick enters or quits Input Mode. This is indicated by a blinking cursor on Mode.
Click moves blinking cursor from line to line.
Rotating CW/CCW changes values.
Settings chan1
Screen-2 : Settings of channel 1
The cursor blinks on 10 kHz digit to change frequency by rotating encoder shaft.


The sweep frequency starts at the frequency set for channel 0 and stops at the frequency set for channel 1. The possible modes are shown below.

Sweep rising slope
Screen-3 : Sweep settings – rising slope
Sweep : Is stopped
Mode : Rising slope from freq of channel 0 to freq of channel 1, sweep is executed once
Time : Duration of one frequency step 10 ms
Step : Frequency change from step to step is 1 Hz
Sweep falling slope
Screen-3 : Sweep settings – falling slope
Mode : Falling slope from freq of channel 1 to freq of channel 0
Sweep rising then falling slope
Screen-3: Sweep settings – rising then falling slope
Mode : Rising then falling slope increasing from freq of channel 0 to freq of channel 1 and returning to freq of channel 0
Sweep falling then rising slope
Screen-3: Sweep settings – falling then rising slope
Mode : Falling then rising slope decreasing from freq of channel 1 to freq of channel 0 and returning to freq of channel 1
Sweep rising slope cyclic
Screen-3: Sweep settings - rising slope, repeating
Sweep : Is running
Mode : Rising slope, repeatedly executed.

6. Example Waveforms

Waveform pictures are all taken with amplitude set to 133 units of the digital potentiometer, corresponding to 2000 mVpp. We see clearly the decreasing amplitude and degradation of the waveform at higher frequencies.

Sine 1kHz
Sine 10kHz
Sine 100kHz
Sine 1MHz
Sine 2MHz
Sine 3MHz
Sine 4MHz
Sine 5MHz
Triangle 1kHz
Triangle 10kHz
Triangle 100kHz
Triangle 1MHz
Square 1kHz
Square 10kHz
Square 100kHz
Square 1MHz

7. Statechart

The statechart, created with the free tool YAKINDU™, shows the basic operation of the frequency generator.

Rotating the shaft of the rotary encoder cycles through the 4 screens.

  • Screen0 : Displays the settings of both channels. Clicking the pushbutton when output signal is on toggles between the two channels. LongClicks switches on and of the output signal. Clicking when a channel is off switches to the other channel in the on-state.
  • Screen1 : Allows changing mode, amplitude and frequency of channel 0 by rotating the rotary encoder shaft.
  • Screen2 : Allows changing mode, amplitude and frequency of channel 1 by rotating the rotary encoder shaft.
  • Screen3 : Allows changing the various settings of sweep
Finite State Machine

The next statechart shows the settings of channel 0 in more detail. The individual digits of the frequency are reached by clicking and then changing value by rotating the rotary encoders shaft.

Duration and frequency step for sweep mode are set in an analog way.

Finite Statechart Chn0

8. Program Code

My programming environment is not the native Arduino™ IDE but PlatformIO™ on top of Microsoft's Visual Studio Code™. This combination offers many advantages and allows a much better structuring of the code into several modules especially when we adopt The Object Oriented way.

The code below shows the main program. It mostly consists of comments. The setup() function in turn calls setup() of the classes AD9833FuncGen and ControlKnob and initializes the LCD. Astonishingly the main loop() consists of a single line namely the call of the loop() function of the class ControlKnob. This class realizes the Finite State Machine and handles the 4 actions onClick(), onLongClick(), onCW() and onCCW().

Interested? Please download the entire program code. The zip-file contains the complete PlatformIO project.