MAX14921 Cell Voltage Readout via esp32

Hello there,

I'm trying to read cell voltages using a pcb and the max14921 chip, i have attached 3 cells (min requirement of the chip) and in total currently 9.7V (6V min).

Unfortunately i was not able to find any working code for this chip, there are a few libraries out there (Github, different forums,...) but none of them are really working.

I've tried to contact the vendor, but i haven't got any response for like 3 weeks..

Attached the Code which i'm using and the corresponding schematic.

Does anyone have an idea, why its not working? I'm receiving raw values of the adc of 65535, which indicates theres something quite wrong.

PS: GPIO38 needs to be high for giving power to the various devices. 14 is the EN_AFE for the max14921.

#include <SPI.h>

// Pinbelegung
const int clockPin = 12; // SCLK
const int miso = 13;     // MISO
const int mosi = 11;     // MOSI
const int adcCS = 44;    // Chip Select für den ADC (AD7685)
const int afeCS = 42;    // Chip Select für den AFE (MAX14921)
const int sampl = 43;    // SAMPL-Pin

// SPI Befehlsdefinitionen aus der Bibliothek
#define MD_AK35_SPI_SAMPLB_ENABLE 0x04
#define MD_AK35_SPI_SAMPLB_DISABLE 0x00
#define MD_AK35_SPI_DIAG_ENABLE 0x02
#define MD_AK35_SPI_DIAG_DISABLE 0x00
#define MD_AK35_SPI_LOPW_ENABLE 0x01
#define MD_AK35_SPI_LOPW_DISABLE 0x00
#define MD_AK35_SPI_TXSEL_BUFFERED 0x18
#define GPIO38 38
#define GPIO14 14

// Zellen Auswahl Tabelle aus der Bibliothek
uint8_t gMD_AK35_CellSelect_Table[16] = {
  0x80, 0xC0, 0xA0, 0xE0, 0x90, 0xD0, 0xB0, 0xF0, 
  0x88, 0xC8, 0xA8, 0xE8, 0x98, 0xD8, 0xB8, 0xF8
};

// Globale Variablen für die Objektparameter
typedef struct {
  uint8_t acqCellNumber;
  int acqCellSampleTmrValueMsec;
  int acqCellRepeatTmrValueMsec;
  int acqCellSettlingTimeUsec;
  uint8_t spiSamplB;
  uint8_t spiDiag;
  uint8_t spiLoPw;
} MD_AK35_INSTANCE_T;

MD_AK35_INSTANCE_T MD_AK35_obj;

void setup() {
  delay(5000);
  pinMode(clockPin, OUTPUT);
  pinMode(miso, INPUT);
  pinMode(mosi, OUTPUT);
  pinMode(adcCS, OUTPUT);
  pinMode(afeCS, OUTPUT);
  pinMode(sampl, OUTPUT);
  pinMode(GPIO38, OUTPUT);
  pinMode(GPIO14, OUTPUT);

  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV8); // Verlangsamt den SPI-Bus
  SPI.setDataMode(SPI_MODE0); // SPI Modus 0
  SPI.setBitOrder(MSBFIRST);  // MSB zuerst
  
  digitalWrite(afeCS, HIGH);  // CS für AFE initial auf HIGH
  digitalWrite(adcCS, HIGH);  // CS für ADC initial auf HIGH
  digitalWrite(sampl, HIGH);  // SAMPL-Pin initial auf HIGH
  digitalWrite(GPIO38, HIGH);
  digitalWrite(GPIO14, LOW);
  
  Serial.begin(9600);

  // Setup initial parameters
  MD_AK35_obj.acqCellNumber = 16;
  MD_AK35_obj.acqCellSampleTmrValueMsec = 4; // Standard: 4 ms
  MD_AK35_obj.acqCellRepeatTmrValueMsec = 6; // Standard: 6 ms
  MD_AK35_obj.acqCellSettlingTimeUsec = 10;  // Standard: 10 us
  MD_AK35_obj.spiSamplB = MD_AK35_SPI_SAMPLB_DISABLE;
  MD_AK35_obj.spiDiag = MD_AK35_SPI_DIAG_DISABLE;
  MD_AK35_obj.spiLoPw = MD_AK35_SPI_LOPW_DISABLE;

  // Durchführung einer Offset-Kalibrierung
  offsetCalibration();
}

void loop() {
  // Zellen 1 bis 8 auslesen und Spannungen anzeigen
  for (uint8_t cell = 1; cell <= 8; cell++) {
    float voltage = measureCellVoltage(cell);
    Serial.print("Spannung an Zelle ");
    Serial.print(cell);
    Serial.print(": ");
    Serial.print(voltage, 3); // Spannung mit 3 Dezimalstellen
    Serial.println(" V");
  }
  Serial.println("\n\n\n");
  
  delay(5000); // Fünf Sekunden warten, bevor erneut gemessen wird
}

void offsetCalibration() {
  // SAMPL aktivieren
  digitalWrite(sampl, HIGH);
  
  // Offset-Kalibrierung starten
  digitalWrite(afeCS, LOW);
  SPI.transfer16(0x000040); // SPI-Befehl für Offset-Kalibrierung
  digitalWrite(afeCS, HIGH);
  
  delay(8); // 8ms warten, wie im Flowchart angegeben
  
  // SAMPL deaktivieren
  digitalWrite(sampl, LOW);
}

float measureCellVoltage(uint8_t cell) {
  uint16_t rawValue = 0;
  float voltage = 0.0;

  // SAMPL aktivieren
  digitalWrite(sampl, HIGH);
  
  // Zellenauswahl SPI-Befehl erstellen
  uint8_t spiCmd = gMD_AK35_CellSelect_Table[cell - 1];
  spiCmd |= (MD_AK35_obj.spiSamplB | MD_AK35_obj.spiDiag | MD_AK35_obj.spiLoPw);

  // SPI-Befehl zur Zellenspannungsmessung senden
  rawValue = MD_AK35_SpiTransfer24(0x00, 0x00, spiCmd);
  
  // Chip Select toggeln und erneut senden
  digitalWrite(afeCS, HIGH);
  digitalWrite(afeCS, LOW);
  
  rawValue = MD_AK35_SpiTransfer24(0x00, 0x00, spiCmd);
  
  digitalWrite(afeCS, HIGH);

  // SAMPL deaktivieren
  digitalWrite(sampl, LOW);
  
  // Rohwert in Spannung umrechnen
  voltage = convertToVoltage(readAD7685());
  
  return voltage;
}

// Funktion zur Umrechnung des ADC-Wertes in Spannung
float convertToVoltage(uint16_t adcValue) {
  // Beispiel: Umrechnung für 16-Bit-ADC mit Referenzspannung von 5V
  Serial.print("RAW: ");
  Serial.println(adcValue);
  float voltage = (adcValue / 65535.0) * 5.0;
  return voltage;
}

// Funktion zur Messung vom AD7685
uint16_t readAD7685() {
  uint16_t result = 0;

  // Chip Select des ADC aktivieren
  digitalWrite(adcCS, LOW);
  
  // Kurze Verzögerung, um den Chip zu stabilisieren
  delayMicroseconds(1);

  // 16-Bit Wert über SPI auslesen
  result = SPI.transfer16(0x0000);

  // Chip Select des ADC deaktivieren
  digitalWrite(adcCS, HIGH);

  return result;
}


// SPI Transfer von 3 Datenbytes (aus der Bibliothek)
long MD_AK35_SpiTransfer24(uint8_t byte1, uint8_t byte2, uint8_t byte3) {
  long ak35Data = 0;
  byte spiData = 0;

  spiData = SPI.transfer(byte1);
  ak35Data = spiData;
  ak35Data <<= 8;

  spiData = SPI.transfer(byte2);
  ak35Data |= spiData;
  ak35Data <<= 8;

  spiData = SPI.transfer(byte3);
  ak35Data |= spiData;

  return ak35Data;
}



output