how to reduce interrupt latency?

Weizzh
Posts: 19
Joined: Mon Nov 22, 2021 9:32 am

how to reduce interrupt latency?

Postby Weizzh » 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):

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);
}

Who is online

Users browsing this forum: No registered users and 235 guests