Concurrent VSPI and HSPI not working

dl9rdz
Posts: 2
Joined: Sun Oct 06, 2019 8:23 pm

Concurrent VSPI and HSPI not working

Postby dl9rdz » Sun Oct 06, 2019 8:44 pm

Hi,

I am using Arduino ESP32 on a Lilygo T-Beam v1.0 ESP32 (same problem on an older v0.7 T-Beam).

Seems like using VSPI and HSPI concurrently on two different cores fails.

I am using HSPI to communicate with the T-Beam's on-board SX1278 (a task on core 0) and VSPI to communicate with an external 2" ILI9225 TFT display. SX1278 communication is working perfectly while the Arduino main loop (on core 1) is idle in an delay(), but when the Arduinio main loop is communicating with the display using VSPI, all SPI read operation in the SX1278 task return 0 (my measurent means are somewhat limited, but on my analog oscilloscope it seems like the chip select is not pulled to low on the SX1278 while the display is active!?????).

The problem originally arouse in a larger project, but I was able it to reduce it to the following lines of code (arduinio sketch):

Code: Select all

#include <axp20x.h>
#include <SPI.h>
#include <TFT_22_ILI9225.h>
#include <../fonts/FreeSans9pt7b.h>

SPIClass sxspi(HSPI);
SPIClass spiDisp(VSPI);
AXP20X_Class axp;
TFT_22_ILI9225 *tft;

#define        REG_OP_MODE                                      0x01
const uint8_t FSK_SLEEP_MODE = 0x00;
const uint8_t FSK_STANDBY_MODE = 0x01;
#define        REG_RSSI_VALUE_FSK                       0x11
#define        REG_FRF_MSB                                      0x06
#define        REG_FRF_MID                                      0x07
#define        REG_FRF_LSB                                      0x08
#define        REG_OCP                                          0x0B

#define SX127X_CRYSTAL_FREQ 32000000
#define SX127X_FSTEP (SX127X_CRYSTAL_FREQ*1.0/(1<<19))
#define SX1278_SS SS

class SX1278FSK {
  private:
    SPIClass _spi;
    SPISettings spiset;

  public:
    void begin(SPIClass &spi) {
      spiset = SPISettings(10000000L, MSBFIRST, SPI_MODE0);
      pinMode(SX1278_SS, OUTPUT);
      digitalWrite(SX1278_SS, HIGH);
      _spi = spi;
      _spi.begin(SCK, MISO, MOSI, SS); 
      _spi.setBitOrder(MSBFIRST);
      _spi.setClockDivider(SPI_CLOCK_DIV4);
      _spi.setDataMode(SPI_MODE0);
      delay(100);
      int rate = 0x1B;
      rate |= B00100000;
      writeRegister(REG_OP_MODE, FSK_SLEEP_MODE);     // Sleep mode (mandatory to change mode)
      writeRegister(REG_OP_MODE, FSK_SLEEP_MODE);
      writeRegister(REG_OP_MODE, FSK_STANDBY_MODE);   // Set FSK Standby mode to write in registers
      writeRegister(REG_OCP, rate);           // Modifying maximum current supply
      int x = readRegister(REG_OP_MODE);
      delay(100);

    }
    byte readRegister(byte address) {
      byte value = 0x00;
      _spi.beginTransaction(spiset);
      digitalWrite(SX1278_SS, LOW);
      bitClear(address, 7);           // Bit 7 cleared to write in registers
      _spi.transfer(address);
      value = _spi.transfer(0x00);
      digitalWrite(SX1278_SS, HIGH);
      _spi.endTransaction();
      //Serial.printf("Register %02x reads value %02x\n", address, value);
      return value;
    }
    void writeRegister(byte address, byte data) {
      _spi.beginTransaction(spiset);
      digitalWrite(SX1278_SS, LOW);
      bitSet(address, 7);                     // Bit 7 set to read from registers
      _spi.transfer(address);
      _spi.transfer(data);
      digitalWrite(SX1278_SS, HIGH);
      _spi.endTransaction();
       //Serial.printf("Register %02x: writing value %02x\n", address&0x7f, data);
    }
    uint8_t setFrequency(float freq) {
      // set mode to FSK STANDBY
      writeRegister(REG_OP_MODE, FSK_STANDBY_MODE);
      uint32_t frf = freq * 1.0 * (1 << 19) / SX127X_CRYSTAL_FREQ;
      writeRegister(REG_FRF_MSB, (frf & 0xff0000) >> 16);
      writeRegister(REG_FRF_MID, (frf & 0x00ff00) >> 8);
      writeRegister(REG_FRF_LSB, (frf & 0x0000ff));
      return 0;
    }
    float getFrequency() {
      uint8_t fmsb = readRegister(REG_FRF_MSB);
      uint8_t fmid = readRegister(REG_FRF_MID);
      uint8_t flsb = readRegister(REG_FRF_LSB);
      return ((fmsb << 16) | (fmid << 8) | flsb) * 1.0 / (1 << 19) * SX127X_CRYSTAL_FREQ;
    }
};

SX1278FSK sx1278;

void sx1278Task(void *parameter) {
  while (1)
  {
    sx1278.setFrequency(403300000);
    float f = sx1278.getFrequency();
    Serial.printf("  freq:%f\n", f);
    delay(100);
  }
}

void setup()
{
  Serial.begin(115200);

  // For T-Beam 1.0 => Enable 3.3V on DCDC1 for display
  Wire.begin(21, 22);
  if (!axp.begin(Wire, AXP192_SLAVE_ADDRESS)) {
    Serial.println("AXP192 Begin PASS");
  } else {
    Serial.println("AXP192 Begin FAIL");
  }
  axp.setPowerOutPut(AXP192_LDO2, AXP202_ON);
  axp.setPowerOutPut(AXP192_LDO3, AXP202_ON);
  axp.setPowerOutPut(AXP192_DCDC2, AXP202_ON);
  axp.setPowerOutPut(AXP192_EXTEN, AXP202_ON);
  axp.setPowerOutPut(AXP192_DCDC1, AXP202_ON);
  axp.setDCDC1Voltage(3300);

  // setup display
  tft = new TFT_22_ILI9225(14, 2, 0, 4, 13, 0, 100);
  tft->begin(spiDisp);
  tft->setOrientation(1);
  tft->clear();
  tft->setGFXFont(&FreeSans9pt7b);

  // setup sx1278
  sx1278.begin(sxspi);
  delay(100);
  sx1278.setFrequency(403700000);
  xTaskCreatePinnedToCore( sx1278Task, "sx1278Task",
                           10000, /* stack size */
                           NULL, /* paramter */
                           1, /* priority */
                           NULL, 0);  /* task handle*/
}

#define XSKIP 14
#define YSKIP 22
void drawString(uint8_t x, uint8_t y, const char *s) {
  int16_t w, h;
  tft->getGFXTextExtent(s, x * XSKIP, y * YSKIP, &w, &h);
  int len = strlen(s);
  tft->fillRectangle(x * XSKIP, y * YSKIP + 3, x * XSKIP + len * 14, y * YSKIP + 22, COLOR_BLACK);
  tft->drawGFXText(x * XSKIP, (1 + y)*YSKIP, s, COLOR_WHITE);
}

void loop() {
  Serial.printf("Mainloop...\n");
  for (int i = 0; i < 50; i++) {
    char buf[50];
    sprintf(buf, "cnt: %d  ", i);
    drawString(0, 1, buf);
  }
  Serial.printf("Mainloop delay\n");
  delay(1000);
}

The output on the console is the following:

Code: Select all

22:27:52.459 -> Mainloop...
22:27:52.459 ->   freq:0.000000
22:27:52.562 ->   freq:0.000000
22:27:52.634 ->   freq:0.000000
22:27:52.737 ->   freq:0.000000
22:27:52.846 ->   freq:0.000000
22:27:52.953 ->   freq:0.000000
22:27:53.060 ->   freq:0.000000
22:27:53.129 ->   freq:0.000000
22:27:53.237 ->   freq:0.000000
22:27:53.343 ->   freq:0.000000
22:27:53.444 ->   freq:0.000000
22:27:53.554 ->   freq:0.000000
22:27:53.628 -> Mainloop delay
22:27:53.661 ->   freq:403300000.000000
22:27:53.734 ->   freq:403300000.000000
22:27:53.838 ->   freq:403300000.000000
22:27:53.945 ->   freq:403300000.000000
22:27:54.052 ->   freq:403300000.000000
22:27:54.157 ->   freq:403300000.000000
22:27:54.232 ->   freq:403300000.000000
22:27:54.339 ->   freq:403300000.000000
22:27:54.450 ->   freq:403300000.000000
22:27:54.552 ->   freq:403300000.000000
22:27:54.628 -> Mainloop...
22:27:54.665 ->   freq:0.000000
22:27:54.733 ->   freq:0.000000
22:27:54.839 ->   freq:403300000.000000
22:27:54.941 ->   freq:403300000.000000
22:27:55.045 ->   freq:403300000.000000
22:27:55.154 ->   freq:0.000000
22:27:55.256 ->   freq:0.000000
22:27:55.360 ->   freq:0.000000
22:27:55.461 ->   freq:0.000000
22:27:55.560 ->   freq:0.000000
22:27:55.662 ->   freq:0.000000
22:27:55.730 ->   freq:0.000000
22:27:55.833 -> Mainloop delay
22:27:55.833 ->   freq:403300000.000000
22:27:55.937 ->   freq:403300000.000000
22:27:56.040 ->   freq:403300000.000000
22:27:56.142 ->   freq:403300000.000000
22:27:56.245 ->   freq:403300000.000000
22:27:56.346 ->   freq:403300000.000000
22:27:56.447 ->   freq:403300000.000000
22:27:56.549 ->   freq:403300000.000000
22:27:56.651 ->   freq:403300000.000000
22:27:56.754 ->   freq:403300000.000000
I.e., while loop() is accessing the display, the read operation the the core0 task most of the time returns 0. During the delay() operation in the main loop(), everything is fine. The problem disappears if I pin the sx1278 task to core 1, but in the larger real project, core 0 should be busy with other things, and core 0 should perform some time-critical background tasks, so moving the task to a different core is not a easy option (and, what I understand from the SPI documentation (https://docs.espressif.com/projects/esp ... aster.html), the SPI API should be thread safe. Well.. it says "thread safe for different devices on the same bus", but it is my interpretation that it then should also be thread safe for different devices on different busses). But maybe the problem is a different one???

dl9rdz
Posts: 2
Joined: Sun Oct 06, 2019 8:23 pm

Re: Concurrent VSPI and HSPI not working

Postby dl9rdz » Mon Oct 07, 2019 9:52 pm

After some more debugging, this appears more as a library problem than an ESP32 problem.

The TFT_22_ILI9225 library uses software SPI if the configuration uses non-standard pins for SPI. If I modify the library to use hardware SPI in all cases (which is fine on ESP32, but probably not on other platforms), everything seems to work as expected.

The software SPI code directly manipulates GPIO_OUT_REG, which might not be a good idea as it is not an atomic operation. So I guess I have found to root cause...

Who is online

Users browsing this forum: Basalt and 68 guests