Controlling AD9951 DDS with mbed

10 Jan 2011

hello,

I'm a student who wants to work with DDS AD9951 with SPI bus.

here you can find datasheet : http://www.analog.com/static/imported-files/data_sheets/AD9951.pdf

We found where to connect every pins from mbed to DDS ( MBED to DDS :12 to 4 ; 13 to 6 ; 14 to 3 ; GND to RESET )

is that right ?

We want to have a frequency out of 100 Mhz, what kind of word we have to send ?

In the future, we want to sweep from 20MHz to 30 with a step of 10Hz.

Thanks in advance for your help.

RJ

17 Jan 2011

Hello everybody, that's the code we did but it doesn't work, and due to our difficulty in programmation, we can't fix it :

#include "mbed.h"

SPI dds(p5, p6, p7);
DigitalOut le(p8);
DigitalOut reset(p9);




// Writes the three bytes in data to teh DDS device.
void ddsWrite(int data) {
le = 0;
dds.write(data & 0xFF);
le = 1;
}

int main() {
le = 1;
dds.format(8,0);
dds.frequency(1000000);
reset=1;
reset=0;


ddsWrite(0x00); //adress of register
ddsWrite(0x00);
ddsWrite(0x00);
ddsWrite(0x00);
ddsWrite(0x0C);

ddsWrite(0x01);//adress of register
ddsWrite(0x18);
ddsWrite(0x02);
ddsWrite(0xA4);

ddsWrite(0x02);
ddsWrite(0xC4);
ddsWrite(0x50);

ddsWrite(0x03);
ddsWrite(0xFC);

ddsWrite(0x04);
ddsWrite(0x86);
ddsWrite(0x00);
ddsWrite(0x00);
ddsWrite(0x00);

ddsWrite(0x05);
ddsWrite(0x00);
ddsWrite(0x00);


// }
}

17 Jan 2011

Quote:

We found where to connect every pins from mbed to DDS ( MBED to DDS :12 to 4 ; 13 to 6 ; 14 to 3 ; GND to RESET )

But then your code does:-

SPI dds(p5, p6, p7);
DigitalOut le(p8);
DigitalOut reset(p9);

Do you have a circuit diagram as how you wired this up?

17 Jan 2011

we just changed the spi to p5,p6,p7 and reset to P9 and diagrams are good.

we think it's a mistake in or code. how to send 32 bits, 24bits etc.., with mbed. And we dont know how to include the serial adress in the word we have to send).

17 Jan 2011

Page 21 of the datasheet shows that while accessing the registers via SPI that the CS signal stay low during the entire transfer. However, your code, ddsWrite(), lowers CS, writes one byte and then raises CS again.

You need to code functions that write data over SPI according the datasheet.

17 Jan 2011

I dont understand how to include the serial adress: for example, P14 of the datasheet, Control function Register n°1, CFR1, have 0x00 adress and 32 bit. I have to send the adress and then a 32bits word? or directly 40 bits word? We are lost.

Thanks for your help

17 Jan 2011

Well, you send 4 x 8 bits to the ic...
If you're a student, and this is a project, why don't you read through the datasheet,
and try some code? Instead of asking already?

Lerche

17 Jan 2011

We read the datasheet and we dont understand some things. Sorry if i missed some explanations.

For example, CFR1, adresse 0x00 and other 32 bits,

void ddsWriteCFR1(uint32_t data) {
    le = 0;
    dds.write(data >> 16 & 0xFF);
    dds.write(data >> 8 & 0xFF);
    dds.write(data & 0xFF);
    le = 1;
}

void ddsWriteAdress(int data) {
    le = 0;
    dds.write(data & 0xFF);
    le = 1;
}

and in the main fct:
ddsWriteAdress(0x00); //and then
ddsWriteCFR1(0x...);
In our code, we dend the adresse and then the 32 bits. And we dont know if it's correct or not.

We are not good programmers.

17 Jan 2011

OK, lets try and get you going with the basics. Start a new project. Then add a new file to the project called AD9951.h and paste the following code into it:-

// Note, I don't have an AD9951 so this is untested experimental code.
// It compiles but I have no idea if it actually works. It's purely demo code.

#ifndef AD9951_H
#define AD9951_H

#include "mbed.h"

class AD9951 {

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

    AD9951(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName rst) :
        _spi(mosi, miso, sclk), _cs(cs), _rst(rst) 
    { 
        _spi.format(8, 0);
        _spi.frequency(1000000);
        _cs.write(1); 
        _rst.write(1);
        wait(0.1);
        _rst.write(0);
    };
        
    // Write a 32bit register at the specified address.
    void write_reg_4byte(int address, uint32_t value) {
        _cs.write(0);
        _spi.write(0x80 | (address & 0xF)); // Instruction byte
        _spi.write((value >> 24) & 0xFF);
        _spi.write((value >> 16) & 0xFF);
        _spi.write((value >> 8) & 0xFF);
        _spi.write((value >> 0) & 0xFF);
        _cs.write(1);
    }
    
    // Read a 32bit register at the specified address.
    uint32_t read_reg_4byte(int address) {
        uint32_t value = 0;
        _cs.write(0);
        _spi.write(address & 0x7f);  // Instruction byte
        value |= _spi.write(0);
        value = value << 8;
        value |= _spi.write(0);
        value = value << 8;
        value |= _spi.write(0);
        value = value << 8;
        value |= _spi.write(0);
        _cs.write(1);
        return value;
    }    
        
    // Write the CFCR1 regsiter
    void cfcr1_write(uint32_t reg) { write_reg_4byte(0, reg); }
    
    // Read the CFCR1 register
    uint32_t cfcr1_read(void) { return read_reg_4byte(0); }
        
    // This is just a start. Now carry on expanding this file
    // to add additional functionality for your device.
        
    // The rest is now up to you to write. Note, some registers
    // are 24bit and not 32bit so you will need extra write_reg_
    // and read_reg_ functions.     
       
};

#endif

Now, in main.cpp change it like this:-

#include "mbed.h"
#include "AD9951.h"

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

int main() {

    uint32_t cfcr1;
    
    cfcr1 = myDevice.cfcr1_read();
    
    pc.printf("CFCR1 register = %ld\n", cfcr1);

    while(1) {
        myled = 1;
        wait(0.2);
        myled = 0;
        wait(0.2);
    }
}

This should be enough to get you started and see how to take a datasheet and write code that matches it.

Try to get your device into a library object as shown so that all the AD9951 specific code is in it's own librray. Then you can dedicate your main.cpp to the actual program without the hardware "getting in the way" of your developement.

Note, the AD9951.h code above is only a starting point and should show reading and writing a 32bit register at the specified address. Other registers are different widths so will require different code. Read the datasheet that explains register widths etc etc.

Good luck! :)

17 Jan 2011

Thanks for your help.

here is the entire code we did :

#include "mbed.h"

SPI dds(p5, p6, p7);
DigitalOut le(p8);
DigitalOut reset(p9);


void adWriteCFR1(int64_t data)
{
    le = 0;
    dds.write(data >> 32 & 0xFF);
    dds.write(data >> 24 & 0xFF);
    dds.write(data >> 16 & 0xFF);
    dds.write(data >> 8 & 0xFF);
    dds.write(data & 0xFF);
    le = 1;
}

void adWriteCFR2(int32_t data)
{
    le = 0;
    dds.write(data >> 16 & 0xFF);
    dds.write(data >> 8 & 0xFF);
    dds.write(data & 0xFF);
    le = 1;
}

void adWriteASF(int32_t data)
{
    le = 0;
    dds.write(data >> 8 & 0xFF);
    dds.write(data & 0xFF);
    le = 1;
}

void adWriteARR(int32_t data)
{
    le = 0;
    dds.write(data & 0xFF);
    le=1;
}

void adWriteFTW0(int64_t data)
{
    le = 0;
    dds.write(data >> 32 & 0xFF);
    dds.write(data >> 24 & 0xFF);
    dds.write(data >> 16 & 0xFF);
    dds.write(data >> 8 & 0xFF);
    dds.write(data & 0xFF);
    le = 1;
}


void adWritePOW0(int32_t data)
{
    le = 0;
    dds.write(data >> 8 & 0xFF);
    dds.write(data & 0xFF);
    le = 1;
}

int main() {
    le = 1;
    dds.format(8,0);
    dds.frequency(1000000);
    reset=1;
    reset=0;
    
    adWriteCFR1(0x00200008);
    adWriteCFR2(0x011802A0);
    adWriteASF(0x02C450);
    adWriteARR(0x03FC);
    adWriteFTW0(0x042A0070A3);
    adWritePOW0(0x050000);
but it doesnt work..

We are going to work with yours.

Thank you.

 

 

17 Jan 2011

void adWriteCFR1(int64_t data)
{
    le = 0;
    dds.write(data >> 32 & 0xFF);
    dds.write(data >> 24 & 0xFF);
    dds.write(data >> 16 & 0xFF);
    dds.write(data >> 8 & 0xFF);
    dds.write(data & 0xFF);
    le = 1;
}

Why using uint64_t for a 32bit value? Use uint32_t.

Note, the first byte written is the "instruction byte" followed by four data bytes.

Your line adWriteCFR1(0x00200008); simply doesn't match your function prototype (takes a 64bit but you only pass 32bits).

As you say, look at the example code I did.

17 Jan 2011

// Note, I don't have an AD9951 so this is untested experimental code.
// It compiles but I have no idea if it actually works. It's purely demo code.

#ifndef AD9951_H
#define AD9951_H

#include "mbed.h"

class AD9951 {

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

    AD9951(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName rst) :
        _spi(mosi, miso, sclk), _cs(cs), _rst(rst) 
    { 
        _spi.format(8, 0);
        _spi.frequency(1000000);
        _cs.write(1); 
        _rst.write(1);
        wait(0.1);
        _rst.write(0);
    };
        
    // Write a 32bit register at the specified address.
    void write_reg_4byte(int address, uint32_t value) {
        _cs.write(0);
        _spi.write(0x80 | (address & 0xF)); // Instruction byte
        _spi.write((value >> 24) & 0xFF);
        _spi.write((value >> 16) & 0xFF);
        _spi.write((value >> 8) & 0xFF);
        _spi.write((value >> 0) & 0xFF);
        _cs.write(1);
    }
    
     // Write a 24bit register at the specified address.
    void write_reg_3byte(int address, uint32_t value) {
        _cs.write(0);
        _spi.write(0x80 | (address & 0xF)); // Instruction byte
        _spi.write((value >> 16) & 0xFF);
        _spi.write((value >> 8) & 0xFF);
        _spi.write((value >> 0) & 0xFF);
        _cs.write(1);
    }
    
     // Write a 16bit register at the specified address.
    void write_reg_2byte(int address, uint32_t value) {
        _cs.write(0);
        _spi.write(0x80 | (address & 0xF)); // Instruction byte
        _spi.write((value >> 8) & 0xFF);
        _spi.write((value >> 0) & 0xFF);
        _cs.write(1);
    }
    
     // Write a 8bit register at the specified address.
    void write_reg_1byte(int address, uint32_t value) {
        _cs.write(0);
        _spi.write(0x80 | (address & 0xF)); // Instruction byte
        _spi.write((value >> 0) & 0xFF);
        _cs.write(1);
    }
    
    
    
    // Read a 32bit register at the specified address.
    uint32_t read_reg_4byte(int address) {
        uint32_t value = 0;
        _cs.write(0);
        _spi.write(address & 0x7f);  // Instruction byte
        value |= _spi.write(0);
        value = value << 8;
        value |= _spi.write(0);
        value = value << 8;
        value |= _spi.write(0);
        value = value << 8;
        value |= _spi.write(0);
        _cs.write(1);
        return value;
    }    
        
    // Write the CFCR1 regsiter
    void CFR1_write(uint32_t reg) { write_reg_4byte(0x00, reg); }
    void CFR2_write(uint32_t reg) { write_reg_3byte(0x01, reg); }
    void ASF_write(uint32_t reg) { write_reg_2byte(0x02, reg); }
    void ARR_write(uint32_t reg) { write_reg_1byte(0x03, reg); }
    void FTW0_write(uint32_t reg) { write_reg_4byte(0x04, reg); }
    void POW0_write(uint32_t reg) { write_reg_2byte(0x05, reg); }
    
    
    // Read the CFCR1 register
    uint32_t cfcr1_read(void) { return read_reg_4byte(0); }
        
    // This is just a start. Now carry on expanding this file
    // to add additional functionality for your device.
        
    // The rest is now up to you to write. Note, some registers
    // are 24bit and not 32bit so you will need extra write_reg_
    // and read_reg_ functions.     
       
};

#endif

and main :

 

#include "mbed.h"
#include "AD9951.h"

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

int main() {



    myDevice.CFR1_write(0x00200008);
    myDevice.CFR2_write(0x1802A4);
    myDevice.ASF_write(0xC450);
    myDevice.ARR_write(0xFC);
    myDevice.FTW0_write(0x2A0070A3);
    myDevice.POW0_write(0x0000);




    uint32_t cfcr1;

    cfcr1 = myDevice.cfcr1_read();

    pc.printf("CFCR1 register = %ld\n", cfcr1);

    while (1) {
        myled = 1;
        wait(0.2);
        myled = 0;
        wait(0.2);
    }
}

 

just there it's the complete code. When we run it, we read CFR1 = -1. What does that means ?

17 Jan 2011

-1 is 0xFFFFFFFF so it seems it's just reading MISO high all the time. It's time to get a scope and and see what's going on and doing some of your own debugging. There's really only so far you can go in a forum.

17 Jan 2011

I have a question on your code below :

 

void write_reg_3byte(int address, uint32_t value) {
_cs.write(0);
_spi.write(0x80 | (address & 0xF)); // Instruction byte

 

where did the 0x80 come from, please ? I try to understand but it's very hard for me =)

 

17 Jan 2011

0x80 is hexadecimal (numbers in base 16 format).

It's used here to ensure the byte has bit7 set which the datasheet says is required for the instructtion by to mean "write". the address & 0xF is used to ensure the address you supply is within the 0 to 15 (0xF == 15 decimal) rregister address range.

Could you tell us, are you a student building a project? These are fundamental questions regarding programming you're asking. Perhaps you have a tutor that these questions really ought to be aimed at?

18 Jan 2011

Thanks for your help. We modified some parts of your code and it's work. Thank you for your advice. We spent our night on it.

We are student building a huge project and the informatic (code) part is really a little one. That's why we asked some basics questions. We try to learn at the same time but it's very difficult for us, that's why your help is very precious for us.

Moreover, our tutor is helping us too but he is not a good C/C++ programmer at all.

Have a good day

 

12 Dec 2015

There has been an error in the AD9951 data sheet which leads to some confusion about byte order when reading or writing registers. Analog Devices is correcting this error in the next revision of the data sheet. I have a tested and working set of routines based on the procedures written earlier in this post by Andy Kirkham.

/***************

Copied and modified from discussion forum but the basic code was originally done by Andy Kirkham

Andy Kirkham's proviso below ...... Note, I don't have an AD9951 so this is untested experimental code. It compiles but I have no idea if it actually works. It's purely demo code.

I started working on this circa 20 April, 2015 for use in my Return Loss bridge.

The order of the bytes sent or received is, in the AD9951 data sheet, the opposite of what actually works. That is to say, there is a conflict between the description and the example given in the next paragraph. I gather from the Web, everyone assumes most-significant bytes first.!

Jim Koehler, VE5FP Comox, October, 2015

Routines work correctly, December, 2015

***************/

  1. ifndef AD9951_H
  2. define AD9951_H
  1. include "mbed.h"
  2. include "definitions.h" the definitions.h file contains the definitons of 'multiplier' and ClockFrequency' used in these procedures

class AD9951 {

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

public:

AD9951(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName rst, PinName io_update) : _spi(mosi, miso, sclk), _cs(cs), _rst(rst), _iou(io_update) { _spi.format(8, 0); _spi.frequency(1000000); _cs.write(0); _rst.write(1); wait(0.01); _rst.write(0); _cs.write(1); deselect the AD9951 ... it is again selected in each of the procedures and functions };

void Pulse_IO_Update(void) { _iou.write(1); wait_us(25); _iou.write(0); }

Write a 32bit register at the specified address. void write_reg_4byte(int address, uint32_t value) { _cs.write(0); wait_us(10); _spi.write(address); _spi.write((value >> 24) & 0xFF); _spi.write((value >> 16) & 0xFF); _spi.write((value >> 8) & 0xFF); _spi.write(value & 0xFF); wait_us(10); Pulse_IO_Update(); wait_us(10); _cs.write(1); }

Write a 24bit register at the specified address. void write_reg_3byte(int address, uint32_t value) { _cs.write(0); wait_us(10); _spi.write(address); _spi.write((value >> 16) & 0xFF); _spi.write((value >> 8) & 0xFF); _spi.write(value & 0xFF); wait_us(10); Pulse_IO_Update(); wait_us(10); _cs.write(1); }

Write a 16bit register at the specified address. void write_reg_2byte(int address, uint32_t value) { _cs.write(0); wait_us(10); _spi.write(address); _spi.write((value >> 8) & 0xFF); _spi.write(value & 0xFF); wait_us(10); Pulse_IO_Update(); wait_us(10); _cs.write(1); }

Write a 8bit register at the specified address. void write_reg_1byte(int address, uint32_t value) { _cs.write(0); wait_us(10); _spi.write(address); _spi.write(value & 0xFF); wait_us(10); Pulse_IO_Update(); wait_us(10); _cs.write(1); }

Read a 32bit register at the specified address. uint32_t read_reg_4byte(int address) { uint32_t value = 0; _cs.write(0); wait_us(10); _spi.write(address | 0x80); value = _spi.write(0) << 24; value |= _spi.write(0) << 16; value |= _spi.write(0) << 8; value |= _spi.write(0); wait_us(10); _cs.write(1); return value; }

Read a 24bit register at the specified address. uint32_t read_reg_3byte(int address) { uint32_t value = 0; _cs.write(0); wait_us(10); _spi.write(address | 0x80); value = _spi.write(0) << 16; value |= _spi.write(0) << 8; value |= _spi.write(0); wait_us(10); _cs.write(1); return value; }

Read a 16bit register at the specified address. uint32_t read_reg_2byte(int address) { uint32_t value = 0; _cs.write(0); wait_us(10); _spi.write(address | 0x80); value = _spi.write(0) << 8; value |= _spi.write(0); wait_us(10); _cs.write(1); return value; }

Read a 8bit register at the specified address. uint32_t read_reg_1byte(int address) { uint32_t value = 0; _cs.write(0); wait_us(10); _spi.write(address | 0x80); value = _spi.write(0); wait_us(10); _cs.write(1); return value; }

Write the regsiters void cfcr1_write(uint32_t reg) {write_reg_4byte(0x00, reg);} void cfcr2_write(uint32_t reg) {write_reg_3byte(0x01, reg);} void asf_write(uint32_t reg) {write_reg_2byte(0x02, reg);} void arr_write(uint32_t reg) {write_reg_1byte(0x03, reg);} void ftw_write(uint32_t reg) {write_reg_4byte(0x04, reg);} void pos_write(uint32_t reg) {write_reg_2byte(0x05, reg);}

Read the registers uint32_t cfcr1_read(void) {return read_reg_4byte(0);} uint32_t cfcr2_read(void) {return read_reg_3byte(1);} uint32_t asf_read(void) {return read_reg_2byte(2);} uint32_t arr_read(void) {return read_reg_1byte(3);} uint32_t ftw_read(void) {return read_reg_4byte(4);} uint32_t pos_read(void) {return read_reg_2byte(5);}

reset the AD9951

void reset(void) { _rst.write(1); wait(0.01); _rst.write(0); }

initialize the AD9951 to use external clock rather than a crystal for the clock void init (void) {

_rst.write(1); wait(0.01); _rst.write(0); wait_us(10); cfcr1_write(0x00000202); set SDIO to input only and disable SYNC_CLK cfcr2_write(0xa6); multiply by 20x, set the VCO_range bit }

double frequency(double f) { returns the actual frequency in floating point uint64_t ftw;

ftw = (int64_t) (f * two_to_32) / ((int64_t) (multiplier * ClockFrequency)); ftw_write((uint32_t) ftw); ftw = (int64_t) ftw_read(); return (double)((ftw * multiplier * ClockFrequency ) / ((int64_t) two_to_32)); return ftw_read(); }

float amplitude(float amp) { gives full amplitude with 100.0, zero output for 0.0, returns amplitude //

uint32_t v;

if (amp >= 100.0) amp = 100.0; if (amp <= 0.0) amp = 0;

v = (int) ((amp / 100.0) * 16383.0); asf_write(v); return (asf_read() * 100.0 / 16383);

}

uint32_t read_register(int i) { reads and returns the appropriate register uint32_t v = 99;

switch (i) { case 0: v = cfcr1_read(); break; case 1: v = cfcr2_read(); break; case 2: v = asf_read(); break; case 3: v = arr_read(); break; case 4: v = ftw_read(); break; case 5: v = pos_read(); break; }

return v; } };

  1. endif