SPI3 with esp32s2 not working in 3wire mode

jiauka
Posts: 1
Joined: Sat Dec 22, 2018 1:12 pm

SPI3 with esp32s2 not working in 3wire mode

Postby jiauka » Fri Dec 05, 2025 10:42 am

Hi!
I do have a 3wire device (STLED316S) connected to an esp32s2

When using SPI2_HOST as SPi host it wroks fine and I can read the bus, but when using SPI3_HOST it does not work. And I do need to use the SPI3_HOST, the #2 host is connected to another device.


Here is a simple single file test C source code in order to reproducde the problem, I do the test with idf 5.5.1

Code: Select all

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"

#define NBR_OF_DIGIT 6

#define STB_PIN 10
#define CLK_PIN 14
#define DATA_PIN 15

static spi_host_device_t _host=SPI3_HOST;
static spi_device_handle_t _spiDev;
// STLED316S driver object
#define SEVEN_DOT (0x80)

const uint8_t _digitTable[10] = { 
  0x3F, // 0
  0x06, // 1
  0x5B, // 2
  0x4F, // 3
  0x66, // 4
  0x6D, // 5
  0x7D, // 6
  0x07, // 7
  0x7F, // 8
  0x6F, // 9
};

const uint8_t _digitSymbols[] = { 
  0x08, // _
  0x40, // -
  0x30, // | L
  0x06, // | R
  SEVEN_DOT, // dot
  0x01, // _ up
};
/******************************************************************************/
/* Typedef                                                                    */
/******************************************************************************/
typedef enum
{
  DIGITall = 0,
  DIGITn1 = 1,
  DIGITn2 = 2,
  DIGITn3 = 3,
  DIGITn4 = 4,
  DIGITn5 = 5,
  DIGITn6 = 6
} DIGITnum_t;

static   uint8_t _dispDataBuffer[7]; //!< Memory buffer used for display
static  uint8_t _nbrOfDigit=6;
const uint8_t STLED316S_DISP_ON_CMD = 0x0D;
const uint8_t STLED316S_DISP_OFF_CMD = 0x0E;
const uint8_t STLED316S_DATA_WR = 0x00;
const uint8_t STLED316S_DATA_RD = 0x40;
const uint8_t STLED316S_ADDR_INC = 0x00;
const uint8_t STLED316S_ADDR_FIXED = 0x20;
const uint8_t STLED316S_CONF1_PAGE = 0x10;
const uint8_t STLED316S_DIG_PAGE = 0x00;
const uint8_t STLED316S_LED_PAGE = 0x08;
const uint8_t STLED316S_BRT_LED_PAGE = 0x18;
const uint8_t STLED316S_READ_PAGE = 0x08;
const uint8_t STLED316S_ADDR_LED_DATA = 0x00;
const uint8_t STLED316S_ADDR_KEY_DATA1 = 0x01;
const uint8_t STLED316S_ADDR_KEY_DATA2 = 0x02;
const uint8_t STLED316S_CONF_BRT_CONSTANT = 0x18;
const uint8_t STLED316S_CONF_BRT_VARIABLE = 0x00;

void initSTLed(void)
{
  esp_err_t err;
  spi_bus_config_t buscfg = {};
  buscfg.mosi_io_num = DATA_PIN;
  buscfg.miso_io_num = -1;           // Half-duplex uses MOSI for RX
  buscfg.sclk_io_num = CLK_PIN;
  buscfg.quadwp_io_num = -1;
  buscfg.quadhd_io_num = -1;
  buscfg.max_transfer_sz = 64;
  ESP_ERROR_CHECK(spi_bus_initialize(_host, &buscfg, SPI_DMA_CH_AUTO)); //SPI_DMA_CH_AUTO));
  
  spi_device_interface_config_t devcfg = {};
  devcfg.clock_speed_hz = 500000; //clock_speed_hz
  devcfg.mode = 0;
  devcfg.spics_io_num = STB_PIN;

  devcfg.queue_size = 4;
  devcfg.flags = SPI_DEVICE_HALFDUPLEX | SPI_DEVICE_NO_DUMMY |SPI_DEVICE_BIT_LSBFIRST|SPI_DEVICE_3WIRE;
  devcfg.command_bits = 8;
  devcfg.address_bits = 0;
  devcfg.dummy_bits = 0;
  
  err=spi_bus_add_device(_host, &devcfg, &_spiDev);
  printf("[initSTLed] %s %d\n",esp_err_to_name(err),STB_PIN);
}
uint8_t readData(uint8_t address)
{

  uint8_t rxData=0;
  spi_transaction_t t = {};
  t.cmd=STLED316S_DATA_RD | STLED316S_READ_PAGE | address;
  t.length = 0;
  t.rxlength = 8;
  t.tx_buffer = NULL;
  t.rx_buffer = &rxData;
  t.flags = 0; //SPI_TRANS_USE_TXDATA;

  esp_err_t ret = spi_device_polling_transmit(_spiDev, &t);

//  printf("%02x\n",rxData);
  return rxData;
}

uint16_t readKeyScan(void)
{
  uint16_t keyState = 0;
  keyState = readData(STLED316S_ADDR_KEY_DATA2) << 8;
  keyState |= readData(STLED316S_ADDR_KEY_DATA1);
  return keyState;
}

void writeData(uint8_t *data, uint8_t lenght)
{
  spi_transaction_t t = {};
  t.cmd=data[0];
  t.length = lenght > 1?(size_t)(lenght-1) * 8:0;
  t.tx_buffer = lenght > 1?&data[1]:NULL;
  t.flags = 0; //SPI_TRANS_USE_TXDATA;
  esp_err_t ret = spi_device_polling_transmit(_spiDev, &t);

}

/**
 * @brief Controls the LEDs of a digit with raw data
 *
 * @param DIGITnum : DIGITall->All or Digit number (DIGITn1..DIGITn6)
 * @param raw : b7->SEG8 / b6->SEG7 / b5->SEG6 / b4->SEG5 / b3->SEG4 / b2->SEG3 / b1->SEG2 / b0->SEG1
 */
void dispRAW(DIGITnum_t DIGITnum, uint8_t raw)
{
  uint8_t i;

  if (DIGITnum > _nbrOfDigit)
    return;

  if (DIGITnum == DIGITall)
    for (i = 0; i < _nbrOfDigit; i++)
      _dispDataBuffer[i + 1] = raw;
  else
    _dispDataBuffer[DIGITnum] = raw;

  _dispDataBuffer[0] = STLED316S_DATA_WR | STLED316S_DIG_PAGE | STLED316S_ADDR_INC;
//  writeData(&_dispDataBuffer[0], 1);
  writeData(&_dispDataBuffer[0], _nbrOfDigit+1);
}
void SevenSet(int value)
{
  dispRAW(DIGITn1, _digitTable[(value / 1000) % 10]);
  dispRAW(DIGITn2, _digitTable[(value / 100) % 10]);
  dispRAW(DIGITn3, _digitTable[(value/ 10) % 10]);
  dispRAW(DIGITn4, _digitTable[(value/ 1) % 10]);
}
void app_main(void)
{
    uint16_t v=1234;

    /* Print chip information */
    esp_chip_info_t chip_info;
    uint32_t flash_size;
    esp_chip_info(&chip_info);
    printf("This is %s chip with %d CPU core(s), %s%s%s%s, ",
           CONFIG_IDF_TARGET,
           chip_info.cores,
           (chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi/" : "",
           (chip_info.features & CHIP_FEATURE_BT) ? "BT" : "",
           (chip_info.features & CHIP_FEATURE_BLE) ? "BLE" : "",
           (chip_info.features & CHIP_FEATURE_IEEE802154) ? ", 802.15.4 (Zigbee/Thread)" : "");

    unsigned major_rev = chip_info.revision / 100;
    unsigned minor_rev = chip_info.revision % 100;
    printf("silicon revision v%d.%d, ", major_rev, minor_rev);
    if(esp_flash_get_size(NULL, &flash_size) != ESP_OK) {
        printf("Get flash size failed");
        return;
    }

    printf("%" PRIu32 "MB %s flash\n", flash_size / (uint32_t)(1024 * 1024),
           (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");

    printf("Minimum free heap size: %" PRIu32 " bytes\n", esp_get_minimum_free_heap_size());
    initSTLed();
    SevenSet(v++);
    for (int i = 10; i >= 0; i--) {
      SevenSet(v++);
      printf("key=%x\n",readKeyScan());
      printf("Restarting in %d seconds...\n", i);
      vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    printf("Restarting now.\n");
    fflush(stdout);
    esp_restart();
}

lichurbagan
Posts: 60
Joined: Thu Nov 13, 2025 3:20 pm

Re: SPI3 with esp32s2 not working in 3wire mode

Postby lichurbagan » Sun Dec 07, 2025 1:21 pm

Change your spi_bus_initialize call to pass 0 (no DMA). That often fixes half-duplex/3-wire read issues because DMA + half-duplex read+write is a problematic codepath in some IDF versions:

Replace:

Code: Select all

ESP_ERROR_CHECK(spi_bus_initialize(_host, &buscfg, SPI_DMA_CH_AUTO));
with:

Code: Select all

ESP_ERROR_CHECK(spi_bus_initialize(_host, &buscfg, 0)); // disable DMA
Docs show disabling DMA is an accepted workaround for half-duplex limitations (it limits max transfer length, but your transfers are tiny).
https://docs.espressif.com/projects/esp ... aster.html

Who is online

Users browsing this forum: Qwantbot and 4 guests