mbed and AD 9954

09 Nov 2011

Dear all,

I am trying to connect a Direct Digital Synthesizer via the SPI interface to the mbed controller. Since I never worked with electronic boards/ circuits and C it is a bit difficult for me to find the mistakes.

I found the code from Remy for the AD 9951 (http://mbed.org/forum/helloworld/topic/1664/?page=1#comment-8348) and modified it a bit. I use it in the form posted below.

The datasheet of the AD 9954 DDS is here http://www.analog.com/static/imported-files/data_sheets/AD9954.pdf I connected the pins as follows

DDS							mbed
pin		function			pin	
25		SCLK				7 (sclk)
23		SDIO				5 and 6 (mosi and miso)
17		FUD_DUT2			10 (_fud)
15		PWRDWNCTRL_DUT2		ground
13		RESET_DUT2			9 (rst)
12		CS_DUT2				ground

I get no output on the oscilloscope and in the Tera Term it just reads out c2= 0 FTWO = 0

I really don't know what is going wrong. Maybe one of you can give me a tip?

#### AD9954.h #######

#ifndef AD9954_H
#define AD9954_H

#include "mbed.h"

class AD9954 {

protected:
    SPI         _spi;
    DigitalOut  _cs;
    DigitalOut  _rst;
    
public:

    AD9954(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName rst) :
        _spi(mosi, miso, sclk), _cs(cs), _rst(rst)
    { 
        // see http://mbed.org/handbook/SPI and page 23 why format 0
        _spi.format(8, 0);
        _spi.frequency(1000000);
		
        // master reset the device:
        _rst.write(1);
         wait(0.1);
        _rst.write(0);
    };
	
    // Write a 32bit register at the specified address.
    void write_reg_4byte(uint32_t address, uint32_t value) {
      // Instruction byte: page 25 in datasheet, we want to write (0), so 0100 + internal adress of the register to be written in
        _spi.write(0x00 | (address & 0x1F));
        _spi.write((value >> 24) & 0xFF);
        _spi.write((value >> 16) & 0xFF);
        _spi.write((value >> 8) & 0xFF);
        _spi.write((value >> 0) & 0xFF);
    }
    
    // Write a 24bit register at the specified address.
    void write_reg_3byte(uint32_t address, uint32_t value) {
      // Instruction byte: page 25 in datasheet, we want to write (0), so 0100 + internal adress of the register to be written in
        _spi.write(0x00 | (address & 0x1F));
        _spi.write((value >> 16) & 0xFF);
        _spi.write((value >> 8) & 0xFF);
        _spi.write((value >> 0) & 0xFF);
    }
        
    // Read a 32bit register at the specified address.
    uint32_t read_reg_4byte(uint32_t address) {
        uint32_t value = 0x00000000;
        _spi.write(0x80 | (address & 0x1F)); // Instruction byte
        value |= _spi.write(0x00);
        value = value << 8;
        value |= _spi.write(0x00);
        value = value << 8;
        value |= _spi.write(0x00);
        value = value << 8;
        value |= _spi.write(0x00);
        return value;
    }  
    
    uint32_t read_reg_3byte(uint32_t address) {
        uint32_t value = 0x000000;
        _spi.write(0x80 | (address & 0xF)); // Instruction byte
        value |= _spi.write(0x00);
        value = value << 8;
        value |= _spi.write(0x00);
        value = value << 8;
        value |= _spi.write(0x00);
        return value;
    }   
 
    // Write 
    void CFR1_write(uint32_t reg) { write_reg_4byte(0x00, reg); }
    void CFR2_write(uint32_t reg) { write_reg_3byte(0x01, reg); }
    void FTW0_write(uint32_t reg) { write_reg_4byte(0x04, reg); }
    
    // Read 
    uint32_t CFR1_read(void) { return read_reg_4byte(0x0); }
    uint32_t CFR2_read(void) { return read_reg_3byte(0x1); }
    uint32_t FTWO_read(void) { return read_reg_4byte(0x4); }  
};

#endif

##### main #######

#include "mbed.h"
#include "AD9954.h"

Serial pc(USBTX, USBRX);
DigitalOut myled(LED1);
DigitalOut _fud(p10);
AD9954 myDevice(p5, p6, p7, p8, p9);

int main() {

    _fud.write(0);
        // in register 2: 
		//bit3-bit7 contain the multiplication value for the clock. multiply the 10 MHz input by 10: [01010], the vco is then not in the upper range, therefore bit2=0,
        // from this follows bit7-bit0: 00 00 0101 0000 -> 00 00 50
        myDevice.CFR2_write(000050);
        // now we have a 100 MHz clock, the frequency tuning word for around 6 MHz output is ((12.5:2)/100)*2^(32) =268435456 
        //= 10000000
        myDevice.FTW0_write(0x10000000);
	// issue I/O update
    _fud.write(1);
    wait(0.1);
    _fud.write(0);
    
        uint32_t c2; 
        c2 = myDevice.CFR2_read();    
        pc.printf("c2= %X\n", c2);
        
        uint32_t ftwo;  
        ftwo = myDevice.FTWO_read();    
        pc.printf("FTWO = %X\n", ftwo);

   return 0;
}
17 Mar 2016

Hello, I am a newbie,too.

I have some question about the "read register" code.

Why we have to write "value |= _spi.write(0x00);" after the instruction byte (_spi.write(0x80 | (address & 0x1F)))

thank you!!! : ))

17 Mar 2016

On spi you read and write at the same time, the write function returns the data received while it was writing.

So to read a value you first write the address you want to read and then you write something (anything, it doesn't matter normally) that is the same length as the data you expect to get back. 0 is a nice safe value to write.

As for the actual syntax used, the |= is in there because the code is reading a value longer than 1 byte and needs to combine multiple reads into a single value.

value |= spi.write()

is shorthand for

value = value | spi.write()

every time that is called in the code it is guaranteed that the least significant 8 bits of value will be 0 which means it's effectively the same as saying

value = value + spi.write()

If you look at the code the line afterwards is normally

value = value << 8;

which then shifts value to the left 8 bits ensuring that the least significant 8 bits are again 0.

In terms of end result

        value = 0;
        value |= _spi.write(0x00);
        value = value << 8;
        value |= _spi.write(0x00);
        value = value << 8;
        value |= _spi.write(0x00);

Is identical to

        value = 0;
        value = value + _spi.write(0x00);
        value = value * 256;
        value = value + _spi.write(0x00);
        value = value * 256;
        value = value + _spi.write(0x00);

They both read 3 bytes and combine the result into a single number.

So why do it the first way? Because bitwise operators like | and << are just about the fastest operations possible on a CPU. Additions and multiplies can be significantly slower.

24 Mar 2016

I was just wondering what hardware you were using. I did some searching for DDS development boards and there seems to be plenty for the AD9850/9851 but the AD9951-4 seem to be only used in some complete signal generator designs.

Also I think you might get better results separating MOSI and MISO, and using the SDO pin, if that is still an option. I'm not certain but the "two wire" method requires the MBED MOSI pin to go tri-state during read operations and I can't see anything in the code to make that happen.