Multiple SPI devices - Obvious mistake?

Hi all,

I'm using two separate MCP3201 ADC's (http://search.digikey.com/us/en/products/MCP3201-BI%2FP/MCP3201-BI%2FP-ND/319425) on one SPI bus but I cannot get them to both communicate accurately. I have connected all the MISO pins to pin 50, one of slave selects to 53, and the other slave select to 49. The MOSI pin is not needed for this device. Each ADC works by itself when the other ADC isn't on the same bus but not together. Is there anything obvious that I'm missing or is this a problem with the ADC? This is the code I am using:

#include <SPI.h>                      // SPI Library

unsigned int li_bit = 0;            // Sunlight bit count
unsigned int hih_bit = 0;           // Humidity bit count
const int lipin = 53;               // Slave select pins
const int hihpin = 49;

void setup(){
  Serial.begin(1200);               // Begin serial moniter
  
  pinMode(hihpin,OUTPUT);
  digitalWrite(hihpin,HIGH);
  
  pinMode(lipin,OUTPUT);
  digitalWrite(lipin,HIGH);
  
  SPI.begin();                      // Start SPI 
  SPI.setBitOrder(MSBFIRST);        // Set SPI protocols
  SPI.setDataMode(SPI_MODE0);
  
  Serial.println("Type to Start");  // Wait for serial signal
  while(!Serial.available());  
}

void loop(){
  li_bit = mcp3201(lipin);       // Find licor digital value
  hih_bit = mcp3201(hihpin);
  Serial.print(li_bit);           // Print
  Serial.print("   ");
  Serial.println(hih_bit);          
  delay(1000);                      // Delay
}

unsigned int mcp3201(const int pinnum){               // Function to interact MCP3201 ADC -- Pass Slave Select pin in
  word bitnum = 0;                                    // Initialize return
  digitalWrite(pinnum, LOW);                          // Start communication
  byte msb = SPI.transfer(0);                         // Communicate
  byte lsb = SPI.transfer(0);
  digitalWrite(pinnum, HIGH);                         // End communication
  bitnum = ((bitnum | msb) << 8) | lsb;              // Concatenate bytes into word
  bitnum = bitnum >> 1;               // Remove extraneous data
  bitnum = bitnum << 4;
  bitnum = bitnum >> 4;
  return(bitnum);                   // Return number of bits
}

Thanks in advance for any and all help!

I haven't looked at your code yet but I would not use pin 50 for an SS, it's designed to be SS for the 5260 when it's in slave mode which clearly it isn't here.


Rob

Just looking at the chip specs it seems to need two clock pulses to take the sample, then 12 to read the result (that's 14 bits) then crap (actually the reading again but LSB first) for the next two bits. So you get this

xxBB BBBB BBBB BBxx

Where the Bs are valid data bits. So I think your function should be more like this

unsigned int mcp3201(const int pinnum){               // Function to interact MCP3201 ADC -- Pass Slave Select pin in
  word bitnum = 0;                                    // Initialize return
  digitalWrite(pinnum, LOW);                          // Start communication
  byte msb = SPI.transfer(0);                         // Communicate
  byte lsb = SPI.transfer(0);
  digitalWrite(pinnum, HIGH);                         // End communication
  bitnum = ((msb << 8) | lsb) & 0x3fff;              // Concatenate bytes into word, clear upper two bits
  bitnum = bitnum >> 2;               // Remove extraneous data
  return(bitnum);                   // Return value
}

Rob

Hi Graynomad,

First of all, thank you for telling me about the slave pin, I changed it from 53 to 48.

Second, the way I interpreted the datasheet, the MSB has two unknown bits, a null bit, and then the 5 highest order bits. The LSB has the 7 lowest order bits and a crap bit. That's why I remove one bit off of the right end and then 4 off of the left end. Wouldn't the function you showed me simply remove the crap bit and the lowest order bit?

Finally, when connected on the bus, even after changing the slave selects, only one of the ADC's is working (the one connected to slave pin 49).

Yep you're right about the bit placement, so we get

xx0B BBBB BBBB BBBx

so I think

 bitnum = ((msb << 8) | lsb) & 0x1fff;            
 bitnum = bitnum >> 1;

will do it.

No idea why only one of the ADCs would work.

Do they both work without the other being connected?

Do you have the same problem if you swap the CS pins?

Does the problem move with the CS signal or stay with one of the chips?

In other words, try to isolate the problem.


Rob

Hi Graynomad,

I did what you recommended and tried to isolate the problem, and I'm getting the weirdest results. Both devices work separately when nothing else is on the bus. When both devices are on the bus, only one of them works, and it's always the same one. I know this because switching slave select pins do not change any behavior. It may be a problem with the chip and I may contact the manufacturer or look for an alternative.

Nevertheless, I appreciate your help!

Looks to me like you are running too fast. The chip says max speed of 1.6 MHz. Your code is outputting at 4 MHz.

Try adding:

  SPI.setClockDivider(SPI_CLOCK_DIV16);

That gives 1 Mhz clock pulses.

Hi Nick,

Thank you for reminding me about the clock speeds, that didn't even occur to me! Unfortunately, adding the clock divider function didn't change anything, still only one of them is working. I tried putting a delay of 50ms between both of my function calls but that didn't do anything either. If it helps, the one that doesn't work either outputs 0 or 4095, and it seems random about the order.

Do you think I should just write this chip off?

You said they both worked on their own. Can you draw up your exact wiring?

Absolutely. Here are two pics, one with just the one ADC and one with both ADC connected. I apologize for the shoddy diagrams, I'm slowly learning fritzing.

madvoid:
First of all, thank you for telling me about the slave pin, I changed it from 53 to 48.

Not in your diagram you didn't.

My apologies, when I was doing testing I moved some of the wires around. The slave pins are 48 and 49, and the wiring that I have show that as well.

I can't see anything obviously wrong, apart from the strangeness of this:

  bitnum = bitnum >> 1;               // Remove extraneous data
  bitnum = bitnum << 4;
  bitnum = bitnum >> 4;

Can't you just do an "and" rather than shifting back and forth 4 bits?

Anyway, I would build in a small delay between reading the two sensors. Maybe the first one doesn't "let go" quickly enough.

You're right about the and statement, thank you. I put a 50ms delay between the two function calls but no change unfortunately. Thank you all for your help but I think I'm going to look for an alternative adc.

madvoid:
Both devices work separately when nothing else is on the bus. When both devices are on the bus, only one of them works, and it's always the same one.

What do you mean "on the bus"? Without changing anything else, if you just disconnect power to one of them does the other work? And vice-versa? And then try just disconnecting MOSI but leaving everything else (including power) connected.

Hi Nick,

I did a number of things you recommended and I'm just getting more confused by the behavior of these chips. To begin, just for ease of communication, I'm going to define the two ADCs as ADC1 and ADC2. ADC1 has the op amp connected to it and ADC2 has the hih4030 humidity sensor attached to it. As you can see from the pictures, ADC1 is connected to the ADC2 pins, which then have wires running to the Arduino. The location of the wires that actually run to the Arduino will turn out to be important.

So far, the output has shown a couple different things:
Case 1. ADC2 displays a valid output and ADC1 either displays 0 or 4095
Case 2. ADC1 displays a valid output and ADC2 either displays 0 or 4095

Here are the conditions that will produce each of the outputs listed above:

Case 1: The wires that actually run to the Arduino need to be connected to ADC2 and bridging wires connected to ADC1. Removing power or bridging wires do nothing to the output.
Case 2: The wires that actually run to the Arduino need to be connected to ADC1 and bridging wires connected to ADC2. As in Case 1, removing power or bridging wires do nothing to the output.

This shows me that the wires that actually run to the Arduino is what is important. I also tried connecting the wires that actually run to the Arduino to a complete separate set of rows on the breadboard and run both ADC wires to that but it just followed case 1. I have tried two different Arduinos (both Mega2560s) and two different breadboards to no avail.

I might order a couple of those and see if I can reproduce this. Might take a few days as they don't seem to be in stock. And I can always use them for other projects.

Meanwhile, your tests seem to show that both devices work equally well (or badly, heh).

It looks to me like they are designed to be used in the way you are using them.

Do you have decoupling caps? Try putting a 0.1 uF between Vcc and Gnd on both of the ADC chips and see if that makes a difference (right near VCC).

Hi Nick,

I do have decoupling capacitors in place; 1 uF as recommended by the datasheet. I haven't tested the accuracy of these ADC's yet but if you do get one, they're very easy to work with (as long as you're only working with one :)). Once again, thank you for your help, I really appreciate it. I'll continue to work at it and if anything new happens, I'll post an update.

Well I've hooked it up and I have to say it seems to work perfectly!

Even without any decoupling capacitors (or any delay) they both work fine.

Here's how I wired it up (following your diagram basically):

There are links between pins 3 and 4 of both chips, not particularly easy to see.

Instead of a voltage reference I connected +5v to AREF, so the range was 0 to 5V.

On the RH ADC I used 2 x 10K resistors as a voltage divider, so it should be about 2.5V on the IN+ pin.
On the LH ADC I set up a pot between 5V and Gnd, so by turning the knob I could "dial up" any voltage.

I modified the sketch a bit so I could see the voltages, this is what I used:

#include <SPI.h>                      // SPI Library
#include <Streaming.h>

const byte lipin = 48;               // Slave select pins
const byte hihpin = 49;

void setup(){
  Serial.begin(115200);               // Begin serial moniter
  
  pinMode(hihpin,OUTPUT);
  digitalWrite(hihpin,HIGH);
  
  pinMode(lipin,OUTPUT);
  digitalWrite(lipin,HIGH);
  
  SPI.begin();                      // Start SPI 
  SPI.setClockDivider(SPI_CLOCK_DIV16);
}

void loop(){
  unsigned int li_bit;            // Sunlight bit count
  unsigned int hih_bit;           // Humidity bit count

  li_bit = mcp3201(lipin);      
  hih_bit = mcp3201(hihpin);
  Serial << "Sunlight: " << _HEX(li_bit) << " Humidity: " << _HEX(hih_bit) << endl;
  Serial << "Sunlight: " << (float) li_bit / 4096.0 * 5.0 << " Humidity: " << (float) hih_bit / 4096.0 * 5.0 << endl;
  delay(1000);                    
}

unsigned int mcp3201(const int pinnum){               // Function to interact MCP3201 ADC -- Pass Slave Select pin in
  int bitnum;                                  
  digitalWrite (pinnum, LOW);                          // Start communication
  byte msb = SPI.transfer (0);                         // Communicate
  byte lsb = SPI.transfer (0);
  digitalWrite (pinnum, HIGH);                         // End communication
  bitnum = word (msb & 0x1F, lsb) >> 1;
  return bitnum;              
}

Output (first line in hex - the raw figure, second line as a float which is the voltage):

Sunlight: 791 Humidity: 803
Sunlight: 2.36 Humidity: 2.50
Sunlight: 791 Humidity: 802
Sunlight: 2.36 Humidity: 2.50
Sunlight: 790 Humidity: 803
Sunlight: 2.36 Humidity: 2.50
Sunlight: 791 Humidity: 802
Sunlight: 2.36 Humidity: 2.50
Sunlight: 790 Humidity: 802
Sunlight: 2.36 Humidity: 2.50
Sunlight: 785 Humidity: 802
Sunlight: 2.35 Humidity: 2.50
Sunlight: 36E Humidity: 803
Sunlight: 1.07 Humidity: 2.50
Sunlight: 1D5 Humidity: 802
Sunlight: 0.57 Humidity: 2.50
Sunlight: AFB Humidity: 802
Sunlight: 3.43 Humidity: 2.50
Sunlight: C6A Humidity: 802
Sunlight: 3.88 Humidity: 2.50

As you can see the "Humidity" figure is always 2.5V, which you expect because of the voltage divider. The other figure varies as I turn the knob. So it's all fine. I can't explain your setup problems, but I would get a meter out and check a few things.