SPI slave speed problem
SPI slave speed problem
Hello group
I'm hoping to solve this SPI slave problem. I've tried with esp-idf coding and Arduino coding.
Scenario: To simplify, I have a SPI "black-box" master that sends me 2 blocks of 5 bytes (Trace 1).
I read the first block but never the second.
As I see it, I think the ~ 3nS pulse on the slave CS line (Trace 4 on the scope), in the middle of the two "5-Byte" blocks, is the problem.
I thought my best bet would be to have the slave.queue/slave.yield commands in their own task for speed but it hasn't solved the problem.
The replies are all the first block, never the second.
Thanks in advance for your suggestions.
Test Code:
#include <Arduino.h>
#include <sys/types.h>
#include <SPI.h>
#include "ESP32DMASPISlave.h"
#define HSPI_MISO -1
#define HSPI_MOSI 11
#define HSPI_SCLK 12
#define HSPI_SS 10
#define MSG5_SIZE 5U
ESP32DMASPI::Slave slave;
uint16_t ResultsHex[80] = {0};
uint8_t *slave_buffer1;
uint8_t *slave_buffer2;
constexpr uint8_t CORE_TASK_SPI_SLAVE {0};
constexpr uint8_t CORE_TASK_PROCESS_BUFFER {0};
static TaskHandle_t task_handle_wait_spi = 0;
static TaskHandle_t task_handle_process_buffer = 0;
void dump_buf(const char* title, uint8_t* buf, uint8_t len) {
printf("%4s[%2d]: ", title, len);
for (uint8_t i = 0; i < len; i++)
printf("%02X ", buf);
}
void task_wait_spi(void* pvParameters) {
while (1) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
slave.yield();
if (slave.remained() == 0) {
slave.queue(slave_buffer1, NULL, MSG5_SIZE);
slave.yield();
}
// delayMicroseconds(11);
xTaskNotifyGive(task_handle_process_buffer);
}
}
// int i;
void task_process_buffer(void* pvParameters) {
while (1) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
// printf("Avail %u, size %u\n", slave.available(), slave.size());
slave.queue(slave_buffer2, NULL, MSG5_SIZE);
xTaskNotifyGive(task_handle_wait_spi);
}
}
void setup() {
Serial.begin(115200); while (!Serial) {} ;
printf("\n* * * * * * * * * * * * * * * * * * * *\n");
printf("%s - SLAVE ", __FILE__);
printf("* * * * * * * * * * * * * * * * * * * *\n");
// pinMode(INT_PIN, INPUT_PULLUP);
pinMode(HSPI_SCLK, INPUT_PULLUP);
pinMode(HSPI_MOSI, INPUT_PULLUP);
pinMode(HSPI_SS, INPUT_PULLUP);
delay(2000);
// attachInterrupt(digitalPinToInterrupt(INT_PIN), blink, RISING);
slave_buffer1 = (uint8_t*)heap_caps_calloc(MSG5_SIZE, 1, MALLOC_CAP_DMA);
slave_buffer2 = (uint8_t*)heap_caps_calloc(MSG5_SIZE, 1, MALLOC_CAP_DMA);
memset(slave_buffer1, 0x0, MSG5_SIZE);
delay(1);
slave.setDataMode(SPI_MODE1);
slave.setMaxTransferSize(1024);
slave.setDMAChannel(2); // Auto
slave.setQueueSize(80); // transaction queue size
slave.begin(HSPI, HSPI_SCLK, HSPI_MISO, HSPI_MOSI, HSPI_SS); //SCLK, MISO, MOSI, SS
xTaskCreatePinnedToCore(task_wait_spi, "task_wait_spi", 10000, NULL, 2, &task_handle_wait_spi, CORE_TASK_SPI_SLAVE);
delay(1);
xTaskCreatePinnedToCore(task_process_buffer, "task_process_buffer", 10000, NULL, 2, &task_handle_process_buffer, CORE_TASK_PROCESS_BUFFER);
delay(1);
xTaskNotifyGive(task_handle_wait_spi);
delay(1);
}
void loop() {
dump_buf("Rx1: ", slave_buffer1, MSG5_SIZE);
dump_buf("Rx2: ", slave_buffer2, MSG5_SIZE);
printf("\n");
delay(1);
}
I'm hoping to solve this SPI slave problem. I've tried with esp-idf coding and Arduino coding.
Scenario: To simplify, I have a SPI "black-box" master that sends me 2 blocks of 5 bytes (Trace 1).
I read the first block but never the second.
As I see it, I think the ~ 3nS pulse on the slave CS line (Trace 4 on the scope), in the middle of the two "5-Byte" blocks, is the problem.
I thought my best bet would be to have the slave.queue/slave.yield commands in their own task for speed but it hasn't solved the problem.
The replies are all the first block, never the second.
Thanks in advance for your suggestions.
Test Code:
#include <Arduino.h>
#include <sys/types.h>
#include <SPI.h>
#include "ESP32DMASPISlave.h"
#define HSPI_MISO -1
#define HSPI_MOSI 11
#define HSPI_SCLK 12
#define HSPI_SS 10
#define MSG5_SIZE 5U
ESP32DMASPI::Slave slave;
uint16_t ResultsHex[80] = {0};
uint8_t *slave_buffer1;
uint8_t *slave_buffer2;
constexpr uint8_t CORE_TASK_SPI_SLAVE {0};
constexpr uint8_t CORE_TASK_PROCESS_BUFFER {0};
static TaskHandle_t task_handle_wait_spi = 0;
static TaskHandle_t task_handle_process_buffer = 0;
void dump_buf(const char* title, uint8_t* buf, uint8_t len) {
printf("%4s[%2d]: ", title, len);
for (uint8_t i = 0; i < len; i++)
printf("%02X ", buf);
}
void task_wait_spi(void* pvParameters) {
while (1) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
slave.yield();
if (slave.remained() == 0) {
slave.queue(slave_buffer1, NULL, MSG5_SIZE);
slave.yield();
}
// delayMicroseconds(11);
xTaskNotifyGive(task_handle_process_buffer);
}
}
// int i;
void task_process_buffer(void* pvParameters) {
while (1) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
// printf("Avail %u, size %u\n", slave.available(), slave.size());
slave.queue(slave_buffer2, NULL, MSG5_SIZE);
xTaskNotifyGive(task_handle_wait_spi);
}
}
void setup() {
Serial.begin(115200); while (!Serial) {} ;
printf("\n* * * * * * * * * * * * * * * * * * * *\n");
printf("%s - SLAVE ", __FILE__);
printf("* * * * * * * * * * * * * * * * * * * *\n");
// pinMode(INT_PIN, INPUT_PULLUP);
pinMode(HSPI_SCLK, INPUT_PULLUP);
pinMode(HSPI_MOSI, INPUT_PULLUP);
pinMode(HSPI_SS, INPUT_PULLUP);
delay(2000);
// attachInterrupt(digitalPinToInterrupt(INT_PIN), blink, RISING);
slave_buffer1 = (uint8_t*)heap_caps_calloc(MSG5_SIZE, 1, MALLOC_CAP_DMA);
slave_buffer2 = (uint8_t*)heap_caps_calloc(MSG5_SIZE, 1, MALLOC_CAP_DMA);
memset(slave_buffer1, 0x0, MSG5_SIZE);
delay(1);
slave.setDataMode(SPI_MODE1);
slave.setMaxTransferSize(1024);
slave.setDMAChannel(2); // Auto
slave.setQueueSize(80); // transaction queue size
slave.begin(HSPI, HSPI_SCLK, HSPI_MISO, HSPI_MOSI, HSPI_SS); //SCLK, MISO, MOSI, SS
xTaskCreatePinnedToCore(task_wait_spi, "task_wait_spi", 10000, NULL, 2, &task_handle_wait_spi, CORE_TASK_SPI_SLAVE);
delay(1);
xTaskCreatePinnedToCore(task_process_buffer, "task_process_buffer", 10000, NULL, 2, &task_handle_process_buffer, CORE_TASK_PROCESS_BUFFER);
delay(1);
xTaskNotifyGive(task_handle_wait_spi);
delay(1);
}
void loop() {
dump_buf("Rx1: ", slave_buffer1, MSG5_SIZE);
dump_buf("Rx2: ", slave_buffer2, MSG5_SIZE);
printf("\n");
delay(1);
}
-
- Posts: 9727
- Joined: Thu Nov 26, 2015 4:08 am
Re: SPI slave speed problem
Likely the CS pulse in the middle is the issue, indeed: it makes the SPI hardware think the transaction stopped which makes the software process the result. Because the CS pulse high time is only a few uS, software can't set up a new transaction in time to capture the 2nd transaction, and it gets lost.
You could try solving this by making the middle CS pulse 'disappear' somehow, but given the timings involved, it may be tricky to do in software, and it depends on other behaviour of the black box if it could be done succesfully.
You could try solving this by making the middle CS pulse 'disappear' somehow, but given the timings involved, it may be tricky to do in software, and it depends on other behaviour of the black box if it could be done succesfully.
Re: SPI slave speed problem
Thank your for your reply. I was wondering if there is a way to use interrupts on the CS line?
I have investigated the esp-idf code looking for possible solutions, but so far I haven't had any luck
I have investigated the esp-idf code looking for possible solutions, but so far I haven't had any luck
-
- Posts: 9727
- Joined: Thu Nov 26, 2015 4:08 am
Re: SPI slave speed problem
Yes, you should be able to put a GPIO interrupt on any pin regardless of its function. (Probably best to initialize the GPIO interrupt before initializing SPI, by the way.)
Another option: You can also control the CS SPI input signal by manually using the GPIO matrix to the always-high or always-low signal... so you could at the start, force the CS SPI input signal low, put a rising-edge interrupt on your physical CS pin (which now doesn't directly control the SPI slave CS anymore as that is under software control), and as soon as that triggers, delay a bit for the 2nd bit of data to come in and manually make the SPI slave CS high again.
Another option: You can also control the CS SPI input signal by manually using the GPIO matrix to the always-high or always-low signal... so you could at the start, force the CS SPI input signal low, put a rising-edge interrupt on your physical CS pin (which now doesn't directly control the SPI slave CS anymore as that is under software control), and as soon as that triggers, delay a bit for the 2nd bit of data to come in and manually make the SPI slave CS high again.
Re: SPI slave speed problem
Thank you again. I shall make another incursion into this possibility, probably tomorrow or so.
Re: SPI slave speed problem
Good news
thank you. it was what I needed to unblock this problem. I moved my test code to esp-idf to have more control on the SPI's and took control of the incoming CS line to the SPI slave on the ESP, having modified the CS line logic on my "black box".
The annoying pulse in the middle of trace-4 is no longer.
Only took an extra line and a bit of delay to get it working.
static void cs_high()
{
gpio_set_level(MASTER_SS, 1);
ets_delay_us(30);
gpio_set_level(SLAVE_SS, 1);
}
static void cs_low()
{
gpio_set_level(MASTER_SS, 0);
ets_delay_us(2);
gpio_set_level(SLAVE_SS, 0);
}
thank you. it was what I needed to unblock this problem. I moved my test code to esp-idf to have more control on the SPI's and took control of the incoming CS line to the SPI slave on the ESP, having modified the CS line logic on my "black box".
The annoying pulse in the middle of trace-4 is no longer.
Only took an extra line and a bit of delay to get it working.
static void cs_high()
{
gpio_set_level(MASTER_SS, 1);
ets_delay_us(30);
gpio_set_level(SLAVE_SS, 1);
}
static void cs_low()
{
gpio_set_level(MASTER_SS, 0);
ets_delay_us(2);
gpio_set_level(SLAVE_SS, 0);
}
Re: SPI slave speed problem
Hello again
trying to make a single ESP run what I need, I assume I need to unite "a GPIO pin" with the "spics_in" signal of the SPI slave.
Investigating I thought that this would do it ...
esp_rom_gpio_connect_in_signal( someGPIOpin, spi_periph_signal[SPI3_HOST].spics_in , false );
However a high/low signal sent to "someGPIOpin", doesn't appear on the slave CS pin.
What am I missing, please?
trying to make a single ESP run what I need, I assume I need to unite "a GPIO pin" with the "spics_in" signal of the SPI slave.
Investigating I thought that this would do it ...
esp_rom_gpio_connect_in_signal( someGPIOpin, spi_periph_signal[SPI3_HOST].spics_in , false );
However a high/low signal sent to "someGPIOpin", doesn't appear on the slave CS pin.
What am I missing, please?
-
- Posts: 9727
- Joined: Thu Nov 26, 2015 4:08 am
Re: SPI slave speed problem
Fancy trick that does not need any GPIO: (note: this particular snipped is untested but should work)
Code: Select all
#include "rom/gpio.h"
//make CS of the SPI peripheral high
esp_rom_gpio_connect_in_signal( GPIO_FUNC_IN_HIGH, spi_periph_signal[SPI3_HOST].spics_in , false );
//make CS low
esp_rom_gpio_connect_in_signal( GPIO_FUNC_IN_LOW, spi_periph_signal[SPI3_HOST].spics_in , false );
Re: SPI slave speed problem
Evening
I liked your last reply, but I didn't spend too much time on it as "it didn't work". Maybe because the code wants to tell an "input pin" to modify it's level, which isn't done.
So, I've used 2 pins to get past this problem and I'm advancing again with the next 1000 lines of code
Many thanks
I liked your last reply, but I didn't spend too much time on it as "it didn't work". Maybe because the code wants to tell an "input pin" to modify it's level, which isn't done.
So, I've used 2 pins to get past this problem and I'm advancing again with the next 1000 lines of code
Many thanks
Re: SPI slave speed problem
Next problem....
I'm using esp-idf v4.4 and It seems the limitation of using DMA with SPI still exists...
The master SPI only ever sends 5 bytes so I have DMA switched off, no problem, except 5 is not a multiple of 8.
There is however one master command that expects to receive 1280 bits, which is not possible with slave DMA switched on.
(maximum of 64 bytes, 512 bits)
I had hoped that the 64 bytes of dedicated SPI memory was goingto be enough.
I've only just discovered this because the code I'm working on is over 20k lines.
If I switch on DMA for the slave SPI, I only ever see 4 bytes returned.
Any workarounds? Before I start rewriting code?
By the way, we are using a ESP32 WROOM (8MB) and at our stage of development, for example, should a different ESP solve this, thats a valid possibility for us.
Thank you
I'm using esp-idf v4.4 and It seems the limitation of using DMA with SPI still exists...
The master SPI only ever sends 5 bytes so I have DMA switched off, no problem, except 5 is not a multiple of 8.
There is however one master command that expects to receive 1280 bits, which is not possible with slave DMA switched on.
(maximum of 64 bytes, 512 bits)
I had hoped that the 64 bytes of dedicated SPI memory was goingto be enough.
I've only just discovered this because the code I'm working on is over 20k lines.
If I switch on DMA for the slave SPI, I only ever see 4 bytes returned.
Any workarounds? Before I start rewriting code?
By the way, we are using a ESP32 WROOM (8MB) and at our stage of development, for example, should a different ESP solve this, thats a valid possibility for us.
Thank you
Who is online
Users browsing this forum: No registered users and 63 guests