SPI read write for 32bits data

Hi, I had a IC that need to be read and write with 32bits data. Can the SPI.h libaray handle 32bits data transfer? How can I declare my variable? Is it long int? Can I program something like SPI.transfer(0xFFFFFFFE);? How about reading?

How can I read a 32bits data from SPI?

Anyone had a file that can read write a 32bits SPI data?

Thanks a lot

Sending 8 bits 4 times = sending 32bits.

Paradigm:
How can I declare my variable? Is it long int?

That part is up to you. Depending on where you're receiving this data from, and what it's made up of, you might declare it as an array of 4 bytes (or characters) or a pair of int, or as a single long. All will give you 32 bits of data to play with, but when you do the SPI.transfer() for transmit or receive; or ShiftOut() / shiftIn() you'll be sending it in 4 byte-sized pieces (ie 8 bits at a time as dhenry posted above).

Paradigm:
How about reading? How can I read a 32bits data from SPI?

The same way, a byte at a time.

What is the IC you're wanting to communicate with?

Cheers ! Geoff

You can use a ‘union’ to efficiently and quickly convert a long to a series of four bytes or 2 ints or vice versa:

#include <SPI.h>

union FourByte{
    unsigned long bit32;
    unsigned int bit16[2];
    unsigned char bit8[4];
};

void setup(){
  SPI.begin();
  
  //save a 32bit value
  unsigned long someValue = 0xFFFFFFFEUL;
  //transmit 32bit value:
  transfer(someValue);
  
  //read 32bit value
  someValue = transfer(0);
  
  //read and write at the same time.
  someValue = transfer(someValue);

  //concatinate ints:
  FourByte bob;
  bob.bit16[0] = 10;
  bob.bit16[1] = 20;
  
  transfer(bob.bit32); //sends 1310730 = 20*65536+10

  //concatinate bytes:
  bob.bit8[0] = 0x1; //1
  bob.bit8[1] = 2; //2
  bob.bit8[2] = 0b11; //3
  bob.bit8[3] = B100; //4
  
  transfer(bob.bit32); //transfers 0x04030201
}

unsigned long transfer(unsigned long value){
  FourByte data = {value};
  for(byte i = 0; i < 4; i++){
    data.bit8[i] = SPI.transfer(data.bit8[i]);
  }
  return data.bit32;
}

Hi,

If I were to send 4 bytes of data to make up 32bits data transfer, for 0x12345678 to be sent, do I send 0x78 first or do I send 0x12 first?

As for 4 bytes ready, after reading the 4 bytes, how can I combine them together to become a long int? It is a signed long int

It is a multimeter IC chip that can measure AC voltage and current.

This is an example C code that I got from a website for this IC. They wrote their own function call SPI_Read. How can I use SPI.h in the arduino library to help me read this 32bits data? Sorry I made a mistake all are unsigned long.

Read_Data=SPI_Read(0x03FEFEFE);

Hope all expert can help. Thanks a lot

do I send 0x78 first or do I send 0x12 first?

That depends on the "endianness" of the device you are talking to.

If you don't have documentation try one way and if that doesn't work try the other way.

Read_Data=SPI_Read(0x03FEFEFE);

What's with the huge parameter to a Read function. Is this really a "transfer" that sends 0x03FEFEFE and returns 4 bytes into Read_Data?

Do you have the source to SPI_Read()?


Rob

Sorry I do not have access to the SPI_Read() function. Just asking anyone can help with the 2 functions I need to read and write to my ICs

unsigned long SPI_read(unsigned long Address)

and

void SPI_write(unsigned long Comd)

I dont know what to put inside to this 2 functions so that I can use the SPI.transfer to let my program works. I hope all expert can help me here. Thanks a lot

Many people here could write functions to do this, but without knowing what the chip is there's little point really.

Do you have a data sheet for the chip?

How can I read a 32bits data from SPI?

var0 = SPI.transfer(0);
var1 = SPI.transfer(0);
var2 = SPI.transfer(0);
var3 = SPI.transfer(0);

But I doubt that will do what you want, we need the chip info.


Rob

Hi Gray,

Attached is the datasheet. Thanks a lot

CS5464_F3.pdf (477 KB)

Paradigm:
Hi Gray,

Attached is the datasheet. Thanks a lot

Well I think you might be confused. From your datasheet:

7.4 Command Interface
Commands and data are transferred most-significant bit
(MSB) first. Figure 1 on page 12, defines the serial port
timing. Commands are clocked in on SDI using SCLK.
They are a single byte (8 bits) long and fall into one of
four basic types:

  1. Register Read
  2. Register Write
  3. Synchronizing
  4. Instructions

There are 32 different byte size registers to work with on the chip, but it’s designed for simple SPI byte size read and write operations.

Lefty

Hi Lefty,

It says when reading data from SDO, example my instantaneous voltage (which is 32bits), I need to send the SYNC0 and SYNC1 to the SDI pins if not other command is needed. Thats why I need to send (0x04FFFEFF). Or did I misinterpret the datasheet. With CS pin, I dont need to send the SYNC0. Anyhow, my data is also 32bits. How can i read the 32bits data with SPI.transfer? Or write a sub-routine of

unsigned long SPI_read(byte Address)

7.6.2 Synchronization (SYNC0 and SYNC1)
The serial interface is bidirectional. While reading data on the SDO output, the SDI input must be receiving
commands. If no command is needed during a read, SYNC0 or SYNC1 commands can be sent while read
data is received on SDO.
The serial port is normally initialized by de-asserting CS. An alternative method of initialization is to send 3 or
more SYNC1 commands followed by a SYNC0. This is useful in systems where CS is not used and tied low.

Pls help. Thanks a lot

0x04FFFEFF

If this is what you want to send, and SS is the slave select pin number, then this should do it.

digitalWrite(SS,LOW);
var0 = SPI.transfer(0x04);
var1 = SPI.transfer(0xFF);
var2 = SPI.transfer(0xFE);
var3 = SPI.transfer(0xFF);
digitalWrite(SS,HIGH);

edit: Some devices need a small pause between enabling the SPI interface and starting the transfer. I usually put a "delayMicroseconds(1);" after "digitalWrite(SS,LOW);".

Modified my example from before based on the datasheet:

#include <SPI.h>

union FourByte{
    struct {
      unsigned long value:24; //24bit register values go in here
      byte command:8; //8bit command goes in here.
    };
    byte bit8[4]; //this is just used for efficient conversion of the above into 4 bytes.
};

void setup(){
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE3); //I believe it to be Mode3
  SPI.setClockDivider(SPI_CLOCK_DIV16);
  pinMode(SS, OUTPUT); //not really necessary as it is done by the SPI library.
  digitalWrite(SS, HIGH);
  //Page
  //example of reading data
  unsigned long voltage = SPI_read(4);//Instantaneous Voltage Channel 1
  
  //example of writing data
  union FourByte data;
  data.command = 0b01000000; //Write to config register
  data.value = 1; //This is the default value from datasheet, just using it as an example.
  SPI_write(data);
}

void loop(){
  
}

void SPI_write(union FourByte data) {
  digitalWrite(SS,LOW); //Using CS pin, so sync1/sync0 bytes not needed
  for(char i = 3; i >= 0; i--){
    SPI.transfer(data.bit8[i]); //transfer all 4 bytes of data - command first, then Big Endian transfer of the 24bit value.
  }
  digitalWrite(SS,HIGH);
}

unsigned long SPI_read(byte command){
  digitalWrite(SS,LOW); //SS goes low to mark start of transmission
  union FourByte data = {0xFEFEFE,command}; //generate the data to be sent, i.e. your command plus the Sync bytes.
  for(char i = 3; i >= 0; i--){
    data.bit8[i] = SPI.transfer(data.bit8[i]); //send the data whilst reading in the result
  }
  digitalWrite(SS,HIGH); //SS goes high to mark end of transmission
  return data.value; //return the 24bit value recieved.
}

I changed to this…but still not working…

#include <SPI.h>

int CS = 10;


union FourByte{
    struct {
      unsigned long value:24; //24bit register values go in here
      byte command:8; //8bit command goes in here.
    };
    byte bit8[4]; //this is just used for efficient conversion of the above into 4 bytes.
};

void setup(){
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  
  Serial.begin(9600);
  pinMode(CS, OUTPUT);
  digitalWrite(CS, HIGH);
  //Page
  //example of reading data
 // unsigned long voltage = SPI_read(0x04);//Instantaneous Voltage Channel 1
  
  //example of writing data
  union FourByte INI;
  INI.command = 0b11111111; //Write to config register
  INI.value = 0xFFFFFE; //This is the default value from datasheet, just using it as an example.
  SPI_write(INI);
  
  
  union FourByte data;
  data.command = 0b11101000; //Write to config register
  data.value = 0xFFFFFE; //This is the default value from datasheet, just using it as an example.
  SPI_write(data);
}

void loop(){
  unsigned long voltage = SPI_read(0x04);
  Serial.print("Voltage=");
  Serial.println(voltage); 
  delay(1000); 
}

void SPI_write(union FourByte data) {
  digitalWrite(CS,LOW); //Using CS pin, so sync1/sync0 bytes not needed
  for(char i = 3; i >= 0; i--){
    SPI.transfer(data.bit8[i]); //transfer all 4 bytes of data - command first, then Big Endian transfer of the 24bit value.
  }
  digitalWrite(CS,HIGH);
}

unsigned long SPI_read(byte command){
  digitalWrite(CS,LOW); //SS goes low to mark start of transmission
  union FourByte data = {0xFEFEFE,command}; //generate the data to be sent, i.e. your command plus the Sync bytes.
  for(char i = 3; i >= 0; i--){
    data.bit8[i] = SPI.transfer(data.bit8[i]); //send the data whilst reading in the result
  }
  digitalWrite(CS,HIGH); //SS goes high to mark end of transmission
  return data.value; //return the 24bit value recieved.
}

Hi Jay,

You also working on the same IC? Any luck? What hardware connection are you using? Any schematic to show? Thanks

@Jay

You are correct about the changes from byte to char in the for loops, that was a typo on my end. Also setting SS high at the beginning is necessary (though setting it to an output is done by the SPI library).

One thing I have noticed is that you are trying to use SPI mode 0, and I believe the IC to be SPI mode 3 (data is read on the first rising edge after a falling one according to the timing diagrams).

@Paradigm

The union works by placing an array of 4 bytes in the same memory location as the struct. The struct contains a 24bit variable and a byte variable.
Due to the way unions work this basically means that the byte variable 'command' is stored at the same place as the element 3 in the array. Similarly the variable 'value' is stored in the same memory space as the three other bytes in the array (elements 0,1,2).
Basically what this means is that you can convert between the array and the struct without doing any calculations, making it a very efficient method.

I will have a play with the code to see if I can find anything which may be causing a problem.

Right, all of the code appears to be doing what is expected, however I have just had it up on the oscilloscope and the default SCLK rate (8MHz) is far far faster than the chips absolute maximum SCLK rate (2MHz).

Also, it is definitely MODE_3. The datasheet talks about the SDI setup time before rising edge which is indicative of MODE_3 which the timing diagrams confirm.

Try this:

#include <SPI.h>

union FourByte{
    struct {
      unsigned long value:24; //24bit register values go in here
      byte command:8; //8bit command goes in here.
    };
    byte bit8[4]; //this is just used for efficient conversion of the above into 4 bytes.
};

void setup(){
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE3); //I believe it to be Mode3
  SPI.setClockDivider(SPI_CLOCK_DIV16);
  pinMode(SS, OUTPUT); //not really necessary as it is done by the SPI library.
  digitalWrite(SS, HIGH);
  //Page
  //example of reading data
  unsigned long voltage = SPI_read(4);//Instantaneous Voltage Channel 1
  
  //example of writing data
  union FourByte data;
  data.command = 0b01000000; //Write to config register
  data.value = 1; //This is the default value from datasheet, just using it as an example.
  SPI_write(data);
}

void loop(){
  
}

void SPI_write(union FourByte data) {
  digitalWrite(SS,LOW); //Using CS pin, so sync1/sync0 bytes not needed
  for(char i = 3; i >= 0; i--){
    SPI.transfer(data.bit8[i]); //transfer all 4 bytes of data - command first, then Big Endian transfer of the 24bit value.
  }
  digitalWrite(SS,HIGH);
}

unsigned long SPI_read(byte command){
  digitalWrite(SS,LOW); //SS goes low to mark start of transmission
  union FourByte data = {0xFEFEFE,command}; //generate the data to be sent, i.e. your command plus the Sync bytes.
  for(char i = 3; i >= 0; i--){
    data.bit8[i] = SPI.transfer(data.bit8[i]); //send the data whilst reading in the result
  }
  digitalWrite(SS,HIGH); //SS goes high to mark end of transmission
  return data.value; //return the 24bit value recieved.
}

Hi Tom, I saw that the chip can run at 4MHz. Where you found the 2MHz? Because from the breakout board supplied by my vendor, the crystal connected to my IC is a 4MHz IC. My Uno is using 16MHz so I have to use the default value of 4 to get it to 4Mhz? Or must I still write SPI.setClockDivider(SPI_CLOCK_DIV4);

Or maybe I can try on different clock speed :slight_smile:

You are correct that the clock for the IC should be 4MHz. However at 4MHz, the serial clock frequency for its SPI interface is limited to a maximum of 2MHz, which is specified on page 11 of the datasheet.

As the Uno runs at 16MHz, then
SPI_CLOCK_DIV2 = 8MHz,
SPI_CLOCK_DIV4 = 4MHz,
SPI_CLOCK_DIV8 = 2MHz,
SPI_CLOCK_DIV16 = 1MHz.

As such you will see this line in the code in my last post:
SPI.setClockDivider(SPI_CLOCK_DIV16);

So that the SPI interface will run at 1MHz. It is usually best to run below the maximum value which is why I suggest using 1MHz not 2MHz.