Spi.read() function

I’m trying to get my Impee to connect to a read-only SPI device (the MAX31855 thermocouple IC) and read 32bits of data from it. I’ve gotten it to work on an Arduino but not my Impee. I attempted to use the harware.spi.read( ) command but I couldn’t find to much information on it. My current test code only returns (null : 0x0x0). Any suggestions on how to read from an SPI device?

`// Register with the server
imp.configure(“spi1”, [], []);

//pins: imp 1 chip select
// imp 2 MISO
// imp 5 SCK
// imp 7 Not used (MOSI)

hardware.spi257.configure(SIMPLEX_TX | MSB_FIRST | CLOCK_IDLE_LOW , 1000); // Configure SPI_257 at about 1MHz
hardware.pin1.configure(DIGITAL_OUT); //Configure the chip select pin
hardware.pin1.write(1); // Pull CS high
imp.sleep(0.1); // Wait 100 ms
hardware.spi.write("\ff"); // I was told I needed to invoke the write function before I could read
hardware.pin1.write(0); // Pull CS low
local temp = hardware.spi.read(32); // Read 32 bits of data. String? Blob?
hardware.pin1.write(1); // Pull CS high
server.show(temp); //show data`

You need to do spi.write before every read - check this thread out for details:
http://forums.electricimp.com/discussion/145/spi-not-working-or-i-just-cant-start-it#Item_10

And You configured the SPI simplex tx - that should be rw mode I think …

Thanks for commenting, I called spi.write() before spi.read() in the code above, did i invoke it incorrectly? Changing SIMPLEX_RX to SIMPLEX_TX did not change the output.

spi.read() doesn’t work. Unfortunately there’s a workaround – “unfortunately”, because it means we can never now fix spi.read(), so we’ll have to call the fixed version something different.

The workaround, which it can’t hurt if one more person uses, is to always use spi.read(1) to read a single byte, and do an spi.write() of a single byte before each one. You need to read four bytes, so that’d be:
hardware.pin1.write(0); hardware.spi257.write("\\xFF"); t1 = hardware.spi257.read(1); hardware.spi257.write("\\xFF"); t2 = hardware.spi257.read(1); hardware.spi257.write("\\xFF"); t3 = hardware.spi257.read(1); hardware.spi257.write("\\xFF"); t4 = hardware.spi257.read(1); hardware.pin1.write(1); t = t1+t2+t3+t4;
and then t is your four-byte temperature string.

Peter

When I read t using the line server.show(t[0]); it just returns a 0. The same is true for all 4 bytes retrieved using different indexes even though my expected output is non-zero.

What’s your spi.configure() line? You probably don’t want SIMPLEX_RX or SIMPLEX_TX.

Peter

Thanks, that seems to have solved it. Looking back, that’s what Brown said too, I just didn’t understand his abbreviation at the time.

Ah cool, glad you got it working! You’ll be pleased to know we’re currently working on making the SPI API a lot more sane.

Peter

I was just getting ready to try the exact same thing. The MAX31855 from Adafruit is a great T-Couple amplifier. Could you post your completed code?

I don’t have completed code yet. I can get the individual bytes read and I can display positive temperatures but I’m having trouble reading negative temperatures. The sensor reports negative numbers in “two’s complement” format (e.g. 0.25 is represented as 1111 1111 1111 11). In fact, if I take the two’s complement like this:
if(tc & 0x7fff){ tc = ~tc; tc = tc + 1; };
I get the absolute value of the negative temperature.

Does anyone have any idea how to have Squirrel treat my 16 bit variable “tc” as a signed integer?

Here is my current code:

`// Register with the server
imp.configure(“spi1”, [], []);

//pins: imp 1 chip select
// imp 2 MISO
// imp 5 SCK
// imp 7 Not used (MOSI)

hardware.spi257.configure(MSB_FIRST | CLOCK_IDLE_LOW , 1000); // Configure SPI_257 at about 1MHz
hardware.pin1.configure(DIGITAL_OUT); //Configure the chip select pin
hardware.pin1.write(1);//pull CS high
imp.sleep(0.1);//wait 100 ms
hardware.pin1.write(0); //pull CS low to start the transmission of temp data
hardware.spi257.write("\xFF"); //send byte to initialize clock
local t1 = hardware.spi257.read(1); //read byte 0
hardware.spi257.write("\xFF"); //send byte to initialize clock
local t2 = hardware.spi257.read(1); //read byte 1
hardware.spi257.write("\xFF"); //send byte to initialize
local t3 = hardware.spi257.read(1); //read byte 2
hardware.spi257.write("\xFF"); //send byte to initialize
local t4 = hardware.spi257.read(1); //read byte 3
hardware.pin1.write(1); // pull CS high
local t = t1+t2+t3+t4; //Concatenate

//Shift bits to combine into a 16 bit signed integer containing the temp and the fault bit
local tc = t[0]<<8 | t[1];
//Shift bits into a 16 bit signed integer (cold junction/faults)
//local cjc = t[2]<<8 | t[3];

function readCelsius(){
if (tc & 0x1){
return 2000 //if there is a fault return 2000 degrees
};
tc = tc & 0xfffc; //mask the last two bits
return tc / 16.0;
}

server.show(tc);
server.show(readCelsius());
`

You could use a blob to convert this. Bit hacky, but:

local b=blob(2);
b.writen(tc, ‘w’); // write a 16 bit unsigned to the stream
b.seek(0); // back to start
local signed=b.readn(tc,‘s’); // read a 16 bit signed from the stream

Hugo, that’s gross! Squirrel doesn’t have a 16-bit signed type, but the normal 32-bit is signed, so to sign-extend 16 bits to 32 the answer is:
tc = (tc<<16)>>16;

Peter

Peter,

That seems to have done the trick. Here’s the function in case anyone needs it:

function readCelsius(){ if (tc & 0x1){ return 2000 //if there is a fault return 2000 degrees }; tc = tc & 0xfffc; //mask the last two bits tc = (tc<<16)>>16; return (tc / 16.0); }

@peter: I didn’t think it’d sign extend without a cast… but mine works too, right? :slight_smile:

@hugo It worked too, I just had to change the last line to this:
local signed=b.readn('s'); // read a 16 bit signed from the stream

hi all, I also would like to use the MAX31855. at first read it seems one would need to consume 3 pins for the SPI plus an additional for the CS pin. That would leave a scant 2 pins for the remainder of a project. Can the flag SIMPLEX_RX be used for this read-only SPI compatible device? One does not transmit any data to MAX31855. I hope then the MOSI pin could be configured as a digital output and employed as the chip select line.

Yep, that would work fine. Configure SPI first then reconfigure the MOSI as a DIGITAL_OUT.

But don’t use SIMPLEX_RX. In current versions (i.e. prior to release-12, which isn’t out yet), SIMPLEX_RX does something odd which you don’t want. Just reconfigure the MOSI pin after configuring SPI and everything should work fine. (And will continue to work fine when release-12 comes out.)

Peter

Thank you all. I have the adafruit MAX31855 board working thanks totally to this thread. Thank you rivers for sharing your code; it has saved me many many hours of effort

I am using pins 1 8 and 9 as GPIO for other things, re-purposed pin 7 to be the CS pin for the MAX as described above/previous.