PDA

View Full Version : SPI read problem on Teensy 3.0



ToPi
02-21-2013, 08:20 AM
Hi!

I encountered a problem while trying to read data from an SPI device from my teensy 3.0.

My goal is to read values from an 9-DOF IMU over SPI (YEI 3-Space embedded IMU). All data regarding this IMU is provided here:

http://tech.yostengineering.com/3-space-sensor/product-family/embedded

(Data Mode 0, up to 6MHz).

I successfully tested this device using an MBED board. I was able to write data (e.g. set led color on the IMU board) and read data (e.g. quaternions).
So basically I know this IMU works. When "contacting" the device, it returns various bytes to display its state. Returned byte values usually are between 0 and 4.

To make it short, returned byte values from SPI.transfer always were 255. I Checked the wiring and soldering several times, but nothing changed.

In order to find out if it was an issue with my SPI understanding or if the teensy was damaged, I've tested a Sparkfun 7-segment display, URL:

https://www.sparkfun.com/products/11442

together with the code samples provided by sparkfun. It works like a charm. So writing data over SPI to the slave device seems to work.

Next, I tried out an SPI-Gyro from Polulu, but i also didn't receive anything else than "255". Disconnecting the DIN-pin also didn't affect my reading - stil 255.

My device connections were:

CS0 (pin 10) = slave select, pulled low for communication
DOUT/DIN (pins 11, 12) for communications
pin 13 = sck

I use power supply from the pins provided at that side of the teensy where the USB connector is provided (3.3 V (100 mA max, should be OK), Gnd).

Here is the code for the YEI three space IMU. This code should initiate the transmission of the Hardware serial number. However, bytes returned should be something lik 0 (Idle), 4 (aggregating command bytes), 2 (processing command)


#include "SPI.h"

// Pin definitions
const int SSpin = 10;

int i=0;
// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(SSpin, OUTPUT);
Serial.begin(9600);
SPI.begin();
SPI.setBitOrder(MSBFIRST);
SPI.setClockDivider(SPI_CLOCK_DIV8);
SPI.setDataMode(SPI_MODE0);
digitalWrite(SSpin, HIGH);
}

// the loop routine runs over and over again forever:
void loop() {

if (Serial.available())
{
//wait until connection established, user entered any char
Serial.println("Enter");
char ch = Serial.read();

delayMicroseconds(200);
int a;

// initiate communication
digitalWrite(SSpin, LOW);
delay(1);

// Tell the slave that a command is transmitted, device should return 0
ch = SPI.transfer(0xE6);
a = ch;
Serial.println (a);
delay(1);

// Tell the slave to send Hardware serial
ch = SPI.transfer(0xF6);
a = ch;
Serial.println (a);
delay(1);

// Send 0xFF until command processed, return value 2.
ch = SPI.transfer(0xFF);
a = ch;
Serial.println (a);
delay(1);

// end communication, further data reading needs to be established
digitalWrite(SSpin, HIGH);
delay(1);

}

delay(500); // wait for a second*/

}



Until now, I don't have a logic analyzer - if I checked wiring long enough, I didn't need it yet. Therefore, I can't provide Oscilloscope readings. Does somebody know what is going on?

potatotron
06-19-2013, 03:10 AM
Hi,

Did you ever have a chance to get this working? I've run into the exact same problem with a TI CC3000 wireless module. The init code works on a Nano (via resistors to convert the 5V logic to 3.3V for the CC3000) but when I connect to a Teensy 3.0 all I get back is FFs.

Thanks!

t3andy
06-20-2013, 01:12 AM
SPI.setDataMode(SPI_MODE0);
Try using the "other modes" eg. Mode2:cool:


BTW ... keep your SPI connections as short as possible. ~ 2 "

potatotron
06-20-2013, 03:38 AM
Thanks for your help, but no luck on that front.

The TI docs say the CC3000 needs CPOL=0 and CPHA=1 which for Arduino is mode 1.

When I try mode 0 I get back data, but everything is 1 bit off:

First string (HCI_CMND_SIMPLE_LINK_START ack):
02 00 FF 00 00 00 00 00 00 00 (expected)
81 00 7F 80 00 00 00 00 00 00 (observed)

Second string ((HCI_CMND_SIMPLE_LINK_START command complete):
02 00 00 00 05 04 00 40 01 00 (expected)
81 00 00 00 02 82 00 20 00 80 (observed)

Which I suppose makes sense...mode 0 is sampling DIN/MISO at the wrong time, so the timing of the reads is off 1 cycle.

When I try modes 2 & 3 I also get back all FFs, and the CC3000 never finishes the expected blips cycle of its IRQ notify pin startup.

I know the CC3000 and the code is good; they work with an Arduino Nano (but the Nano can't handle TI's CC3000 library, which wants about 4K RAM for Ethernet packet buffers).

potatotron
06-20-2013, 05:03 AM
I'm not sure if this is a bug or not, but when I bit-bang SPI I get the correct response. See attached code for how I'm doing it.

If I use hardware SPI with CPHA=0 (SPI_MODE0) I get incorrect data (everything is 1 bit off).

If I use hardware SPI with CPHA=1 (SPI_MODE1) I get all FFs.

If I bit bang the SPI bits, I get what I should from the CC3000.

N.B. The hardware tests use SPI_CLOCK_DIV16 to match the rate when send the bits manually, but I get the same results even if I use DIV2, DIV4, or DIV8. The TI chip can speak SPI up to 24 Mhz. Is there anything in Teensyland that can do a delay of less than 1 microsecond?

stevech
06-20-2013, 06:33 AM
1 bit off.. clock phase option choice wrong?

potatotron
06-20-2013, 08:12 AM
Yes I think that's what it is. I have tried the Arduino-specific ways to change that; it doesn't work. Do you know of another way ?

stevech
06-20-2013, 03:37 PM
I'm just leaning the Teensy3. Perhaps you can use the hardware core teensy3 functions to configure the SPI to suit. I didn't readily find where the core SPI code is located. Most of the Teensy3 API seems to call core (K20) functions.

t3andy
06-22-2013, 10:23 PM
BTW ... sometimes on the SPI MOSI (DO) output needs a pullup ~10K

Matt Gartner
12-15-2013, 05:19 AM
Yes I think that's what it is. I have tried the Arduino-specific ways to change that; it doesn't work. Do you know of another way ?

I think I had the same problem as ToPi, but got it sorted out thanks to some of your code potatotron. I noticed the cc3300 code you posted was far more "explicit" with communications (ie. setting the SS low before each byte write and then high afterward). I also found code on another site...http://forums.adafruit.com/viewtopic.php?f=22&t=37725 that was also having trouble.

Hopefully this code helps anyone else out there...I think setting all SPI parameters helped as well. I'm working with a Sparkfun Mega Pro Mini 3.3v, but I'd assume it will work with other arduinos...

My thanks to you all for helping me solve this! All your code snippets and suggestions helped me.

Best regards,
Matt



#include <SPI.h>




// Needed to convert the bytes from SPI to float
union u_types {
byte b[4];
float fval;
} data[3]; // Create 3 unions, one for each euler angle


void setup() {

SPI.begin();
SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0);
SPI.setClockDivider(SPI_CLOCK_DIV4);
pinMode(MOSI, OUTPUT);
digitalWrite(MOSI, LOW);
pinMode(SCK, OUTPUT);
digitalWrite(SCK, LOW);
pinMode(MISO, INPUT_PULLUP);
pinMode(SS, OUTPUT);
digitalWrite(SS, LOW);
Serial.begin(57600);

}

byte writeByteTo3Space(byte byteToWrite) {

byte Result = 0;
digitalWrite(SS,LOW);
Result = SPI.transfer(byteToWrite);
digitalWrite(SS,HIGH);
return Result;
}

void loop() {




// Clear the internal data buffer on the IMU
byte result = writeByteTo3Space(0x01);
delay(10);

// Send start of packet:
result = writeByteTo3Space(0xF6);
delay(1);
// Send command (tared euler angles)
result = writeByteTo3Space(0x01);
delay(1);
// Get status of device:
result = writeByteTo3Space(0xFF);


while (result != 0x01 ) { // Repeat until device is Ready
delay(1);
result = writeByteTo3Space(0xFF);

}
// Get the 12 bytes of return data from the device:
for (int ii=0; ii<3; ii++) {
for (int jj=0; jj<4; jj++) {
data[ii].b[jj] = writeByteTo3Space(0xFF);
delay(1);
}
}

for( int mm=0; mm<3; mm++) {
endianSwap(data[mm].b);
}

Serial.print(data[0].fval);
Serial.print(",");
Serial.print(data[1].fval);
Serial.print(",");
Serial.println(data[2].fval);

}

inline void endianSwap(byte temp[4]) {
byte myTemp = temp[0];
temp[0] = temp[3];
temp[3] = myTemp;
myTemp = temp[1];
temp[1] = temp[2];
temp[2] = myTemp;
}

Massel
03-09-2014, 08:17 AM
Hi,

I have an equal problem with a MPU6000 chip and SPI, as ToPi mentioned I also get very large values.
I figured out that these big values are given instead of negative values! Positive values are right, but when the values turn negative I just get something like 65000 (16 bit).
Is this a common problem with the Teensy and SPI? I'm using the Teensy 3.1.

Any solutions?

lsnyman
03-12-2014, 10:03 PM
Same problem with a MAX31855. Arduino code does not work. Just returns 0's

PaulStoffregen
03-12-2014, 10:27 PM
Hi,

I have an equal problem with a MPU6000 chip and SPI, as ToPi mentioned I also get very large values.
I figured out that these big values are given instead of negative values! Positive values are right, but when the values turn negative I just get something like 65000 (16 bit).
Is this a common problem with the Teensy and SPI? I'm using the Teensy 3.1.

Any solutions?

Yes, this is a common problem when AVR-based code combines 2 bytes into a 16 bit integer without explicitly casting it to int16_t.

The MPU60X0 library from FreeIMU is known to work with Teensy3.

https://github.com/PaulStoffregen/FreeIMU/tree/master/libraries/MPU60X0

Here's a recent post about fixing this in one of Adafruit's libraries.

http://forums.adafruit.com/viewtopic.php?f=19&t=50245&p=254014&hilit=teensy#p255043

The solution is usually just that simple, typecast the 16 bit result to int16_t, so the compiler "knows" it's a 16 bit signed integer. Then when you assign it to "int" (which is 32 bit on ARM), the compiler automatically does the right thing.



x = (int16_t)(xlo | (xhi << 8));

PaulStoffregen
03-12-2014, 10:28 PM
Same problem with a MAX31855. Arduino code does not work. Just returns 0's

Are you using a particular library? I can take a look, but you need to post more detailed info, like a link to the github source or other place where the library is maintained.