12 bits of sweet resolution

Hallo all.

I'm trying to upgrade my Uno to have 12-bits of resolution, 1024 just isn't quite cutting it hey.
But I am having trouble getting the AD-Converter to work.

I've added a picture, to show how everything is connected. (Tried making it as clear as possible).'
The IC is a MCP3202.
Then I have my code, which I found on the web.

// MCP3202 2 channel ADC
// 3202 is different software-wise from 3204/3208

#define SELPIN 10
#define DATAOUT 11
#define DATAIN 12
#define SPICLOCK 13

/////////////////////////////////////////////////////////////////////////////

void setup(){
  pinMode(DATAOUT, OUTPUT);
  pinMode(DATAIN, INPUT);
  pinMode(SPICLOCK, OUTPUT);
  pinMode(SELPIN, OUTPUT); 

  digitalWrite(SELPIN, HIGH); 
  digitalWrite(DATAOUT, LOW); 
  digitalWrite(SPICLOCK, LOW); 

  Serial.begin(9600);
}

void loop(){
  byte i;
  int ain[2]; //creating a small array to save into
  
  ain[0] = read_adc(SELPIN, 2);  //calling the read function and saving in the array
  ain[1] = read_adc(SELPIN, 1);

  Serial.println(ain[0]);   //printing the different array value
  Serial.println(ain[1]); 
  
  Serial.print('\n');  //making it look pretty
  delay(500);
}

int read_adc(byte cs, byte channel)  
{
  int adcvalue = 0;
//it gets a little fuzzy from here
  
byte commandbits = B11010000; //command bits - start, mode, chn, MSBF //preparing command bits

  //allow channel selection
  commandbits|=(channel<<5);  //shifting the selected channel into the command bits.(probably?)

  digitalWrite(cs, LOW); //Select adc

  // setup bits to be written //
  for (int i=7; i>=4; i--){
    digitalWrite(DATAOUT,commandbits&1<<i);
    //cycle clock
    digitalWrite(SPICLOCK, HIGH);
    digitalWrite(SPICLOCK, LOW);    
  }

  digitalWrite(SPICLOCK, HIGH);    //ignores 1 null bit
  digitalWrite(SPICLOCK, LOW);

  //read bits from adc
  for (int i=11; i>=0; i--){
    adcvalue+=digitalRead(DATAIN)<<i;
    //cycle clock
    digitalWrite(SPICLOCK, HIGH);
    digitalWrite(SPICLOCK, LOW);
  }
  digitalWrite(cs, HIGH); //turn off device
  return adcvalue;
}

This is what I've tried so far and when I open the Serial Monitor...
All I get is the Max value then zeros. And every now and again some random value.

Does anyone know where I've gone wrong?

The minimal SPI clock is 10kHz with this device - do check that when bitbanging it..
I would use normal SPI communication instead..

I was under the impression I was doing normal SPI??

If you were using real SPI you would be connected to D10-11-12-13 and the code would look more like this:

See figure 6-1 & 6-2

digitalWrite (SSpin, LOW);
SPI.transfer(0x01); // 1 is the start bit
upperDAC = SPI.transfer(Bxxx000000); // xxx are the configuration bits, lower 4 bits of upperDAC are D11-D8 from the chip
lowerDAC = SPI.transfer (0x00); // to data to ADC is ignored, lowerDAC contains the D87-D0 from the chip
digitalWrite (SSpin, HIGH;
dataDAC =  (upperDAC<<8) + lowerDAC; // put the data together

Instead of all this in setup()

pinMode(DATAOUT, OUTPUT);
pinMode(DATAIN, INPUT);
pinMode(SPICLOCK, OUTPUT);
pinMode(SELPIN, OUTPUT);

digitalWrite(SELPIN, HIGH);
digitalWrite(DATAOUT, LOW);
digitalWrite(SPICLOCK, LOW);

you would only have

pinMode(SELPIN, OUTPUT);
SPI.begin();
(and whatever the command is to set the SPI clock divisor to 16 - see the SPI libray page)

alternative? - http://www.atmel.com/images/doc8003.pdf -

Be careful with hardware SPI - the max clock rate on the 3202 is 1.8MHz.

-j

Did you put a meter on the ADC input to verify?

-j

Okay... Taken awhile to really search through these alternatives.

The oversampling just adds way to much noise. I can't use it. But thank you for the suggestion.

I looked into using SPI and I still get screwy readings. bunch of zeros. Sometimes a really low negative value.

This is my revised code:

// MCP3202 2 channel ADC
// 3202 is different software-wise from 3204/3208 (which are identical)
#include <SPI.h>

#define SELPIN 10
#define DATAOUT 11
#define DATAIN 12
#define SPICLOCK 13


/////////////////////////////////////////////////////////////////////////////

void setup(){
  pinMode(SELPIN, OUTPUT); 
  SPI.begin();
  SPI.setClockDivider(16);
  Serial.begin(9600);
}

void loop(){
  byte i;
  int ain[2];
  
  ain[0] = read_adc(SELPIN, 2);
  ain[1] = read_adc(SELPIN, 1);

  Serial.println(ain[0]);   
  Serial.println(ain[1]); 
  
  Serial.print('\n');
  delay(500);
}

int read_adc(byte cs, byte channel)
{
  int upperDAC = 0;
  int lowerDAC = 0;
  int dataDAC = 0;
  
//  byte commandbits = B11000000; //command bits - start, mode, chn (3), dont care (3)
  byte goof = B11000000; //command bits - start, mode, chn, MSBF 

  digitalWrite(cs, LOW); //Select adc
  SPI.transfer(0x01); //1 is the start bit
  upperDAC = SPI.transfer(goof);
  lowerDAC = SPI.transfer(0x00);
  digitalWrite(cs, HIGH);

dataDAC = (upperDAC<<8) + lowerDAC;
return dataDAC;
}

Is it something wrong in my code? Is the board wired wrong?
I tried switching Arduino pin 11 and 12 around in an act of desperation.

So I took a good hard look at my code and realized some pretty important things.

First it is code for a DAC and I'm trying to achieve an ADC. (Facepalm.)
I've taken out the DATAOUT, DATAIN and SPICLOCK define in the beginning. (using SPI makes that redundant)
I've taken out the "upperDAC = transfer(goof)" out. and made the lowerDAC into dataDAC.
I am also no longer shifting it.

So after all these changes I loaded up my code and presto!!
Nothing works! now I'm no longer getting garbage, but super garbage!!

I really don't get this chip.
please help.
Here's my code for in case.

// MCP3202 2 channel ADC
// 3202 is different software-wise from 3204/3208 (which are identical)
#include <SPI.h>

#define SELPIN 10

/////////////////////////////////////////////////////////////////////////////

void setup(){
  pinMode(SELPIN, OUTPUT); 
  SPI.begin();
//  SPI.setClockDivider(16); // I don't really understand this line.
  Serial.begin(9600);
}

void loop(){
  byte i;
  int ain[2];
  
  ain[0] = read_adc(SELPIN, 2);
  ain[1] = read_adc(SELPIN, 1);

  Serial.println(ain[0]);   
  Serial.println(ain[1]); 
  
  Serial.print('\n');
  delay(500);
}

int read_adc(byte cs, byte channel)
{
  int upperADC = 0;
  int lowerADC = 0;
  int dataADC = 0;
  
//  byte commandbits = B11000000; //command bits - start, mode, chn (3), dont care (3)
  byte goof = B11000000; //command bits - start, mode, chn, MSBF 

  digitalWrite(cs, LOW); //Select adc
  SPI.transfer(goof);
  SPI.transfer(0x01); //1 is the start bit
  dataADC = SPI.transfer(0x00); //receiving the ADC value.
  //lowerADC = SPI.transfer(0x00);
  digitalWrite(cs, HIGH);

//dataADC = (upperADC<<8) + lowerADC;
return dataADC;
}

See the Figure 6.1. and 6.2. of the datasheet - there is depicted exactly what needs to be done when operating it over SPI (3x8bit transfer)..
For example (not tested):

// Pito 8/2013 for MCP3202
..
unsigned int adc_value0, adc_value1;  // 12bit ADC values
unsigned char dummy, adc_h4, adc_l8;  // 4 high and 8 low bits

SPI.begin();

SPI.setBitOrder(MSBFIRST);
SPI.setDataMode(SPI_MODE0);
SPI.setClockDivider(SPI_CLOCK_DIV16);    // 16MHz/16 = 1MHz SPI clock 

// from datasheet figure 6.2
// channel 0
digitalWrite(cs, LOW);    // Select adc
dummy = SPI.transfer(0x01); 
adc_h4 = SPI.transfer(0xA0) & 0x0F;    // unipolar, channel=0, msb first=0, returns B11-B8 in lower nibble
adc_l8 = SPI.transfer(0x00);    // returns B7-B0
digitalWrite(cs, HIGH);    // Deselect adc
adc_value0 = (adc_h4 << 8) + adc_l8;  //   fixed: brackets required

// channel 1
digitalWrite(cs, LOW);    // Select adc
dummy = SPI.transfer(0x01); 
adc_h4 = SPI.transfer(0xE0) & 0x0F;    // unipolar, channel=1, msb first=0, returns B11-B8 in lower nibble
adc_l8 = SPI.transfer(0x00);    //returns B7-B0
digitalWrite(cs, HIGH);    // Deselect adc
adc_value1 = (adc_h4 << 8) + adc_l8;   //   fixed: brackets required

Some bitbanging stuff when SPI is difficult:
http://forum.arduino.cc/index.php?topic=55466.msg398068#msg398068

Thank you Pito for all the help, I really appreciate it.. It was really cool of you to write out a section of code for me. Helped to understand the chip better.. But I'm afraid I still can't get the little bugger to work...

I changed up my code yet again. now I have this:

// Pito 8/2013 for MCP3202
// MCP3202 2 channel ADC
// 3202 is different software-wise from 3204/3208 (which are identical)
#include <SPI.h>

#define SELPIN 10

unsigned long adc_value0, adc_value1;  // 12bit ADC values
unsigned char dummy, adc_h4, adc_l8;  // 4 high and 8 low bits

/////////////////////////////////////////////////////////////////////////////

void setup(){
  pinMode(SELPIN, OUTPUT); 
  SPI.begin();
  Serial.begin(9600);
  
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV16); //1MHz clock 
}

void loop(){
  int alpha = 0;
  int omega = 0;
  
  alpha = read_adc0();
  omega = read_adc1();

  Serial.println(alpha);   
  Serial.println(omega);  
    
  Serial.print('\n');
  delay(500);
}

int read_adc0(){
// from datasheet figure 6.2
// channel 0

digitalWrite(SELPIN, LOW); // Select adc
dummy = SPI.transfer(0x01); 
adc_h4 = SPI.transfer(0xA0); // unipolar, channel=0, msb first=0, returns B11-B8 in lower nibble
adc_l8 = SPI.transfer(0x00); // returns B7-B0
digitalWrite(SELPIN, HIGH); // DeSelect adc
adc_h4 = (adc_h4 & 0x0F);
adc_value0 = adc_h4 << 8 + adc_l8;

return adc_value0;
}

int read_adc1(){
// channel 1
digitalWrite(SELPIN, LOW); // Select adc
dummy = SPI.transfer(0x01); 
adc_h4 = SPI.transfer(0xE0); //  unipolar, channel=1, msb first=0, returns B11-B8 in lower nibble
adc_l8 = SPI.transfer(0x00); //returns B7-B0
digitalWrite(SELPIN, HIGH); // DeSelect adc
adc_h4 = (adc_h4 & 0x0F);
adc_value1 = adc_h4 << 8 + adc_l8;

return adc_value1;
}

Still no good. Just get some really odd values. But I can't even link it to something... Just random jibberish.
That link you put it... That's actually where I got my original code from.

Any other ideas that I could try?

Double check your wiring (MISO, MOSI, SCK, SS). You need a decoupling capacitor on 3202' Vdd (against gnd).
Connect adc inputs to ground (or Vcc) and print out adc_4h, adc_8l, and adc_value0 and 1.
PS: if you let the analog adc inputs float, you will read garbage..

!!!!!!!!!!!!!!Thank you so much!!!!!!!!!!

It works now!
It was a math fault.

//Pito 10/8/2013
// MCP3202 2 channel ADC
#include <SPI.h>

#define SELPIN 10

unsigned long adc_value0, adc_value1;  // 12bit ADC values
unsigned char dummy, adc_h4, adc_l8;  // 4 high and 8 low bits

/////////////////////////////////////////////////////////////////////////////

void setup(){
  pinMode(SELPIN, OUTPUT); 
  SPI.begin();
  Serial.begin(9600);
  
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV16); //1MHz clock 
}

void loop(){
  int alpha = 0;
  int omega = 0;
  
  alpha = read_adc0();
  omega = read_adc1();

  Serial.println(alpha);   
  Serial.println(omega);  
    
  Serial.print('\n');
  delay(500);
}

int read_adc0(){
// from datasheet figure 6.2

// channel 0
digitalWrite(SELPIN, LOW); // Select adc
dummy = SPI.transfer(0x01); 
adc_h4 = SPI.transfer(0xA0); // unipolar, channel=0, msb first=0, returns B11-B8 in lower nibble
adc_l8 = SPI.transfer(0x00); // returns B7-B0
digitalWrite(SELPIN, HIGH); // DeSelect adc
adc_h4 = (adc_h4 & 0x0F);
adc_value0 = (adc_h4 << 8) + adc_l8;    //adding the brackets over here is what made the difference.

return adc_value0;
}

int read_adc1(){
// channel 1
digitalWrite(SELPIN, LOW); // Select adc
dummy = SPI.transfer(0x01); 
adc_h4 = SPI.transfer(0xE0); //  unipolar, channel=1, msb first=0, returns B11-B8 in lower nibble
adc_l8 = SPI.transfer(0x00); //returns B7-B0
digitalWrite(SELPIN, HIGH); // DeSelect adc
adc_h4 = (adc_h4 & 0x0F);
adc_value1 = (adc_h4 << 8) + adc_l8;

return adc_value1;
}

This code works beautifully.
Thank you so much Pito

Welcome :slight_smile:

dataDAC =  (upperDAC<<8) + lowerDAC; // put the data together

I suggested that back in #3, altho I had DAC names sprinkled in with ADC for some reason.