SPI read write for 32bits data

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.

@Paradigm yes,I'm on the same boat as you. :slight_smile: and I used Energy IC diagram as in the data sheet page 42 to join the Arduino Uno....working out on the program..

@Tom Thanks for the correction.. How can I print the voltage?I mean when I print out,the voltage value remains at 0.Should this line be in the loop?

unsigned long voltage = SPI_read(4);//Instantaneous Voltage Channel 1

Hi Jaay, did you fabricate the circuit yourself or bought it from vendor? How you come out with all the values and rating of the components? Hope you can help me also. As the breakout board that I had is from a vendor. Wish to fabricate it myself

Hi Tom,

From your code

//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);
}

The data.value = 1; and you said its a default value from datasheet. Can I ask where you find this statement? Can you kindly advise? Thanks a lot. Because I always thought the data.value should be the sync0 or sync1 value example: data.value = 0xFEFFFE; is it? Thanks a lot for your help

I also got it from vendor :).All I do is read the data sheet and managing for the code based on the data sheet.But I'm also still on the way so we can help each other out.

@Tom BTW What's this line also...Thanks a lot

data.value = 1; //This is the default value from datasheet, just using it as an example.

Hi Jaay,

Too bad, I thought I can replicate the vendor breakout board. Its ok. As long as I can start reading from the board, I can always buy more from the vendor. Hahaha

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.

How do you figure that? The way I see it, the timing diagram shows the capture is on the first rising edge after the CS goes LOW. That timing specification is marked (on the write timing) as "t3" (time required between the CS Falling and SCLK Rising) (edit: and "t6" on the read timing).

The read timing shows this best. SCLK is idle LOW with clock pulses going HIGH. That is SPI Mode 0.

Are we having fun yet? :slight_smile:

This is the test code I would use to test read. It reads the same register every second.

#include <SPI.h>

// change this to your SS pin
#define CS5464 8

void setup() {
  Serial.begin(9600);

  // disable CS5464 SPI
  pinMode(CS5464,OUTPUT);
  digitalWrite(CS5464,HIGH);

  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV8);

  Serial.println("Setup complete");  
}

void loop() {
  delay(1000);
  long testVal = readMeter(4);
  Serial.println(testVal);
}

long readMeter(byte meterCommand) {
  long lBuffer = 0;
  byte rtnVal[3];
  
  // enable CS5464 SPI
  digitalWrite(CS5464,LOW);

  // delay 1us after CS LOW 
  delayMicroseconds(1);

  // send your command
  SPI.transfer(meterCommand);

  // read the 3 bytes
  // you could use a for loop here
  rtnVal[0] = SPI.transfer(0xFF);
  rtnVal[1] = SPI.transfer(0xFF);
  rtnVal[2] = SPI.transfer(0xFF);

  // disable CS5464 SPI
  digitalWrite(CS5464,HIGH);  

  // assemble into long type
  lBuffer = lBuffer | rtnVal[0];
  lBuffer = lBuffer << 8;
  lBuffer = lBuffer | rtnVal[1];
  lBuffer = lBuffer << 8;
  lBuffer = lBuffer | rtnVal[2];
  
  return lBuffer;
}