ESP32 SPI issue (voltage levels)

I have (technically had because it smoked) a PCB board with an ESP32 Dev Board plugged in. I had 4 external ADCs. ADC 1 and 2 were on hspi and ADC 3 and 4 were on vspi (doesn't matter if hspi/vspi are switched). When I run the code with the ESP32 powered via my USB everything works fine. When I remove the power and rely solely on L7805CV to supply 5V to the ESP's 5V pin ADC 3 and 4 stop working. I cut my USB cable off and touched the 5V on the USB directly to the 5V on the ESP32 and ADC pin 3 and 4 came alive. This makes me think the issue is with the supply voltage level.

As far as the hardware/wiring is concerned I had everything connected to the same ground. The L7805CV was nowhere near its current rating (was only slightly warm) but I did notice that the voltage on the L7805CV was only 4.97V while the voltage from the USB was 5.01V. Could this be causing the issue? I need to redesign my board and I'm trying to understand why I was seeing this issue.

Here is the code I used to read the 4 channels. I tried really long delays before and after setting the pins high and low and didn't make a difference. The highlights (I think) are the pins I used (SPI1_SCK = 14; SPI1_MISO = 12; ADC_CS_1 = 15; ADC_CS_2 = 21; SPI2_SCK = 18; SPI2_MISO = 19; ADC_CS_3 = 25; ADC_CS_4 = 26;)

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

const int SPI1_SCK = 14;
const int SPI1_MISO = 12;
const int ADC_CS_1 = 15;
const int ADC_CS_2 = 21;

const int SPI2_SCK = 18;
const int SPI2_MISO = 19;
const int ADC_CS_3 = 25;
const int ADC_CS_4 = 26;


unsigned char upper_byte;
unsigned char middle_byte;
unsigned char lower_byte;
unsigned int data;

//uninitalised pointers to SPI objects
SPIClass vspi(VSPI);
SPIClass hspi(HSPI);

// function declarations
int readData(SPIClass*, unsigned int);

// RS485 properties
HardwareSerial SerialPort(2); //UART2 on ESP32 module
const int RXpin = 16; //pin 16 recommended for RXD
const int TXpin = 17; //pin 17 recommended for TXD
const int slave_address = 102; //address must be hard coded (will not auto generate)

int regs[256]; //register array available to master
bool debug_read_esp = false;
bool debug_read_all = false; // only will read data coming to ESP32, good to determine if communication exists
bool debug_write = false;

const unsigned int numSamples = 40; // number of samples to take for each channel

void setup() {

  Serial.begin(9600);
  // Set up the first SPI interface
  pinMode(ADC_CS_1, OUTPUT);
  digitalWrite(ADC_CS_1, HIGH);
  hspi.begin(SPI1_SCK, SPI1_MISO, -1, ADC_CS_1);

  pinMode(ADC_CS_2, OUTPUT);
  digitalWrite(ADC_CS_2, HIGH);
  hspi.begin(SPI1_SCK, SPI1_MISO, -1, ADC_CS_2);

  hspi.setClockDivider(SPI_CLOCK_DIV64); // ESP32 has SPI clock speed of 240MHz / 64 = 3.75MHz
  hspi.setDataMode(SPI_MODE2); // CPOL = 1, CPHA = 0 (clock is high in idle state, data sampled on rising edge)

  // Set up the second SPI interface
  pinMode(ADC_CS_3, OUTPUT);
  digitalWrite(ADC_CS_3, HIGH);
  vspi.begin(SPI2_SCK, SPI2_MISO, -1, ADC_CS_3);

  pinMode(ADC_CS_4, OUTPUT);
  digitalWrite(ADC_CS_4, HIGH);
  vspi.begin(SPI2_SCK, SPI2_MISO, -1, ADC_CS_4);

  vspi.setClockDivider(SPI_CLOCK_DIV64); // ESP32 has SPI clock speed of 240MHz / 64 = 3.75MHz
  vspi.setDataMode(SPI_MODE2); // CPOL = 1, CPHA = 0 (clock is high in idle state, data sampled on rising edge)

  SerialPort.begin(9600, SERIAL_8N1, RXpin, TXpin); //UART: baud rate, serial config., rxd pin, txd pin

  delay(2000);
  SerialPort.flush();
  Serial.flush();

}

void loop() {
  
  unsigned int data_total_0 = 0;
  unsigned int data_total_1 = 0;
  unsigned int data_total_2 = 0;
  unsigned int data_total_3 = 0;

  //read the data into the registers
  for (int i = 0; i < numSamples; i++){
    data_total_0 += readData(&hspi, ADC_CS_1); // read data from channel 1
    data_total_1 += readData(&hspi, ADC_CS_2); // read data from channel 2
    data_total_2 += readData(&vspi, ADC_CS_3); // read data from channel 3
    data_total_3 += readData(&vspi, ADC_CS_4); // read data from channel 4
  }

  regs[0] = data_total_0 / numSamples;
  regs[1] = data_total_1 / numSamples;
  regs[2] = data_total_2 / numSamples;
  regs[3] = data_total_3 / numSamples;

  // check for a modbus command over the serial port
  if (SerialPort.available() > 7){
    decode();
  }
}

int readData(SPIClass* spi, unsigned int cs_pin) {
  //Format is 0000UUUU MMMMMMMM LLLL0000
  // returns 16 bit data
  digitalWrite(cs_pin, LOW);
  delay(1);
  upper_byte = spi->transfer(0x00);
  middle_byte = spi->transfer(0x00);
  lower_byte = spi->transfer(0x00);
  data = ((upper_byte << 12) | (middle_byte << 4) | (lower_byte >> 4));
  digitalWrite(cs_pin, HIGH);
  return data;
}

Do you capacitors on the input and output of th L7805?
Are your ADCs 3.3V?

Yes I have recommended capacitors on the L7805 and additional filtering capacitor right before the ESP32. .33uF and 10uF on the input voltage (12V) and .1uF on the output. Additional .1uF right next to the esp32.

ADCs are 5V (AD7680ARJZ-REEL7). Everything was working perfectly on one SPI channel of the ESP32. The second channel worked fine connected to to USB power except not as consistent results I assume since the the power wasn't regulated as well and all 5V was on a single net.

I'm thinking of using a 9V LDO on the next iteration to supply power to the ESP32.

Where is the schematics, the wiring? Evaluating Shakespeare type of description.... No.

I'll take it as a compliment.

ESP32-WROOM
AD7680ARJZ-REEL7

Thinking about trying 2 separate voltage supplies on the next board. One LDO 5V for the ADCs and designated 6V LDO for ESP32. What do you think?

@jrlaufer
One BIG problem that I see is that you have the voltage divider on Sdata backwards.
You are dividing the 5V by 3.
Also the Logic level inputs on the AD7680 are NOT TTL compatiable so you may have problems there also, especially at Vil

@jim-p HUGE catch with the voltage divider being backwards. I have the oscilloscope going now and there is still hope that this board is alive (I am seeing pulse signals off of the ADC still). I switched the voltage divider and am testing to see if it will work now.

Regarding the AD7680 not being TTL compatible, I think it should be okay based on the Table 2, am I missing something?

The max VIL for the AD7680 is only 0.4V
VOL for ESP is 0.1*Vdd. For Vdd exactly = 3.3V that's only 0.33V, not much of a noise margin.
If the tolerance on the 3.3V supply is +/-2%, then VOL could be as high as 0.337V and that only gives you 0.4- 0.337 = 63mV noise margin.
Your signals either need to be very very clean or you need to level shift.

If you are going to design a PCB you need to read and understand everything in the datasheets, especially when dealing with mixed voltages (3.3V, 5V)

Thanks again @jim-p. I did read the AD7680 many times very carefully when I designed the board. Unfortunately I didn't consider noise, I thought that 0.4 VIL would be okay with the ESP32.

Seems like the issue with the voltage level are at CS and SCLK which are currently taking 3.3V outputs on the ESP32. I see three ways of potentially solving this problem.

  1. Add 10:1 voltage divider on CS and SCLK. That way on the high side nominally I have 3.33V*.9 = 3V and .33V*.9 =.3 so now I have 100mV of noise allowed VIL and 200mV allowed on VIH.
  2. Keep noise level low like you said, I can add a .1uf and maybe 1uf capacitor to ground in hopes that this makes the signal stable.
  3. Possibly an easy solution if it worked - I could set the internal pulldown of the esp32 and instead of setting the output as low I could set it to floating/high-z.

Do you like any of these solutions or do you prefer a different one? I'm not totally sure how a level shifter would help me in this case since since I wouldn't expect shifting 3.3V to 5V on CS and SCLK would make VOL any better.

For context this is the first PCB I designed with surface mount components. In the past I have always used through hole, lead times are terrible for the PCB assemblies.

I did not get around at looking at VOH and VIH
For the ESP it is 0.8*Vdd = 2.64, which is lower than the 2.8V VIH for the AD7680, so resistors will just make things worse.

Use a 74LVC2G34 to do the level shifting. Power it with 5V

Thanks so much for your help. I see it goes to GND/VCC. I've done this before with an AND gate but this seems like the better way to go when using surface mount components. I'm ordering the PCB expedited so in a couple weeks I should be able to test it out and I'll report back.

Here is my updated schematic. I decided to also put the the ESP32 on a designated LDO. It says 6V but I can experiment with different levels in the allowable range. Thanks again!

There is no reason to put more than 5V on the EXT_5V input.
It just connects directly to a 3.3V regulator. More than 5V will just cause the regulator to dissapate more heat.
I don't think that the problems you had were related to supply voltage levels, just looked that way.

I agree about it not ending up being a power issue. I'll probably end up putting two 5V LDOs in. Besides, it can only help to separate the ESP32 power from the ADC reference.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.