SPI MISO-MOSI arduino

Hey All,
Currently, I am making a pulse oximetre using AFE4490 by Texas Instrument and arduino Mega 2560.
I have a problem relating to the SPI communication in arduino.
This SPI communication is required to make a writing and reading register module.
So, I am making its function for both writing and reading register. The writing seems good, but the reading might be confusing for me.

I write it like this

unsigned long AFE4490Read (unsigned char address)
{       
	SPIInit(); 

        digitalWrite (SPISTE, LOW);         
        byte data = shiftIn (SOMI, SCLK, LSBFIRST);  	   
              
        ///////////data 0/////////////
        shiftOut (SIMO, SCLK, LSBFIRST, 0X00); /*SIMO shifting out byte module */
          
        byte data0 = shiftIn (SOMI, SCLK, LSBFIRST);
        data0 = (data&0X000F);
        delay(20);

        ///////////data 1/////////////
        shiftOut (SIMO, SCLK, LSBFIRST, 0X00); /*SIMO shifting out byte module */
        
        byte data1 = shiftIn (SOMI, SCLK, LSBFIRST);
        data1 = (data >>4)& (0X00F);
        delay(20);
        
        ///////////data 2/////////////
        shiftOut (SIMO, SCLK, LSBFIRST, 0X00); /*SIMO shifting out byte module */

        byte data2 = shiftIn (SOMI, SCLK, LSBFIRST);
        data2 = (data >>8)& (0XF);
        delay(20);
       
        ///////////data 3/////////////         
	shiftOut (SIMO, SCLK, LSBFIRST, address); /*SIMO shifting out byte module */
        
        byte data3 = 0X00;
        data3 = shiftIn (SOMI, SCLK, LSBFIRST);
        SPI.end();
        
        digitalWrite (SPISTE, HIGH);
}

So the reading procedure is happened by sending 32 bits, the three first 8-bits will be sent to arduino MISO and will be recognized as data bit register, meanwhile the last 8-bits will be sent to the AFE4490 and will be recognized as an address register bit.
I understand how to sending out the value using shiftOut, but I am not sure how i should write in in order to read the MISO data in arduino. So far, I am sketching the MISO data reading as shown above. But, I don't it works properly.

Please suggest me how to do this.
Thank you in advance!

Stella

I think that this bit of code doesn't do what you expect:

byte data2 = shiftIn (SOMI, SCLK, LSBFIRST);
data2 = (data >>8)& (0XF);

data2 is a byte; it's only 8 bits long. When you right-shift it by 8 bits, there's nothing left. After this operation, data2 will always be 0. Is that what you wanted?

stellalaksono:
Hey All,
Currently, I am making a pulse oximetre using AFE4490 by Texas Instrument and arduino Mega 2560.
I have a problem relating to the SPI communication in arduino.
This SPI communication is required to make a writing and reading register module.
So, I am making its function for both writing and reading register. The writing seems good, but the reading might be confusing for me.

Please suggest me how to do this.
Thank you in advance!

Stella

The Arduino boards have built in SPI interfaces you know? You don't need to shift out data manually. Just use the SPI pins and the SPI library.

You would simply access the chip like this (pseudo code - example assumes 24 bits of control / address and 8 bits of return data):

#include <SPI.h>

    uint8_t control_data_hi_byte = (whatever);
    uint8_t control_data_mid_byte = (whatever);
    uint8_t control_data_lo_byte = (whatever);
    uint8_t result; // return data will go here

    digitalWrite (chip_select, LOW); // activate device chip select (may be active high - check docs)
    SPI.transfer (control_data_hi_byte); // send control / address bits
    SPI.transfer (control_data_mid_byte); // send control / address bits
    SPI.transfer (control_data_lo_byte); // send control / address bits
    result = SPI.transfer (0); // dummy 0 is ignored, data is read
    digitalWrite (chip_select, HIGH); // de-activate device chip select (may be active high - check docs)

Or, if you don't care about speed, want to use ANY pins for SPI and bit-banging is OK for you, you can use code like this (equivalent to "SPI.transfer()" above):

// transfer one byte via SPI Mode 3
uint8_t spi_transfer (uint8_t data)
{
    uint8_t bits = 8;
    while (bits--) {
        digitalWrite (_sck_pin, LOW);
        digitalWrite (_mosi_pin, data & _BV (bits) ? HIGH : LOW);
        digitalWrite (_sck_pin, HIGH);
        digitalRead (_miso_pin) ? data |= _BV (bits) : data &= ~_BV (bits);
    }
    return data;
}

This code transfers data using SPI mode 3. To do modes 0, 1 or 2 you need to change the order of SCK low/high or high/low, and change whether the sck pin is changed first or the digital read/write is done first. See the attached image for determining which mode you need.

Hope this helps.

Hey Krupski,

Thanks for your complete reply!
I also previously thinking about using the SPI that is already build in the arduino and using SPI.transfer();
However, I just do not understand how the SPI.transfer() function can notice whether the arduino is about to send the bit
or to receive the bit? While in my system, I want the MISO pin to be able to read the digital value from AFE4490. I am worried that if I used this function, it will sending the bits from the chip instead of collecting the bits.

I'm just not sure about this, so for safety purpose, I use the shiftOut/shiftIn.
Perhaps you could enlighten me about this?

Thank you soo much.

Stella

stellalaksono:
Hey Krupski,

Thanks for your complete reply!
I also previously thinking about using the SPI that is already build in the arduino and using SPI.transfer();
However, I just do not understand how the SPI.transfer() function can notice whether the arduino is about to send the bit
or to receive the bit? While in my system, I want the MISO pin to be able to read the digital value from AFE4490. I am worried that if I used this function, it will sending the bits from the chip instead of collecting the bits.

I'm just not sure about this, so for safety purpose, I use the shiftOut/shiftIn.
Perhaps you could enlighten me about this?

Thank you soo much.

Stella

SPI transfers do both sending and receiving. If you are sending a "write" type of command to the slave device, it reads the data clocked in on the MOSI pin. It may or may not drive "valid" data out to the MISO pin, but that doesn't matter.

If you do a read of the device, IT drives data bits out to the MISO pin which the SPI port "assembles" into an 8 bit byte.

To WRITE to a device, you do this:

    read_data = SPI.transfer (write_data); // content of "read_data" is irrelevant.

To READ from a device, you do this:

    read_data = SPI.transfer (write_data); // value of "write data" is ignored

As a more "realistic" example, imagine wanting to get the hour value from an imaginary real time clock chip and then writing a new value to the minutes register:

// clock chip control bit defines
// bit   7   6   5   4   3   2   1   0
//      R/W  W   M   D   Y   H   M   S
// note: R/W: 0=write, 1=read
// note: W=day of week, 0=sunday
// note: M,D,Y,H,M,S=month,day,year,hour,minute,second
// (good practice to document important stuff in your code)

    uint8_t cmd; // command byte
    uint8_t data; // data byte

    // example: read the year from the RTC
    cmd = 0b10001000; // read, year
    digitalWrite (chip_select, LOW); // enable rtc
    SPI.transfer (cmd); // send command to rtc (read year) (reply is irrelevant)
    data = SPI.transfer (0); // read year reply from rtc
    digitalWrite (chip_select, HIGH); // disable rtc

    // example: write the minute to the RTC
    cmd = 0b00000010; // write, minutes
    digitalWrite (chip_select, LOW); // enable rtc
    SPI.transfer (cmd); // send command to rtc (write minutes) (reply is irrelevant)
    SPI.transfer (58); // write "58" to the rtc minutes register (reply is irrelevant)
    digitalWrite (chip_select, HIGH); // disable rtc

Make sense?

@tmd3

data2 is a byte; it's only 8 bits long. When you right-shift it by 8 bits, there's nothing left. After this operation, data2 will always be 0. Is that what you wanted?

Yes you are right, I just miscalculate it...Thanks! :slight_smile:

@krupski
It seems logical for me. Thanks! This is much better explanation then most of the example shown in the arduino example.

I tried to implement the code, but I kinda got a funny results...

So this is how i finally improved my reading function:

unsigned long AFE4490Read (unsigned char address)
{       
  SPIInit();
  unsigned long data;
  unsigned long data0 = (data&0X0000FF);
  unsigned long data1 = (data >>8)& (0X00FF);
  unsigned long data2 = (data >>16)& (0XFF);
  unsigned long data3 = 0X00;
  unsigned long dataComplete;
  
  digitalWrite (SPISTE, LOW);
  
  SPI.transfer (data);
    
  shiftOut (SIMO, SCLK, LSBFIRST, 0); 
  SPI.transfer (data0);
  delay (20);
  
  shiftOut (SIMO, SCLK, LSBFIRST, 0); 
  SPI.transfer (data1);
  delay (20);
  
  shiftOut (SIMO, SCLK, LSBFIRST, 0); 
  SPI.transfer (data2);
  delay (20);
  
  shiftOut (SIMO, SCLK, LSBFIRST, address);
  SPI.transfer (data3);
  dataComplete = SPI.transfer (0);
  
  Serial.print(address);   
  Serial.print(" "); 
  
  digitalWrite (SPISTE, HIGH);
  
}

I put the serial.print (address) purposely in order to see that this function generates correctly.

And in the void setup, this is how i write to show the reading value:

void setup()
{
   Serial.begin(9600);
   pinMode (SOMI, INPUT);
   pinMode (SPISTE, OUTPUT);
   pinMode (SCLK, OUTPUT);
   pinMode (SIMO, OUTPUT);
   pinMode (AFEADCRDY, INPUT);
   digitalWrite (SPISTE, HIGH);
   digitalWrite (SCLK,LOW);
   AFE4490Init (); 
   SPIInit();       
   AFE4490EnableInterrupt();       
            for (int count= 0; count<60; count++) 
            {
                    Serial.println(count);
                    delay (1000);
                    Serial.print("Red Data:"); 
                    Serial.println(AFE4490Read(LED2VAL)); 
                    Serial.print("Red Ambient Data:"); 
                    Serial.println(AFE4490Read(ALED2VAL)); 
                    Serial.print("Red Different:");
                    Serial.println(AFE4490Read(ALED1VAL));
                    Serial.print("IR Data:"); 
                    Serial.println(AFE4490Read(LED2ABSVAL)); 
                    Serial.print("IR Ambient:"); 
                    Serial.println(AFE4490Read(LED1VAL)); 
                    Serial.print("IR Different:"); 
                    Serial.println(AFE4490Read(LED1ABSVAL)); 
            }
   AFE4490DisableInterrupt ();
}

The results i got from this, is like this:
0
Red Data:42 2701197825
Red Ambient Data:43 2701197825
Red Different:45 2701197825
IR Data:46 2701197825
IR Ambient:44 2701197825
IR Different:47 2701197825

Which is i don't think it's right. I have not connected my arduino to my AFE4490 yet, so I was expecting it to show 0.
Things more funny is, when i move the serial.print (address) command after digitalWrite (SPISTE,HIGH), the numbers changed into this:

Red Data:42
66080
Red Ambient Data:43
66080
Red Different:45
66080
IR Data:46
66080
IR Ambient:44
66080
IR Different:47
66080

Does anyone know what caused this?
Thank you!

Stella

stellalaksono:
@tmd3

data2 is a byte; it's only 8 bits long. When you right-shift it by 8 bits, there's nothing left. After this operation, data2 will always be 0. Is that what you wanted?

Yes you are right, I just miscalculate it...Thanks! :slight_smile:

@krupski
It seems logical for me. Thanks! This is much better explanation then most of the example shown in the arduino example.

I tried to implement the code, but I kinda got a funny results...

So this is how i finally improved my reading function:

I wouldn't use the word "improved" since you did it all wrong. Specifically why did you RIGHT shift the data? All that did is drop bits off the end to fall into the bit-bucket.

OK now, upon looking at the data sheet for your device, I see that it transfers 32 bits at a time.

The top 8 bits are an address, the bottom 24 bits are data.

The pin called "SPI STE" is the device select pin, and it's polarity is active low.

The SCLK idle state is "low" and data is transferred on the rising edge, therefore it is using SPI mode 1 (one).

You need to connect it to the SPI pins of the Arduino and connect the SPI STE pin to the Arduino SS pin.

Then you need to initialize the SPI code to use Mode 1, MSB first and choose an SPI clock of clk/4X (clk/2X will probably work according to the data sheet, but it's cutting close, so go clk/4X to be safe).

Try this code. Note that I can't test it since I don't have your chip available to me. Note that the DIRECTION that you shift bits is important. If you reverse that direction in an effort to "improve" the code, then don't complain when it doesn't work. If you use "shiftout" instead of SPI in an effort to "improve" the code, then don't complain when it doesn't work.

Again, note that this code should be CLOSE, but I don't know if it works because I can't test it here. And note you need to include the SPI.h library and initialize the SPI correctly before using it. I'm not going to write the WHOLE PROGRAM for you.

uint32_t pulseox_read (uint8_t address)
{
    uint32_t data = 0;
    digitalWrite (spi_ste_pin, LOW); // enable device
    SPI.transfer (address); // send address to device
    data |= (SPI.transfer (0) << 16); // read top 8 bits -> data
    data |= (SPI.transfer (0) << 8); // read middle 8 bits -> data
    data |= SPI.transfer (0); // read bottom 8 bits -> data
    digitalWrite (spi_ste_pin, HIGH); // disable device
    return data; // return with 24 bits of read data
}

void pulseox_write (uint8_t address, uint32_t data)
{
    digitalWrite (spi_ste_pin, LOW); // enable device
    SPI.transfer (address); // send address to device
    SPI.transfer ((data >> 16) & 0xFF); // write top 8 bits
    SPI.transfer ((data >> 8) & 0xFF); // write middle 8 bits
    SPI.transfer (data & 0xFF); // write bottom 8 bits    
    digitalWrite (spi_ste_pin, HIGH); // disable device
}

Lastly, note that if you run this code without the device connected, you will get random data returned to you (whatever state the SPI pins happened to be in at the time). Testing the code without the device connected and assuming you will get a zero in return is a false assumption. The only to test if it WORKS is to connect the device.

Good luck.

Hey Krupski,

Thank you again for your time to enlighten me. I am truly appreciated although it is irritating you.
i am just confident with my coding because it results 0 binary, which is for me logical since i have not connect the device to the arduino, and i thought the other reason of this zero results, are the hardware problem.

I'll try to digest the way you are thinking how the code should work.
But still, i want to keep it original. so, let see how i am going to combine what i have understood by myself and from your tutorial.

Sorry for troubling you. I am too early to satisfy my work. I shouldn't do that.
I'll probably come back to you when I found another confusion. I hope you don't mind.

Thank you! thank you!

Stella

Uhh, yeah! I also forgot to ask this too...

I have been thinking to use ICSP in arduino, since it allows me to set the voltage as i desire. So i do not require any logic shifter to do this.
But I do not know yet,
how the arduino will undestand that i am using ICSP header?

normally, with digital I/O, i just simply introduce arduino to the pin using pinMode();
But, how about ICSP? how i write it in the function?

and what about the chip select in the ICSP?
it is not provided, does it works automatically? or should i use digital I/O pin for chip select or anything else should be done?

Help will be highly appreciated.
Thank you!

Stella