Go Down

Topic: Ethernet shield & 24 Bit ADC Problem [multiple SPI devices] (Read 3438 times) previous topic - next topic


Got the following things:
1) Arduino UNO
2) Ethernet (&MicroSD Card)
3) 24 Bit ADC

When I hook up 1+2 I can write to my micro SD just fine.
When I hook up 1+3 I can get the volts off the ADC just fine.

My problems start when I try to combine both shields.
The end result can write to the micro SD card but the ADC doesn't work. Spits out junk.

I don't seem to be able to grasp how exactly SPI works and what needs to change in order to make both work.
From what I have gathered by trolling this forum and wikipedia is that I have to interchange modes on the SS pin.

I can move the pin 10 on the ADC shield to pin 9 for example but I still don't know how I can tell my program that the SS for the ADC is pin 9.

Any help/tips greatly appreciated! :)
Thanks in advance.

Code: [Select]

#include <Stdio.h>

#include <SdFat.h>
#include <SdFatUtil.h>

#ifndef cbi
#define cbi(sfr, bit)     (_SFR_BYTE(sfr) &= ~_BV(bit))
#ifndef sbi
#define sbi(sfr, bit)     (_SFR_BYTE(sfr) |= _BV(bit))

#define LTC_CS 2         // LTC2400 Chip Select Pin  on Portb 2
#define LTC_MISO  4      // LTC2400 SDO Select Pin  on Portb 4
#define LTC_SCK  5       // LTC2400 SCK Select Pin  on Portb 5

#define putstring(str) SerialPrint_P(PSTR(str))
#define putstring_nl(str) SerialPrintln_P(PSTR(str))

Sd2Card card;
SdVolume volume;
SdFile root;
SdFile f;

void setup() {

cbi(PORTB,LTC_SCK);      // LTC2400 SCK low
sbi (DDRB,LTC_CS);       // LTC2400 CS HIGH


// init SPI Hardware
sbi(SPCR,MSTR) ; // SPI master mode
sbi(SPCR,SPR0) ; // SPI speed
sbi(SPCR,SPR1);  // SPI speed
sbi(SPCR,SPE);   //SPI enable

#define BUFFSIZE 73         // we buffer one NMEA sentence at a time, 83 bytes is longer than the max length
char buffer[BUFFSIZE];      // this is the double buffer
char buffer2[12];

Serial.println("LTC2400 ADC Test");
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
  // initialize the SD card at SPI_HALF_SPEED to avoid bus errors with
  // breadboards.  use SPI_FULL_SPEED for better performance.
  if (!card.init(SPI_HALF_SPEED, 4)) {
    Serial.println("Card init. failed!");
  if (!volume.init(&card)) {
    Serial.println("No partition!");
  if(!root.openRoot(&volume)) {
    Serial.println("Can't! open root dir");
  int i;
  strcpy(buffer, "GPSLOG00.TXT");
  for (i = 0; i < 100; i++) {
    buffer[6] = '0' + i/10;
    buffer[7] = '0' + i%10;
    // create if does not exist, do not open existing, write, sync after write
    if (f.open(&root, buffer, O_CREAT | O_EXCL | O_WRITE | O_SYNC)) break;
  if(!f.isOpen()) {
    Serial.print("couldnt create "); Serial.println(buffer);
float volt;
float v_ref=3.0;          // Reference Voltage, 5.0 Volt for LT1021 or 3.0 for LP2950-3

long int ltw = 0;         // ADC Data ling int
int cnt;                  // counter
byte b0;                  //
byte sig;                 // sign bit flag
char st1[20];             // float voltage text

void loop() {

cbi(PORTB,LTC_CS);             // LTC2400 CS Low
// if (!(PINB & (1 << PB4))) {    // ADC Converter ready ?
   //    cli();

   b0 = SPI_read();             // read 4 bytes adc raw data with SPI
   if ((b0 & 0x20) ==0) sig=1;  // is input negative ?
   b0 &=0x1F;                   // discard bit 25..31
   ltw |= b0;
   ltw <<= 8;
   b0 = SPI_read();
   ltw |= b0;
   ltw <<= 8;
   b0 = SPI_read();
   ltw |= b0;
   ltw <<= 8;
   b0 = SPI_read();
   ltw |= b0;


   sbi(PORTB,LTC_CS);           // LTC2400 CS Low

   if (sig) ltw |= 0xf0000000;    // if input negative insert sign bit
   ltw=ltw/16;                    // scale result down , last 4 bits have no information
   volt = ltw * v_ref / 16777216; // max scale

   Serial.print(";  ");
   String vvv = printFloat(volt,6);           // print voltage as floating number
   Serial.println("  ");
// }
sbi(PORTB,LTC_CS); // LTC2400 CS hi

byte SPI_read()
SPDR = 0;
while (!(SPSR & (1 << SPIF))) ; /* Wait for SPI shift out done */
return SPDR;
//  printFloat from  tim / Arduino: Playground
// printFloat prints out the float 'value' rounded to 'places' places
//after the decimal point
String printFloat(float value, int places) {
// this is used to cast digits
int digit;
float tens = 0.1;
int tenscount = 0;
int i;
float tempfloat = value;

char res[10];
int ii = 0;

// if value is negative, set tempfloat to the abs value

   // make sure we round properly. this could use pow from
//<math.h>, but doesn't seem worth the import
// if this rounding step isn't here, the value  54.321 prints as

// calculate rounding term d:   0.5/pow(10,places)
float d = 0.5;
if (value < 0)
   d *= -1.0;
// divide by ten for each decimal place
for (i = 0; i < places; i++)
   d/= 10.0;
// this small addition, combined with truncation will round our

tempfloat +=  d;

if (value < 0)
   tempfloat *= -1.0;
while ((tens * 10.0) <= tempfloat) {
   tens *= 10.0;
   tenscount += 1;

// write out the negative if needed
if (value < 0){
if (tenscount == 0){
   Serial.print(0, DEC);
for (i=0; i< tenscount; i++) {
   digit = (int) (tempfloat/tens);
   Serial.print(digit, DEC);
   tempfloat = tempfloat - ((float)digit * tens);
   tens /= 10.0;

// if no places after decimal, stop now and return
if (places <= 0)
   return String(res);

// otherwise, write the point and continue on

for (i = 0; i < places; i++) {
   tempfloat *= 10.0;
   digit = (int) tempfloat;
   // once written, subtract off that digit
   tempfloat = tempfloat - (float) digit;

return String(res);


Each SPI device needs it's own select pin. The obvious way to do this is to start at 10 and start going down, like you want to do.

SPI requires that you drive the appropriate select pin low before starting a read or write, and high again after you're done, like:

Code: [Select]

byte SPITransfer(volatile byte data)
  // Transfer byte to SPI device and return the byte retrieved.

  SPDR = data; // Start the transmission
  while (!(SPSR & (1<<SPIF))) {
    // Wait for the end of the transmission
  return SPDR;  // Return the received byte

void SPIWriteRegister(byte cs_port,byte registerAddress, byte data)
  // Write <data> byte to the SC16IS750 register <registerAddress>.
  if (cs_port == 9)
    PORTB &= ~_BV(PB1);
  if (cs_port == 10)
    PORTB &= ~_BV(PB2);
//  digitalWrite(cs_port, LOW);      //Select the device.


  if (cs_port == 9)
    PORTB |= _BV(PB1);
  if (cs_port == 10)
    PORTB |= _BV(PB2);
//  digitalWrite(cs_port, HIGH);     // Deselect the device.

byte SPIReadRegister(byte cs_port,byte registerAddress)
  // Read byte from SC16IS750 register at <registerAddress>.

  // Used in SPI read operations to flush slave's shift register
  const byte SPI_DUMMY_BYTE = 0xFF;
  char result;

  if (cs_port == 9)
    PORTB &= ~_BV(PB1);
  if (cs_port == 10)
    PORTB &= ~_BV(PB2);
//  digitalWrite(cs_port, LOW);      //Select the device.

  SPITransfer(SPI_READ_MODE_FLAG | registerAddress);
  result = SPITransfer(SPI_DUMMY_BYTE);

  if (cs_port == 9)
    PORTB |= _BV(PB1);
  if (cs_port == 10)
    PORTB |= _BV(PB2);
//  digitalWrite(cs_port, HIGH);     // Deselect the device.

  return result; 

Notice how I'm passing in the digital pin I want to use directly (9 or 10 in this example) but I don't use it directly... I hit the port directly instead for speed. The original line of code, using digitalWrite is commented out.

Also be careful with your setup code. SPI only needs to be setup once, but the connected devices all need to set up individually after that.

Also note... there are different flavors of SPI, there may be compatibility issues. I use SPI to access UART chips, all are the same so I avoid any compatibility issues.



I see what you're doing wrong.

You're setting the code up for one SPI or the other, but not specifically setting the CS for the unused one high. Then wondering why it doesn't work when BOTH boards are plugged in.

In setup() define both CS pins and set both high. Then you can both shields plugged in. The one that's never selected in loop() will just sit there and be quiet.


I now understand a bit more about SPI :)
Took some time to sink in but it's finally there.

The code you provided actually was what I needed to get going.
Code: [Select]
  if (cs_port == 9)
    PORTB &= ~_BV(PB1);
  if (cs_port == 10)
    PORTB &= ~_BV(PB2);

If I have understood everything correctly PortB are pins 8-13
For example PORTB should be something like 000000 (6 zeros for each pin, 8,9,10...13)
So starting from left to right each bit represents a pin.
pos 0 is 8, pos 1 is 9 etc.

So for example PORTB &= ~_BV(PB0) will use pin 8 and PORTB &= ~_BV(PB4) will use pin12.

Understand this allowed me to simply alter the pin position on my second shield (ADC) to pin9 and everything works now :)

Thank you very much for a quick and very detailed reply.

ps.If you think that I have understood something wrong please correct me in order for other people to be able to understand it correctly! :D



I am beginner in Arduino and I try to understand how works the SPI library but it's difficult for me !!

In soft There is something that i don't understand.

For example in the line " SPCR |= _BV(MSTR);" I don't now what "_BV" is? Or what does it do?

Help me please !!

And sorry for my English, I'm French  :D

Thanks in advance.

Go Up