How to read a MMA7455L using SPI?

Hi, I am just a newbie and not very good at English, please excuse me.

I have trouble in reading a MMA7455L sensor board using my Arduino UNO (SPI).

My wire connection:

Clk -> Pin 13
MOSI -> Pin 11
MISO -> Pin 12
Data Ready -> Pin7
CS or SS? -> Pin 10

My Code:

#include <SPI.h> // include the SPI library

const byte mode = 0x16; // Mode Control Register is $16
const byte measure = 0b00000101; // 1st 01 set to 2g, 2nd 01 set to measure mode
const byte xout = 0x06; // X-Axis value register is $06
const byte yout = 0x07; // Y-Axis value register is $07
const byte zout = 0x08; // Z-Axis value register is $08

const int pinReady = 7;
const int pinCS = 10;

void setup() {
Serial.begin(9600); // open serial port
SPI.begin(); // start the SPI library
pinMode(pinReady, INPUT); // initalize the pins
pinMode(pinCS, OUTPUT); // initalize the pins

// Configure MMA7455 into 2g sensitivies and measure mode
digitalWrite(pinCS, LOW); // select the sensor
SPI.transfer(mode); // Send register location
SPI.transfer(measure); // Send value into that register
digitalWrite(pinCS, HIGH); // de-select the sensor
delay(100); // give the sensor time to set up
}

void loop() {
if (digitalRead(pinReady == HIGH)) {
digitalWrite(pinCS, LOW); // select the sensor
byte valueX = SPI.transfer(xout);
byte valueY = SPI.transfer(yout);
byte valueZ = SPI.transfer(zout);
digitalWrite(pinCS, HIGH); // de-select the sensor

Serial.print("X: ");
Serial.print((int) valueX);
Serial.print(" Y: ");
Serial.print((int) valueY);
Serial.print(" Z: ");
Serial.println((int) valueZ);
}
delay(1000);
}

The Serial Monitor print out only:
X: 0 Y: 0 Z: 0
X: 0 Y: 0 Z: 0
X: 0 Y: 0 Z: 0
X: 0 Y: 0 Z: 0
....

I have read from other post and the MMA7455L data sheet that:

A SPI read transfer consists of a 1-bit Read/Write signal, a 6-bit address, and 1-bit don’t care bit. (1-bit R/W=0 + 6-bits address

  • 1-bit don’t care). The data to read is sent by the SPI interface during the next transfer.

In order to write to one of the 8-bit registers, an 8-bit write command must be sent to the MMA7455L. The write command consists
of an MSB (0=read, 1=write) to indicate writing to the MMA7455L register, followed by a 6-bit address and 1 don’t care bit[/li]

This is my first attempt to use SPI and MMA7455L sensor. I don't know what the above mean, can anyone suggest me how to write the code to read /write from the sensor? Thanks a lot.

Andy

Things to try:

  1. Slow down the SPI clock in case you are trying to go too fast. The default is the 16 MHz clock frequency divided by 4, or 4 MHz. You definitely don't need it that fast. Try calling SPI.setClockDivider(SPI_CLOCK_DIV128); after you initialize the SPI port. You can always go to lower dividers later to speed up the SPI clock once the system is debugged.

  2. Your SPI transfers to get accelerometer data don't look right. Have a look at Figure 11 in the datasheet. You need TWO SPI transfers to read ONE value. The first byte sent sends the read command, then you have to send another byte to actually get the data from the accelerometer:

    byte valueX,valueY,valueZ;
   digitalWrite(pinCS, LOW);
   (void) SPI.transfer(xout);
   valueX = SPI.transfer(0 /*don't care value*/);
   digitalWrite(pinCS, HIGH);

   digitalWrite(pinCS, LOW);
   (void) SPI.transfer(yout);
    valueY = SPI.transfer(yout);
   digitalWrite(pinCS, HIGH);

etc.

  1. Finally, I think your xout/yout/zout values need to be shifted left by 1 bit since the read command is a 1-bit read/write flag, a 6-bit register address (what you currently have), then a 1-bit don't-care bit to make 8 bits:
const byte xout = 0x06<<1;             // X-Axis value register is $06
const byte yout = 0x07<<1;             // Y-Axis value register is $07
const byte zout = 0x08<<1;             // Z-Axis value register is $08

--
The Gadget Shield: accelerometer, RGB LED, IR transmit/receive, speaker, microphone, light sensor, potentiometer, pushbuttons

Thanks a lot RuggedCircuits, that really helpful. I have great difficulties in understanding the protocol of SPI.

From the sample code you provide, may I ask:

To read X value, you suggest:
(void) SPI.transfer(xout);
valueX = SPI.transfer(0 /don't care value/);

but when read Y value, you suggest:
(void) SPI.transfer(yout);
valueY = SPI.transfer(yout);

why use "0" when reading X while "yout" when reading Y value? Please excuse me if my question look silly, I really don't know much about bit / byte things.

One more question, I can understand to make 0x06 a reading address, it should be:

const byte xout = 0x06<<1;

Then, what should I do to make "mode address" 0x16 a "write" address?
My understanding is "1 (write)" + 6bit address + "1 don't care"
If I convert 0x16 into binary "10110"
then add "1" + 010110 + 'don't care' to become "10101100" -> 0xAC
I did try this approach before but turn out fail.

Can you show me how to add a "1" to the address to make it Write? Thanks a lot.

but when read Y value, you suggest:
(void) SPI.transfer(yout);
valueY = SPI.transfer(yout);

Sorry...just a typo on my part. yout can be 0, or anything else really. There is no meaning to this value, it is just something that needs to be written in order to read a value at the same time.

Your thinking on the mode address is correct. It should be 1_010110_0 or 0xAC. Your code looks right....except....try making pinCS high before you bring it low. Right now you configure the pin as an output and bring it low, meaning it was probably never high. Some chips look for a falling edge on the chip select pin, so it's important that it's high first before it goes low. Just a thought.

--
Beat707: MIDI drum machine / sequencer / groove-box for Arduino

Really thanks a lot for your help, I guess I have better understanding on the code.

But unfortunately, the code still doesn't work, here is my revised code:

#include <SPI.h> // include the SPI library

const byte mode = 0xAC; // Write Mode Control Register "1"+0x16+"1bit"
const byte measure = 0x05; // 2g sensitive + Measure Mode "00000101"
const byte xout = 0x0C; // Read XOUT register "0"+0x06+"0"
const byte yout = 0x0E; // Read YOUT register "0"+0x07+"0"
const byte zout = 0x10; // Read ZOUT register "0"+0x08+"0"

const byte i2c_add = 0x9A; // Write I2C address "1"+0x0D+"0"
const byte i2cdis = 0x9D; // Disable I2C 0b10011101, bit 6:0 is read only

const int pinReady = 7;
const int pinCS = 5;

void setup() {
Serial.begin(9600); // open serial port
SPI.begin(); // start the SPI library
SPI.setClockDivider(SPI_CLOCK_DIV128); // tune down the speed of SPI
pinMode(pinReady, INPUT); // initalize the pins
pinMode(pinCS, OUTPUT); // initalize the pins

digitalWrite(pinCS, LOW); // low make the chip in SPI mode
delay(1000); // wait for the chip to setup

// Disable I2C, without this code the DataRady pin never go HIGH
digitalWrite(pinCS, HIGH); // turn from HIGH to LOW to give the signal
delay(10); // without this delay, DataReady pin never go HIGH
digitalWrite(pinCS, LOW); // select the sensor
SPI.transfer(i2c_add); // I2C address write command
SPI.transfer(i2cdis); // disable the I2C
digitalWrite(pinCS, HIGH); // de-select the sensor

// Setup the measure mode
digitalWrite(pinCS, HIGH); // turn from HIGH to LOW to give the signal
digitalWrite(pinCS, LOW); // select the sensor
SPI.transfer(mode); // Write to mode address
SPI.transfer(measure); // Send the mode setting value
digitalWrite(pinCS, HIGH); // de-select the sensor
delay(10); // give the sensor time to set up
}

void loop() {
byte valueX, valueY, valueZ;
if (digitalRead(pinReady) == HIGH) { // data ready
digitalWrite(pinCS, LOW); // select the sensor
(void) SPI.transfer(xout); // read value X
valueX = SPI.transfer(0); // '0' is don't care value
digitalWrite(pinCS, HIGH); // de-select the sensor

digitalWrite(pinCS, LOW); // select the sensor
(void) SPI.transfer(yout); // read value Y
valueY = SPI.transfer(0); // '0' is don't care value
digitalWrite(pinCS, HIGH); // de-select the sensor

digitalWrite(pinCS, LOW); // select the sensor
(void) SPI.transfer(zout); // read value Z
valueZ = SPI.transfer(0); // '0' is don't care value
digitalWrite(pinCS, HIGH); // de-select the sensor

// print out the value
Serial.print("X: ");
Serial.print((int) valueX);
Serial.print(" Y: ");
Serial.print((int) valueY);
Serial.print(" Z: ");
Serial.println((int) valueZ);
}
delay(1000);
}

I still get only "0" from serial monitor
X: 0 Y: 0 Z: 0
X: 0 Y: 0 Z: 0
X: 0 Y: 0 Z: 0
X: 0 Y: 0 Z: 0

I had followed datasheet's instruction to disable the I2C, otherwise the data ready pin never goes HIGH. My previous code failed to check the data ready pin.

Since the chip is I2C / SPI selectable, and pulling CS pin down will make it SPI; high for I2C. I am somewhat fuzzy about this, since it is unavoidable to set the CS pin high before I can pull it low to select it using SPI. Will this make the sensor into I2C mode?

Really sorry to have so many questions. Thank you.

I don't think you need to disable the I2C. It's disabled whenever the CS pin is low and you're talking over SPI.

I think your 'measure' value is wrong. You currently have it set up for 'level' mode instead of 'measuring' mode. I think your 'measure' value should be 0x05<<1 because once again you're off by 1 bit in the bit positions.

--
The Ruggeduino: compatible with Arduino UNO, 24V operation, all I/O's fused and protected

You are so nice!! Thanks a lot!!

I did try to amend the code accordingly for past 2 nights, but still fail to get meaningful result. I doubt the board is inferior since even I upload previous code, it won't response like what is before. At least the DataReady pin refuse to show HIGH again.

May be digital sensor is overkill for me. I may opt for an analog sensor instead.

But I really appreciate your help. Thanks a lot RuggedCircuits.