Bizzare SPI issue. 3 engineers stumped!

Right, so I'm making a driver for the AD5024. A 4 channel DAC chip made by analog devices. Datasheet here: http://www.analog.com/static/imported-files/data_sheets/AD5024_AD5044_AD5064.pdf

Now, here's the trick. I have the chip working flawlessly on C# .NET Microframework. I am using very similar code with only a few tweaks to the variable types and of course the hardware interface changes.
NETMF driver: AD5024 NETMF driver - Pastebin.com
Failing Arduino driver: Arduino AD5024 driver (not working) - Pastebin.com

  • I know the data bytes are being formed correctly. I did a Serial.print() in the loop that sends the SPI data, and I can clearly see everything is working properly. The issue exists in the SPI interface itself.

I am using the following code to begin the SPI bus:

	pinMode(10, OUTPUT);		// Slave select, so that 
	SPI.begin();				// SPI to DAC
	SPI.setBitOrder(MSBFIRST);
	SPI.setDataMode(SPI_MODE0);
	SPI.setClockDivider(SPI_CLOCK_DIV16); // SPI clock 1000Hz

Keep in mind, this doesn't work no matter what mode is set to.

Now, to make troubleshooting a bit easier, I took scope grabs of the same command (ad5024Write(AD5024_COM_UPDATE_TO_N, AD5024_ADDR_DACD, 257);).

Note the difference between the NETMF (working) and the Arduino (obviously not working). For one, the clock is inverted, but a bigger one is in the HIGH blobs and their position under the clock.

NETMF:

Arduino:

Does anybody have ANY idea what on earth is going on here? I have 3 engineers totally stumped as to what I could change in the SPI lib to change this, or what could be going wrong.

chris1seto:
I have the chip working flawlessly on C# .NET Microframework.

I'm not sure what that means.

How about grabbing a logic analyzer?

Note the difference between the NETMF (working) and the Arduino (obviously not working).

That's not obvious to me.

chris1seto:
I am using the following code to begin the SPI bus:

	pinMode(10, OUTPUT);		// Slave select, so that 
SPI.begin();				// SPI to DAC
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0);
SPI.setClockDivider(SPI_CLOCK_DIV16); // SPI clock 1000Hz



Keep in mind, this doesn't work no matter what mode is set to.

Yeah, that works. How about posting all your code?

The SPI interface works. People use it to talk to SD cards, Ethernet cards, all sorts of stuff. So the stuff you haven't posted will be the issue.

HIGH blobs and their position under the clock.

Is this a new way to say that the data is different?

I have the chip working flawlessly on C# .NET Microframework.

I don't know what that means either, but whatever presumably you can't be running the same code on both platforms?

What code is this?

ad5024Write(AD5024_COM_UPDATE_TO_N, AD5024_ADDR_DACD, 257)) // is this right, yours had a smiley

Is it supposed to send the value 257 as two bytes, if so neither of those traces seem to show that value so I'd say they are both not working.


Rob

I am not an engineer, so maybe I can help.

When you changed the SPI_MODE, did the polarity of the clock change?
SPI_MODE0 and SPI_MODE1 should use a base clock of 0. SCK is normally LOW, clock pulse goes HIGH.
SPI_MODE2 and SPI_MODE3 should use a base clock of 1. SCK is normally HIGH, clock pulse goes LOW.

SPI_MODE0 and SPI_MODE3 capture data on the rising edge of the clock.
SPI_MODE1 and SPI_MODE2 capture data on the falling edge of the clock.

There is more info here about clock polarity and phase.

Allow me to explain a few things:

I don't know what that means either, but whatever presumably you can't be running the same code on both platforms?

.NETMF is another microcontroller platform. I coded a driver for this DAC for both boards. The NETMF one works, the Arduino one doesn't. You can see the board I am using for the .NET stuff here: http://www.ghielectronics.com/catalog/product/133

Yeah, that works. How about posting all your code?

This is all my code. Granted, it's pieced together as it's distributed across a bunch of files. right now, there's nothing else going on in those files, it's all commented out. SPI is initialized exactly the way I pasted, and then there is a call to

ad5024Write(AD5024_COM_UPDATE_TO_N, AD5024_ADDR_DACD, 257)

is made, which uses the function posted on Pastebin. That's all there is to it.

The SPI interface works. People use it to talk to SD cards, Ethernet cards, all sorts of stuff. So the stuff you haven't posted will be the issue.

There isn't anything else that I haven't posted. the rest of the program is commented out.

Is it supposed to send the value 257 as two bytes, if so neither of those traces seem to show that value so I'd say they are both not working.

Hmm, well, yeah, the upper bit of 257 should be in the bit before the byte that contains the rest of the value. this command does work on the .NET board, though.

As far as the clock phase and polarity, from the .NET trace we can see the clock idles low, which means it will be either MODE0 or MODE1. From the DAC's datasheet, it says MODE1 is correct, but this doesn't solve any problems.

Mode 1 should be correct. Have you taken scope grabs in that mode? Does the new scope grab look like the NETMF grab?

Mode1:
http://files.chrisseto.com/LGW

I downloaded both the NETMF and the mode 1 grabs so I could check the timing better. It seems to me by the timing on the scope grab that works (NETMF), that is mode 0. The data is set to be sampled on the rising edge. The falling edge is the data change. Or am I mistaken?

I'm not sure exactly, but here is the mode 0 capture:
http://files.chrisseto.com/M84

Mode 0 capture looks a lot more like the NETMF capture to me. Data changes happen on the falling edge and data valid on the rising edge. ??

I honestly don't know any more.

No matter what mode it's in, the device is either flaky or doesn't work at all (modes 2/3)

chris1seto:
This is all my code.

Not really, but I managed to assemble it into something that compiled. You might have done that yourself to set up a test case.

  #include <SPI.h>
  
    /*
            AD5024 Arduino Driver
                    -- Chris Seto, June 2012
    ****/
     
    // Constants
     
    // AD5024 Commands
    #define AD5024_COM_WRITE_TO_N                           0
    #define AD5024_COM_UPDATE_TO_N                          1
    #define AD5024_COM_WRITE_TO_N_UPDATE_ALL        2
    #define AD5024_COM_WRITE_TO_UPDATE_N            3
    #define AD5024_COM_POWER_DOWN_POWER_UP          4
    #define AD5024_COM_LOAD_CLEAR_CODE_REGISTER     5
    #define AD5024_COM_LOAD_LDAC                            6
    #define AD5024_COM_RESET                                        7
    #define AD5024_COM_DAISY_CHAIN_ENABLE           8
     
    // AD5024 Addresses
    #define AD5024_ADDR_DACA                                        0
    #define AD5024_ADDR_DACB                                        1
    #define AD5024_ADDR_DACC                                        2
    #define AD5024_ADDR_DACD                                        3
    #define AD5024_ADDR_DACALL                                      15
     
    /*
            AD5024 Arduino Driver
                    -- Chris Seto, June 2012
    ****/
     
    void ad5024Write(word command, word addr, word value)
    {
            // Range check the value. Must be 12 bits
            if (value > 4095)
            {
                    value = 4095;
            }
     
            // Time to assemble the DAC packet
            byte dacPacket[4] = { 0, 0, 0, 0 };
     
            // 0 -- 4 bits don't care / command
            dacPacket[0] = (byte)command;
     
            // 1 -- 4 bits address / Left most 4 bits of value
            dacPacket[1] |= (byte)((word)addr << 4);        // Address
            dacPacket[1] |= (byte)(value >> 8);     // Value
           
            Serial.print("leftmost:");
            Serial.println(dacPacket[1]);
     
            // 2 -- Rightmost 8 bits of value
            dacPacket[2] = (byte)(value & 255);
            Serial.print("Rightmost:");
            Serial.println(dacPacket[2]);
           
            digitalWrite(10, LOW);
            // Spool off to the device
            for (int i = 0; i < 4; i++)
              {
              SPI.transfer(dacPacket[i]);
              }
            digitalWrite(10, HIGH);
    }

void setup ()
{
  Serial.begin (115200);
  pinMode(10, OUTPUT);		// Slave select, so that 
  SPI.begin();				// SPI to DAC
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV16); // SPI clock 1000Hz
}

void loop ()
{
  ad5024Write(AD5024_COM_UPDATE_TO_N, AD5024_ADDR_DACD, 257);
  delay (100);
}

This is what I get from the above:

It looks right to me. In what way do you believe it is wrong? Be specific.

I took out the delay(5) before and after doing the SPI writes. Why do you want those? The device might not like being selected and not used for 5 mS.

If the NETMF program clock/data works, modes 2 and 3 are not correct. You are down to either mode 0 or 1, depending on when the data is sampled. Mode 0 data is changed on the falling edge (like NETMF) so data will be valid (sampled) on the rising edge. Mode 1 data is changed on the rising edge so data will be valid (sampled) on the falling edge.

That is all I have.

Thanks, this is right in the sense that this is what I am doing.

Gimmie a few minutes and I'll drop this down and see what this does to the DAC

Doesn't work in either MODE0 or MODE1. No response from the DAC on any channels. I can see the data on the line going in, but the DAC doesn't seem to do anything.

But you can't point to anything wrong with the logic analyzer output? So it is something electrical or timing.

Judging by the datasheet, it's mode 1. Are you using sync or async mode?

All of the pins on the DAC that are not Vrefs, Vouts, power, or SPI are grounded (POR, CLR, etc) SYNC* is connected to pin 10, or CS*

I don't immediately see anything wrong with the LA capture, but it's a little hard to tell what shoudl ook right with this chip given that there's people already saying that the NETMF capture doesn't look quite right either.

EDIT: I'm in standalone mode with the pin tied LOW permanently.

there's people already saying that the NETMF capture doesn't look quite right either.

That would be me, but now I've looked at the code (should have done that before) I see that there's addressing info in the word as well so it's probably OK.


Rob