Skip to content

How to work/interfacing with more than 8 SPI devices on ESP32 #1904

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
kennythum opened this issue Sep 27, 2018 · 13 comments
Closed

How to work/interfacing with more than 8 SPI devices on ESP32 #1904

kennythum opened this issue Sep 27, 2018 · 13 comments
Labels
Status: Stale Issue is stale stage (outdated/stuck)

Comments

@kennythum
Copy link

Dear gurus, Im currently having a project to work with 8 K-type thermocouples, all using MAX6675 individually to read the value and communicate with ESP32 via SPI. Meanwhile, it requires to log the data into a SD card, so there goes another SPI device. My question is, how could all these devices interface with the ESP32?? I'd done some research and all i could found was HSPI or VSPI could only support up to 3 devices. Anyone could shared some light? :roll:

@stickbreaker
Copy link
Contributor

@kennythum I would add more hardware. I would connect a MCP23S08(8bit SPI I/O expander) to the SPI use its eight output bits as inputs to an octal bus driver, connect the Output_Enable pin of the bus driver to the ESP32 as the thermocouple's CS pin. Place 3.3k pullups on each of the Octal drivers outputs. These pullups keep the CS pins high when the bus drivers output is disabled.

Using it would be a two step process:

  • First you would communicated with the MCP23S08, setting its output pins to select the Thermocouple you wanted to access.
  • Now you do the SPI communications with the "selected' Thermocouple. Programmatically, all the thermocouples use the same ESP32 pin as CS,

This design uses the SPI bus and 2 CS pins to control eight thermocouples. Up to eight MCP23S08 can share the same CS pin, so you could add seven more MCP23S08, that would allow you 64 Thermocouples. Or, use the MCPS17(16bit SPI I/O expander) and go way overboard!

Chuck.

@kennythum
Copy link
Author

@kennythum I would add more hardware. I would connect a MCP23S08(8bit SPI I/O expander) to the SPI use its eight output bits as inputs to an octal bus driver, connect the Output_Enable pin of the bus driver to the ESP32 as the thermocouple's CS pin. Place 3.3k pullups on each of the Octal drivers outputs. These pullups keep the CS pins high when the bus drivers output is disabled.

Using it would be a two step process:

  • First you would communicated with the MCP23S08, setting its output pins to select the Thermocouple you wanted to access.
  • Now you do the SPI communications with the "selected' Thermocouple. Programmatically, all the thermocouples use the same ESP32 pin as CS,

This design uses the SPI bus and 2 CS pins to control eight thermocouples. Up to eight MCP23S08 can share the same CS pin, so you could add seven more MCP23S08, that would allow you 64 Thermocouples. Or, use the MCPS17(16bit SPI I/O expander) and go way overboard!

Chuck.

Wow, thanks for the suggestion. But must it be SPI to IO expander? I have some I2C to 8-bit IO IC laying around, not sure is it working. Another question, why can't we use the GPIO of ESP32 to use as CS? I think the ESP32 has some quite amount of GPIO right?

@stickbreaker
Copy link
Contributor

@kennythum I just recommended an SPI I/O expander because you were already using the SPI bus. I don't know of your 3 pin CS restriction, But, if that is the case then an I/O expander is necessary.
You could use and I2C expander, it is just much slower than SPI, your choice.

Chuck.

@MARSsk
Copy link

MARSsk commented Oct 5, 2018

@kennythum I'm also trying the same project as taking values from thermocouples using SPI bus, but the not able to get the correct readings. Following is the code with which I'm trying to interface

`#include "max6675.h"

void setup()
{
Serial.begin(9600);
Serial.println("MAX6675 test");
delay(500);
}

void loop()
{
// basic readout test, just print the current temp
int cs1 = 14;
int cs2 = 25;
int cs3 = 26;
int thermoDO = 27;
int thermoCLK = 12;
for(int i=1; i<4; i++){
if (i=1){
digitalWrite(cs1,LOW);
digitalWrite(cs2,LOW);
digitalWrite(cs3,LOW);
delay(500);
digitalWrite(cs1,HIGH);
Serial.println("temp 1 : ");
MAX6675 thermocouple(thermoCLK, cs1, thermoDO);
delay(100);
Serial.print("C = ");
Serial.println(thermocouple.readCelsius());
Serial.print("F = ");
Serial.println(thermocouple.readFahrenheit());
delay(1000);
}
if (i=2){
digitalWrite(cs1,LOW);
digitalWrite(cs2,LOW);
digitalWrite(cs3,LOW);
delay(500);
digitalWrite(cs2,HIGH);
Serial.println("temp 2 : ");
MAX6675 thermocouple(thermoCLK, cs2, thermoDO);
delay(100);
Serial.print("C = ");
Serial.println(thermocouple.readCelsius());
Serial.print("F = ");
Serial.println(thermocouple.readFahrenheit());
delay(1000);
}
if (i=3){
digitalWrite(cs1,LOW);
digitalWrite(cs2,LOW);
digitalWrite(cs3,LOW);
delay(500);
digitalWrite(cs3,HIGH);
Serial.println("temp 3 : ");
MAX6675 thermocouple(thermoCLK, cs3, thermoDO);
delay(100);
Serial.print("C = ");
Serial.println(thermocouple.readCelsius());
Serial.print("F = ");
Serial.println(thermocouple.readFahrenheit());
delay(1000);
}
delay(1000);
}
}`

@kennythum
Copy link
Author

I think you messed up with the CS...you read the device by trigger LOW on the CS pin, not HIGH.

When a device's Slave Select pin is low, it communicates with the master. When it's high, it ignores the master. This allows you to have multiple SPI devices sharing the same MISO, MOSI, and CLK lines.

Adapted from arduino SPI.

@MARSsk
Copy link

MARSsk commented Oct 5, 2018

Thank you...!!! @kennythum
Please check if there is any other issue with my code as I'm still not getting the readings, like varying from 14°C to 1887°C.

@kennythum
Copy link
Author

kennythum commented Oct 11, 2018

@kennythum I would add more hardware. I would connect a MCP23S08(8bit SPI I/O expander) to the SPI use its eight output bits as inputs to an octal bus driver, connect the Output_Enable pin of the bus driver to the ESP32 as the thermocouple's CS pin. Place 3.3k pullups on each of the Octal drivers outputs. These pullups keep the CS pins high when the bus drivers output is disabled.

Using it would be a two step process:

  • First you would communicated with the MCP23S08, setting its output pins to select the Thermocouple you wanted to access.
  • Now you do the SPI communications with the "selected' Thermocouple. Programmatically, all the thermocouples use the same ESP32 pin as CS,

This design uses the SPI bus and 2 CS pins to control eight thermocouples. Up to eight MCP23S08 can share the same CS pin, so you could add seven more MCP23S08, that would allow you 64 Thermocouples. Or, use the MCPS17(16bit SPI I/O expander) and go way overboard!

Chuck.

Helo @stickbreaker . I have tried as per your suggestion. Meanwhile, I can't get the reading properly from the MCP23S17 which connected with arrays of MAX6675 modules. Weird thing was, when i remove everything, and just left out one MAX6675 module on the SPI bus, it can read well, with the normal value (temperature). I do as you suggested, pullup resistor on each CS pin for each MAX6675, and all MAX6675 MISO/SCLK share the pin/bus on the SPI with the MCP23S17. Question, when i read 1 of the MAX6675 which over the MCP23S17, the value i obtain is the value from MCP23S17? or the selected MAX6675?? since they both sharing the same SPI bus, and both, well, consider as CS PIN enabled. God, please help me out...

@stickbreaker
Copy link
Contributor

@kennythum here is my expander circuit for a MEGA2560 and ESP12 SPI sharing circuit:

SPIExpander circuit

spi expander

This is the Arbitration circuit: between ESP12 and MEGA2560

spi arbitration

Explanation:

The arbitration circuit uses MEGA_SPI!REQ and ESP_SPI!REQ to generate MEGA_!SPI or ESP_!SPI.
After both of the !REQ inputs are HIGH, the first one that goes low OWNS the bus. This allows me to share peripherals between a ESP12 and an ATMega2560. Once a CPU wins, it owns the bus until it releases its !REQ.

The ESP12 SPI (ESP_SCK,ESP_MOSI, ESP_MISO, ESP_CS1, ESP_CS2) use the lower four bits of the MCP23S08 as four !CS pins. GP4 is an output bit, GP5 is readback of SPI bus ownership(arbitration win).

my ESP12 SPI code has two phases:

  • First Stage: it loads the MCP23S08 with the selected devices CS (RAM,FLASH,SD, or RTCC) and ESP_SPI!REQ), then it reads back ESP_!SPI. if ESP_!SPI was low, then it jumps to second Stage. Else, it sets ESP_SPI!REQ high, delays a random amount of time, then retries first Stage.
  • Second Stage: after the successful acquisition of the SPI bus, the ESP12's CS2 is used as the device CS pin. different SPI devices can be selected by changing the MCP23S08 bits. When the ESP12 is finished with the SPI bus, GP4 is set high to release the bus.

@kennythum You probably don't need the arbitration circuit, just remove IC11A and control the 'G' pin of the bus driver(74LVC244) directly from your ESP12 with your Second CS pin.
You use CS1 to control the MCP23S08 and CS2 to communicate with the thermocouple you want. All thermocouples think CS2 is their exclusive CS pin.
So, accessing a thermocouple is a two stage process:

  • First Stage: set the GPn bit low for the thermocouple you want to access. (all other bits are high)
  • Second Stage: control the selected thermocouple with CS2.

Chuck.

@kennythum
Copy link
Author

@kennythum here is my expander circuit for a MEGA2560 and ESP12 SPI sharing circuit:

SPIExpander circuit

spi expander

This is the Arbitration circuit: between ESP12 and MEGA2560

spi arbitration

Explanation:

The arbitration circuit uses MEGA_SPI!REQ and ESP_SPI!REQ to generate MEGA_!SPI or ESP_!SPI.
After both of the !REQ inputs are HIGH, the first one that goes low OWNS the bus. This allows me to share peripherals between a ESP12 and an ATMega2560. Once a CPU wins, it owns the bus until it releases its !REQ.

The ESP12 SPI (ESP_SCK,ESP_MOSI, ESP_MISO, ESP_CS1, ESP_CS2) use the lower four bits of the MCP23S08 as four !CS pins. GP4 is an output bit, GP5 is readback of SPI bus ownership(arbitration win).

my ESP12 SPI code has two phases:

  • First Stage: it loads the MCP23S08 with the selected devices CS (RAM,FLASH,SD, or RTCC) and ESP_SPI!REQ), then it reads back ESP_!SPI. if ESP_!SPI was low, then it jumps to second Stage. Else, it sets ESP_SPI!REQ high, delays a random amount of time, then retries first Stage.
  • Second Stage: after the successful acquisition of the SPI bus, the ESP12's CS2 is used as the device CS pin. different SPI devices can be selected by changing the MCP23S08 bits. When the ESP12 is finished with the SPI bus, GP4 is set high to release the bus.

@kennythum You probably don't need the arbitration circuit, just remove IC11A and control the 'G' pin of the bus driver(74LVC244) directly from your ESP12 with your Second CS pin.
You use CS1 to control the MCP23S08 and CS2 to communicate with the thermocouple you want. All thermocouples think CS2 is their exclusive CS pin.
So, accessing a thermocouple is a two stage process:

  • First Stage: set the GPn bit low for the thermocouple you want to access. (all other bits are high)
  • Second Stage: control the selected thermocouple with CS2.

Chuck.

Thank thank you so much for replying me. At least giving me some little hope, else im clueless!

Question 1: why can't we connect all the SPI devices CS to GPn of the MCP23S08? And LOW the GPn and set all the other bit to high, as chip select to the target SPI device, while sharing the same MISO/MOSI/SCLK with the MCP23S08?? what is the main reason of crossing over a 74LVC244DW??

Question 2: Do all the SPI devices share the same MISO/MOSI/SCLK with the MCP23S08?? and from your circuit, I should read my value of the selected device from CS2?

Question 3: Is MCP23S08/MCP23S17 of these chip work as merely IO extender? Like really only work as HIGH/LOW function of its pins?? yet we cant interfacing any other function from it's GPIOs??

Thank you a billions for giving me hope! Srsly...appreciate your kindness.

@stickbreaker
Copy link
Contributor

@kennythum #1 timing. When CS goes low is the start of an SPI transaction. When CS goes high the slave stops monitoring the SPI bus. This means ONLY ONE CS pin CAN be low at a time. A normal transaction consists of:

  • CS LOW
  • SCK,MOSI active output, MISO input
  • CS HIGH

If you directly connect GPn to the CS input of one of the thermocouples, both the MCP23Sxx and MAX will be driving MISO.
There need to be a delay between CS low and bus activity.
How would you control the MCP after you selected a MAX? Both would be listening to SCK, MOSI and driving MISO?

#2 YES

#3 Input, output, weak pullup, weak pulldown, interrupt on level, change

Chuck

@MarginRazvan
Copy link

I believe that SPI bus is not the best option for comunicating with other devices. I think it is best to try and establish the communication via the I2C bus.

@stale
Copy link

stale bot commented Aug 1, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the Status: Stale Issue is stale stage (outdated/stuck) label Aug 1, 2019
@stale
Copy link

stale bot commented Aug 15, 2019

This stale issue has been automatically closed. Thank you for your contributions.

@stale stale bot closed this as completed Aug 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Stale Issue is stale stage (outdated/stuck)
Projects
None yet
Development

No branches or pull requests

4 participants