Page 1 of 1

ESP32 SPI big delay between transactions on external ADC

Posted: Fri Jan 07, 2022 11:29 am
by MemoryLeak
Hello,

I'm using MCP3201 which is very easy to use. For this transaction I don't need to send anything, just listen to 16 bits of data.
I should get 100k samples per second, but it takes a very long time, like 4 times more.

SPI Clock:
111111111.png
111111111.png (296.04 KiB) Viewed 6212 times
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stddef.h>
  4. #include <string.h>
  5.  
  6. #include "sdkconfig.h"
  7. #include "freertos/FreeRTOS.h"
  8. #include "freertos/task.h"
  9. #include "esp_system.h"
  10. #include "esp_spi_flash.h"
  11. #include "xtensa/hal.h"
  12. #include "soc/rtc.h"
  13. #include "driver/gpio.h"
  14. #include "driver/spi_master.h"
  15.  
  16. #define SPI_MODE 0
  17. #define GPIO_MOSI 13
  18. #define GPIO_MISO 12
  19. #define GPIO_SCLK 14
  20. #define GPIO_CS 15
  21. #define SPI_CLOCK 1600000
  22.  
  23.  
  24. void app_main() {
  25.     printf("SPIbus Example \n");
  26.  
  27.     spi_bus_config_t config;
  28.     config.mosi_io_num = GPIO_MOSI;
  29.     config.miso_io_num = GPIO_MISO;
  30.     config.sclk_io_num = GPIO_SCLK;
  31.     config.quadwp_io_num = -1;  // -1 not used
  32.     config.quadhd_io_num = -1;  // -1 not used
  33.     config.max_transfer_sz = 24;
  34.  
  35.     spi_host_device_t host;
  36.     host = HSPI_HOST;
  37.     esp_err_t err = spi_bus_initialize(host, &config, 0);
  38.     printf("SPI bus init status: %s\n", esp_err_to_name(err));
  39.  
  40.     spi_device_handle_t spi_device;
  41.     spi_device_interface_config_t dev_config;
  42.     dev_config.command_bits = 0;
  43.     dev_config.address_bits = 0;
  44.     dev_config.dummy_bits = 0;
  45.     dev_config.mode = SPI_MODE;
  46.     dev_config.duty_cycle_pos = 128;  // default 128 = 50%/50% duty
  47.     dev_config.cs_ena_pretrans = 0;  // 0 not used
  48.     dev_config.cs_ena_posttrans = 0;  // 0 not used
  49.     dev_config.clock_speed_hz = SPI_CLOCK;
  50.     dev_config.spics_io_num = GPIO_CS;
  51.     dev_config.flags = 0;  // 0 not used
  52.     dev_config.queue_size = 1;
  53.     dev_config.pre_cb = NULL;
  54.     dev_config.post_cb = NULL;
  55.     err = spi_bus_add_device(host, &dev_config, &spi_device);
  56.     printf("SPI add device to bus status: %s\n", esp_err_to_name(err));
  57.  
  58.     while(1) {
  59.         spi_transaction_t transaction;
  60.         transaction.flags = SPI_TRANS_USE_RXDATA;
  61.         transaction.cmd = 0;
  62.         transaction.addr = 0;
  63.         transaction.length = 2 * 8;
  64.         transaction.rxlength = 2 * 8;
  65.         transaction.user = NULL;
  66.         transaction.tx_buffer = NULL;
  67.         esp_err_t err = spi_device_transmit(spi_device, &transaction);  
  68.  
  69.         //vTaskDelay(10 / portTICK_PERIOD_MS);
  70.     }
  71.     esp_restart();
  72. }

However on Arduino framework everything look fine:
22222222.png
22222222.png (454.76 KiB) Viewed 6212 times
And code:
  1. #include <SPI.h>
  2.  
  3. #define LOOP_X 50
  4. #define LOOP_Y 50
  5.  
  6. static const int spiClk = 100000;
  7. #define cs 15
  8. SPIClass SPI1(HSPI);
  9. uint32_t millis_ref;
  10. #define SPEED 1000
  11. volatile uint32_t fps_counter;
  12.  
  13.  
  14. void setup() {
  15.   Serial.begin(115200);
  16.  
  17.   SPI1.begin();
  18.   SPI1.beginTransaction(SPISettings(1600000, MSBFIRST, SPI_MODE0));
  19.  
  20.     pinMode(cs, OUTPUT);
  21.     digitalWrite(cs, HIGH);
  22. }
  23.  
  24. int read_test_next() {
  25.     short val;
  26.     byte inByte;
  27.  
  28.     digitalWrite(cs, LOW);
  29.     val = SPI1.transfer(0x00);
  30.     val = val << 8;
  31.     inByte = SPI1.transfer(0x00);
  32.     val = val | inByte;
  33.     digitalWrite(cs, HIGH);
  34.     val = val >> 1;
  35.     return val & 0x3FF;
  36. }
  37.  
  38.  
  39. void loop() {
  40.     while(millis() - millis_ref <= SPEED) {
  41.       for (size_t i = 0; i < LOOP_Y; i++) {
  42.         for (size_t j = 0; j < LOOP_X; j++) {
  43.           kk = read_test_next();
  44.           fps_counter += 1;
  45.         }
  46.       }
  47.     }
  48.  
  49.    Serial.print("fps: ");
  50.    Serial.println(fps_counter);
  51.      
  52.    millis_ref += SPEED;
  53.    fps_counter = 0;
  54. }

Why is this happening? How can I speed up reading on the espressif framework? Ultimately I aim at reading 2Msps when buying a better ADC.

Re: ESP32 SPI big delay between transactions on external ADC

Posted: Sat Jan 08, 2022 3:14 am
by ESP_Sprite
You probably want to use polling mode here for the SPI transaction. Also note that the SPI peripheral on the ESP32 in general isn't that good at running small transactions at high speed, as the CPU needs to handle each start and end of the packet. If I recall correctly, the S2 and later are more capable of it as they can do segmented transfers.

Re: ESP32 SPI big delay between transactions on external ADC

Posted: Sat Jan 15, 2022 10:20 pm
by MemoryLeak
ESP_Sprite wrote: You probably want to use polling mode here for the SPI transaction. Also note that the SPI peripheral on the ESP32 in general isn't that good at running small transactions at high speed, as the CPU needs to handle each start and end of the packet. If I recall correctly, the S2 and later are more capable of it as they can do segmented transfers.
I've used spi_device_polling_transmit and it seems to work a little better, but it's still a huge lag. And don't tell me these are high speed, they are only 1.6MHz and the documentation says that hardware spi supports up to 80MHz.

Besides, the arduino framework has no problem with that. There are nowhere examples of using low-level functions to handle SPI? because it looks like this library is awfully written
espespesp.png
espespesp.png (1.04 MiB) Viewed 6066 times

Re: ESP32 SPI big delay between transactions on external ADC

Posted: Sun Jan 16, 2022 1:40 am
by ESP_Sprite
There's the technical reference manual for the chip if you want to poke bits into registers directly, and if you want to have a slightly higher level, there's the SPI HAL and LL abstraction layers in ESP-IDF. There's not really any examples for that (that aren't the drivers themselves) as for 99% of people, the driver is good enough.