SPI 32 bit

Hey folks,

I have always had problems working with bitwise operators and need some help. I am reading 32 bits from an LTC2440 ADC using the following code (seems to work) and which I found here: sample code for LTC2440 24-bit ADC

unsigned long SpiRead(void) {

  unsigned long result = 0.0;
  byte sig = 0;    // sign bit
  byte b;
  unsigned char incoming_adc[10];

  while (digitalReadFast(BUSYPIN)==HIGH) {      // wait until busy pin goes low and ADC result is ready
  }  
  digitalWriteFast(SLAVESELECT,LOW);            // take the SS pin low to select the chip
  delayMicroseconds(1);                         // probably not needed, only need 25 nsec delay

  b = SPI.transfer(0xff);                       // B3
  if ((b & 0x20) == 0) sig=1;                   // is input negative ?
  b &=0x1f;    // discard bits 25..31
  result = b;
  result <<= 8; 
  b = SPI.transfer(0xff);                       // B2
  result |= b;
  result = result<<8;
  b = SPI.transfer(0xff);                       // B1
  result |= b;
  result = result<<8;
  b = SPI.transfer(0xff);                       // B0
  result |= b;

  digitalWriteFast(SLAVESELECT,HIGH);           // take the SS pin high to bring MISO to hi-Z and start new conversion

  if (sig) result |= 0xf0000000;              // if input is negative, insert sign bit (0xf0.. or 0xe0... ?)      
  result = result / 16.0;                               // scale result down , last 4 bits are "sub-LSBs" 
  return(result); 
}

Could anyone possibly explain the bitwise operator parts of the code for me in detail, I would VERY much appreciate it since I have tried understanding it but it is proving very difficult. I am using an arduino uno though, so I'm not sure if it can store 32 bits and it might have to be split up into a set of 16 bits?

Here are the bit descriptions of the bits from the LTC2440 datasheet:
http://cds.linear.com/docs/en/datasheet/2440fd.pdf pg. 11

Bit 31 (first output bit) is the end of conversion (EOC) indicator.
This bit is available at the SDO pin during the conversion and sleep states whenever the CS pin is LOW.
This bit is HIGH during the conversion and goes LOW when the conversion is complete.

Bit 30 (second output bit) is a dummy bit (DMY) and is
always LOW.

Bit 29 (third output bit) is the conversion result sign indicator (SIG).

If V_IN is >0, this bit is HIGH.
If V_IN is <0, this bit is LOW.

Bit 28 (fourth output bit) is the most significant bit (MSB) of the result.
This bit in conjunction with Bit 29 also provides the underrange or overrange indication.
If both Bit 29 and Bit 28 are HIGH, the differential input voltage is above +FS.
If both Bit 29 and Bit 28 are LOW, the differential input voltage is below –FS.

Bits ranging from 28 to 5 are the 24-bit conversion result MSB first.

Bit 5 is the Least Signifi cant Bit (LSB).

Bits ranging from 4 to 0 are sub LSBs below the 24-bit level.
Bits 4 to bit 0 may be included in averaging or dis-carded without loss of resolution.

Thank you!

First you must take a look to the Data types. For 32 bits data types you have long type:
http://arduino.cc/en/Reference/Long

For bitwise logic operators you can see the following links:

TechnicRC:
Could anyone possibly explain the bitwise operator parts of the code for me in detail, I would VERY much appreciate it since I have tried understanding it but it is proving very difficult. I am using an arduino uno though, so I'm not sure if it can store 32 bits and it might have to be split up into a set of 16 bits?

You don't need to split anything up.

The Arduino SPI hardware transfers 8 bits at a time, so to transfer 32 bits, you need 4 SPI calls.

Imagine this: You have 4 dinner plates. Each plate is an 8 bit byte.

You receive the first byte, put it on the first plate, then place the plate on the table.

You receive the second byte, place it on the second plate, then lift up the first plate and slide the second one under it.

You receive the third byte, place it on the third plate, then lift up the first two plate and slide #3 underneath.

For the fourth and final byte, you do the same thing.

Now you have a vertical stack of 4 plates, The top plate, having been lifted up (shifted) three times now occupies the most significant byte of the 32 bit long word. The bottom plate is the least significant byte.

Let's look at it another way. Imagine I want to send you the hex number 0x12345678, 8 bits (one byte) at a time.

You start out with an "empty" uint32_t (32 bit long word).

I send you the first byte which is 0x12. You place it into your variable. It's current value is 0x00000012

I then send the next byte (0x34) to you. You shift your variable to the left 8 bits which results in your variable now being 0x00001200

Then you add in the second byte which results in you having 0x00001234

Now I send you the third byte (0x56). You again shift your variable to the left 8 bits resulting in 0x00123400. Then add in byte #3 which results in you having 0x00123456

I send the last byte (0x78). Again you left shift 8 bits which results in 0x12345600. Then add in your last byte and you have 0x12345678

Hope this makes sense.

The terminal only prints 29 bits and not 32? Where are the missing bits?

Maybe leading zeros?

result <<= 8;

This line looks wrong.

Yes they are leading zeros according to table 1 on page 11 of this manual. http://cds.linear.com/docs/en/datasheet/2440fd.pdf Good call. However now I have come up against another problem. Bit 29 and Bit 28 both become 1 when a negative Analog IN voltage is applied but bit 29 should definitely be 0 when a negative Analog In voltage is applied (same Table). I checked with a multimeter, and there sure is a negative voltage coming into the ADC. Do you guys think it could be a coding error?

michinyon:

result <<= 8;

This line looks wrong.

It looks right. Its the same as

  result = result << 8 ;

Awesome. Going back to the original code, could someone please help me understand these snippets of code

  if ((b & 0x20) == 0) sig=1;                   // is input negative ?
  b &=0x1f;    // discard bits 25..31

and

if (sig) result |= 0xf0000000;              // if input is negative, insert sign bit (0xf0.. or 0xe0... ?)

Also why do you use 0xFF in SPI.transfer() and not something like 0x00?

b = SPI.transfer(0xff);

TechnicRC:
(...)
Also why do you use 0xFF in SPI.transfer() and not something like 0x00?

b = SPI.transfer(0xff);

This I think is because you want the content of the byte in the 0xff byte of memory.

TechnicRC:
(...)

  if ((b & 0x20) == 0) sig=1;                   // is input negative ?

b &=0x1f;    // discard bits 25..31



(...)

This part is checking in the bit 5 is one and if is save that in the value sig (from signal). The next line makes that you take away that bit 5 (and all the bit that you may have at the left of that bit)

The last chunk of code I think that rewrites the information of that bit 5 back to the final result.

This part is checking in the bit 5 is one and if is save that in the value sig (from signal).

Ohh I see, 0x20 is equivalent to binary 00100000.

But I still don't quite see how this line works

b &=0x1f;

Could you possibly provide an explanation in binary? I believe the comment should be // discard bits 29..31 and not // discard bits 25..31. Correct? Since the bits ranging from 28 to 5 are the ADC conversion results

That can be translated to "keep all the bits from 4 to 0 and throw away all the others".

For example if you have 1010 1010b and you make an AND (the b&=0x0f is the same that b = b & 0x1f, and is doing an bitwise AND) with 0x1f=0001 1111b the result will be 0000 1010b that is delete (or reset) the bits 7 and 5.

EDIT: Yes they are the bits 29..31.

Fantastic, great explanation! So the comment should read "discard bits 29..31"

Also is there a way to print the binary number with all leading/trailing zeros? At the moment only 5 bits are being printed and including the 2 zero bits at the beginning of the transmission, this brings the total to 7. So I am still missing one, could a trailing zero be dropped also and not print out? Maybe there is in fact another leading zero so printing the entire byte would be most helpful

After read a bit of the datasheet of your ADC, I realise that you don't need the bits 4..0 of the result too:
http://cds.linear.com/docs/en/datasheet/2440fd.pdf

The best way to print binary is print hexadecimal! It have all the information of the binary and is much more easy to read. Then you only need to know that:

0 => 0000
1 => 0001
2 => 0010
3 => 0011
4 => 0100
5 => 0101
6 => 0110
7 => 0111
8 => 1000
9 => 1001
A => 1010
B => 1011
C => 1100
D => 1101
E => 1110
F => 1111

EDIT: After write this post I went back reading your first post, and actually you are discarding the bits 4..0, by doing this:

  result = result / 16.0;                               // scale result down , last 4 bits are "sub-LSBs"

You are awesome luisilva,

Your help was much appreciated. Merci!