Bit-banging the AD9833 DDS module

By | December 10, 2015

I bought an AD9833 DDS module from China – you know the one:


I wanted to control it from an Atmel ATtiny and my weapon of choice was the ATtiny44. Now I could just have easily used an ATtiny2313 (both devices are 14-pin), but I didn’t – and I went on and built a PCB.

So when I came to program the ATtiny44, I realised it does not have a proper USART-SPI-mode (which the ‘2313 does). The ATtiny44 (along with the ’24 and ’84 variants and the ATtiny85) just have the USI hardware module which can be crow-barred into service as an SPI master with some heavy lifting.

Well I tried three different software libraries to try and talk to the AD9833 and failed miserably on all three counts. Getting desperate, I decided to bit-bang the AD9833 to see if it was actually working. The bit-bang worked perfectly first time and – as I do not need to write to the AD9833 at any great speed – I decided to implement the bit-bang code and forget trying to get a library to work for now – although no doubt I will need to revisit the library again at some point.

This code should work on pretty much any Atmel Tiny or Mega processor.

Basically, we need a processor which is running at a decent speed (8MHz in my case) and any three pins: chip-select, data and clock. We manipulate these pins in accordance with the timing  and level requirements of the AD9833 datasheet. Level requirements are: chip-select is active low, clock is normally high and data is clocked on the falling edge of the clock. Data is clocked out MSB first.

We are following the software protocol in application note AN-1070; so to get the AD9833 to output a sinewave we need to send:

  • 0x2100 – put the AD9833 into reset and tell it to accept 14bit words
  • 0xnnnn – Lo word of the set-frequency-instruction
  • 0xnnnn – Hi word of the set-frequency-instruction
  • 0xC000 – set the phase angle to zero
  • 0x2000 – take the AD9833 out of reset and output sinewave

There is a formula on the application note to calculate the frequency Lo word and Hi word for any given frequency. I wrote a spreadsheet to work these numbers out for you. You can download the spreadsheet or use the php calculator wot I wrote.

So, let’s say we want to output a sinewave at 1000Hz. We use one of the resources above to find the register values, which are low = 0x69F1 and high = 0x4000 (assuming your module has a 25MHz crystal/oscillator fitted!).

You can download the code below as a .c text file.

If you are using Arduino, you can change SET(DDRA,ddsDataPin) to pinMode(ddsDataPin,OUTPUT) and SET(PORTA,ddsDataPin) to digitalWrite(ddsDataPin,HIGH), etc.

The ASM_NOP() function gives a single processor cycle delay (no-operation).

Have fun.


#define F_CPU 8000000UL

#include <avr/io>
#include <inttypes.h>

#define ddsSelectPin PA0
#define ddsClockPin PA2
#define ddsDataPin PA3

#define ASM_NOP() asm volatile ("nop" :: )
#define SET(x,y) (x|=(1<<y))
#define CLEAR(x,y) (x&=(~(1<<y)))
#define CHECK(x,y) (x&(1<<y))
#define TOGGLE(x,y) (x^=(1<<y))

uint16_t freqLoWord = 0x69F1; //what sample frequency is this? 1kHz!
uint16_t freqHiWord = 0x4000;

void setDDR(void) {
  //set data direction (1 = output)
  SET(DDRA, ddsSelectPin); //output
  SET(DDRA, ddsDataPin); //output
  SET(DDRA, ddsClockPin); //output
  //pullups and initial states
  SET(PORTA, ddsSelectPin); //high = unselected
  SET(PORTA, ddsClockPin); //high = idle

void writeSPI(uint16_t word) {
  for (uint8_t i = 0; i < 16 ; i++) {
    if(word & 0x8000) SET(PORTA,ddsDataPin); //bit is 1, set high
    else CLEAR(PORTA,ddsDataPin); //bit is 0, set low
    CLEAR(PORTA,ddsClockPin); //data is valid on falling edge
    word = word<<1; //shift left by 1 bit
CLEAR(PORTA,ddsDataPin); //idle low

void writeDDS() {
  CLEAR(PORTA, ddsSelectPin); //low = selected
  writeSPI(0x2100); // enable 16bit words and set reset bit
  writeSPI(0xC000); // set phase register to zero
  writeSPI(0x2000); // clear reset bit =&gt; audio on
  SET(PORTA, ddsSelectPin); //high = deselected

void setup(void) {

int main(void) {
  return 0;

Leave a Reply