Read values from ADC (MCP3564) with SPI fails

Hey,

i want to read values from an MCP3564. This is my sketch for my teensy 4.1.

#include <Arduino.h>
#include <SPI.h>

#define DEBUG false //DEBUG MODE
#define MCLK_INTERNAL true //true for internal clock, false for external
#define MCLK_FREQ 20000000 //disabled if MCLK_INTERNAL is true
#define OSR_SETTING OSR_128 //oversampling ratio

//Unchanging Definitions, Objects, and Variables
enum OSR : uint8_t {
  OSR_32    = 0x00,
  OSR_64    = 0x01,
  OSR_128   = 0x02,
  OSR_256   = 0x03, 
  OSR_512   = 0x04,
  OSR_1024  = 0x05,
  OSR_2048  = 0x06,
  OSR_4096  = 0x07,
  OSR_8192  = 0x08,
  OSR_16384 = 0x09,
  OSR_20480 = 0x0A,
  OSR_24576 = 0x0B,
  OSR_40960 = 0x0C,
  OSR_49152 = 0x0D,
  OSR_81920 = 0x0E,
  OSR_98304 = 0x0F
};
enum Channel : uint8_t {
  CH_0 = 0x00,
  CH_1 = 0x01,
  CH_2 = 0x02,
  CH_3 = 0x03,
  CH_4 = 0x04,
  CH_5 = 0x05,
  CH_6 = 0x06,
  CH_7 = 0x07,
  A_Vdd = 0x08,
  A_GND = 0x09,
  REFIN_pos = 0x0B,
  REFIN_neg = 0x0C,
  TEMP_P = 0x0D,
  TEMP_M = 0x0E,
  VCM = 0x0F
};

constexpr uint32_t SPI_Speed = 20000000;
constexpr uint8_t DEV_ADDR =        0b01000000;

constexpr uint8_t ADC_REG_ADDR =    0b00000000;
constexpr uint8_t CONFIG0_ADDR =    0b00000100;
constexpr uint8_t CONFIG1_ADDR =    0b00001000;
constexpr uint8_t CONFIG2_ADDR =    0b00001100;
constexpr uint8_t CONFIG3_ADDR =    0b00010000;
constexpr uint8_t IRQ_ADDR =        0b00010100;
constexpr uint8_t MUX_ADDR =        0b00011000;
constexpr uint8_t SCAN_ADDR =       0b00011100;
constexpr uint8_t TIMER_ADDR =      0b00100000;
constexpr uint8_t OFFSETCAL_ADDR =  0b00100100;
constexpr uint8_t GAINCAL_ADDR =    0b00101000;
constexpr uint8_t LOCK_ADDR =       0b00110100;

constexpr uint8_t FAST_CMD =        0b00000000;
constexpr uint8_t CONV =            0b00101000;
constexpr uint8_t STBY =            0b00101100;
constexpr uint8_t RESET =           0b00111000;
constexpr uint8_t READ =            0b00000001;
constexpr uint8_t INC_WRITE =       0b00000010;
constexpr uint8_t INC_READ =        0b00000011;

constexpr uint8_t CONFIG0 =         0b11000010 | (MCLK_INTERNAL <<5);
constexpr uint8_t CONFIG1 =         0b00000000 | (OSR_SETTING <<2);
constexpr uint8_t CONFIG2 =         0b10001011; //Contains Gain
constexpr uint8_t CONFIG3 =         0b11000000; //Contains Mode
constexpr uint8_t IRQ =             0b01110110; //contains IRQ stuff
constexpr uint8_t MUX =             0b00000001; //contains channel selection MUX_VIN+ = CH0 vs MUX_VIN- = CH1
constexpr uint32_t SCAN =           0b000000000000000100000000; //contains scan stuff 24 bits
constexpr uint32_t TIMER =          0; //contains timer stuff 24 bits
constexpr uint8_t OFFSETCAL =       0; //contains offset stuff
constexpr uint8_t GAINCAL =         0; //contains gain calibration stuff
constexpr uint8_t RESERVED =        0; //contains dummy for reserved registers 
constexpr uint8_t LOCK =            0b10100101; //contains Write Access Password

#define SPI_CS_PIN 10
#define SPI_MOSI_PIN 11
#define SPI_MISO_PIN 12
#define SPI_SCK_PIN 13
#define ADC_INT_PIN 3
#define ADC_MCLK_PIN 2

//Function Prototypes
void ADC_Init();
void readADC();

void setup() {
  Serial.begin(115200);
  delay(2000);
  ADC_Init();
  startADC();
  pinMode(ADC_INT_PIN, INPUT);
}

void ADC_Init() {
  SPI.begin();
  SPI.usingInterrupt(digitalPinToInterrupt(ADC_INT_PIN));
  pinMode(SPI_CS_PIN, OUTPUT);
  delay(100);
  SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
  digitalWriteFast(SPI_CS_PIN, LOW);
  delayNanoseconds(100);
  SPI.transfer(DEV_ADDR | RESET | FAST_CMD); 
  digitalWriteFast(SPI_CS_PIN, HIGH);
  delayMicroseconds(1);
  SPI.endTransaction();
  delay(1);
  
  Serial.println("Reset erfolgreich durchgeführt ...");

  SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
  digitalWriteFast(SPI_CS_PIN, LOW);
  delayNanoseconds(500);
  SPI.transfer(DEV_ADDR | CONFIG0_ADDR | INC_WRITE); 
  SPI.transfer(CONFIG0);
  SPI.transfer(CONFIG1);
  SPI.transfer(CONFIG2);
  SPI.transfer(CONFIG3);
  SPI.transfer(IRQ);
  SPI.transfer(MUX);
  SPI.transfer(0xFF&(SCAN>>16));
  SPI.transfer(0xFF&(SCAN>>8));
  SPI.transfer(0xFF&(SCAN));
  digitalWriteFast(SPI_CS_PIN, HIGH);
  delayMicroseconds(1);
  SPI.endTransaction();
  delay(1);
}

void startADC() {
  SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
  digitalWriteFast(SPI_CS_PIN, LOW);
  SPI.transfer(DEV_ADDR | CONV | FAST_CMD); 
  digitalWriteFast(SPI_CS_PIN, HIGH);
  SPI.endTransaction();
}

void readADC() {
  SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
  digitalWriteFast(SPI_CS_PIN, LOW);
  SPI.transfer(DEV_ADDR | ADC_REG_ADDR | READ); 
  uint32_t ADC_DATA = SPI.transfer32(0);
  digitalWriteFast(SPI_CS_PIN, HIGH);
  SPI.endTransaction();
  Serial.print("ADC raw: ");
  Serial.println(ADC_DATA);
  delay(500);
}

void loop() {
  readADC();
}

So at first, i initialize the ADC (reset and write registers so it is in the configuration i want it to be, after that i fire a fast command to get the adc in conversion mode). After that, i want to read the values. But when i run my sketch, i only get zeros. I have an accelerometer (ADXL1002) connected to my ADC. So i should see the Vref (coming from an external voltage regulator) instead of zeros. Is there a mistake in my SPI communication process or anywhere else? Is there a possibility to check if the SPI commands where successfull? I didn't even manage to get a register red out.

I'd serve the CS pin before beginTransaction() and after endTransaction.

Why the delay(100), that means that the level of SPI_CS_PIN can be floating for 100 milliseconds which is an eternity in processor-time.

And I agree with DrDiettrich about CSPIN.

  pinMode(SPI_CS_PIN, OUTPUT);
  digitalWriteFast(SPI_CS_PIN, HIGH);  //  default not selected (assumption)
  delay(100);
  digitalWriteFast(SPI_CS_PIN, LOW);
  SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
  delayNanoseconds(100);
  SPI.transfer(DEV_ADDR | RESET | FAST_CMD); 
  delayMicroseconds(1);
  SPI.endTransaction();
  digitalWriteFast(SPI_CS_PIN, HIGH);

First test is to connect a potentiometer to the ADC (W) with GND (A) and 5V (B) so it acts as a voltage divider. That shows if the ADC works or not.

Alternative is to measure / verify the ADC input with a multimeter.

Do you have a link to the datasheet?

MCP3564 doesn't support SPI.transfer32(). You must read the 3 data bytes manually (24 bits, MSB first), optionally followed by the STATUS byte (if you want it).

Please stop quoting the full preceding post.

I would suggest that you collect two chips of the ADC of package (20-Lead TSSOP), place one chip in the breadboard and then use UNOR3 to play with the Truth table of the ADC chip and then come to the Forum with meaningful questions.

You must terminate all the 20-pin of the chip in a way they should be which might require trail-and-error efforts. This is the reason I have requested to get two ADC chips. Alternately, you can use a breakout board if there is any.

Thank you for your reply. I adjusted my sketch the way you suggested and it didn't help out. But i found another mistake on the hardware i made. I forgot to connect the GND from my teensy with the Digital GND of the ADC. Shame on me. So my SPI pins were just floating i guess.

I then ran my sketch again and also tracked the four SPI pins on an oscilloscope. The communication itseld seems to work, as i can see falling and rising edges on all pins. But the problem is, the data i get is still not as expected.

I first execute a fast command that resets all registers to default. Then i read the four config registers. And only for one register (config 1) i get the expected value i wrote to it (0x0C / 12). The three other registers are all ones (0xFF / 255).

Do you have any idea what is going on?

I already verified the ADC working correctly with multimeter.

Here is a link to the datasheet:
MCP3561/2/4 Family Data Sheet https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP3561-2-4-Family-Data-Sheet-DS20006181C.pdf

Thanks,

i just found an example code for arduino zero on the website of microchip (MCP3564 | Microchip Technology).

I will now see what they did there and try to adjust the code for my approach.

That may result from a failing transmission. Show your related code.

I'm pretty clueless. I have written a sketch in which iteratively the CONFIG registers are read out, then a reset is carried out, then the CONFIG registers are read out again, then a configuration is carried out and then the CONFIG registers are read out again. In my serial monitor i can see the value of config1 register toggling from between 8 and 12, which are the values i write to it in reset and configuration. So config1 register is behaving like expected. But still, all other config register are 255 all the time.

Here is my code:

#include <Arduino.h>
#include <SPI.h>

#define MCLK_INTERNAL true //true for internal clock, false for external
#define OSR_SETTING OSR_128 //oversampling ratio

//Unchanging Definitions, Objects, and Variables
enum OSR : uint8_t {
  OSR_32    = 0x00,
  OSR_64    = 0x01,
  OSR_128   = 0x02,
  OSR_256   = 0x03, 
  OSR_512   = 0x04,
  OSR_1024  = 0x05,
  OSR_2048  = 0x06,
  OSR_4096  = 0x07,
  OSR_8192  = 0x08,
  OSR_16384 = 0x09,
  OSR_20480 = 0x0A,
  OSR_24576 = 0x0B,
  OSR_40960 = 0x0C,
  OSR_49152 = 0x0D,
  OSR_81920 = 0x0E,
  OSR_98304 = 0x0F
};
enum Channel : uint8_t {
  CH_0 = 0x00,
  CH_1 = 0x01,
  CH_2 = 0x02,
  CH_3 = 0x03,
  CH_4 = 0x04,
  CH_5 = 0x05,
  CH_6 = 0x06,
  CH_7 = 0x07,
  A_Vdd = 0x08,
  A_GND = 0x09,
  REFIN_pos = 0x0B,
  REFIN_neg = 0x0C,
  TEMP_P = 0x0D,
  TEMP_M = 0x0E,
  VCM = 0x0F
};

constexpr uint32_t SPI_Speed =      10000000;
constexpr uint8_t DEV_ADDR =        0b01000000;

constexpr uint8_t ADC_REG_ADDR =    0b00000000;
constexpr uint8_t CONFIG0_ADDR =    0b00000100;
constexpr uint8_t CONFIG1_ADDR =    0b00001000;
constexpr uint8_t CONFIG2_ADDR =    0b00001100;
constexpr uint8_t CONFIG3_ADDR =    0b00010000;
constexpr uint8_t IRQ_ADDR =        0b00010100;
constexpr uint8_t MUX_ADDR =        0b00011000;
constexpr uint8_t SCAN_ADDR =       0b00011100;
constexpr uint8_t TIMER_ADDR =      0b00100000;
constexpr uint8_t OFFSETCAL_ADDR =  0b00100100;
constexpr uint8_t GAINCAL_ADDR =    0b00101000;
constexpr uint8_t LOCK_ADDR =       0b00110100;

constexpr uint8_t FAST_CMD =        0b00000000;
constexpr uint8_t CONV =            0b00101000;
constexpr uint8_t STBY =            0b00101100;
constexpr uint8_t RESET =           0b00111000;
constexpr uint8_t READ =            0b00000001;
constexpr uint8_t INC_WRITE =       0b00000010;
constexpr uint8_t INC_READ =        0b00000011;

constexpr uint8_t CONFIG0 =         0b11000010 | (MCLK_INTERNAL <<5);
constexpr uint8_t CONFIG1 =         0b00000000 | (OSR_SETTING <<2);
constexpr uint8_t CONFIG2 =         0b10001011; //Contains Gain
constexpr uint8_t CONFIG3 =         0b11000000; //Contains Mode
constexpr uint8_t IRQ =             0b01110110; //contains IRQ stuff
constexpr uint8_t MUX =             0b00000001; //contains channel selection MUX_VIN+ = CH0 vs MUX_VIN- = CH1
constexpr uint32_t SCAN =           0b000000000000000100000000; //contains scan stuff 24 bits
constexpr uint32_t TIMER =          0; //contains timer stuff 24 bits
constexpr uint8_t OFFSETCAL =       0; //contains offset stuff
constexpr uint8_t GAINCAL =         0; //contains gain calibration stuff
constexpr uint8_t RESERVED =        0; //contains dummy for reserved registers 
constexpr uint8_t LOCK =            0b10100101; //contains Write Access Password

int counter = 0;

#define SPI_CS_PIN 10
#define SPI_MOSI_PIN 11
#define SPI_MISO_PIN 12
#define SPI_SCK_PIN 13
#define ADC_INT_PIN 3
#define ADC_MCLK_PIN 2

//Function Prototypes
void ADC_Init();

void setup() {
  pinMode(SPI_CS_PIN, OUTPUT);
  pinMode(ADC_INT_PIN, INPUT);
  Serial.begin(115200);
  delay(2000);
  SPI.setMOSI(11);
  SPI.setMISO(12);
  SPI.setSCK(13);
  SPI.begin();
  ADC_Init();
}

void ADC_Init() {
  digitalWrite(SPI_CS_PIN, LOW);
  SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
  SPI.transfer(DEV_ADDR | RESET | FAST_CMD); 
  SPI.endTransaction();
  digitalWrite(SPI_CS_PIN, HIGH);

  delay(5);
  
  Serial.println("Reset erfolgreich durchgeführt.");

  digitalWrite(SPI_CS_PIN, LOW);
  SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
  SPI.transfer(DEV_ADDR | CONFIG0_ADDR | INC_WRITE); 
  SPI.transfer(CONFIG0);
  SPI.transfer(CONFIG1);
  SPI.transfer(CONFIG2);
  SPI.transfer(CONFIG3);
  SPI.transfer(IRQ);
  SPI.transfer(MUX);
  SPI.transfer(0xFF&(SCAN>>16));
  SPI.transfer(0xFF&(SCAN>>8));
  SPI.transfer(0xFF&(SCAN));
  SPI.endTransaction();
  digitalWrite(SPI_CS_PIN, HIGH);
  delay(5);

  Serial.println("Konfiguration erfolgreich durchgeführt.");
}


void loop() {
  if (counter == 2) {
    digitalWrite(SPI_CS_PIN, LOW);
    SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
    SPI.transfer(DEV_ADDR | RESET | FAST_CMD); 
    SPI.endTransaction();
    digitalWrite(SPI_CS_PIN, HIGH);
    Serial.println("Reset done.");
    delay(5000);
  }

  if (counter == 3) {
    digitalWrite(SPI_CS_PIN, LOW);
    SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
    SPI.transfer(DEV_ADDR | CONFIG0_ADDR | INC_WRITE); 
    SPI.transfer(CONFIG0);
    SPI.transfer(CONFIG1);
    SPI.transfer(CONFIG2);
    SPI.transfer(CONFIG3);
    SPI.transfer(IRQ);
    SPI.transfer(MUX);
    SPI.transfer(0xFF&(SCAN>>16));
    SPI.transfer(0xFF&(SCAN>>8));
    SPI.transfer(0xFF&(SCAN));
    SPI.endTransaction();
    digitalWrite(SPI_CS_PIN, HIGH);
    delay(5);

    Serial.println("Konfiguration erfolgreich durchgeführt.");

    counter = 0;
  }

  digitalWrite(SPI_CS_PIN, LOW);
  SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
  SPI.transfer(DEV_ADDR | CONFIG0 | READ);
  uint8_t CONFIG0_DATA = SPI.transfer(0x00);
  SPI.endTransaction();
  digitalWrite(SPI_CS_PIN, HIGH);
  delay(5);
  Serial.print("CONFIG0 read: ");
  Serial.println(CONFIG0_DATA);
  delay(5000);

  digitalWrite(SPI_CS_PIN, LOW);
  SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
  SPI.transfer(DEV_ADDR | CONFIG1 | READ);
  uint8_t CONFIG1_DATA = SPI.transfer(0x00);
  SPI.endTransaction();
  digitalWrite(SPI_CS_PIN, HIGH);
  delay(5);
  Serial.print("CONFIG1 read: ");
  Serial.println(CONFIG1_DATA);
  delay(5000);

  digitalWrite(SPI_CS_PIN, LOW);
  SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
  SPI.transfer(DEV_ADDR | CONFIG2 | READ);
  uint8_t CONFIG2_DATA = SPI.transfer(0x00);
  SPI.endTransaction();
  digitalWrite(SPI_CS_PIN, HIGH);
  delay(5);
  Serial.print("CONFIG2 read: ");
  Serial.println(CONFIG2_DATA);
  delay(5000);

  digitalWrite(SPI_CS_PIN, LOW);
  SPI.beginTransaction(SPISettings(SPI_Speed, MSBFIRST, SPI_MODE0));
  SPI.transfer(DEV_ADDR | CONFIG3 | READ);
  uint8_t CONFIG3_DATA = SPI.transfer(0x00);
  SPI.endTransaction();
  digitalWrite(SPI_CS_PIN, HIGH);
  delay(5);
  Serial.print("CONFIG3 read: ");
  Serial.println(CONFIG3_DATA);
  delay(5000);

  counter = counter + 1;
 }

CS also should be set HIGH.

Which controller do you use that allows to switch SPI pins?

I added the digitalWrite to set CS pin HIGH after the pinMode command.

The controller i use is the Teensy 4.1.

I now managed to progress a little. I found an Arduino Zero code example in the Documentation on the Manufacturer-Website ( MCP3564 | Microchip Technology).

I had to adjust the Code, because there were definitions in the .h files, which caused an error ("expected unqualified-id before numeric constant"), as the definitions were already declared in the teensy-files.

I read out the CONFIG (0 to 3) and LOCK register after configuration. The LOCK register has the value for unlocked, so i have write access to the registers. For CONFIG0 and CONFIG2 i get the bits i expected (which where written to the registers in the configuration). But for CONFIG1 and CONFIG3 i get slightly different bits. I don't have any idea how this is possible. Any idea? The adjusted files are attached below.
MCP3x6x_ADC_Arduino_Example.zip (17,1 KB)

There is a second example code for arduino on the website, which i will now elaborate. Maybe it is working better than the example i now use.