Go Down

Topic: SPI read write for 32bits data (Read 7736 times) previous topic - next topic

Paradigm

Hi Jay,

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

Tom Carpenter

@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.
~Tom~

Tom Carpenter

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:
Code: [Select]
#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.
}
~Tom~

Paradigm

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 :-)

Tom Carpenter

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.
~Tom~

Jaay

@Paradigm yes,I'm on the same boat as you.  :) 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?
Code: [Select]
unsigned long voltage = SPI_read(4);//Instantaneous Voltage Channel 1

Paradigm

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

Paradigm

Hi Tom,

From your code
Code: [Select]

//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

Jaay

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

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

Paradigm

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

SurferTim

#25
Nov 22, 2012, 03:22 pm Last Edit: Nov 22, 2012, 03:56 pm by SurferTim Reason: 1
Quote
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.
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus

Are we having fun yet?  :)

SurferTim

This is the test code I would use to test read. It reads the same register every second.
Code: [Select]
#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;
}

Tom Carpenter


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

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


Basically that is setting the value you want to write to a register.

The bit of code relavent to that snippit is:
Code: [Select]
 //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);


In the code, the command is set to write to the config register (0b01000000 from the datasheet - page 26), and then the value to write to that register is 1 (if you look at the list of commands in the datasheet it shows that the default value for the config register is 1, this was just an example of usage, not a requirement).

When you are reading a register, the lower three bytes should be 0xFEFEFE as you pointed out. This is done in the SPI_Read() function for you.




@Paradigm yes,I'm on the same boat as you.  :) 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?
Code: [Select]
unsigned long voltage = SPI_read(4);//Instantaneous Voltage Channel 1



That is an example of how to read a register, it doesn't actually do anything with the returned value. You could do something like this:

Code: [Select]

void loop() {
 delay(1000);
 unsigned long voltage = SPI_read(4);//Instantaneous Voltage Channel 1
 Serial.println(voltage); //print the voltage to the serial monitor (you will need to have called Serial.begin(...) in the setup() for it to work)
}




Quote
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.
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus

Are we having fun yet?  :)



The way I see the timing diagrams, it doesn't matter where the clock IDLEs in SPI. The clock is high when/as the SS line goes low. So we have:

SCLK is high.
SS drops low.
SCLK drops low.
MOSI is set to the correct value
SCLK rises high -> data is clocked in on the rising edge

All of that is indicative of MODE3.

Another way of putting it is that there is always a falling edge of SCLK before the first rising edge on which data is clocked - this is MODE3.
~Tom~

SurferTim

Quote
Another way of putting it is that there is always a falling edge of SCLK before the first rising edge on which data is clocked - this is MODE3.

I don't know what datasheet you are looking at, but not according to the read timing in the datasheet posted here. There is no falling edge before the rising edge. The write timing indicates there could be a falling edge prior to the rising edge, but it won't propagate the data.

It is probably just me, since yours is working, correct?

Paradigm

Hi Tim and Tom,

            I did the Mode 3 and ClockDiv. But still cant read anything from the IC. Do I miss out something? Must we initalise the IC before it can be use? How can I send the continuos conversion command to the IC to continue reading data?

Hi Jaay,

           Any luck? You program can work on your hardware?

           Thanks a lot

Go Up