SPI Returns Zero with IMU Sensor on Teensy 3.2

Status
Not open for further replies.

randomvibe

Well-known member
I'm attempting to open SPI communication with my IMU Sensor (LSM6DS3 breakout) from Sparkfun (https://www.sparkfun.com/products/13339). It's worked with the Arduino-Due, but I'm having trouble with the Teensy 3.2. My program keeps returning zeros when I ping the WHO_AM_I address. Code below demos the issue. I appreciate your help.

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

#define   READ    0x80 
#define   WRITE   0x00
#define   SSX     20

SPISettings SETA(100000, MSBFIRST, SPI_MODE3); 


void setup()
{
    uint8_t   address;
    uint16_t  out1, out2, out3;

    // INITIALIZE PINS
    delayMicroseconds(3000000);
    Serial.begin(115200); 


    // INITIALIZE SPI
    pinMode (SSX, OUTPUT);
    SPI.setMOSI(7);
    SPI.setMISO(8);
    SPI.setSCK(14);
    SPI.begin(); 

    delayMicroseconds(1000);

    // SPI COMMUNICATION
    SPI.beginTransaction(SETA);    
    digitalWrite(SSX,LOW);
    
    address = 0x0F;                // WHO-AM-I BYTE
    out1 = SPI.transfer(address);    
    out2 = SPI.transfer(READ);    
    out3 = SPI.transfer(0x00);  
         
    digitalWrite(SSX,HIGH); 

    
    Serial.println("Who Am I:");
    Serial.println(out1);
    Serial.println(out2);
    Serial.println(out3);    
}


void loop()
{ 
}
 
Also tried removing the statements: SPI.setMOSI, SPI.setMISO and SPI.setSCK, but no luck.

Is the SPI library fully functional for the Teensy 3.2?
 
SPI certainly can work since it's driving my project at the moment. Though I think you have data in and data out swapped over. If this is on a bread board would suggest using the default pins to start with, which among other things means you can see SPI activity as the on board LED goes active. If the pins aren't the problem would also suggest checking
https://www.pjrc.com/teensy/td_libs_SPI.html
and making sure nothing in the example is breaking anything in the transaction method they use.
 
Though I think you have data in and data out swapped over.

My code sets the pins specified in table displayed here: https://www.pjrc.com/teensy/td_libs_SPI.html . Are those not the default?

As far as the chip-select pin, what is the default for that? I use pin 20 because the example code listed in the html above uses it.

The onboard amber LED does go off.

WMXZ said:
in particular, transaction method handles chip select, and should not be done by hand.
What is meant by "should not be done by hand"?
 
I've only looked breifly, but I don't see SPI.endTransaction() in your program.

You need to pair every beginTransaction with endTransaction.
 
I've only looked breifly, but I don't see SPI.endTransaction() in your program. You need to pair every beginTransaction with endTransaction.

I tried that too, but no luck. The Teensy's onboard LED does light.

My latest code is shown below. It was implemented on the Arduino 1.6.8 IDE using Teensyduino v1.28. It compiles fine and displays the serial commands, but the SPI.transfer lines return zeros. The WHO_AM_I register should return 69h. What am I missing here?

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

#define   READ    0x80 
#define   WRITE   0x00
#define   MOSIX   7
#define   MISOX   8
#define   SCKX    14
#define   SSX     20

SPISettings SETA(1000000, MSBFIRST, SPI_MODE3); 


void setup()
{
    uint8_t   address;
    uint16_t  out1, out2, out3;


    // INITIALIZE PINS
    delayMicroseconds(6000000);
    Serial.begin(115200); 
    Serial.println("Hello:");


    // INITIALIZE SPI
    pinMode (SSX, OUTPUT);
    digitalWrite(SSX,HIGH);
 
    SPI.begin(); 
    SPI.setMOSI(MOSIX);
    SPI.setMISO(MISOX);
    SPI.setSCK(SCKX);

    delayMicroseconds(1000000);

    // SPI COMMUNICATION
    SPI.beginTransaction(SETA);    
    digitalWrite(SSX,LOW);
    
    address = 0x0F;  // WHO-AM-I BYTE
    
    delayMicroseconds(1);
    out1 = SPI.transfer(address);    
    delayMicroseconds(1);
    out2 = SPI.transfer(READ);    
    delayMicroseconds(1);
    out3 = SPI.transfer(0x00);  
            
    digitalWrite(SSX,HIGH); 

    SPI.endTransaction();

  
    Serial.println("Who Am I:");
    Serial.println(out1);
    Serial.println(out2);
    Serial.println(out3);
}


void loop()
{ 
}
 
My suggestion at the moment if it's possible is to recheck your wiring and if possible post a photo of the setup. Do you have access to an osciliscope? My suggested next step would be to probe the pins on the break out board and confirm it's getting all the signals etc elctrically.
 
Might also be worthwhile trying I2C instead of SPI. Even if you *really* want SPI, using I2C could at least confirm if all the hardware actually works.
 
Photos enclosed. PJRC board is A Teensy 3.2. The IMU is a LSM9DS0 chip on a Sparkfun breakout board (https://www.sparkfun.com/products/13339). The IMU board and wire harness (3 feet in length) were SPI and I2C tested on an Arduino-Due, so the wiring works. The order of the pins from left-to-right are: GND, VDD, MOSI, SCL, MISO, and CS (https://learn.sparkfun.com/tutorial...up-guide?_ga=1.29217596.1274800483.1464756633).

I also tried the spi4teensy3 library, but no luck. Documentation is very limited, so I'm not sure of the implementation.

What else can I try?
 

Attachments

  • IMG_0303.jpg
    IMG_0303.jpg
    96.4 KB · Views: 191
  • IMG_0305.jpg
    IMG_0305.jpg
    89.5 KB · Views: 135
  • IMG_0306.jpg
    IMG_0306.jpg
    101.3 KB · Views: 162
Sorry, I may be completely off, but looking at your photos, it looks like you are running AGND (Analog ground) to the sensor. Have you tried running one of the GND pins instead? Not sure how much of a difference it would make.
 
Code:
    SPI.begin(); 
    SPI.setMOSI(MOSIX);
    SPI.setMISO(MISOX);
    SPI.setSCK(SCKX);

You need to configure the SPI pins before SPI.begin(), like this:

Code:
    SPI.setMOSI(MOSIX);
    SPI.setMISO(MISOX);
    SPI.setSCK(SCKX);
    SPI.begin();

KurtE also has a good point about the ground pin. Use normal ground, not analog ground.

Sparkfun also has a library with several examples for this product. The library has specific code for Teensy 3.1, so presumably it's been tested and is known to work on Teensy. If all else fails, at least for testing, perhaps use the normal SPI pins and give Sparkfun's library a try. Even if you can't use it for various reasons, running their known good code rather than code you've written from scratch could help you get up and running. From there, perhaps adapt to what you need?
 
The Sparkfun library for SPI did not work, at least on the Arduino-Due. I2C did work out-of-the-box. I had to write my own code for SPI, and eventually it worked.

Back to the Teensy, I'll use the normal ground. But I think I found the problem: it's my wire harness. The two ends are not identical. The breadboard end is ordered to match another project. I'll check it tonight. Thanks for your help.
 
The Sparkfun library for SPI did not work, at least on the Arduino-Due.

But did you even try Sparkfun's library with Teensy?

Since it has a #define and comment specifically mentioning Teensy 3.1, odds are very good it will "just work" on your Teensy 3.2, even though it's incompatible with Arduino Due.

In fact, there are a *LOT* of libraries and programs that work on Teensy but not on Arduino Due. Teensy has quite a lot of work in the core library for compatibility with code written for Arduino Uno. Due does not. So testing with Due is a very poor way to evaluate what you should do with Teensy.
 
On close inspection it may be that White_Orange and White_Brown are swapped.

Using the Lib noted in p#17 would be the right start.

Also - to answer above p#5 question - the pins in use are the ALTERNATE pins, not the default in the table you linked to.
 
On close inspection it may be that White_Orange and White_Brown are swapped.

The photo makes it appear that way, but I checked it closely, and the wire harness is okay after all.

I changed the SPI pins from the "alternates" to the "default", but no luck. I put the SPI.set* commands before the SPI.begin() call, but no luck. Added a longer delayMicroseconds() delay, but no luck.

Then success at last. I changed the SPISettings SPI mode from SPI_MODE3 to SPI_MODE0, and the WHO_AM_I call returned the correct byte. The hint came from examining the Sparkfun library. Apparently, SPI_MODE has different meanings between Teensy and Due. I recommend adding a bit more documentation in: https://www.pjrc.com/teensy/td_libs_SPI.html

Here's my working code:

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

#define   READ    0x80 
#define   WRITE   0x00
#define   MOSIX   11
#define   MISOX   12
#define   SCKX    13
#define   SSX     10

SPISettings SETA(1000000, MSBFIRST, SPI_MODE0); 


void setup()
{
    uint8_t   address;
    uint16_t  out1, out2;


    // INITIALIZE PINS
    delayMicroseconds(6000000);
    Serial.begin(115200); 
    Serial.println("Hello:");


    // INITIALIZE SPI
    pinMode(SSX, OUTPUT);
    digitalWrite(SSX,HIGH);
 
    SPI.setMOSI(MOSIX);
    SPI.setMISO(MISOX);
    SPI.setSCK(SCKX);
    SPI.begin(); 


    // SPI COMMUNICATION
    SPI.beginTransaction(SETA);
      
    digitalWrite(SSX,LOW);
    
    address = 0x0F;  // WHO-AM-I BYTE
    
    delayMicroseconds(1);
    out1 = SPI.transfer(address | READ);    
    out2 = SPI.transfer(0x00);  
            
    digitalWrite(SSX,HIGH); 

    SPI.endTransaction();

  
    Serial.println("Who Am I:");
    Serial.println(out2);
}


void loop()
{ 
}

My goal is to stream back accel and gyro readings at a prescribed time step while the IMU is stationary. On the Arduino-Due, I get ticks in the readings well beyond the noise floor of the measurements. I'm not sure if it's the IMU chip or the Due. We'll see what happens with the Teensy.
 
Last edited:
The SPI functionality works fine when reading and writing to a handful of registers to initialize the IMU device. But when I stream the accel & gyro readings every 0.01 seconds, I pick up a lot of dropouts. By dropouts I mean the SPI readings return nearly zero counts or 2^16 counts (IMU resolution is 16 bits). I noticed when I move the CAT5 cable away from my wireless keyboard, the situation improves. Or if I bend the CAT5 wires at the ends, the readings change drastically. My CAT5 cable is approximately 3 feet long. Is a different cable recommended here? Is I2C more robust in this situation? Thanks.
 
If you wish to use SPI for this either reduce it's speed or use shielded CAT5 cables. I'd recommend doing both

At 3 feet you probably want to start considering a different communication protocol

I2C I'd consider to suffer from the same issues. It is by default slower though
 
Maybe I'll go with a shielded CAT6 cable then.

To reduce noise and EMI interference with my device, the 3 foot cable, and a Teensy 3.2, what is the best approach for pairing up the SPI lines (CLK, MOSI, MISO, SS)? Is it best to pair up MOSI with MISO, and CLK with Ground? Or MOSI with Ground, MISO with VCC, CLK with SS? What about pull-up resistors for this situation? Thanks.
 
Isolate the CLK with a GND line. MOSI and MISO can go together as they probably won't be transmitting at the same time. SS can go wherever unless it toggles a lot which it normally doesn't.
Ground all unused cables at both ends
 
Isolate the CLK with a GND line. MOSI and MISO can go together as they probably won't be transmitting at the same time. SS can go wherever unless it toggles a lot which it normally doesn't.
Ground all unused cables at both ends

Are pull-ups required for Teensy 3.2 SPI? I2C requires 4.7K resistors, but is the same true for SPI?
 
Only pullups I've seen are on the CS lines to make sure no sharing devices 'float' into an active transmission for another device. I just saw this myself having a touch display where I didn't start the software on the Touch part of the display unit and the T_CS pin wasn't held high so it corrupted the display data on the shared data lines.
 
Status
Not open for further replies.
Back
Top