In a nutshell, I am using this ADC to read the output of a sensor via an amplifier. I verified that the output of the Op-Amp was what I expected, and it is. The voltage is correct when measured right on the ADC's pin. It is powered with 3.3V coming from the Raspberry, with Vref pin also at 3.3V.
So the problem comes from the ADC (I already tried with two chips). It reads values that are wrong and very noisy. Here is a sample of the output I get :
Raw Value: 1338 | Voltage: 1.078V
Raw Value: 1079 | Voltage: 0.870V
Raw Value: 88 | Voltage: 0.071V
Raw Value: 816 | Voltage: 0.658V
Raw Value: 1250 | Voltage: 1.007V
Raw Value: 1496 | Voltage: 1.206V
Raw Value: 0 | Voltage: 0.000V
Raw Value: 1095 | Voltage: 0.882V
Raw Value: 711 | Voltage: 0.573V
Raw Value: 1482 | Voltage: 1.194V
Raw Value: 327 | Voltage: 0.264V
Raw Value: 1344 | Voltage: 1.083V
Raw Value: 967 | Voltage: 0.779V
Raw Value: 38 | Voltage: 0.031V
Raw Value: 647 | Voltage: 0.521V
Raw Value: 3673 | Voltage: 2.960V
Raw Value: 967 | Voltage: 0.779V
Raw Value: 704 | Voltage: 0.567V
Raw Value: 1087 | Voltage: 0.876V
Raw Value: 999 | Voltage: 0.805V
Raw Value: 384 | Voltage: 0.309V
Raw Value: 583 | Voltage: 0.470V
Raw Value: 1112 | Voltage: 0.896V
Raw Value: 1343 | Voltage: 1.082V
Raw Value: 1279 | Voltage: 1.031V
Raw Value: 2672 | Voltage: 2.153V
Raw Value: 1248 | Voltage: 1.006V
In this example, 3.3V were applied on the pin.
Here is the library used to get values from the ADC :
#include "MCP3208.h"
#include <wiringPi.h>
using namespace std;
MCP3208::MCP3208(int CS, int CLK, int MOSI, int MISO) {
this->CS = CS;
this->CLK = CLK;
this->MOSI = MOSI;
this->MISO = MISO;
}
void MCP3208::setupSPI() {
wiringPiSetup();
pinMode(CS, OUTPUT);
pinMode(CLK, OUTPUT);
pinMode(MOSI, OUTPUT);
pinMode(MISO, INPUT);
digitalWrite(CS, HIGH);
digitalWrite(CLK, LOW);
digitalWrite(MOSI, LOW);
}
int MCP3208::readADC(int channel) {
if (channel > 7 || channel < 0) return -1;
digitalWrite(CS, LOW); //Starts the conversation
// Constructs command
// 0b -> Binary
// 11 -> Start of the command
// (channel << 3) -> Specified channel converted to binary and moved to the bits 5, 4 and 3
// Last 000 -> Don't Care Bits
unsigned char command = 0b11000000 | (channel << 3);
// Sends command, left to right
for (int i = 7; i >= 0; i--) {
digitalWrite(MOSI, (command >> i) & 1); // Extracts the value of the i-th bit by shifting bits to the right and leaving the desired bit last (to the right) with the mask '& 1'
digitalWrite(CLK, HIGH);
digitalWrite(CLK, LOW);
}
// Read 12-bit ADC data
int value = 0;
for (int i = 11; i >= 0; i--) {
value <<= 1; // Shifts all existing bits to the left, to create space for the next bit to be read
digitalWrite(CLK, HIGH); // Signals the ADC to output the next bit on MISO
if (digitalRead(MISO)) {
value |= 1; // Adds the bit when it is equal to 1, 0 by default when shifting bits
}
digitalWrite(CLK, LOW);
}
digitalWrite(CS, HIGH);
return value;
}
And here is the program used to actually print the values :
#include "/home/pi/Desktop/MCP3208/MCP3208.h"
#include <wiringPi.h>
#include <stdio.h>
// MCP3208 parameters
#define CS 10 // Chip Select
#define CLK 14 // Clock
#define MOSI 12 // Master Out Slave In
#define MISO 13 // Master In Slave Out
MCP3208 adc(CS, CLK, MOSI, MISO);
int main() {
adc.setupSPI();
while (1) {
int value = adc.readADC(1); // Read from channel 0
float voltage = value * 3.3 / 4095.0;
printf("Raw Value: %d | Voltage: %.3fV\n", value, voltage);
delay(500);
}
return 0;
}
As a side note, the circuit has already been tested with a MCP3008, but it was replaced by the MCP3208 to get a higher precision on the readings. Based on the datasheets of both ADC, it is possible, in theory at least, to replace one with the other without changing the rest of the circuit. However, I may be wrong on that and it may be the cause of all those headaches, I just don't know what I need to check precisely.
I hope my explanations were clear enough !