Multibyte SPI read with Dimitech 2820L Global Orientation Module

Hi all, I wonder is people could point me in the right direction.

I am attempting to read multiple bytes of data coming back on a modified SPI interface from a combined GPS, Acellerometer, Compass, Thermometer sensor module from Dimitech.

http://www.dimitech.com/downloads/dtx1-2820l.pdf

I'm having a hell of a time with it. :frowning:

I thought I would start small and just read back the configuration of the GPS module on the board, using the Address/Command structure from the data sheet.

Which I take to mean, pull the CS low, send the Address (0x30), send the register to read (0x40, GPS mode register). It will then send the "dummy" packing byte of 0xFF, the data "payload" and then its own address (0x30 again) to signal the final packet.

Below is my code which is a mashup of a tronixstuff tutorial and the SparkFun ADXL345_Basic.pde

/*
 Example 34.1 - SPI bus demo using a Microchip MCP4162 digital potentiometer [http://bit.ly/iwDmnd]
 http://tronixstuff.com/tutorials > chapter 34 | CC by-sa-nc | John Boxall
*/

#include "SPI.h" // necessary library

char values[3];

void setup()
{
  pinMode(10, OUTPUT); // we use this for SS pin
  SPI.begin(); // wake up the SPI bus.
  SPI.setBitOrder(LSBFIRST);
  SPI.setDataMode(SPI_MODE1);
  SPI.setClockDivider(8);
  
  digitalWrite(10, LOW);
    SPI.transfer(0x30); //address byte
    SPI.transfer(0x73); //GPS Mode Register
    SPI.transfer(0x02); //Set 10Hz update mode
  digitalWrite(10, HIGH);
  
  Serial.begin(9600);
}

void loop()
{
  digitalWrite(10, LOW);
  
  SPI.transfer(0x30); 
 
  //Continue to read registers until we've read the number specified, storing the results to the input buffer.

  for(int i=0; i<3; i++){
    values[i] = SPI.transfer(0x70); //GPS Status Read byte
  }
 
  digitalWrite(10, HIGH);

  for(int i=0; i<3; i++){
     Serial.println(values[i], HEX);
  }

  Serial.println("Hello?");
  delay(1000);
}

And a sample of what I see in the serial monitor window.

FFFFFFFF
FFFFFFFF
FFFFFFFF
Hello?
FFFFFFFF
FFFFFFFF
FFFFFFFF
Hello?
FFFFFFFF
FFFFFFFF
FFFFFFFF
Hello?

I believe I am supposed to receive back an 0xFF (dummy byte), 0x02 (10Hz mode) and finally 0x30, its address signalling the end of the transfer.

Any suggestions?

Cheers
Gregg.

The bit order is not LSBFIRST. The datasheet you posted shows MSB first. Section 4.4 "SPI Timings".

  SPI.setBitOrder(MSBFIRST);

Cheers for the heads up, I had taken my LSB requirement from "Section 4.3 Available Commands" where everything lists as LSB. I foolishly made the assumption it was all like that.

However this simple change has had no effect on the output, so I suspect I have something else wrong :frowning:

I think I would try this. There are a couple changes, including a 1us delay after pulling the SS LOW, and the SPI clock divider.

#include "SPI.h" // necessary library

char values[3];

void setup()
{
  pinMode(10, OUTPUT); // we use this for SS pin
  digitalWrite(10, HIGH);

  SPI.begin(); // wake up the SPI bus.
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE1);
  SPI.setClockDivider(SPI_CLOCK_DIV8);
  
  digitalWrite(10, LOW);
  delayMicroseconds(1);
  SPI.transfer(0x30); //address byte
  SPI.transfer(0x73); //GPS Mode Register
  SPI.transfer(0x02); //Set 10Hz update mode
  digitalWrite(10, HIGH);
  
  delay(1000);

  Serial.begin(9600);
}

void loop()
{
  digitalWrite(10, LOW);
  delayMicroseconds(1);  
  SPI.transfer(0x30); 
 
  //Continue to read registers until we've read the number specified, 
  // storing the results to the input buffer.

  for(int i=0; i<3; i++){
    values[i] = SPI.transfer(0x70); //GPS Status Read byte
  }
 
  digitalWrite(10, HIGH);

  for(int i=0; i<3; i++){
     Serial.println(values[i], HEX);
  }

  Serial.println("Hello?");
  delay(1000);
}

These are the clock dividers for the SPI bus from SPI.h. There is no 8. Maybe that causes a problem?

#define SPI_CLOCK_DIV4 0x00
#define SPI_CLOCK_DIV16 0x01
#define SPI_CLOCK_DIV64 0x02
#define SPI_CLOCK_DIV128 0x03
#define SPI_CLOCK_DIV2 0x04
#define SPI_CLOCK_DIV8 0x05
#define SPI_CLOCK_DIV32 0x06

edit: Corrected spelling in code.

Get rid of the "magic number" 10 and put a constant there, like SS.

Make values "byte" rather than "char" that will get rid of the leading FFs.

Other than that (and what SurferTim said) it looks OK. I would put the logic analyzer on it. :slight_smile:

Check your wiring.

SurferTim:
These are the clock dividers for the SPI bus from SPI.h. There is no 8. Maybe that causes a problem?

#define SPI_CLOCK_DIV4 0x00

#define SPI_CLOCK_DIV16 0x01
#define SPI_CLOCK_DIV64 0x02
#define SPI_CLOCK_DIV128 0x03
#define SPI_CLOCK_DIV2 0x04
#define SPI_CLOCK_DIV8 0x05    //  <---------- here
#define SPI_CLOCK_DIV32 0x06

Yes, there is.

  SPI.setClockDivider(8);

Oh yes, that is totally wrong. You have to use the defines SurferTim posted.

8 is not the same as SPI_CLOCK_DIV8.

I rewired everything and now my code now looks like the following, sadly still no major improvements.

/*
 Example 34.1 - SPI bus demo using a Microchip MCP4162 digital potentiometer [http://bit.ly/iwDmnd]
 http://tronixstuff.com/tutorials > chapter 34 | CC by-sa-nc | John Boxall
*/

#include "SPI.h" // necessary library

byte values[3];
int CS=10;

void setup()
{
  pinMode(CS, OUTPUT); // we use this for SS pin
  SPI.begin(); // wake up the SPI bus.
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE1);
  SPI.setClockDivider(SPI_CLOCK_DIV8);
  
  digitalWrite(CS, LOW);
  delay(1);
    SPI.transfer(0x30); //address byte
    SPI.transfer(0x73); //GPS Mode Register
    SPI.transfer(0x02); //Set 10Hz update mode
  digitalWrite(CS, HIGH);
  
  Serial.begin(9600);
}

void loop()
{
  digitalWrite(CS, LOW);
    delay(1);
    
  SPI.transfer(0x30);
 
  //Continue to read registers until we've read the number specified, storing the results to the input buffer.

  for(int i=0; i<3; i++){
    values[i] = SPI.transfer(0x70);
  }
 
  digitalWrite(CS, HIGH);

  for(int i=0; i<3; i++){
     Serial.println(values[i]);
  }

  Serial.println("Hello?");
  delay(1000);
}

My output looks like this.

255
255
255
Hello?
255
255
255
Hello?
255
255
255
Hello?

Which is at least as expected moving from char to byte, however still represents the same 0xFF.

Going to see if I cant find someone with an Analyser to plug it into, if not I suppose its a chance to buy a new toy tool. :slight_smile:

Tim and Nick, many thanks for your help. If anyone spots anything else, I am gratefully receiving suggestions, they don't even need to be on a postcard!

Personally I would get rid of the:

    delay(10);

The datasheet doesn't mention it, and generally you expect SPI to work at the microsecond rate. It might time out with that long a delay.

I would work on the setup function call to determine if the device is initialized correctly.

void setup()
{
  pinMode(10, OUTPUT); // we use this for SS pin
  digitalWrite(10, HIGH);

  SPI.begin(); // wake up the SPI bus.
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE1);
  SPI.setClockDivider(SPI_CLOCK_DIV8);
  
  digitalWrite(10, LOW);
  delayMicroseconds(1);
  SPI.transfer(0x30); //address byte
  byte data1 = SPI.transfer(0x73); //GPS Mode Register
  byte data2 = SPI.transfer(0x02); //Set 10Hz update mode
  digitalWrite(10, HIGH);
  
  delay(1000);

  Serial.begin(9600);
  Serial.print("Data1 = ");
  Serial.println(data1, HEX);
  Serial.print("Data2 = ");
  Serial.println(data2, HEX);
}

The datasheet shows the device returns 0xF5 for data1, and 0x30 for data2 if all is correct. You may need to pull the device out of sleep mode also. The datasheet does not mention if sleep mode is enabled or disabled by default.

Note: I use delayMicroseconds(1) calls, not delay(1).

I beleive I have integrated your suggestions into the latest revision of the code SurferTim (sorry I hadnt spotted the Microsecond delays in the first readthrough)

/*
 Example 34.1 - SPI bus demo using a Microchip MCP4162 digital potentiometer [http://bit.ly/iwDmnd]
 http://tronixstuff.com/tutorials > chapter 34 | CC by-sa-nc | John Boxall
*/

#include "SPI.h" // necessary library

byte values[3];
int CS=10;

void setup()
{
  pinMode(CS, OUTPUT); // we use this for SS pin
  digitalWrite(CS, HIGH);

  SPI.begin(); // wake up the SPI bus.
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE1);
  SPI.setClockDivider(SPI_CLOCK_DIV8);
  
  digitalWrite(CS, LOW);
  delayMicroseconds(1);
  
  //device intitialise
  SPI.transfer(0x30); //address byte
  SPI.transfer(0x43); //Status Register byte
  SPI.transfer(0x03); //New Status Byte - 0x00 – SLEEP, 0x01 – IDLE, 0x02 – SENSOR, 0x03 – ACTIVE The device is fully powered on. GPS is enabled. All the commands on mSPI are serviced.
  
  //GPS Initialise
  byte data1 = SPI.transfer(0x73); //GPS Mode Register
  byte data2 = SPI.transfer(0x02); //Set 10Hz update mode
  
  //Module Config Readback
  byte data3 = SPI.transfer(0x40); //Read Device Config
  byte data4 = SPI.transfer(0x70); //Read GPS Config
  
  digitalWrite(CS, HIGH);
  
  delay(1000);
  
  // Device Config Response
  Serial.begin(9600);
  Serial.print("Data1 = ");
  Serial.println(data1, HEX);
  Serial.print("Data2 = ");
  Serial.println(data2, HEX);
  Serial.print("Data3 = ");
  Serial.println(data3, HEX);
  Serial.print("Data4 = ");
  Serial.println(data4, HEX);
}

void loop()
{
  digitalWrite(CS, LOW);
  delayMicroseconds(1);
    
  SPI.transfer(0x30);
 
  //Continue to read registers until we've read the number specified, storing the results to the input buffer.

  for(int i=0; i<3; i++){
    values[i] = SPI.transfer(0x70);
  }
 
  digitalWrite(CS, HIGH);

  for(int i=0; i<3; i++){
     Serial.println(values[i]);
  }

  Serial.println("Hello?");
  delay(1000);
  
}

This still gave me the dreaded FF's

So in an effort to see if it was an issue with my wiring (I am using a pair of Sparkfun Logic Level converters, so there are quite a few hookups) I tried a simple SPI loopback program. I had to change the SPI_CLOCK_DIV to 64 before I saw the same values that went out, come back. The same couldn't be said for the GOM :confused:

/*
 Example 34.1 - SPI bus demo using a Microchip MCP4162 digital potentiometer [http://bit.ly/iwDmnd]
 http://tronixstuff.com/tutorials > chapter 34 | CC by-sa-nc | John Boxall
*/

#include "SPI.h" // necessary library

byte values[3];
int CS=10;

void setup()
{
  pinMode(CS, OUTPUT); // we use this for SS pin
  digitalWrite(CS, HIGH);

  SPI.begin(); // wake up the SPI bus.
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE1);
  SPI.setClockDivider(SPI_CLOCK_DIV64);

  Serial.begin(9600);
}

void loop()
{
  digitalWrite(CS, LOW);
  delayMicroseconds(1);
    
  byte data1 = SPI.transfer(0x00);
  byte data2 = SPI.transfer(0x22);
  byte data3 = SPI.transfer(0x33);
  byte data4 = SPI.transfer(0xFF);

  digitalWrite(CS, HIGH);
  
  Serial.println(data1, HEX);
  Serial.println(data2, HEX);
  Serial.println(data3, HEX);
  Serial.println(data4, HEX);

  delay(1000);
  
}

Further tests with the echo test indicate that 'FF' is the default response to "not connected".

I clearly need a Logic Analyser to see if any packets are even coming back from the module.

If you are getting back FF, probably not, but one wonders if the query is going out the way you think. Can you post a photo of your setup please?

Nick has a good idea. Insure you have all connections correct, and a photo or diagram for us would be helpful. If that is all correct, then...

See if disabling sleep mode does any better. I reduced the SPI speed even more also. The datasheet states there is a typical 170ns delay required after sending the address byte before the device will be ready, so I added another 1us delay after sending the address byte.

void setup()
{
  pinMode(10, OUTPUT); // we use this for SS pin
  digitalWrite(10, HIGH);

  SPI.begin(); // wake up the SPI bus.
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE1);
  SPI.setClockDivider(SPI_CLOCK_DIV16);
  
  // disable sleep mode
  digitalWrite(10, LOW);
  delayMicroseconds(1);
  SPI.transfer(0x30); //address byte
  delayMicroseconds(1);
  SPI.transfer(0x43); //Write status
  SPI.transfer(0x03); //Set status to active (no sleep)
  digitalWrite(10, HIGH);

  // wait 1us
  delayMicroseconds(1);

  // set GPS mode
  digitalWrite(10, LOW);
  delayMicroseconds(1);
  SPI.transfer(0x30); //address byte
  delayMicroseconds(1);
  byte data1 = SPI.transfer(0x73); //GPS Mode Register
  byte data2 = SPI.transfer(0x02); //Set 10Hz update mode
  digitalWrite(10, HIGH);
  
  delay(1000);

  Serial.begin(9600);
  Serial.print("Data1 = ");
  Serial.println(data1, HEX);
  Serial.print("Data2 = ");
  Serial.println(data2, HEX);
}

Does it still print the FF?

Sadly yes, still the annoying FF SurferTim.

As requested here is a schematic.


Larger - http://i.imgur.com/xz7fIjM.png

And the rats nest.....


Larger - http://i.imgur.com/SUKWy9v.jpg

Can you give a link to the logic level converters please?

...and explain how you are powering that module. If you are using the 3.3v bus on the Arduino, it may not provide enough current.

edit: Insure you have a good ground connection to the Arduino for all those devices.

The Logic Level Converters are these https://www.sparkfun.com/products/8745

I had buzzed out the connections high and low side, so I am pretty confident they are behaving normally.

The module is powered from the 5v bus on the arduino, however I have just checked the 3v3 max current draw an its 50mA max from the Arduino, however the specs show it can draw up to 69mA in active sensor mode. That also doesn't take into account the current drawn by the Level Converters either.

Looks like I will have to sort out a breadboard psu and a 3v3 arduino so I can stop using those level converters and clean up the wiring.

I suspect this project needs to be paused until the above are sourced. Thanks for all your help so far, I will keep everyone updated.

According to Dimitech there is a new firmware for this module. The datasheet shows SPI mode 0 now (previous typo?). Level shifting is definitely needed for 5V boards. Maybe updating the firmware would help?