I2C :: How to interface a SPI sensor

16 Jan 2012

Hi !

I am new on SPI interface. I enjoy to understand this fabulous technology !

My problème : Write and read data (temperature) on an EM4325 rfid tag (yes, yes, it is also a sensor !)

http://www.datasheets.org.uk/indexdl/Datasheet-093/DSA0072652.pdf

First :: I read the class doc. First or second, I read the wiki page on SPI.

So I start my script like follow :

Serial pc(USBTX, USBRX);
SPI device(p5, p6, p7);//mosi on mosi, miso on miso, slc on sclc gnd on gnd and power supply with 2 V DC
DigitalOut cs(p8);//cs information
DigitalOut myled(LED1);
int main() {
myled = 1;
cs=1;
    while(1) {
        myled=0;
        wait(1);
        int rep = device.write(0x00);
        pc.printf("%d", rep);
        myled = 1;
        wait(1);
    }
}

My question : how to read a data in spi. I have understood that I need cs = 1 and write 0x00 with write method of the spi class.

16 Jan 2012
16 Jan 2012

As an SPI Master device you cant receive anything on spi without sending, so what you've done is correct, you send a 0x00 byte, and read the byte you receive, (its not the reply, its what was in the slave devices buffer as it received the new byte),

If the mbed is the slave device its easier http://mbed.org/projects/libraries/api/mbed/trunk/SPISlave

16 Jan 2012

Thanks for you reply ! but... To summarise what you have written, the integer "rep" in my code is the value witch is in the eeprom of the EM4325. Bt how to read the temperature value ? There are no need to read a register like any example of i2c sensor class development.

Regards, GG

16 Jan 2012

I'm not sure I understand what you're question is, the datasheet should explain what bytes you need to send to get the temperature( it doesn't though neither I found did) and how to convert the value from raw to Celsius, there must be a more detailed one?

16 Jan 2012

Okay.... Now I understand, the byte I send will determine what kind of information (in the slave) my integer "rep" will return. For myself, it was a statue integer to determine if the information was correctly written in the slave !

Regards,

16 Jan 2012

ah ok =) glad you've got it figured out, sorry for misunderstanding the question

19 Jan 2012

Also don't forget to setup the SPI interface using device.format( b, m ) where b is your bits per read/write and m is your SPI mode. The bits per r/w should be in the datasheet for the part and the SPI mode can be determined by looking at the timing diagrams (when the serial data gets setup and read in respect to the clock sync signal. After that you might also need to call device.frequency(f) where f is a compatible SPI frequency if needed. I think the code might assume defaults, but it's always good to double check! Hope that helps :)

20 Jan 2012

Thanks for your answer !

I have a complete datasheet, but it confidential. I through all the document to looking for what byte I need to write an what frequency the device run. Is it in the memory map ?

20 Jan 2012

I'm afraid the communications layer on top of the SPI protocol could be anything, might be as simple as sending the memory address you want the byte from but the memory map looks far too complex for that, there should be a whole section on it in the datasheet

<edit> It could just stream out the data continuously, if you set the mbed up as the SPI slave as the fact sheet says, you could use interupts. You may need to send an initial command still though,

without a datasheet its all assumptions

20 Jan 2012

Hi Gerald! Here's the datasheet you want, http://www.emmicroelectronic.com/webfiles/product/rfid/ds/EM4325_FS.pdf

Take a look at "BAP tag or embedded application with EM4325 as SPI slave and another component as SPI Master" on the third page and make sure you have yours setup in a similar way (though I'm sure you can omit the AUX->Wakeup connection for now).

The cool thing about SPI (usually) is that the clock frequency isn't as picky as other protocols like RS-232. Usually, vendors will specify only a 'max SPI frequency,' generally limited by the chip's internal clock cycle. Since this vendor didn't specify any min/max clock setups, let's start low and see what happens.

Serial pc(USBTX, USBRX);
SPI device(p5, p6, p7);
DigitalOut cs(p8);
DigitalOut myled(LED1);
int main() {
myled = 1;
cs=1;
// New Code
char rep = 0xAA;
device.mode(8,1);
device.frequency(50000) // 50kHz on sck
pc.printf("Starting SPI Communication =^.^=\n");
    while(1) {
        myled=0;
        wait(1);
        cs = 0; // Most SPI devices use CS as active-low, which means logic 0 is when the chip is selected
        rep = device.write(0x00);
        cs = 1;
        pc.printf("Reply from device is: %d \n", rep);
        myled = 1;
        wait(1);
    }
}
@End code

Try that, and if it doesn't work then cycle through using SPI modes 0-3 while moving the frequency down a bit. Let me know if it works :D!

20 Jan 2012

Oh ya, also try reading address 0x04 and other locations, doesn't look like 0x00 is a good address to read...

20 Jan 2012

Ya, slave mode looks like it would be much easier, and if we could get a full data sheet it would make things much nicer! Did the chip come with any other information Gerald? Given that it's a device that takes security into consideration, reading data from it might require a few extra steps before we get a reply :3 although throwing it a few random bits never hurts. Also try changing the SPI mode to device.mode(16,1) since a few of those addresses were larger than 0xFF

21 Jan 2012

Thanks for you reply !

I have just tried to work with your remarks, "mode" and "frequency" indication. In a part of the datasheet, I can read :

Quote:

The maximum SCLK frequency from the spi master shall be 2 MHz

So, ok for

device.frequency(50000) // 50kHz on sck 

But for the format(8,1) I don't understand...

--

With the new code, rep = 0 !

I will try to work with another mode ?!

21 Jan 2012

I have changed the mode (enigmatic things !) for the mode (1,16). Now rep = 249.

Moreover, it the same "rep" value when I write 0xFF

21 Jan 2012

does this always output the same value?

   while(1) {
        for(uint16_t i = 0; i < 255; ++i){
            uint16_t rep = device.write(i);
            pc.printf("Reply from device is: %d \n", rep);
            wait(5);
        }
    }
21 Jan 2012

very good idea ! But it would be better with this paper ? /media/uploads/gege089/memorymap.pdf

21 Jan 2012

many rep=0 in the last code ! just the 00 for whitch rep = 249.....

21 Jan 2012

it appears from that pdf the addresses you want are 0x100, and 0x101; as the reply is always from the previous command on spi not the one you just sent,

device.write(0x100); //command
uint16_t rep1 = device.write(0x00); //send dummy byte to get reply
device.write(0x101); //command
uint16_t rep2 = device.write(0x00); //send dummy byte to get reply

does this work? probably not you probably have to send a command before that saying that the next command will be the register i want to read,

21 Jan 2012

I have already try the 100 adress. Without uint16_t rep1 = device.write(0x00);, send dummy byte to get reply, the answer is the same. But with it, it is now rep = 170. But when I put a finger on thee sensor, the temperature do not increase....

21 Jan 2012

you need to combine, rep1 and rep2 into a value, if they are 8bit values,

int16_t value;
(uint8_t*)value[0] = rep2; //LSW
(uint8_t*)value[1] = rep1; //MSW

or the other way round im not sure, Im not sure this is what your getting returned, but its worth a try, it could be an error value :/

21 Jan 2012

It was a mistake in my last post.... rep1 and rep2 was equal to 0; I have coded that:

uint16_t value[2];
value[0] = rep2; //LSW
value[1] = rep1; //MSW

pc.printf("Reply from device is: %d \n", value);

And the value = 268468216 and the value no change with the temperature.

21 Jan 2012

if both rep1 and rep2 are 0, we probably need to send a command saying were going to read a value,

your code is outputting the pointer to the variable "value", not its value, notice it wasn't an array in my code, but that doesn't matter if we haven't managed to get any data yet :/

21 Jan 2012

Chris Pepper wrote:

Im not sure what you mean by "the register i need to write", to get the sensor data you need to send 0xE5 to the device, within 20ms it should respond with 9 bytes of data, just keep sending 0x00 to get all the bytes

OKay, I propose for my infinity loop the follow code :

while(1) {
        myled=0;
        wait(1);
        cs = 0; 
        device.write(0xE5);
        wait(0.2);
        rep = decide.write(0x00);
        cs = 1;
       // That what I do not understand *
       ....
       //
        myled = 1;
        wait(1);
    }
  • In other words, how can I extract from the 9 bytes the sensor data ?
21 Jan 2012

here's a (horribly) rough way to check your reply:

cs = 0;
char idx = 0x00;

// Array to hold *possible* data
char replies[128];

// Make sure memory within is known
do
{ 
  replies[idx++] = 0;
}
while(idx<128)
idx = 0;

// Try desperately to get a response
while(idx<128) // EDITED: make sure to change this to idx < 128 o_o'
{
  replies[idx++] = device.write( "why are you ignoring me ._.?" );
  replies[idx++] = device.write( 0x00 );
}

for( idx = 0; idx < 128; idx++ )
  if(replies[idx] > 0x00)
    pc.printf("Non-zero value found in array at %i.\n  Value: %d\n", idx, replies[idx]);

if(replies)
  delete[] replies;

It's not pretty, but it should dump anything the device sends you into the array then output it's array position and value to your USB-serial connection. Make sure you replace the string with addresses of interest :P ... This should give us some good debug info for how to talk to the device :)

21 Jan 2012

The device should respond within 20ms, so id send the command wait for 20ms, then try to get the data,

uint8_t packet[9];

while(1){
  cs = 0;
  device.write(0xE5); //command byte
  wait(20);
  uint8_t reply = device.write(0x00); // itl send 0x00 before packet
  wait(1);
  for(uint8_t i = 0; i < 9; ++i) {
    packet[i] = device.write(0x00);
    wait(1);
  }
  cs = 1; // must raise cs between commands but after the reply
}

21 Jan 2012

Okay, but when I printf the data in the tab packet, after the cs=1 command, as follow:

  for(uint8_t j=0; j<9;j++){
  uint8_t value = packet[j];
  printf("value : %d\n", value);
  }

8 times "value = 0" appear in my terminal !

21 Jan 2012

have you put the device back into 8bit spi mode? device.mode(8,0);

can try (8,1), (8,2) and (8,3) too, The command is right at least we know that much now

21 Jan 2012

It is the good way !

Mode(3, 0)

248 | 0 | 93 | 0 | 0 | 0 | 0 | 0 | 0

The value (byte ?) 93 mean to be the temperature information, but neither in °C nor °F. I will read the datasheet (but in finality that not what I would like to do with that chip...)

Mode(3, 1)

same results

--

just 3 questions before going to sleep ;) : - Why we use the mode(3, 0) -> if you have a google training about that... (that the 8 of the "command code" in the datasheet ? - What is LSW / MSW ? - What the difference between "Get sensor data" with 0xE4 and "Get sensor data after new sample" with 0xE5 ?

Regards,

GM

21 Jan 2012

you mean mode(8,0)? mode(3,0) isnt a mode, needs to be 8bit or 16bit,

LSW means Least Significant Word and MSW is Most Significant Word, and a word is 2 bytes,

0xE4 send the temperature it has in its memory, 0xE5 checks the temperature and sends that

anyway to reconstruct the data returned, byte 0 = reply status (dont know what that means) byte 1 - 4 is a 32bit value for the temperature that we need to reconstruct

uint8_t value[4];
value[0] = packet[3];
value[1] = packet[4]; //LSW
value[2] = packet[1];
value[3] = packet[2]; //MSW

uint32_t int_value = *(reinterpret_cast<uint32_t *>(value); //convert the value to a 32bit integer

Im not sure about the byte order though