My main issue is the code for the readRegister function I need to write. I would like to reference the Barometric Pressure Sensor example, http://arduino.cc/en/Tutorial/BarometricPressureSensor ,
to help explain my question.
If you take a look at pg. 23/42 from the LIS3DH datasheet and pg. 23/37 from the pressure sensor's data sheet (https://www.sparkfun.com/datasheets/Components/SCP1000-D01.pdf),
I want to focus on reading using SPI. So, the Pressure Sensor code makes sense why they shift to the left and then they have two spaces left on the two right most bits to manipulate and I'm pretty happy with my understanding of that example code and why it works.
However, the accelerometer I am using seems to have its read/write bit on the left side of the spi diagram (although in the data sheet it says its bit 0?) The MS bit I want to stay 0 and then I am supposed to read the address, but I do not see how am I supposed to change the readRegister function for it to work on this accelerometer. I did this:
unsigned int readRegister(byte thisRegister)
{
unsigned int result = 0; // result to return
// now combine the address and the command into one byte
byte dataToSend = thisRegister | READ;
//Set the Chip select pin low to start an SPI packet
digitalWrite(ss,LOW);
// send the device the register you want to read:
SPI.transfer(dataToSend);
// send a value of 0 to read the first byte returned:
result = SPI.transfer(0x00);
//Set the Chips Select pin high to end the SPI packet.
digitalWrite(ss, HIGH);
return result;
}
where
const byte READ = 0b10000000; // LIS3DH's read command
Can someone please shed some light on my situation, I think this is all I need to figure out to start getting my values. If you need anymore info, please let me know!
const byte READ = 0x80;
const byte MULTIPLE = 0x40;
int X,Y,Z;
void readRegisters() {
//Set the Chip select pin low to start an SPI packet
digitalWrite(ss,LOW);
// send the device the register you want to read:
SPI.transfer( 0x28 | READ | MULTIPLE); // Start reading at the OUT_X_L register (address 0x28)
// send a value of 0 to read the bytes returned:
X = SPI.transfer(0x00) | (SPI.transfer(0x00) << 8);
Y = SPI.transfer(0x00) | (SPI.transfer(0x00) << 8);
Z = SPI.transfer(0x00) | (SPI.transfer(0x00) << 8);
//Set the Chips Select pin high to end the SPI packet.
digitalWrite(ss, HIGH);
}
john, thank you for the reply, but the code doesn't work for me. My plan was to read this way:
int getValue(int axis)
{
int Val = 0;
int h,l;
if (axis == 1)
{
l = readRegister(OUT_X_L);
h = readRegister(OUT_X_L);
}
else if (axis == 2)
{
l = readRegister(OUT_Y_L);
h = readRegister(OUT_Y_H);
}
else if (axis == 3)
{
l = readRegister(OUT_Z_L);
h = readRegister(OUT_Z_H);
}
Val = ((h << 8) | l);
return Val;
}*/
I believe the missing factor is just how do I have to shift the register address before I implement the READ byte. Shifting two to the right (thisRegister = thisRegister >> 2;) does not work because I loose information that way, and shifting to the left does not work because that would be the solution for the pressure sensor example. I tried not shifting it at all but failed getting anything valuable as well. Is there some bitwise operations that I am not aware of (I am a beginner) so that I can move the address to the right place.
This is my setup:
void setup()
{
pinMode(ss,OUTPUT); // we use this for the SS at pin 10
//digitalWrite(ss,HIGH);
SPI.begin(); //wake up the SPI bus
//our device requires data to be sent MSB
//(most significant byte) first, pg. 23
SPI.setBitOrder(MSBFIRST);
//clock is idle high and data is shifted in and out
//on the rising edge of the data clock signal, pg. 23
SPI.setDataMode(SPI_MODE3);
SPI.setClockDivider(SPI_CLOCK_DIV2);
//Other clock values have same result
//Create a serial connection to display the data on the terminal.
Serial.begin(9600);
//Serial.println(readRegister(WHO_AM_I));
delay(100);
}
I should get 0x33 (00110011) when I read WHO_AM_I. I am using that as a way to check if my readRegister function works.
I hope I explained my problem better this time, thank you for helping.
Yeah, so this is really confusing me. I'm going to post the code I'm using in its entirety in the hopes that someone can find the problem with it. I've just been staring at this for days and I can't figure out what's wrong. S.O.S.
#include <SPI.h>
/*#define DATAOUT 11 //MOSI Arduino pin 11; LIS3DH SDI pin6
#define DATAIN 12 //MISO Arduino pin12; LIS3DH SDO pin7
#define SPICLOCK 13 //SCK Arduino pin13; LIS3DH SPC pin4
#define ss 10 //SS Ardunio pin 10; cs LIS3DH pin8, active low, 0: SPI enabled
*/
//Register Addresses:
const int OUT_X_L = 0x28; //X-axis acceleration data
const int OUT_X_H = 0x29;
const int OUT_Y_L = 0x2A; //Y-axis acceleration data
const int OUT_Y_H = 0x2B;
const int OUT_Z_L = 0x2C; //Z-axis acceleration data
const int OUT_Z_H = 0x2D;
const int CTRL_REG1 = 0x20; //Register that can enable,disable axes
const int WHO_AM_I = 0x0F; //Dummy register
const byte READ = 0b10000000; // LIS3DH's read command
const byte WRITE = 0b00111111; // LIS3DH's write command
const byte TEMP_CFG_REG = 0x1F; //Temperature sensor
//Axis definitions
int x_axis = 1;
int y_axis = 2;
int z_axis = 3;
//Arduino ss pin10
const int ss = 10;
void setup()
{
//delay(3000);
pinMode(ss,OUTPUT); // we use this for the SS at pin 10
//digitalWrite(ss,HIGH);
SPI.begin(); //wake up the SPI bus
//our device requires data to be sent MSB
//(most significant byte) first, pg. 23
SPI.setBitOrder(MSBFIRST);
//clock is idle high and data is shifted in and out
//on the rising edge of the data clock signal, pg. 23
SPI.setDataMode(SPI_MODE3);
SPI.setClockDivider(SPI_CLOCK_DIV2);
//Create a serial connection to display the data on the terminal.
Serial.begin(9600);
//We are writing to the CTRL_REG1 address to enable the axes
//we will turn first three bits on for the axes: 0x07=
//writeRegister(CTRL_REG1,0x07);
//Serial.println(readRegister(TEMP_CFG_REG));
Serial.println(readRegister(WHO_AM_I));
delay(100);
}
void loop()
{
Serial.print(" x = ");
Serial.print(getValue(x_axis));
Serial.print(" y = ");
Serial.print(getValue(y_axis));
Serial.print(" z = ");
Serial.println(getValue(z_axis));
delay(500);
}
int getValue(int axis)
{
int Val = 0;
int h,l;
if (axis == 1)
{
l = readRegister(OUT_X_L);
h = readRegister(OUT_X_H);
}
else if (axis == 2)
{
l = readRegister(OUT_Y_L);
h = readRegister(OUT_Y_H);
}
else if (axis == 3)
{
l = readRegister(OUT_Z_L);
h = readRegister(OUT_Z_H);
}
//I', pretty sure this is how this works: This particular
//acceleromter has 16-bit output values. To get the full value,
//two bytes must be combined for each axis. That is why we shift
//h eight bits left and set the now empty bits with l
Val = ((h << 8) | l);
return Val;
}
unsigned int readRegister(byte thisRegister)
{
byte inByte = 0; // incoming byte from the SPI
unsigned int result = 0; // result to return
// LIS3DH expects the register address in the lower 6 bits
// of the byte. So shift the bits right by two bits:
//thisRegister = thisRegister >> 2;
// now combine the address and the command into one byte
byte dataToSend = thisRegister | READ;
//Set the Chip select pin low to start an SPI packet
digitalWrite(ss,LOW);
//Since we're performing a read operation,
//the most significant bit of the register address
//should be set. Is this REALLY necessary?
//address = (0x80 | address);
// send the device the register you want to read:
SPI.transfer(dataToSend);
// send a value of 0 to read the first byte returned:
result = SPI.transfer(0x00);
//Set the Chips Select pin high to end the SPI packet.
digitalWrite(ss, HIGH);
return result;
}
void writeRegister(byte thisRegister, byte thisValue)
{
// LIS3DH expects the register address in the lower 6 bits
// of the byte. So shift the bits right by two bits:
//thisRegister = thisRegister >> 2;
// now combine the register address and the command into one byte:
byte dataToSend = thisRegister & WRITE;
digitalWrite(ss,LOW);
//Set Chip Select pin low to signal the beginning of an SPI packet.
SPI.transfer(dataToSend);
//Transfer the register address over SPI.
SPI.transfer(thisValue);
//Transfer the desired register value over SPI.
digitalWrite(ss,HIGH);
//Set the Chip Select pin high to signal the end of an SPI packet.
}
Right now the output from the serial monitor looks like:
192
x = -1 y = -1 z = -1
x = -1 y = -1 z = -1
etc..
So you should be getting 51 (0x33) from the WHO_AM_I register but get 192 (0xC0) instead. It's not bit-reversed so perhaps you read the wrong register?
Update: I added writeRegister(CTRL_REG1, 0x87); in setup and now I am getting:
0
x = 0 y = -2048 z = 0
x = 0 y = -1024 z = 8192
x = 0 y = 0 z = 0
x = -7680 y = 0 z = 12544
x = -7424 y = 1024 z = 0
x = 1024 y = -2048 z = 0
x = 0 y = -7168 z = 8192
x = 2048 y = -12032 z = 0
x = -1792 y = -9984 z = 0
x = -7424 y = -7424 z = 12288
So the values are changing as I move it, but quite strangely.
I noticed I had to write to CTRL_REG1 to take it out of powerdown mode (silly me), but now I am not sure which power mode to use so I'll tinker with that.
After I realized I was in powerdown mode, and fixed that, I also made this change to my readRegister function, specifically with the variable types.
byte readRegister(byte thisRegister)
{
byte result = 0; // result to return
// LIS3DH expects the register address in the lower 6 bits
// of the byte. So shift the bits right by two bits:
//thisRegister = thisRegister >> 2;
// now combine the address and the command into one byte
byte dataToSend = thisRegister | READ;
//Set the Chip select pin low to start an SPI packet
digitalWrite(ss,LOW);
//Since we're performing a read operation,
//the most significant bit of the register address
//should be set. Is this REALLY necessary?
//address = (0x80 | address);
// send the device the register you want to read:
SPI.transfer(dataToSend);
//delay(1);
// send a value of 0 to read the first byte returned:
result = SPI.transfer(0x00);
//delay(1);
//Set the Chips Select pin high to end the SPI packet.
digitalWrite(ss, HIGH);
return result;
}
I also added a 100 ohm resistor to the MISO connection. Initially, I had a wire directly connected from the SDO of the accelerator and the MISO pin of the arduino . Now with the added resistor (in series), I get:
24
x = -128 y = 0 z = -64
x = 0 y = 0 z = -64
x = -64 y = 0 z = -128
x = 0 y = 0 z = 0
x = 0 y = 0 z = 0
........
So now I no longer get 0 for the WHO_AM_I register, but its not 0x33. Perhaps this happened because I had an uncontrollable current entering the arduino?
Do you always get 24 (0x18) for the WHO_AM_I register?
00011000 doesn't look much like 00110011 (0x33).
Are you sure you have the Mode right? The SPI Mode determines which clock edge does what. If you get it wrong you will be clocking in data at the wrong time and possibly missing data. Try the other three modes to see if you can get the WHO_AM_I register to read as expected.
The wire that was connected to MISO was faulty, and in my readRegister function I had to change SPI.transfer(0) to SPI.transfer(A5). I used the ST Microelectronics development board and an oscilloscope to look at how the bits were being sent/read when I read a register and for some reason, I always received A5 after I send the desired address to the accelerometer. Also, for some bizarre reason, this only works when I connect the spclock to the oscilloscope. When it is not, then I get 24 again. So I guess I am dealing with a hardware issue but does anyone know what it is exactly?
The values I am getting now (at rest) are:
33
x = -48 y = -32 z = 4080
x = -48 y = -64 z = 4192
x = -48 y = -32 z = 4176
x = -16 y = -64 z = 4128
x = 0 y = -64 z = 4144
x = -48 y = -80 z = 4096
x = -48 y = -64 z = 4128
x = -32 y = -95 z = 4128
x = -32 y = -80 z = 4176
x = -16 y = -64 z = 4096
x = -48 y = -32 z = 4128
x = 0 y = -64 z = 4160
x = -16 y = -48 z = 4096
x = -32 y = -32 z = 4208
....
You have to set the Lpen and HR bits correctly after you start up the chip by setting the ODR bits.
Lpen is in the CTRL1 register and HR is in the CTRL4 register.
It seems there are only two legit settings for these bits
Lpen HR
1 0 Low Power Mode
0 1 Normal Mode
any other settings of these bits will cause the output to glitch. It looks like, when incorrectly set, only one of each data register is filled.
The ApNote has some other juicy bits in it that I'm just digging into...