I try for some time now to design a fast parallel data transfer with an ESP32S3-WROOM1, but it seems to be a tough nut to crack with an ESP...
I need flexible bit width of 4/8-bit to link to a FPGA. Because the number of free IO's we can use for that and due to the overall limitation in available IO's, i try to set up the interface for the worst case with 4-bit. The interface should be bidirectional.
It should be possible to use the SPI in QUAD mode as slave for fast data transmission from FPGA to ESP with use of DMA.
SPI
This is what I have tried so far. Wired thing is that the interface is initialized always with only one data line. All whole receive buffer
is getting filled up with the data received in the first Data line (MOSI). All the othet lines are not represented as depicted in the "Table 30-6. Bit Order Control in GP-SPI Master and Slave Modes" on page 1096 of the ESP32S3 technical refence manual (write /read bit order when using dual/quad/octal modes).
Unfortunately I cant get it to work. After performing spi_slave_initialize(), I checked the SPI2 Registers (I use SPI2 peripheral for that) and see inside SPI_CTRL_REG and SPI_USER_REG that the relevant bits are not set.

I simulate the CLK and CS signal by using another task that simulated this signals with two GPIO's. All four data lines are only "feeded" with pull-up or pull-down via software. The first pull resistor is toggeled in the task spi_master_sim_task() from high to low, generating a bit patern that can be seen in the rx data (0x55). But in my understanding not all received data should be 0x55, this is only the first data line. If the first data line is toggling and the rest of the data lines are reo it sould be something like 0x80, 0x80...
Am I making a mistake in initializing the SPI module?

Thanks very much for any suggestion on how to fix this!!
Best regards and merry Christmas!
Benedikt
This is the code I use (240Mhz, FreeRTOS 1000Hz):
Code: Select all
#include <stdlib.h>
#include <string.h>
#include "driver/rtc_io.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_slave.h"
#include "driver/gpio.h"
#include "esp_log.h"
#define PIN_NUM_FSPID_Data0 11 /* GPIO for SPI Data0 (MOSI in master mode, or Data0 in Quad SPI mode) */
#define PIN_NUM_FSPIQ_Data1 13 /* GPIO for SPI Data1 (MISO in master mode, or Data1 in Quad SPI mode) */
#define PIN_NUM_FSPIWP_Data2 14 /* GPIO for SPI Data2 (WP in Quad SPI mode) */
#define PIN_NUM_FSPIHD_Data3 9 /* GPIO for SPI Data3 (HD in Quad SPI mode) */
#define PIN_NUM_FSPICLK 12 /* GPIO for SPI Clock */
#define PIN_NUM_FSPICS0 10 /* GPIO for SPI Chip Select (CS) */
// this is used to simulate a spio data traffic and test the reception of the spi-slave config
#define PIN_CLK_OUT 4
#define PIN_CS_OUT 5
#define CLK_FREQUENCY_HZ 1000
static const char TAG[] = "main";
/* DMA buffer for received data */
#define MAX_TRANSFER_SIZE 20 /* Maximum transfer size for one DMA transaction (in bytes) */
spi_slave_transaction_t trans = { 0 };
/* Allocate DMA-compatible buffer for receiving data */
WORD_ALIGNED_ATTR uint8_t rx_buffer[MAX_TRANSFER_SIZE] = {0};
WORD_ALIGNED_ATTR uint8_t tx_buffer[MAX_TRANSFER_SIZE] = {0};
/* ISR handler for SPI DMA transactions */
static void spi_slave_isr_post_setup(spi_slave_transaction_t *trans) {
__asm__ volatile("nop");
}
static void spi_slave_isr_post_trans(spi_slave_transaction_t *trans) {
__asm__ volatile("nop");
}
void spi_master_sim_task(void *pvParameter) {
static uint8_t cntr = 0;
static uint8_t data = 1;
gpio_set_level(PIN_CS_OUT, 0);
vTaskDelay(pdMS_TO_TICKS(10));
while (1) {
if(cntr < MAX_TRANSFER_SIZE * 8){ // send 20 bytes of data
// data is changed on the negative edge of SCK and sampled on the positive edge.
vTaskDelay(pdMS_TO_TICKS(5));
gpio_set_level(PIN_CLK_OUT, 1);
vTaskDelay(pdMS_TO_TICKS(5));
gpio_set_pull_mode(PIN_NUM_FSPID_Data0, data);
data = !data;
gpio_set_pull_mode(PIN_NUM_FSPIQ_Data1, GPIO_PULLDOWN_ONLY);
gpio_set_pull_mode(PIN_NUM_FSPIWP_Data2, GPIO_PULLDOWN_ONLY);
gpio_set_pull_mode(PIN_NUM_FSPIHD_Data3, GPIO_PULLDOWN_ONLY);
vTaskDelay(pdMS_TO_TICKS(5));
gpio_set_level(PIN_CLK_OUT, 0);
vTaskDelay(pdMS_TO_TICKS(5));
cntr++;
}else{
cntr=0;
gpio_set_level(PIN_CS_OUT, 1);
vTaskDelay(3000);
gpio_set_level(PIN_CS_OUT, 0);
}
}
}
void app_main(void) {
esp_err_t ret;
ESP_LOGI(TAG, "Initializing SPI as slave...");
/* Configure SPI slave interface */
spi_bus_config_t buscfg = {
.data0_io_num = PIN_NUM_FSPID_Data0, /* Pin for SPI Data0 */
.data1_io_num = PIN_NUM_FSPIQ_Data1, /* Pin for SPI Data1 */
.data2_io_num = PIN_NUM_FSPIWP_Data2, /* Pin for SPI Data2 */
.data3_io_num = PIN_NUM_FSPIHD_Data3, /* Pin for SPI Data3 */
.data4_io_num = -1,
.data5_io_num = -1,
.data6_io_num = -1,
.data7_io_num = -1,
.sclk_io_num = PIN_NUM_FSPICLK, /* Pin for SPI Clock */
.flags = SPICOMMON_BUSFLAG_QUAD, /* Quad SPI mode */
.isr_cpu_id = 0,
.max_transfer_sz = MAX_TRANSFER_SIZE, /* Max DMA transfer size in bytes */
};
/* SPI slave interface configuration */
spi_slave_interface_config_t slvcfg = {
.spics_io_num = PIN_NUM_FSPICS0, /* Pin for SPI CS (Chip Select) */
.flags = 0, /* Default flags */
.queue_size = MAX_TRANSFER_SIZE, /* Transaction queue size */
.mode = 1, /* SPI mode (CPOL, CPHA) */
.post_setup_cb = spi_slave_isr_post_setup, /* ISR for post-setup actions */
.post_trans_cb = spi_slave_isr_post_trans, /* ISR for post-transaction actions */
};
// Enable pull-ups on SPI lines to simulate data when no master is connected on data lines.
gpio_set_pull_mode(PIN_NUM_FSPID_Data0, GPIO_PULLDOWN_ONLY);
gpio_set_pull_mode(PIN_NUM_FSPIQ_Data1, GPIO_PULLUP_ONLY);
gpio_set_pull_mode(PIN_NUM_FSPIWP_Data2, GPIO_PULLDOWN_ONLY);
gpio_set_pull_mode(PIN_NUM_FSPIHD_Data3, GPIO_PULLUP_ONLY);
gpio_set_pull_mode(PIN_NUM_FSPICLK, GPIO_PULLUP_ONLY);
gpio_set_pull_mode(PIN_NUM_FSPICS0, GPIO_PULLUP_ONLY);
// We use only CS and CLK, initialize it here
gpio_set_direction(PIN_CLK_OUT, GPIO_MODE_OUTPUT);
gpio_set_direction(PIN_CS_OUT, GPIO_MODE_OUTPUT);
gpio_set_level(PIN_CLK_OUT, 0);
gpio_set_level(PIN_CS_OUT, 1);
/* Initialize and attach SPI slave interface */
ret = spi_slave_initialize(SPI2_HOST, &buscfg, &slvcfg, SPI_DMA_CH_AUTO);
ESP_ERROR_CHECK(ret);
memset(&trans, 0, sizeof(trans));
ESP_LOGI(TAG, "SPI slave ready to receive data.");
// Create Test task
xTaskCreate(spi_master_sim_task, "clock_task", 2048, NULL, 5, NULL);
while (1)
{
trans.rx_buffer = rx_buffer;
trans.tx_buffer = tx_buffer;
trans.length = MAX_TRANSFER_SIZE * 8; /* Length in bits, send only 20 bytes for testing */
ret = spi_slave_transmit(SPI2_HOST, &trans, portMAX_DELAY);
ESP_ERROR_CHECK(ret);
ESP_LOGI(TAG, "Transaction complete.");
}
}