Hello.
We use ESP-WROOM-32 module (ESP32 rev 0) and ESP-IDF v3.1/
We use SPI in slave mode.
We have encountered the following issue.
We continuously launch SPI transfers from master
(at least 300 times per second).
We have tested two different transfer sizes
(64 bytes with and without DMA, 1536 bytes with DMA).
When necessay amount of data is clocked into ESP32,
interrupt SPI_TRANS_DONE_INT occurs more than once:
a) at least twice after full packet is transferred but before CS signal rises.
b) and at least once after CS signal rises.
So there are at least two extra interrupts.
We consider it is not normal.
And maybe that wouldn't bother us,
but we have to fill transfer queue continuously with outgoing packets.
Because of above-mentioned extra interrupts
at least two transfer descriptors are lost by the SPI driver on each actual SPI transfer
(on each interrupt new transfer descriptor is taken from the queue,
but only the last transfer really occurs).
We tried to find a workaround.
And we processed only one interrupt of the group and ignored all others.
But in this case after some (random) time of operation
SPI slave starst failing to receive packets from master
(only first byte of the transfer is received).
What can be done to fix it?
Best regards,
Pavel
SPI slave: multiple TRANS_DONE interrupts
SPI slave: multiple TRANS_DONE interrupts
Last edited by PavelA on Tue Jul 24, 2018 8:09 am, edited 1 time in total.
-
- Posts: 9764
- Joined: Thu Nov 26, 2015 4:08 am
Re: SPI slave: multile TRANS_DONE interrupts
Do you have some source code you can show us? If this happened on a hardware level, I'd think our SPI driver also wouldn't work, but I haven't seen issues with that yet, so I imagine you're doing something odd in your sources.
Re: SPI slave: multiple TRANS_DONE interrupts
Here is our example.
In it we do not queue multiple transfers.
Just initialize SPI and launch transfers one by one.
We used it to investigate issue with multiple SPI interrupts on each transfer.
In it we do not queue multiple transfers.
Just initialize SPI and launch transfers one by one.
We used it to investigate issue with multiple SPI interrupts on each transfer.
Code: Select all
#define SPI_HOST VSPI_HOST
#define GPIO_MOSI 23
#define GPIO_MISO 19
#define GPIO_SCLK 18
#define GPIO_CS 5
#define INTERNAL_QSIZE 60
#define SPI_BUF_SIZE 1536
#define SPI_PKT_SOF 0x55AA
DRAM_ATTR uint8_t spi_rx_static_buf[SPI_BUF_SIZE];
DRAM_ATTR uint8_t spi_tx_static_buf[SPI_BUF_SIZE];
TaskHandle_t xHandleSlaveTask = NULL;
static int pkt_cnt = 0;
static int fail_cnt = 0;
struct spi_pkt {
uint16_t sof;
uint16_t ver;
uint16_t cmd;
uint16_t length;
uint16_t state;
uint8_t free_txbuf;
uint8_t free_rxbuf;
uint8_t reserved[4];
};
/**
* @brief Main spi task.
*
* @param pvParameters
*/
void spi_slave_task( void * pvParameters)
{
int cnt = 0;
int ret = 0;
spi_slave_transaction_t dt;
ESP_LOGI("spi_slave_task","");
dt.length = SPI_BUF_SIZE * 8;
dt.rx_buffer = spi_rx_static_buf;
dt.tx_buffer = spi_tx_static_buf;
while(1)
{
cnt++;
ret = spi_slave_queue_trans(SPI_HOST, &dt, portMAX_DELAY);
if (ret != ESP_OK)
{
ESP_LOGI("spi_slave_queue_trans", "%d ", ret);
}
spi_slave_get_trans_result(SPI_HOST, &ret_tr, portMAX_DELAY);
if ( dt.rx_buffer ) {
struct spi_pkt* pkt = (struct spi_pkt*) dt.rx_buffer;
if (pkt->sof == SPI_PKT_SOF){
pkt_cnt++;
} else {
fail_cnt++;
}
}
}
}
void spi_slave_drv_init()
{
esp_err_t ret;
//Configuration for the SPI bus
spi_bus_config_t buscfg={
.mosi_io_num=GPIO_MOSI,
.miso_io_num=GPIO_MISO,
.sclk_io_num=GPIO_SCLK
};
//Configuration for the SPI slave interface
spi_slave_interface_config_t slvcfg={
.mode = 0,
.spics_io_num = GPIO_CS,
.queue_size = INTERNAL_QSIZE,
.flags = 0,
};
//Configure handshake line as output
//Enable pull-ups on SPI lines so we don't detect rogue pulses when no master is connected.
gpio_set_pull_mode(GPIO_MOSI, GPIO_PULLUP_ONLY);
gpio_set_pull_mode(GPIO_SCLK, GPIO_PULLUP_ONLY);
gpio_set_pull_mode(GPIO_CS, GPIO_PULLUP_ONLY);
//Initialize SPI slave interface
ret = spi_slave_initialize(SPI_HOST, &buscfg, &slvcfg, 1);
assert(ret==ESP_OK);
ESP_LOGI("SPI slave init","");
BaseType_t xReturned;
xReturned = xTaskCreate(&spi_slave_task, "spi_slave_task", 5 * configMINIMAL_STACK_SIZE, NULL, 5, &xHandleSlaveTask);
if( xReturned != pdPASS )
{
ESP_LOGE ("xTaskCreate", "spi_slave_task: %d", xReturned);
}
}
//Main application
void app_main()
{
spi_slave_drv_init();
}
-
- Posts: 9764
- Joined: Thu Nov 26, 2015 4:08 am
Re: SPI slave: multiple TRANS_DONE interrupts
...Your example doesn't match with what you say, I see just you using the SPI slave driver as normal. I can see no place where you hook the TRANS_DONE interrupt. Does this example already exhibit what you state, and if so, how?
Re: SPI slave: multiple TRANS_DONE interrupts
Yes, in this example we use SPI driver as normal, but even with it we already see multiple TRANS_DONE interrupts.
We also added gpio_set_level() calls on a dedicated GPIO to spi_intr() in spi_slave.c (set "1" after TRANS_DONE bit check and "0" on exit of spi_intr() procedure) to see when interrupts are processed.
We also added gpio_set_level() calls on a dedicated GPIO to spi_intr() in spi_slave.c (set "1" after TRANS_DONE bit check and "0" on exit of spi_intr() procedure) to see when interrupts are processed.
-
- Posts: 9764
- Joined: Thu Nov 26, 2015 4:08 am
Re: SPI slave: multiple TRANS_DONE interrupts
Yes, this is because the driver is optimized to handle multiple queued transactions: it will normally queue the next transaction in the queue in the interrupt handler. However, in your case, you only have one transfer queued and you wait for that transfer to complete before queueing the next one. When the SPI hardware is done with this transfer, the interrupt handler will trigger and see that there's nothing more in the queue, and instead of acknowledging the interrupt to make it go away (as in spi_slave.c:395), it will simply disable the interrupts; the flag is still set but won't generate an interrupt. As soon as you queue a new transfer, the queueing code won't duplicate the logic to handle the new transfer, but will simply re-enable interrupts (spi_slave.c:266), which makes the interrupt handler trigger a second time. This time, it notices there's a new transfer and happily handles it. In other words: the multiple interrupts here are an artifact of the implementation of the driver when used this way and are intentional, and not a hardware feature/flaw.
Re: SPI slave: multiple TRANS_DONE interrupts
In the example I provided we queue transfers one by one.
But in our real application we have to fill transfer queue continuously.
There are usually more than one queued transfers at each particular moment in time.
And several of them a read from the queue
while several interrupts occur after one transfer is done.
But only one new transfer (which is read from the queue last) really follows.
So we just loose a number of queued descriptors.
But in our real application we have to fill transfer queue continuously.
There are usually more than one queued transfers at each particular moment in time.
And several of them a read from the queue
while several interrupts occur after one transfer is done.
But only one new transfer (which is read from the queue last) really follows.
So we just loose a number of queued descriptors.
-
- Posts: 9764
- Joined: Thu Nov 26, 2015 4:08 am
Re: SPI slave: multiple TRANS_DONE interrupts
Again, do you have code we can look at?
Who is online
Users browsing this forum: No registered users and 32 guests