This is a basic MBED driver for the AD9850 digital sine wave synthesizer. It uses a hacked SPI interface to drive the device,

Dependencies:   mbed

I was looking for a way to test the frequency response of a new oscilloscope.

Analog Devices has a nice IC that can produce a digitally synthesized sine wave of frequency between 0 and about 70 MHz. AD9850 link is here: http://www.analog.com/en/rfif-components/direct-digital-synthesis-dds/ad9850/products/product.html

What made things even easier was the availability of ready-to-go evaluation boards from China on Ebay for less than $5.

This is the one I got:

http://www.ebay.com/itm/AD9850-DDS-Signal-Generator-Module-0-40MHz-IC-Test-Equipment-HC-SR08-/221279011148?pt=LH_DefaultDomain_0&hash=item338541c14c

This design is limited to about 40MHz if you want a clean signal without aliasing.

Frequency and phase are set by a 40-bit command that has:

  • 32-bit frequency setting
  • 5-bit phase setting
  • 1-bit power up/down
  • 2-bits factory debug access

/media/uploads/liamg/packets.jpg

Output frequency is given by: fOUT = (frq × CLKIN)/(2^32)

where frq is the 32-bit frequency and CLKIN is the frequency of the on-board crystal (125MHz in this case) So, for example 0x147AE148 is 10MHz.

Frequency resolution has a step size of 29KHz with the 125MHz clock.

You can also change phase in increments of 180°, 90°, 45°, 22.5°, 11.25° or any combination thereof using the 5-bit phase command. However I didn't implement this and left phase as zero.

I used a serial interface from the LPC1768 MBED to the AD9850 board as I didn't want to be bothered with all the wiring of a parallel bus. The required timing looks like this:

/media/uploads/liamg/screen_shot_2013-09-27_at_10.42.33_pm.png

This looks quite like an SPI interface. Much easier to use the LPC's SPI block than to do bit-banging.

AD9850 wants low base clock state and clock falling edge data latching which adds up to SPI mode 0.

Only significant difference to SPI is the FQ_UD (frequency update) command. This looks quite like SPI's chip select line except using positive logic. Can use a GPIO line to fake that.

The other hacks that are required are:

(1) MBED SPI limits the bits per packet to 16. Work around is to use 2 x 16-bit packets and then 1 x 8-bit packet. Another benefit of directly controlling the CS signal with a GPIO line is that CS can be kept low even between the 3 packets required to make up the 40-bits. Normally an SPI block would automatically take CS high again.

(2) Even though the LPC allows for setting LSB or MSB first, MBED only provides for MSB first whereas the AD9850 wants LSB first. Could work around that by writing a direct driver for the LPC. However I thought it was simpler just to write a function that swaps bit 31 with bit 0, bit 30 with bit 1 etc. High performance SPI isn't needed so that is the simplest solution. There are bit swapping algorithms out there, however they are made for single bytes.

Connection of the boards is like this:

/media/uploads/liamg/screen_shot_2013-09-27_at_10.41.02_pm.png

/media/uploads/liamg/breadboard.jpg

D2-D0 are hardwired 1,0,0 to force the AD9850 into serial mode. The AD9850 board is powered at 3V since this is the MBED GPIO voltage.

I used a Saleae Logic logic analyzer to watch the signals

/media/uploads/liamg/spi.png

This shows the 3 packets that make up the 40-bit command. Luckily the AD9850 only specifies the minimum time between bits; there doesn't seem to be a maximum as long as the next FQ_UD pulse does not arrive.

Code loops from 10MHz to 20MHz and back down again.

/media/uploads/liamg/scope.jpg

Committer:
liamg
Date:
Sat Sep 28 06:44:57 2013 +0000
Revision:
1:b0e6c82af2ef
Parent:
0:b5fb7b3adfe2
Version 2 with updated commentary

Who changed what in which revision?

UserRevisionLine numberNew contents of line
liamg 0:b5fb7b3adfe2 1 // MBED driver for AD9850 digital synthesizer using hacked SPI interface
liamg 0:b5fb7b3adfe2 2 // Liam Goudge Sept 2013
liamg 0:b5fb7b3adfe2 3
liamg 0:b5fb7b3adfe2 4 #include "mbed.h"
liamg 0:b5fb7b3adfe2 5
liamg 0:b5fb7b3adfe2 6 SPI device (p5,p6,p7); // MOSI, MISO (not used), SCLK
liamg 0:b5fb7b3adfe2 7 DigitalOut CS(p8); // Use pin 8 as a fake Chip select
liamg 0:b5fb7b3adfe2 8 DigitalOut ADReset(p15); // Pin 15 is reset line for AD9850
liamg 0:b5fb7b3adfe2 9
liamg 0:b5fb7b3adfe2 10 Serial pc(USBTX, USBRX); // tx, rx for debug terminal
liamg 0:b5fb7b3adfe2 11
liamg 0:b5fb7b3adfe2 12 int reverseBits (int source)
liamg 0:b5fb7b3adfe2 13 {
liamg 0:b5fb7b3adfe2 14 // Unfortunately need to invert bit order of the desired frequency setting since MBED only allows for MSB first from SPI. We need LSB first for AD9850
liamg 0:b5fb7b3adfe2 15 int mask=0;;
liamg 0:b5fb7b3adfe2 16 int i=0;
liamg 0:b5fb7b3adfe2 17 int target=0;
liamg 0:b5fb7b3adfe2 18 int bitTarget=0x80000000; // Hard-wired for 32-bit inversion
liamg 0:b5fb7b3adfe2 19
liamg 0:b5fb7b3adfe2 20 for (i=0;i<32;i++) { // ditto
liamg 0:b5fb7b3adfe2 21 mask=1<<i;
liamg 0:b5fb7b3adfe2 22 bitTarget=1<<(31-i); // ditto
liamg 0:b5fb7b3adfe2 23
liamg 0:b5fb7b3adfe2 24 if (source & mask)
liamg 0:b5fb7b3adfe2 25 target=target | bitTarget;
liamg 0:b5fb7b3adfe2 26 }
liamg 0:b5fb7b3adfe2 27 return target;
liamg 0:b5fb7b3adfe2 28 }
liamg 0:b5fb7b3adfe2 29
liamg 0:b5fb7b3adfe2 30 void writeSPI(int frq, int phase)
liamg 0:b5fb7b3adfe2 31 {
liamg 0:b5fb7b3adfe2 32 // Send the 40-bit packet. MBED only allows max 16-bit packets so we send 40-bits as 16, 16, 8
liamg 0:b5fb7b3adfe2 33 device.format(16,0); // 16-bits per packet, mode 0 (CPOL=0 CPHA=0)
liamg 0:b5fb7b3adfe2 34 device.frequency(1000000); //SPI clock set to 1MHz
liamg 0:b5fb7b3adfe2 35
liamg 0:b5fb7b3adfe2 36 wait_ms(5);
liamg 0:b5fb7b3adfe2 37
liamg 0:b5fb7b3adfe2 38 // First do chip select. Need to use a GPIO to fake the chip select since MBED doesn't allow to set positive logic CS signal
liamg 0:b5fb7b3adfe2 39 CS=1; // assert chip select (a.k.a FQ_UD frequency update input to AD9850)
liamg 0:b5fb7b3adfe2 40 wait_ms(5);
liamg 0:b5fb7b3adfe2 41 CS=0;
liamg 0:b5fb7b3adfe2 42
liamg 0:b5fb7b3adfe2 43 device.write(frq>>16); // Write upper 16-bits first starting with bit 31
liamg 0:b5fb7b3adfe2 44 device.write(frq); // Now lower 16 bits starting with bit 15
liamg 0:b5fb7b3adfe2 45
liamg 0:b5fb7b3adfe2 46 device.format(8,0); // Need to reset to 8-bit data since MBED only allows max 16-bit packets. For 40-bits we do 16, 16, 8
liamg 0:b5fb7b3adfe2 47 device.write(phase); // This is phase and factory settings byte
liamg 0:b5fb7b3adfe2 48
liamg 0:b5fb7b3adfe2 49 // Now pulse FQ_UD again to signal the end of the packet
liamg 0:b5fb7b3adfe2 50 CS=0;
liamg 0:b5fb7b3adfe2 51 wait_ms(5);
liamg 0:b5fb7b3adfe2 52 CS=1;
liamg 0:b5fb7b3adfe2 53 wait_ms(5);
liamg 0:b5fb7b3adfe2 54 CS=0;
liamg 0:b5fb7b3adfe2 55 }
liamg 0:b5fb7b3adfe2 56
liamg 0:b5fb7b3adfe2 57
liamg 0:b5fb7b3adfe2 58 int main()
liamg 0:b5fb7b3adfe2 59 {
liamg 0:b5fb7b3adfe2 60 int targetFrq=0x147AE148; // This is desired sine wave frequency for the AD9850, here set to 10MHz
liamg 0:b5fb7b3adfe2 61 int increment=0x346DC6; // This is a 100KHz frequency increment
liamg 0:b5fb7b3adfe2 62
liamg 0:b5fb7b3adfe2 63 // Reset the AD9850. Active high logic. Minimum reset period 5 clock cycles (5/125MHz)
liamg 0:b5fb7b3adfe2 64 ADReset=0;
liamg 0:b5fb7b3adfe2 65 wait_ms(5);
liamg 0:b5fb7b3adfe2 66 ADReset=1;
liamg 0:b5fb7b3adfe2 67 wait_ms(5);
liamg 0:b5fb7b3adfe2 68 ADReset=0;
liamg 0:b5fb7b3adfe2 69
liamg 0:b5fb7b3adfe2 70 while(1)
liamg 0:b5fb7b3adfe2 71 {
liamg 0:b5fb7b3adfe2 72
liamg 1:b0e6c82af2ef 73 while (targetFrq<0x28F5C28F) // up to 20MHz
liamg 0:b5fb7b3adfe2 74 {
liamg 1:b0e6c82af2ef 75 writeSPI(reverseBits(targetFrq),0); // Don't use phase so set to zero.
liamg 0:b5fb7b3adfe2 76 targetFrq=targetFrq+increment;
liamg 0:b5fb7b3adfe2 77 //wait_ms(100);
liamg 0:b5fb7b3adfe2 78 }
liamg 0:b5fb7b3adfe2 79
liamg 1:b0e6c82af2ef 80 while (targetFrq>0x147AE148) // down to 10MHz
liamg 0:b5fb7b3adfe2 81 {
liamg 0:b5fb7b3adfe2 82 writeSPI(reverseBits(targetFrq),0);
liamg 0:b5fb7b3adfe2 83 targetFrq=targetFrq-increment;
liamg 0:b5fb7b3adfe2 84 //wait_ms(100);
liamg 0:b5fb7b3adfe2 85 }
liamg 0:b5fb7b3adfe2 86
liamg 0:b5fb7b3adfe2 87 }
liamg 0:b5fb7b3adfe2 88
liamg 0:b5fb7b3adfe2 89
liamg 0:b5fb7b3adfe2 90 }