Use this forum to chat about hardware specific topics for the ESP8266 (peripherals, memory, clocks, JTAG, programming)

User avatar
By oakiedoke
#36945 Hi,

I'm trying to run ESP8266 as spi slave device and send data to it. I use HSPI. I used source files, which I found in SPI_slave example in Unofficial Development Kit for Espressif ESP8266 in viewtopic.php?f=9&t=820 (I also tested spi drivers from sdk but also with no success). After I run spi_init() function I send command, address and data from host as it is described in 8I-ESP8266__SPI-WiFi_Passthrough_1-Interrupt_Mode__EN_v0.1.pdf or 8J-ESP8266__SPIWiFi_Passthrough_2-Interrupt_Mode__EN_v1.0.pdf (for example when I want to write to module I send: command 0x02 + address 0x00 + 32 bytes of data). After I send 34 bytes I check content of SPI_W0-SPI_W7 registers, but there is still the same data as before transmission. I checked waveforms on oscilloscope, signals look properly I can't find where is problem.
I am also confused becouse in 8N-ESP8266__SPI_Reference__EN_v1.0.pdf there is different description of transmission
and settings of SPI. Clock is setted to be high in the idle state on the waveform, and packet to write data is 0x04 + 1 byte of data (to read data from slave command is 0x06 instead 0x03). Which is properly?

There is also few strange things. There is possibility to read SPI_FLASH_STATUS register using command 0x04 or 0x05 described in 8I-ESP8266__SPI-WiFi_Passthrough_1-Interrupt_Mode__EN_v0.1.pdf, but I can't find what is address of this register, it isn't defined in spi_register.h.

What is properly communication format and where is newest correct driver and documents described it?

I would be very grateful for any help.
User avatar
By disideris
#52182 I have the same problem.

I use an LPCxpresso 4367 as Spi master and an esp8266 as spi slave.

I use the NONOS SDK peripheral test example which has an spi slave example.
This example initializes spi slave mode and constructs with registers USER, USER1 and USER2 a format of 8bits command + 8 bits address + 256 bits data.


As i read from esp technical reference command 0x2 is the one that esp reads from master and stores data to W registers.

I send from master 34 bytes (1 byte=8bit command, 1 byte = 8bit address that i set to zero as indicated from technical reference document and 32 bytes = 256 bits data).

After this no data is stored to W registers.

I found a helpful post (http://d.av.id.au/blog/hardware-spi-hsp ... registers/) about the behaviour of registers but it is incomplete and only for master mode.
Does esspresif have an official datasheet with the behaviour of registers? How are we supposed to figure this out?
User avatar
By Grzesiek
#69360 Hello :)

Regarding document that you put here, so 8I-ESP8266__SPI-WiFi_Passthrough_1-Interrupt_Mode__EN_v0.1.pdf

I was able to get my data from master spi device (odroid xu4 under linux).

So first I set 2 bytes: 0x02, 0x00 and then 32 bytes of payload:
As I use esp-open-rtos here is my code:

Registers have almost the same values like its used in Arduino code.

ESP HSPI code:
Code: Select all#include "espressif/esp_common.h"
#include "FreeRTOS.h"
#include "spi.h"
// https://github.com/esp8266/Arduino/blob/master/libraries/SPISlave/src/hspi_slave.c
// https://github.com/esp8266/Arduino/blob/master/cores/esp8266/esp8266_peri.h

//SPI0, SPI1 & I2S Interupt Register
#define ESP8266_DREG(addr) *((volatile uint32_t *)(0x3FF00000+(addr)))
#define SPIIR ESP8266_DREG(0x20)
#define SPII0 4 //SPI0 Interrupt
#define SPII1 7 //SPI1 Interrupt
#define SPII2 9 //I2S Interrupt

static void (*_hspi_slave_rx_data_cb)(uint8_t * data, uint8_t len) = NULL;
static void (*_hspi_slave_tx_data_cb)(void) = NULL;
static void (*_hspi_slave_rx_status_cb)(uint32_t data) = NULL;
static void (*_hspi_slave_tx_status_cb)(void) = NULL;
static uint8_t _hspi_slave_buffer[33]={0};

void hspi_slave_setData(uint8_t *data, uint8_t len);

static IRAM void spi_slave_isr_handler(void){

  uint32_t status;
  uint32_t istatus;

  istatus = SPIIR;
  // SPI1 ISR
  if(istatus & (1 << SPII1)) {
    status = SPI(1).SLAVE0;
   
    // Disable interrupts.
    SPI(1).SLAVE0 &= ~(SPI_SLAVE0_TRANS_DONE_EN  | \
                       SPI_SLAVE0_WR_STA_DONE_EN | \
                       SPI_SLAVE0_RD_STA_DONE_EN | \
                       SPI_SLAVE0_WR_BUF_DONE_EN | \
                       SPI_SLAVE0_RD_BUF_DONE_EN);
    // Reset.
    SPI(1).SLAVE0 |= SPI_SLAVE0_SYNC_RESET;

    // Clear interrupts.
    SPI(1).SLAVE0 &= ~(SPI_SLAVE0_TRANS_DONE  | \
                       SPI_SLAVE0_WR_STA_DONE | \
                       SPI_SLAVE0_RD_STA_DONE | \
                       SPI_SLAVE0_WR_BUF_DONE | \
                       SPI_SLAVE0_RD_BUF_DONE);

    // Enable interrupts.
    SPI(1).SLAVE0 |=  (SPI_SLAVE0_TRANS_DONE_EN  | \
                       SPI_SLAVE0_WR_STA_DONE_EN | \
                       SPI_SLAVE0_RD_STA_DONE_EN | \
                       SPI_SLAVE0_WR_BUF_DONE_EN | \
                       SPI_SLAVE0_RD_BUF_DONE_EN);

    if ((status & SPI_SLAVE0_RD_BUF_DONE) && _hspi_slave_tx_data_cb){
      printf("SPI_SLAVE0_RD_BUF_DONE\n");
      _hspi_slave_tx_data_cb();
    }

    if((status & SPI_SLAVE0_RD_STA_DONE) && _hspi_slave_tx_status_cb){
      printf("SPI_SLAVE0_RD_STA_DONE\n");
      _hspi_slave_tx_status_cb();
    }
    if((status & SPI_SLAVE0_WR_STA_DONE) && _hspi_slave_rx_status_cb){
      printf("SPI_SLAVE0_WR_STA_DONE\n");
      uint32_t s = SPI(1).WSTATUS;
      _hspi_slave_rx_status_cb(s);
    }

    if((status & SPI_SLAVE0_WR_BUF_DONE) != 0 && (_hspi_slave_rx_data_cb)) {
        uint8_t i;
        uint32_t data;
        _hspi_slave_buffer[32] = 0;

        printf("DATA W0 %d\n",SPI(1).W[0]);
        printf("DATA W1 %d\n",SPI(1).W[1]);
        printf("DATA W2 %d\n",SPI(1).W[2]);
        printf("DATA W3 %d\n",SPI(1).W[3]);
        printf("DATA W4 %d\n",SPI(1).W[4]);
        printf("DATA W5 %d\n",SPI(1).W[5]);
        printf("DATA W6 %d\n",SPI(1).W[6]);
        printf("DATA W7 %d\n",SPI(1).W[7]);
       
        for(i=0; i<8; i++) {
          data = SPI(1).W[i];
          _hspi_slave_buffer[i<<2] = (data >> 24) & 0xff;
          _hspi_slave_buffer[(i<<2)+1] = (data >> 16) & 0xff;
          _hspi_slave_buffer[(i<<2)+2] = (data >> 8) & 0xff;
          _hspi_slave_buffer[(i<<2)+3] = (data & 0xff);
        }
        _hspi_slave_rx_data_cb(&_hspi_slave_buffer[0], 32);
    }

  // SPI0 ISR
  } else if(istatus & (1 << SPII0)) {
    printf("SPI0 ISR occure\n");
    SPI(0).SLAVE0 &= ~(0x3ff); // clear SPI ISR
  // I2S ISR
  } else if(istatus & (1 << SPII2)) {
    printf("SPII2 ISR occure\n");
  }
}

void spi_slave_init(uint8_t bus, void (*tx_data_cb)(void), void (*tx_status_cb)(void), void (*rx_status_cb)(uint32_t), void (*rx_data_cb)(uint8_t *, uint8_t)){
  //clear bit9,bit8 of reg PERIPHS_IO_MUX
  //bit9 should be cleared when HSPI clock doesn't equal CPU clock
  //bit8 should be cleared when SPI clock doesn't equal CPU clock
  ////WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105); //clear bit9//TEST
  if(bus==0){
    gpio_set_iomux_function(6, IOMUX_FUNC(1));    // SPICLK - IOMUX_GPIO6_FUNC_SD_CLK
    gpio_set_iomux_function(7, IOMUX_FUNC(1));    // SPIQ   - IOMUX_GPIO7_FUNC_SD_DATA0
    gpio_set_iomux_function(8, IOMUX_FUNC(1));    // SPID   - IOMUX_GPIO8_FUNC_SD_DATA1
    //gpio_set_iomux_function(11, IOMUX_FUNC(1)); // SPICS0 - IOMUX_GPIO11_FUNC_SD_CMD 

  // Configure MUX to allow HSPI
  }else if(bus==1){
    // WRITE_PERI_REG(PERIPHS_IO_MUX, 0x105);
    // SCK, MOSI, MISO and CS
    gpio_set_iomux_function(12, IOMUX_FUNC(2));   // IOMUX_GPIO12_FUNC_MTDI
    gpio_set_iomux_function(13, IOMUX_FUNC(2));   // IOMUX_GPIO13_FUNC_MTCK
    gpio_set_iomux_function(14, IOMUX_FUNC(2));   // IOMUX_GPIO14_FUNC_MTMS
    //gpio_set_iomux_function(15, IOMUX_FUNC(2)); // IOMUX_GPIO15_FUNC_MTDO
  }

  if (tx_data_cb) _hspi_slave_tx_data_cb = tx_data_cb;
  if (tx_status_cb) _hspi_slave_tx_status_cb = tx_status_cb;
  if (rx_status_cb) _hspi_slave_rx_status_cb = rx_status_cb;
  if (rx_data_cb) _hspi_slave_rx_data_cb = rx_data_cb;

  SPI(bus).SLAVE0 = SPI_SLAVE0_MODE | SPI_SLAVE0_WR_RD_BUF_EN | \
  SPI_SLAVE0_WR_BUF_DONE_EN | SPI_SLAVE0_RD_BUF_DONE_EN | \
  SPI_SLAVE0_WR_STA_DONE_EN | SPI_SLAVE0_RD_STA_DONE_EN | \
  SPI_SLAVE0_TRANS_DONE_EN;

  // MISO pahse uses W8-W15, SPI_USR_DIN_HIGHPART |
  // COMMAND pahse, SPI_USR_COMMAND
  // SPI Slave Edge (0:falling, 1:rising), SPI_CK_I_EDGE
  SPI(bus).USER0 = SPI_USER0_MISO_HIGHPART | SPI_USER0_COMMAND | SPI_USER0_CLOCK_IN_EDGE;
  SPI(bus).CLOCK = 0;

  // 4 bit in SPIxU2 default 7 (8bit)
  SPI(bus).USER2 = ((0x7 & SPI_USER2_COMMAND_BITLEN_M) << SPI_USER2_COMMAND_BITLEN_S);

  // 956243056 = 0x38ff1c70
  SPI(bus).SLAVE1 = 0x38ff1c70;

  // Set 8 bit slave recieve buffer length, the buffer is SPI_FLASH_C0-C7.
  // Set 8 bit slave status register, which is the low 8 bit of register "SPI_FLASH_STATUS".
/*
  SPI(bus).SLAVE1 |= ((0xff & SPI_SLAVE1_BUF_BITLEN_M) << SPI_SLAVE1_BUF_BITLEN_S) | \
  ((0x7&SPI_SLAVE1_STATUS_BITLEN_M)<<SPI_SLAVE1_STATUS_BITLEN_S) | \
  ((0x7&SPI_SLAVE1_WR_ADDR_BITLEN_M)<<SPI_SLAVE1_WR_ADDR_BITLEN_S) | \
  ((0x7&SPI_SLAVE1_RD_ADDR_BITLEN_M)<<SPI_SLAVE1_RD_ADDR_BITLEN_S);
  */

  SPI(bus).PIN = (1 << 19);  // What this is ??

  // Two lines below is to configure spi timing.
  // SPI(bus).CTRL2 |= ( (0x2&SPI_CTRL2_MOSI_DELAY_NUM_M) << SPI_CTRL2_MOSI_DELAY_NUM_S);
 
  SPI(bus).CMD = SPI_CMD_USR;

  // Register isr function, which contains spi, hspi and i2s events
  // Set SPI handler
  _xt_isr_attach(ETS_SPI_INUM, spi_slave_isr_handler);
  _xt_isr_unmask(1 << ETS_SPI_INUM);

  uint8_t msg[32]={0};
  msg[0]=0x81;
  msg[1]=0x82;
  msg[2]=0x83;
  msg[3]=0x84;
  msg[4]=0x85;
  msg[5]=0x86;
  msg[6]=0x87;
  msg[7]=0x88;
  hspi_slave_setData(msg, 32);
}

#define ESP8266_REG(addr) *((volatile uint32_t *)(0x60000000+(addr)))
#define SPI1W(p) ESP8266_REG(0x140 + ((p & 0xF) * 4))

void hspi_slave_setData(uint8_t *data, uint8_t len){
    uint8_t i;
    uint32_t out = 0;
    uint8_t bi = 0;
    uint8_t wi = 8;

    for(i=0; i<32; i++) {
        out |= (i<len)?(data[i] << (bi * 8)):0;
        bi++;
        bi &= 3;
        if(!bi) {
            SPI1W(wi) = out;
            out = 0;
            wi++;
        }
    }
}


Somewhere in your main app:

Code: Select all
void on_hspi_data(uint8_t *data, uint8_t len){

  printf("HSPI got data:\n");
  uint32_t ret = 0;

  for (ret = 0; ret < len; ret++) {
    if (!(ret % 8)) printf(" ");
    printf("%.2X ", data[ret]);
  }
  printf("\n");
}

void on_hspi_tx_data(void){}
void on_hspi_tx_status_cb(void){}
void on_hspi_rx_status_cb(uint32_t status){}

spi_slave_init(1, &on_hspi_tx_data,&on_hspi_tx_status_cb,&on_hspi_rx_status_cb, &on_hspi_data);



Linux SPI test code:
Code: Select all#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

static void pabort(const char *s){
  perror(s);
  abort();
}

static const char *device = "/dev/spidev1.0";
static uint8_t mode;
static uint8_t bits = 8;
static uint32_t speed = 18000000; // 18MHz
static uint16_t delay = 8000;     // 8ms for 4 iteration.

static void efekt0(int fd){
  int ret;
  uint8_t tx[] = {0x02, 0x00};
  uint8_t rx[ARRAY_SIZE(tx)] = {0, };

  struct spi_ioc_transfer tr = {
    .tx_buf = (unsigned long)tx,
    .rx_buf = (unsigned long)rx,
    .len = ARRAY_SIZE(tx),
    .delay_usecs = delay,
    .speed_hz = speed,
    .bits_per_word = 8,
  };

  ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);

  if (ret < 1)
    pabort("can't send spi message");

  for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
    if (!(ret % 6))
      puts("");
      printf("%.2X ", rx[ret]);
    }
  puts("");
}

static void efekt1(int fd){
  int ret;
  uint8_t tx[] = {
    0x21, 0x22, 0x23, // R
    0x24, 0x25, 0x26, // G
    0x27, 0x28, 0x29, // B
    0x30, 0x31, 0x32, // W
    0x33, 0x34, 0x35, // B
    0x36, 0x37, 0x38, // B
    0x39, 0x40, 0x41, // B
    0x42, 0x43, 0x44, // B
    0x45, 0x46, 0x47, // B
    0x48, 0x49, 0x50, // B
    0x51, 0x52
  };
  //uint8_t tx[] = {0x02, 0x11, 0x34};
  uint8_t rx[ARRAY_SIZE(tx)] = {0, };

  struct spi_ioc_transfer tr = {
    .tx_buf = (unsigned long)tx,
    .rx_buf = (unsigned long)rx,
    .len = ARRAY_SIZE(tx),
    .delay_usecs = delay,
    .speed_hz = speed,
    .bits_per_word = 32,
  };

  ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);

  if (ret < 1)
    pabort("can't send spi message");

  for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
    if (!(ret % 6))
      puts("");
      printf("%.2X ", rx[ret]);
    }
  puts("");
}
int main(int argc, char *argv[]){
  int ret = 0;
  int fd;

  fd = open(device, O_RDWR);
  if (fd < 0)
    pabort("can't open device");

  // spi mode
  ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
  if (ret == -1)
    pabort("can't set spi mode");

  ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
  if (ret == -1)
    pabort("can't get spi mode");

  // bits per word
  ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
  if (ret == -1)
    pabort("can't set bits per word");

  ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
  if (ret == -1)
    pabort("can't get bits per word");

  // max speed hz
  ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
  if (ret == -1)
    pabort("can't set max speed hz");

  ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
  if (ret == -1)
    pabort("can't get max speed hz");

  printf("spi mode: %d\n", mode);
  printf("bits per word: %d\n", bits);
  printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);

//  efekt0(fd);
  for (ret = 0; ret<4; ret++){
    efekt0(fd);
    efekt1(fd);
  }
  //efekt0(fd);
  //efekt2(fd);

  close(fd);

  return ret;
}



Its still in progress but at least receiving works more or less fine.
I send 4 times the same data and for 18MHz clock I was forced to set 8ms delay.

Result on esp side looks like:
Code: Select allFour time of:
DATA W0 555885348
DATA W1 623257384
DATA W2 691024178
DATA W3 859059510
DATA W4 926431552
DATA W5 1094861636
DATA W6 1162233672
DATA W7 1230000466
HSPI got data:
 21 22 23 24 25 26 27 28  29 30 31 32 33 34 35 36  37 38 39 40 41 42 43 44  45 46 47 48 49 50 51 52]



In addition I prepare also such table where describe what need to be send from master to slave device for different command:
Code: Select all/*------------------------------------------------------------------------------------------------------------------------------*
 |                         Data workflow bettwen master spi device and this hspi slave driver!                                  |
 |------------------------------------------------------------------------------------------------------------------------------|
 |  Command name | Bit set in status registry | First request                        | Second request                           |
 |------------------------------------------------------------------------------------------------------------------------------|
 |  write status |   SPI_SLAVE0_WR_STA_DONE   | 0x01 + status (2bytes, 8 bits/word)  | None                                     |
 |------------------------------------------------------------------------------------------------------------------------------|
 |  write data   |   SPI_SLAVE0_WR_BUF_DONE   | 0x02 + 0x00   (2bytes, 8 bits/word)  | 32 bytes of data (32bytes, 32 bits/word) |
 |------------------------------------------------------------------------------------------------------------------------------|
 |  read data    |   SPI_SLAVE0_RD_BUF_DONE   | 0x03 + 0x00   (2bytes, 8 bits/word)  | 32 bytes of data (32bytes, 8 bits/word)  |
 |------------------------------------------------------------------------------------------------------------------------------|
 |  read status  |   SPI_SLAVE0_RD_STA_DONE   | 0x04 + 0x00   (2bytes, 8 bits/word)  | None                                     |
 *------------------------------------------------------------------------------------------------------------------------------*/