0

SPI with an LIS3DH and an M0 Adalogger
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

SPI with an LIS3DH and an M0 Adalogger

by 7elEvan on Tue Feb 21, 2017 3:19 pm

Hello,

I'm working with an LIS3DH accelerometer on an M0 Adalogger and need to enable some of the extra functions of the LIS3DH. I figured I would forgo some existing libraries and write up the program from scratch for fun and as a learning experience with SPI peripheral communication. I'm starting with a simple WHO_AM_I code, just reading the WHO_AM_I register after taking the LIS3DH out of power-down mode. Well, I have the code, but it doesn't run on the M0... and it runs on an UNO, I later found out (after removing while (!Serial) and changing SPICLOCK to 13). I'm out of ideas... any help would be greatly appreciated. Thanks!

Code: Select all | TOGGLE FULL SIZE
/*
 * WHO AM I?
 * The ultimate question for an LIS3DH...
 */

#include <SPI.h>

#define DATAOUT 11      //MOSI
#define DATAIN 12       //MISO
#define SPICLOCK 6      //sck
#define SLAVESELECT 10  //ss

void setup() {
 
  while (!Serial)
  Serial.begin(9600);

  SPI.begin();
 
  // Set SPI pin modes
  pinMode(SPICLOCK, OUTPUT);
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SLAVESELECT,OUTPUT);

  // Initialize SPI bus settings
  SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE3));
 
  digitalWrite(SLAVESELECT, HIGH); //disable device

  // Write to CTRL_REG1 to take out of power down mode
  digitalWrite(SLAVESELECT, LOW); //enable device
  SPI.transfer(0x20);
  SPI.transfer(0x8F);
  digitalWrite(SLAVESELECT, HIGH);

  // Read and print WHO_AM_I register
  digitalWrite(SLAVESELECT, LOW); //enable device
  // WHO_AM_I address with bit zero set to "1" as the LIS3DH read command,
  // bit zero to "0" means write, just like bit one to "0" means a single
  // read/write, and bit one to "1" means a multiple read/write
  SPI.transfer(0x8F);
  uint8_t deviceid = SPI.transfer(0x00);
  digitalWrite(SLAVESELECT, HIGH);

  SPI.endTransaction();

  Serial.println(deviceid, HEX);
 
}

void loop() {
  // put your main code here, to run repeatedly:

}

7elEvan
 
Posts: 13
Joined: Mon Aug 01, 2016 11:53 am

Re: SPI with an LIS3DH and an M0 Adalogger

by adafruit_support_mike on Fri Feb 24, 2017 2:09 am

Try removing the lines that toggle CS high and low between setting CTRL_REG1 and writing the WHO_AM_I register, or putting in SPI.endTransaction() and SPI.beginTransaction() calls between them.

adafruit_support_mike
 
Posts: 54125
Joined: Thu Feb 11, 2010 2:51 pm

Re: SPI with an LIS3DH and an M0 Adalogger

by 7elEvan on Fri Feb 24, 2017 12:44 pm

Thanks for the response. I tried both methods, and got the same result of 0xFF (which I realize I should have mentioned before...). The sections of code are below.

This code did return 0x33 just like it should on the UNO. Regardless of what register I try to read, I get 0xFF using the M0, which is making me think the LIS3DH is not coming out of powerdown mode properly after booting. I commented out the section writing to CTRL_REG1, and 0xFF was still returned.

With SPI.endTransmission and SPI.beginTransmission:
Code: Select all | TOGGLE FULL SIZE
digitalWrite(SLAVESELECT, HIGH); //disable device

  // Initialize SPI bus settings
  SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE3));

  // Write to CTRL_REG1 to take out of power down mode
  digitalWrite(SLAVESELECT, LOW); //enable device
  SPI.transfer(0x20);
  SPI.transfer(0x8F);
  digitalWrite(SLAVESELECT, HIGH);

  SPI.endTransaction();

  SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE3));
  // Read and print WHO_AM_I register
  digitalWrite(SLAVESELECT, LOW); //enable device
  // WHO_AM_I address with bit zero set to "1" as the LIS3DH read command,
  // bit zero to "0" means write, just like bit one to "0" means a single
  // read/write, and bit one to "1" means a multiple read/write
  SPI.transfer(0x8F);
  uint8_t deviceid = SPI.transfer(0);
  digitalWrite(SLAVESELECT, HIGH);

  SPI.endTransaction();

  Serial.println(deviceid, HEX);


With the CS-high, -low lines commented out:
Code: Select all | TOGGLE FULL SIZE
  SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE3));

  // Write to CTRL_REG1 to take out of power down mode
  digitalWrite(SLAVESELECT, LOW); //enable device
  SPI.transfer(0x20);
  SPI.transfer(0x8F);
  //digitalWrite(SLAVESELECT, HIGH);


  // Read and print WHO_AM_I register
  //digitalWrite(SLAVESELECT, LOW); //enable device
  // WHO_AM_I address with bit zero set to "1" as the LIS3DH read command,
  // bit zero to "0" means write, just like bit one to "0" means a single
  // read/write, and bit one to "1" means a multiple read/write
  SPI.transfer(0x8F);
  uint8_t deviceid = SPI.transfer(0);
  digitalWrite(SLAVESELECT, HIGH);

  SPI.endTransaction();

7elEvan
 
Posts: 13
Joined: Mon Aug 01, 2016 11:53 am

Re: SPI with an LIS3DH and an M0 Adalogger

by 7elEvan on Fri Feb 24, 2017 6:20 pm

So... I think I've mostly confirmed that the accelerometer isn't coming out of powerdown mode when using the M0. I compared the Adafruit Acceldemo program (with setDataRate(LIS3DH_DATARATE_POWERDOWN) added) to my program by adding the Adafruit library and using lis.read() in the loop. Both programs now return the same values, and the oscilloscope traces of the CS and the MISO pins look very similar regardless of which program I run. Still can't figure out the powerdown mode part, though...

Code below:
Code: Select all | TOGGLE FULL SIZE
/*
 * WHO AM I?
 * The ultimate question for an LIS3DH...
 */

#include <SPI.h>
#include <Adafruit_LIS3DH.h>

// Used for software SPI
#define LIS3DH_CLK 13
#define LIS3DH_MISO 12
#define LIS3DH_MOSI 11
// Used for hardware & software SPI
#define LIS3DH_CS 10

// software SPI
Adafruit_LIS3DH lis = Adafruit_LIS3DH(LIS3DH_CS, LIS3DH_MOSI, LIS3DH_MISO, LIS3DH_CLK);

#define DATAOUT 11      //MOSI
#define DATAIN 12       //MISO
#define SPICLOCK 13      //sck
#define SLAVESELECT 10  //ss

void setup() {
 
  while (!Serial)
  Serial.begin(9600);

  SPI.begin();
 
  // Set SPI pin modes
  pinMode(SPICLOCK, OUTPUT);
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SLAVESELECT,OUTPUT);

  digitalWrite(SLAVESELECT, HIGH); //disable device

  // Initialize SPI bus settings
  SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE3));

  // Write to CTRL_REG1 to take out of power down mode
  digitalWrite(SLAVESELECT, LOW); //enable device
  SPI.transfer(0x20);
  SPI.transfer(0x8F);
  digitalWrite(SLAVESELECT, HIGH);

  SPI.endTransaction();

  delayMicroseconds(25);

  SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE3));

  // Read and print WHO_AM_I register
  digitalWrite(SLAVESELECT, LOW); //enable device
  // WHO_AM_I address with bit zero set to "1" as the LIS3DH read command,
  // bit zero to "0" means write, just like bit one to "0" means a single
  // read/write, and bit one to "1" means a multiple read/write
  SPI.transfer(0x8F);
  uint8_t deviceid = SPI.transfer(0);
  digitalWrite(SLAVESELECT, HIGH);

  SPI.endTransaction();

  Serial.println(deviceid, HEX);
 
}

void loop() {
  // put your main code here, to run repeatedly:
lis.read();      // get X Y and Z data at once
  // Then print out the raw data
  Serial.print("X:  "); Serial.print(lis.x);
  Serial.print("  \tY:  "); Serial.print(lis.y);
  Serial.print("  \tZ:  "); Serial.println(lis.z);

  delay(200);
}

7elEvan
 
Posts: 13
Joined: Mon Aug 01, 2016 11:53 am

Re: SPI with an LIS3DH and an M0 Adalogger

by adafruit_support_mike on Sun Feb 26, 2017 1:18 am

Post a set of traces generated by each set of code and we'll see if we can find anything. 800x600 images usually work best.

adafruit_support_mike
 
Posts: 54125
Joined: Thu Feb 11, 2010 2:51 pm

Re: SPI with an LIS3DH and an M0 Adalogger

by 7elEvan on Fri Mar 03, 2017 7:29 pm

Finally got the images from the scope. It saved the images as 320x240. The yellow trace (Ch1) is the MISO pin, and the blue trace (Ch2) is the CS pin.

I tried multiple things. First, the acceldemo program after start-up set in POWERDOWN with setDataRate.

Code: Select all | TOGGLE FULL SIZE
// Basic demo for accelerometer readings from Adafruit LIS3DH

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_LIS3DH.h>
#include <Adafruit_Sensor.h>

// Used for software SPI
#define LIS3DH_CLK 13
#define LIS3DH_MISO 12
#define LIS3DH_MOSI 11
// Used for hardware & software SPI
#define LIS3DH_CS 10

// software SPI
Adafruit_LIS3DH lis = Adafruit_LIS3DH(LIS3DH_CS, LIS3DH_MOSI, LIS3DH_MISO, LIS3DH_CLK);
// hardware SPI
//Adafruit_LIS3DH lis = Adafruit_LIS3DH(LIS3DH_CS);
// I2C
//Adafruit_LIS3DH lis = Adafruit_LIS3DH();

#if defined(ARDUINO_ARCH_SAMD)
// for Zero, output on USB Serial console, remove line below if using programming port to program the Zero!
//   #define Serial SerialUSB
#endif

void setup(void) {
#ifndef ESP8266
  while (!Serial);     // will pause Zero, Leonardo, etc until serial console opens
#endif

  Serial.begin(9600);
  Serial.println("LIS3DH test!");
 
  if (! lis.begin(0x18)) {   // change this to 0x19 for alternative i2c address
    Serial.println("Couldnt start");
    while (1);
  }
  Serial.println("LIS3DH found!");
 
  lis.setRange(LIS3DH_RANGE_4_G);   // 2, 4, 8 or 16 G!
 
  Serial.print("Range = "); Serial.print(2 << lis.getRange()); 
  Serial.println("G");

  lis.setDataRate(LIS3DH_DATARATE_POWERDOWN);
}

void loop() {
  lis.read();      // get X Y and Z data at once
  // Then print out the raw data
  Serial.print("X:  "); Serial.print(lis.x);
  Serial.print("  \tY:  "); Serial.print(lis.y);
  Serial.print("  \tZ:  "); Serial.print(lis.z);

  /* Or....get a new sensor event, normalized */
  sensors_event_t event;
  lis.getEvent(&event);
 
  /* Display the results (acceleration is measured in m/s^2) */
  Serial.print("\t\tX: "); Serial.print(event.acceleration.x);
  Serial.print(" \tY: "); Serial.print(event.acceleration.y);
  Serial.print(" \tZ: "); Serial.print(event.acceleration.z);
  Serial.println(" m/s^2 ");

  Serial.println();
 
  delay(200);
}


acceldemo_powerdown.jpg
acceldemo_powerdown.jpg (30.9 KiB) Viewed 834 times


Next, I changed setDataRate to 400 Hz.

Code: Select all | TOGGLE FULL SIZE
lis.setDataRate(LIS3DH_DATARATE_400_HZ);


acceldemo_400Hz.jpg
acceldemo_400Hz.jpg (30.49 KiB) Viewed 834 times


Then, I took a single-sequence capture at start-up to get the initialization.

acceldemo_400Hz_ini.jpg
acceldemo_400Hz_ini.jpg (29.93 KiB) Viewed 834 times

7elEvan
 
Posts: 13
Joined: Mon Aug 01, 2016 11:53 am

Re: SPI with an LIS3DH and an M0 Adalogger

by 7elEvan on Fri Mar 03, 2017 7:35 pm

Next, I used my program with the lis.read() command in the loop for comparison, to see if it looks like the acceldemo program in POWERDOWN mode.

Code: Select all | TOGGLE FULL SIZE
/*
 * WHO AM I?
 * The ultimate question for an LIS3DH...
 */

#include <SPI.h>
#include <Adafruit_LIS3DH.h>

// Used for software SPI
#define LIS3DH_CLK 13
#define LIS3DH_MISO 12
#define LIS3DH_MOSI 11
// Used for hardware & software SPI
#define LIS3DH_CS 10

// software SPI
Adafruit_LIS3DH lis = Adafruit_LIS3DH(LIS3DH_CS, LIS3DH_MOSI, LIS3DH_MISO, LIS3DH_CLK);

#define DATAOUT 11      //MOSI
#define DATAIN 12       //MISO
#define SPICLOCK 13      //sck
#define SLAVESELECT 10  //ss

void setup() {
 
  while (!Serial)
  Serial.begin(9600);

  SPI.begin();
 
  // Set SPI pin modes
  pinMode(SPICLOCK, OUTPUT);
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SLAVESELECT,OUTPUT);

  digitalWrite(SLAVESELECT, HIGH); //disable device

  // Initialize SPI bus settings
  SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE3));

  // Write to CTRL_REG1 to take out of power down mode
  digitalWrite(SLAVESELECT, LOW); //enable device
  SPI.transfer(0x20);
  SPI.transfer(0x8F);
  digitalWrite(SLAVESELECT, HIGH);

  SPI.endTransaction();

  delayMicroseconds(25);

  SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE3));

  // Read and print WHO_AM_I register
  digitalWrite(SLAVESELECT, LOW); //enable device
  // WHO_AM_I address with bit zero set to "1" as the LIS3DH read command,
  // bit zero to "0" means write, just like bit one to "0" means a single
  // read/write, and bit one to "1" means a multiple read/write
  SPI.transfer(0x8F);
  uint8_t deviceid = SPI.transfer(0);
  digitalWrite(SLAVESELECT, HIGH);

  SPI.endTransaction();

  Serial.println(deviceid, HEX);
 
}

void loop() {

  // put your main code here, to run repeatedly:
lis.read();      // get X Y and Z data at once
  // Then print out the raw data
  Serial.print("X:  "); Serial.print(lis.x);
  Serial.print("  \tY:  "); Serial.print(lis.y);
  Serial.print("  \tZ:  "); Serial.println(lis.z);

  delay(200);

}

SPI_basic_lisread.jpg
SPI_basic_lisread.jpg (29.23 KiB) Viewed 834 times


Then, the same code as above, but just the initial capture for start-up comparison.
SPI_basic_lisread_ini.jpg
SPI_basic_lisread_ini.jpg (29.93 KiB) Viewed 834 times


And finally, I commented out lis.read() in the loop and took an initial capture.
Code: Select all | TOGGLE FULL SIZE
void loop() {
/*
  // put your main code here, to run repeatedly:
lis.read();      // get X Y and Z data at once
  // Then print out the raw data
  Serial.print("X:  "); Serial.print(lis.x);
  Serial.print("  \tY:  "); Serial.print(lis.y);
  Serial.print("  \tZ:  "); Serial.println(lis.z);

  delay(200);
*/
}

SPI_basic_ini.jpg
SPI_basic_ini.jpg (28.42 KiB) Viewed 834 times


Sorry if this is overkill... let me know if you would like traces of other pins or anything. I'm also new to debugging a board with a scope like this and don't really know what I'm looking for other than differences between codes that work and those that don't...

7elEvan
 
Posts: 13
Joined: Mon Aug 01, 2016 11:53 am

Re: SPI with an LIS3DH and an M0 Adalogger

by adafruit_support_mike on Mon Mar 06, 2017 3:50 am

Quick tip for debugging signal traces: any digital scope will draw one trace over the other, so it helps to offset the traces from each other vertically. You can still see how the signals relate to each other, and nothing gets hidden. You can slide them back on top of each other if you need to see specific details of the timing.

Debugging SPI with a two-channel scope is tricky because there are four relevant signals. You have to use one as a reference and measure the other three separately. The good news is that microcontrollers are good at repeating themselves, so it's fairly easy to make the signals line up once you've captured them.

Using the CS signal as your trigger, try capturing MOSI, MISO, and SCK for the simple read-a-value sketch. Get one set of traces from the Uno where the code works, and another set for your M0.

The SCK traces will be simplest to evaluate. You just want a general idea of when the signal is active. If you don't see any major differences between the patterns from the two boards, you can call the clock timing good and move on.

Also try cranking the scope's timebase down as far as it will go. The screenshots show the timebase at 250us per interval, and SPI transactions usually run in the megahertz range. See if you get any more detail down around 50ns to 100ns.

adafruit_support_mike
 
Posts: 54125
Joined: Thu Feb 11, 2010 2:51 pm

Re: SPI with an LIS3DH and an M0 Adalogger

by 7elEvan on Mon Mar 06, 2017 9:07 pm

Thank you for the tips on debugging. I used a 4-channel scope this time, and have some interesting results. I essentially only used the SPI_basic code mentioned before in this thread, except for the final trace I'll post.

First, the M0 with all channels. Note that CS will always be Channel 4 (green), MOSI is Channel 3 (fuscha?), MISO is Channel 2 (blue), and SCK is Channel 1 (yellow).
20170306_M0.jpg
20170306_M0.jpg (39.06 KiB) Viewed 796 times

UNO CS and MOSI
20170306_CS_MOSI_UNO.jpg
20170306_CS_MOSI_UNO.jpg (36.24 KiB) Viewed 796 times

UNO MISO and SCK
20170306_MISO_SCK_UNO.jpg
20170306_MISO_SCK_UNO.jpg (35.89 KiB) Viewed 796 times


To me, these all look pretty similar at this timescale.

7elEvan
 
Posts: 13
Joined: Mon Aug 01, 2016 11:53 am

Re: SPI with an LIS3DH and an M0 Adalogger

by 7elEvan on Mon Mar 06, 2017 9:11 pm

I did notice, though, that at 1 microsecond timing, the clock cycle never seems to start for the M0.

UNO clock cycle
20170306_UNO_SCK.jpg
20170306_UNO_SCK.jpg (32.27 KiB) Viewed 796 times

M0 clock cycle
20170306_M0_SCK.jpg
20170306_M0_SCK.jpg (32.11 KiB) Viewed 796 times


But, with the acceldemo program, the clock starts up, albeit a little later than the UNO, and the pulse is noticeably wider.
20170306_M0_SCK_acceldemo.jpg
20170306_M0_SCK_acceldemo.jpg (32.02 KiB) Viewed 796 times

7elEvan
 
Posts: 13
Joined: Mon Aug 01, 2016 11:53 am

Re: SPI with an LIS3DH and an M0 Adalogger

by 7elEvan on Wed Mar 08, 2017 4:32 pm

One potentially related thing... I'm running this in SPI_MODE3 because on the datasheet (page 12), it shows the data being collected on the rising edge of a clock pulse that initially drops low, which seems to be what mode 3 is. But, in the library it is mode 0, and the only mode that doesn't return 0x33 on the UNO is mode 2... modes 0, 1, and 3 all work... is this pretty common? Am I missing something about the SPI modes?

7elEvan
 
Posts: 13
Joined: Mon Aug 01, 2016 11:53 am

Re: SPI with an LIS3DH and an M0 Adalogger

by adafruit_support_mike on Thu Mar 09, 2017 2:04 am

Most SPI devices operate in mode 0, so stick with that for now. Decoding the modes is a headache under the best circumstances.

The the trace where CS goes high again without SCK starting looks like a good place to drill in. The transaction is obviously failing, so we need to correlate that with the code.

For scope debugging, it's easiest to use a marker pin:
Code: Select all | TOGGLE FULL SIZE
    digitalWrite( marker, LOW );
   
    for ( int i=2 ; i ; i-- ) {
        digitalWrite( marker, HIGH );
        digitalWrite( marker, LOW );
    }
just use a different number at different places in the code so you can identify the point of execution from the number of pulses.

Zero in on where the CS pin goes low and then goes high, and then we'll know where to dig into the code.

adafruit_support_mike
 
Posts: 54125
Joined: Thu Feb 11, 2010 2:51 pm

Re: SPI with an LIS3DH and an M0 Adalogger

by Daniel91 on Tue Mar 13, 2018 9:48 am

Hello everyone,

I'm a newbie with Arduino. I am trying to read the accelerometer LIS3DH without the Adafruit library, i.e., only with the Arduino SPI library (for educational purposes). I tested the code in the previous post, with the right pin setting, but it did not work. Do you any code i could use as starting point for the WHO_AM_I program?

Thank you,
Daniel

Daniel91
 
Posts: 1
Joined: Tue Mar 13, 2018 9:31 am

Re: SPI with an LIS3DH and an M0 Adalogger

by lashford on Wed Apr 18, 2018 8:46 pm

This subject just seemed to die unanswered. Has anyone gotten the M0 Adalogger talking SPI to the LIS3DH? Is there a working example? I have wasted too much time on this I would like to see some code that works. I never see the SPI clock on my hardware. I have a logic analyzer and can look at the SPI lines but never get the clock transitions or data on MOSI so obviously never get data back from it.

lashford
 
Posts: 1
Joined: Fri Apr 06, 2018 10:23 pm

Please be positive and constructive with your questions and comments.