how to reduce interrupt latency?
Posted: Tue Jul 26, 2022 1:29 am
greetings
sdk: IDF V4.4, hd:ESP32-S3
when a pulse is detected by one io, an spi transaction will be triggered. Through oscillometer I found the interval between the pulse and spi cs signal was as much as 100~200 us, while this thread says the interrupt latency can be reduced to about 2 us. Is there something I missed? [viewtopic.php?t=422&sid=f8aef824aa6a0c2 ... d6efb97ee0]
my code(modified based on examples/peripherals/spi_slave/sender):
sdk: IDF V4.4, hd:ESP32-S3
when a pulse is detected by one io, an spi transaction will be triggered. Through oscillometer I found the interval between the pulse and spi cs signal was as much as 100~200 us, while this thread says the interrupt latency can be reduced to about 2 us. Is there something I missed? [viewtopic.php?t=422&sid=f8aef824aa6a0c2 ... d6efb97ee0]
my code(modified based on examples/peripherals/spi_slave/sender):
Code: Select all
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"
#include "lwip/igmp.h"
#include "esp_wifi.h"
#include "esp_system.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "soc/rtc_periph.h"
#include "driver/spi_master.h"
#include "esp_log.h"
#include "esp_spi_flash.h"
#include "driver/gpio.h"
#include "esp_intr_alloc.h"
/*
SPI sender (master) example.
This example is supposed to work together with the SPI receiver. It uses the standard SPI pins (MISO, MOSI, SCLK, CS) to
transmit data over in a full-duplex fashion, that is, while the master puts data on the MOSI pin, the slave puts its own
data on the MISO pin.
This example uses one extra pin: GPIO_HANDSHAKE is used as a handshake pin. The slave makes this pin high as soon as it is
ready to receive/send data. This code connects this line to a GPIO interrupt which gives the rdySem semaphore. The main
task waits for this semaphore to be given before queueing a transmission.
*/
/*
Pins in use. The SPI Master can use the GPIO mux, so feel free to change these if needed.
*/
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
#define GPIO_HANDSHAKE 11
#define GPIO_MOSI 12
#define GPIO_MISO 13
#define GPIO_SCLK 15
#define GPIO_CS 14
#elif CONFIG_IDF_TARGET_ESP32C3
#define GPIO_HANDSHAKE 3
#define GPIO_MOSI 7
#define GPIO_MISO 2
#define GPIO_SCLK 6
#define GPIO_CS 10
#endif //CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
#define SENDER_HOST SPI2_HOST
// #ifdef CONFIG_IDF_TARGET_ESP32
// #define SENDER_HOST HSPI_HOST
// #elif defined CONFIG_IDF_TARGET_ESP32S2
// #define SENDER_HOST SPI2_HOST
// #elif defined CONFIG_IDF_TARGET_ESP32C3
// #define SENDER_HOST SPI2_HOST
// #endif
//The semaphore indicating the slave is ready to receive stuff.
static xQueueHandle rdySem;
/*
This ISR is called when the handshake line goes high.
*/
static void IRAM_ATTR gpio_handshake_isr_handler(void* arg)
{
//Sometimes due to interference or ringing or something, we get two irqs after eachother. This is solved by
//looking at the time between interrupts and refusing any interrupt too close to another one.
static uint32_t lasthandshaketime;
uint32_t currtime=esp_cpu_get_ccount();
uint32_t diff=currtime-lasthandshaketime;
if (diff<240000) return; //ignore everything <1ms after an earlier irq
lasthandshaketime=currtime;
//Give the semaphore.
BaseType_t mustYield=false;
xSemaphoreGiveFromISR(rdySem, &mustYield);
if (mustYield) portYIELD_FROM_ISR();
}
//Main application
WORD_ALIGNED_ATTR char sendbuf[1024] = {0};
WORD_ALIGNED_ATTR char recvbuf[1024] = {0};
void app_main(void)
{
esp_err_t ret;
spi_device_handle_t handle;
//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,
.quadwp_io_num=-1,
.quadhd_io_num=-1
};
//Configuration for the SPI device on the other side of the bus
spi_device_interface_config_t devcfg={
.command_bits=0,
.dummy_bits=0,
.clock_speed_hz=800000,//caution: clk must NOT be too fast.
.duty_cycle_pos=128, //50% duty cycle
.mode=0,
.spics_io_num=GPIO_CS,
//.spics_io_num = -1,
.cs_ena_posttrans=3, //Keep the CS low 3 cycles after transaction, to stop slave from missing the last bit when CS has less propagation delay than CLK
.cs_ena_pretrans=16,
.queue_size=3
};
//GPIO config for the handshake line.
gpio_config_t io_conf={
.intr_type=GPIO_INTR_NEGEDGE,
.mode=GPIO_MODE_INPUT,
.pull_up_en=1,
.pin_bit_mask=(1<<GPIO_HANDSHAKE)
};
int n=0;
spi_transaction_t t;
memset(&t, 0, sizeof(t));
//Create the semaphore.
rdySem=xSemaphoreCreateBinary();
//Set up handshake line interrupt.
gpio_config(&io_conf);
gpio_install_isr_service(0);
gpio_set_intr_type(GPIO_HANDSHAKE, GPIO_INTR_NEGEDGE);
gpio_isr_handler_add(GPIO_HANDSHAKE, gpio_handshake_isr_handler, NULL);
//Initialize the SPI bus and add the device we want to send stuff to.
ret=spi_bus_initialize(SENDER_HOST, &buscfg, SPI_DMA_CH_AUTO);
assert(ret==ESP_OK);
ret=spi_bus_add_device(SENDER_HOST, &devcfg, &handle);
assert(ret==ESP_OK);
for (int temp=0; temp<1024; temp++)
{
sendbuf[temp] = temp%100;
}
//Assume the slave is ready for the first transmission: if the slave started up before us, we will not detect
//positive edge on the handshake line.
//xSemaphoreGive(rdySem);
while(1) {
// int res = snprintf(sendbuf, sizeof(sendbuf),
// "Sender, transmission no. %04i. Last time, I received: \"%s\"", n, recvbuf);
// if (res >= sizeof(sendbuf)) {
// printf("Data truncated\n");
// }
// t.length=sizeof(sendbuf)*8;
memset(recvbuf, 0, sizeof(recvbuf));
t.length = 197*8; //msp430 buf length,in bits.
// memset(sendbuf, n, sizeof(se210ndbuf));
t.tx_buffer=sendbuf;
t.rx_buffer=recvbuf;
//Wait for slave to be ready for next byte before sending
xSemaphoreTake(rdySem, portMAX_DELAY); //Wait until slave is ready
printf("\nReceived %d interrupt.\n", n);
ret=spi_device_transmit(handle, &t);
for(int temp=0; temp<200; temp++)
{
printf("%02x ", recvbuf[temp]);
if (temp % 12==0)
printf("\n");
}
// vTaskDelay(500/portTICK_RATE_MS);
n++;
}
//Never reached.
ret=spi_bus_remove_device(handle);
assert(ret==ESP_OK);
}