Nonsense data from SCA3000 accelerometer

Edit: Leaving for the weekend. I’ll check back on Monday or Tuesday. Thanks!

I’m trying to interface my Arduino Pro Mini with an SCA3000 accelerometer. Although it appears as though I can communicate with it via SPI, the accelerometer always returns 0x03, then 0x21 over the bus, no matter what I send. That appears to be a valid packet according to the datasheet - in the first byte, bit two is high and the parity bit is correct. But I can’t read the mode register, can’t write the mode register, and can’t read the acceleration data registers - it always just returns 0x21. I’ll attach my code at the bottom of the post. I’m using CPHA=0, CPOL=0, and running the SPI bus at 1MHz (fOSC/16), which is within the specified max SPI speed (1.6MHZ).

On the electrical side, I’m using the breakout board from Sparkfun and a Sparkfun logic level converter. (I know, loyal Sparkfun customer.) I have a standard 3.3V regulator providing low reference voltage to the level converter. I’m feeding 5V into the SCA3000 breakout board, because it has its own on-board regulator. Everything shares a ground. I tried to keep my wires short. Because of the breadboard layout, the total length of each bus wire is probably around 6 inches. There’s also an LCD on board which could be removed and replaced later. Power is coming from the USB connection. If that might be a problem, I could get a regulator out and use a wall wart. I do not have any filtering capacitors on my 3.3V reference voltage regulator, but I assume since it’s powered by a clean 5V source and within 3 inches of what it’s powering that it doesn’t need any. It’s a AZ1117T-3.3EI; you can find the datasheet on alldatasheet.com.

Anybody got any ideas? I’m new at this SPI nonsense, so is there something obvious I missed? I’ve tried looking through other forum posts on the SCA3000, but nobody described this particular difficulty.
Code:

Accel_Printer.pde

// Accel Printer
// Gets acceleration data via SPI from the SCA3000 accelerometer
// Prints them to a standard LCD

// #include <LiquidCrystal.h>
#include "SCA.h"

// LiquidCrystal lcd(14, 15, 16, 2, 3, 4, 5, 6, 7, 8, 9);

#define ACCEL_RST_PIN 17
#define ACCEL_SS_PIN 10

void setup()
{
  pinMode(ACCEL_RST_PIN, OUTPUT);
  digitalWrite(ACCEL_RST_PIN, LOW);
  Serial.begin(9600);
  Serial.println("Resetting accel...");
  delay(2000);
  SCA_init(ACCEL_SS_PIN);
  // now let up the reset pin
  digitalWrite(ACCEL_RST_PIN, HIGH);
  
  unsigned char dont_care;
  int error;
  // try writing the mode register
  if (error = SCA_write_register(0x14, 0x0, &dont_care))
  {
    // we don't care if the previous frame was invalid
    if (error != SCA_PREVIOUS_FRAME_INVALID)
    {
      Serial.print("Error getting device mode! Error: ");
      Serial.println(error);
      while (1);
    }
  }
  
  // now try reading the mode register
  unsigned char sca_mode;
  if (error = SCA_read_register(0x14, &sca_mode))
  {
    Serial.print("Error getting device mode! Error: ");
    Serial.println(error);
    while (1);
  }
  if (sca_mode != 0x0)
  {
    Serial.print("Expected mode 0x0, got mode 0x");
    Serial.println(sca_mode, HEX);
    while (1);
  }
  
  Serial.print("Device mode: 0x");
  Serial.println(sca_mode, HEX);
  delay(2000);
}

void loop()
{
  int x_accel, error;
  if (error = SCA_read_dual_register(0x05, 0x04, &x_accel))
  {
    Serial.print("Error: ");
    Serial.println(error);
    while(1);
  }
  else
  {
    Serial.println(x_accel);
  }
  delay(5);
}

SCA.cpp

// SCA.c
// Class for communicating with the SCA3000 accelerometer via SPI.

#include <avr/io.h>
#include "wiring.h"
#include "HardwareSerial.h"
#include "SCA.h"

int SCA_ssPinNo;

void SCA_init(int ssPinNo)
{
  SCA_ssPinNo = ssPinNo;
  pinMode(SCA_ssPinNo, OUTPUT);
  digitalWrite(SCA_ssPinNo, HIGH);
}

void SCA_prepare_bus()
{
  // Enable SPI, no interrupt, mode 0, master, big-endian bit order, 1MHz bus speed to start (fosc / 16)
  SPCR = _BV(SPE) | _BV(MSTR) | _BV(SPR0);
  // disable 2x bus speed (if it wasn't already)
  SPSR = 0;
  digitalWrite(SCA_ssPinNo, LOW);
}

void SCA_close_bus()
{
  digitalWrite(SCA_ssPinNo, HIGH);
}

// pass 0 for don't care
// synchronous blocking - won't return until finished
unsigned char SCA_SPI_transfer(unsigned char data)
{
  // Serial.print("SPI out: 0x");
  // Serial.println(data, HEX);
  SPDR = data; // begin transmission
  loop_until_bit_is_set(SPSR, SPIF); // wait for transmission to finish
  unsigned char r = SPDR;
  // Serial.print("SPI  in: 0x");
  // Serial.println(r, HEX);
  return r;
}

// must pass 0 or 1 for should_write
// address must be no more than 6 bits
int SCA_transfer(unsigned char address, unsigned char data, unsigned char *ret_val, int should_write)
{
  int error = 0;
  SCA_prepare_bus();
  address <<= 1;
  address |= should_write;
  address <<= 1;
  unsigned char error_info = SCA_SPI_transfer(address);
  error = ((error_info & (1<<1)) ? 0 : SCA_READ_ERROR);
  error = error ? error : ((error_info & 0xBC) ? SCA_READ_ERROR : 0);
  *ret_val = SCA_SPI_transfer(data);
  // compute parity
  unsigned char parity = *ret_val ^ (*ret_val >> 4);
  parity &= 0xf;
  parity = (0x6996 >> parity) & 1;
  error = error ? error : ((error_info ^ parity) ? 0 : SCA_PARITY_MISMATCH);
  error = error ? error : ((error_info & (1<<6)) ? SCA_PREVIOUS_FRAME_INVALID : 0); // show this one at lowest priority - we care about other two more
  SCA_close_bus();
  return error;
}

////////////////////////////////////////////////////// public stuff

/* read_register
 * address: 6-bit address of the register to be accessed
 * ret_val: storage for value read from accelerometer
 * returns: 0 on success, one of the error codes defined in SCA.h on failure
 */
int SCA_read_register(unsigned char address, unsigned char *ret_val)
{
  return SCA_transfer(address, 0, ret_val, 0);
}

/* write_register
 * address: 6-bit address of the register to be written
 *    data: value to be written
 * ret_val: storage for value read from accelerometer
 * returns: 0 on success, one of the error codes defined in SCA.h on failure
 */
int SCA_write_register(unsigned char address, unsigned char data, unsigned char *ret_val)
{
  return SCA_transfer(address, data, ret_val, 1);
}

/* read_dual_register
 * addr_msb: address of msb register
 * addr_lsb: address of lsb register
 *  ret_val: storage for value read
 *  returns: 0 on success, one of the error codes defined in SCA.h on failure
 */
int SCA_read_dual_register(unsigned char addr_msb, unsigned char addr_lsb, int *ret_val)
{
  unsigned char msb, lsb;
  int error;
  if (error = SCA_read_register(addr_msb, &msb))
  {
    return error;
  }
  if (error = SCA_read_register(addr_lsb, &lsb))
  {
    return error;
  }
  *ret_val = msb;
  *ret_val <<= 8;
  *ret_val |= lsb;
  *ret_val >>= 3;
  return 0;
}

SCA.h

// SCA.h
// Functions for communicating with the SCA3000 Accelerometer via SPI.

#ifdef __cplusplus
extern "C" {
#endif

int SCA_read_dual_register(unsigned char addr_msb, unsigned char addr_lsb, int *ret_val);
int SCA_read_register(unsigned char address, unsigned char *ret_val);
int SCA_write_register(unsigned char address, unsigned char data, unsigned char *ret_val);
void SCA_init(int ssPinNo);

#ifdef __cplusplus
}
#endif

// Error Codes
#define SCA_PREVIOUS_FRAME_INVALID 1
#define SCA_READ_ERROR 2
#define SCA_PARITY_MISMATCH 3

Hmm....6 inches of loose wires may be a bit much for a 1 MHz clock. Also 1117-type regulators generally DO require some minimum capacitance (4.7uF or so) at the output for stability, though if you're getting SPI data back from the device that's probably not it.

Try:

1) Shorter wires 2) 100 ohm resistor in series with the clock signal 3) Slower SPI clock 4) 4.7uF on the output of the AZ1117 (bigger is better)

Ah yes, I remember that feeling of confusion very well!

A quick glance looks like you've got the bit shifting correct, but for whatever reason, you have to have a 16bit read rather than use two single byte transfers. I used a sample 16bit read routine I found on VTI's website.

Check out my post over on the SparkFun forum: http://forum.sparkfun.com/viewtopic.php?t=1436 I've also got a related post on the Arduino forum, but the sketch I posted over there wouldn't fit here. I skipped, (or more accurately actually didn't know) about the SPI bus code you included. Thanks for the sample code there!

Do you have intentions of sharing your library when you get it working?

Cheers

emdee, I don't understand what you mean by the 16-bit read. I've got my code so that it uses the hardware SPI support to transfer two bytes back-to-back without letting the SS line go high. I found the VTI sample code from their page about the SCA3000, and it looked like they were doing something similar. I'll try compiling theirs and seeing if it will work with my hardware. If it doesn't, then I'll start making the electrical changes that RuggedCircuits suggested. Thanks again, everybody! EDIT: I forgot - yes, I plan to release the library once it's finished. It's hopefully going to be a part of a much larger project, but I'll release this part separately open-source. :D EDIT: emdee, the post you linked to doesn't seem to relate. Maybe the wrong link?

I’m still just not getting it. I’ve rewired everything, and I’m trying to use the code provided by VTI with minor modifications. The code I’m now testing with is at the bottom of the post.
On the physical side, I’ve ripped everything out of the breadboard and started over. I’ve got the SCK, SS, MOSI, and RESET lines running through the Sparkfun Logic Level Converter. I don’t have an oscilloscope, but the pins that are supposed to hold a steady voltage read correctly using my multimeter. I have MISO connected directly from the accel to the Arduino. It’s a 3.3V part and the Arduino is 5V. Shouldn’t that work? Could the Arduino have set the MOSI pin (pin 12) high at some point in the start up sequence and cooked the accelerometer?
I’ve now got capacitors on both the input and output of the 3.3V regulator. I’m using a 10uF on the input and a 33uF on the output. They’re both electrolytic.
I wanted to try putting the 100 ohm resistor in series with the SCK line, but should I put it in before or after the signal is converted down to 3.3V from 5?
I’m going to try and learn how to use Eagle and post a schematic of what I’m doing.
Thanks again for all your help!

EDIT: I forgot to describe what was wrong! When running the below code, it never finishes the SPI transfer - i.e., the SPIF flag never gets set. Since the Arduino has an LED connected to pin 13, I can see that SCK is running forever, but SPIF never gets set.

/* This example reads SCA3000 X acceleration registers MSB and LSB. 
 * The register address of X channel is 0x05 (MSB). 
 * NOTE: The command byte of SPI bus is : 
 *       (MSB) A A A A A A A RW 0 (LSB) 
 *             |           |  | *- always zero 
 *             |           |  *--- Read / Write Bit 
 *             \-----------*------ Register address, 0x05 at this example 
 * 
 * Because of the real byte sent to SPI bus must be calculated: 
 * BYTE = (Address * 4) + RW*2;     
 * For Read operation RW = 0 and for Write operation RW = 1 
 * 'Address *4' shifts the bit pattern to left by 2 and 'RW*2' by 1. 
 *  BYTE = 0x05*4 + 0; 
 *  BYTE = 0x14;      
 * This calculation is done inside the function. 
 * 
 * Usage of the following function: 
 * 
 * int Xacc; 
 * 
 * Xacc = SCA3000_read_16bit(0x05); 
 * 
 * This example has been written for an Atmel ATmega168 processor in a GCC environment.  
 * Other processors or environments might need different names ports or SPI device names. 
 * SPDR - is the read/ write data to/ from SPI bus register  
 * (see ATmega168 document (doc2545.pdf) pages 159 - 167 for more information) */ 
  
#define SPI_CSB 0x04   // This is PB2 at the port 
#define SPI_PORT PORTB 
 
// Function takes in the 8-bit register address and returns 16-bit register value. 
int SCA3000_Read_16bit(unsigned int Address) 
{ 
 int result; 
 result = 0; 
 Address = Address << 2;  // RW bit is set to zero by shifting the bit  
      // pattern to left by 2  
 
 SPI_PORT = SPI_PORT & (~SPI_CSB); // Set CSB to zero 
 SPDR = Address;   // Write command to SPI bus 
 
 while(!(SPSR & (1 << SPIF))) // Wait until data has been sent 
  ; 
 
 SPDR = 0x00;    // Write dummy byte to line 
      // in order to generate SPI clocks for data read 
 while(!(SPSR & (1 << SPIF))) // Wait until data has been sent 
  ; 
 
 result = SPDR;   // Get MSB of the result 
 result = result << 8;  // Shift the MSB of the result to left by 8 
  
 SPDR = 0x00;    // Write dummy byte to line  
      // in order to generate SPI clocks for data read 
 while(!(SPSR & (1 << SPIF))) // Wait until data has been sent 
  ; 
 
 result |= SPDR;   // Get LSB of the result 
 
 SPI_PORT = SPI_PORT | SPI_CSB; // Set CSB to one 
 return result; 
}

// above part is VTI sample code, copied & pasted
// below part was written by me

#define SS_PIN 10
#define RESET_PIN 9

void setup()
{
  SPSR = 0;
  SPCR = _BV(MSTR) | _BV(SPR1) | _BV(SPR0) | _BV(SPE);
// SPI is run at fOSC/128, so 125KHz on my 16MHz Arduino
  
  pinMode(SS_PIN, OUTPUT);
  digitalWrite(SS_PIN, HIGH);
  
  pinMode(RESET_PIN, OUTPUT);
  digitalWrite(RESET_PIN, LOW);
  delay(1000);
  digitalWrite(RESET_PIN, HIGH);
  
  Serial.begin(9600);
  Serial.println("Starting...");
}

void loop()
{
  Serial.println(SCA3000_Read_16bit(0x05));
  delay(1000);
}

Very strange...once an SPI transfer starts it should complete. I don't see in your code how you are blinking the LED to indicate a transfer is complete.

Try taking the accelerometer out of the equation and just wiring MISO to MOSI (i.e., try a loopback test).

The LED is connected on the Arduino PCB to pin 13, which is also SCK. So, when the clock runs, the LED lights up kind of dimly. I know the LED interfered with SPI in the NG version Arduinos, but it's not supposed to on the Pro Mini. I did try running the Pro Mini as a slave in a different (all 5V) circuit, and that worked. I'll try the loopback thing later this afternoon. Thanks.

EDIT: Tried connecting MOSI to MISO and got nothing but 0 on the SPI in. I didn't pull the accelerometer from the board, but I assumed that wouldn't matter. When I tested with my own code, I got the same thing as before the rewire - messages that exactly fit the protocol specified by the accelerometer SPI interface, but nonsense data (0x3, then 0x21). It even signaled that the "SPI packet" sent while the Arduino was initializing itself was invalid according to the protocol in the datasheet! So, it seems like the SPI bus is working and like I'm just misunderstanding how to use the part.

EDIT again: I tried doing the decremented read trick to read both bytes of the X accel at once. It read 0 for the second byte and returned a malformed SPI packet error when I tried to read the next packet. So, it's returning data according to the SPI packet spec, but not reading the registers. One again, it seems like my hardware is working, but I'm misunderstanding the part.

Well, I finally figured it out, and it's more than a bit embarrassing. I finally re-read the SPI section in the ATMega328P datasheet and discovered that the data direction on the SPI pins is not set automatically. Doh. SCK was set by the bootloader blinking the LED on pin 13, SS was set by my library, and MISO is set automatically, leaving MOSI set as an input when it was supposed to be an output. So, my library for interfacing with the device is almost done. I'll post it somewhere online and provide a link. Thanks for all the help.