Page 1 of 2
SPI Master Slave handshake with spi_device_queue_trans
Posted: Thu Jan 30, 2020 8:13 am
by rosenrot
Dear all,
The master-slave communication example form here
https://github.com/espressif/esp-idf/tr ... /spi_slave is wonderful. It does exactly what it should.
However, I would like to use this example with
and
Code: Select all
esp_err_tspi_device_get_trans_result
I splitted both commands into an SPI_RX and SPI_TX task like this:
Code: Select all
static void SPI_TX (void *arg){
spi_transaction_t t;
memset(&t, 0, sizeof(t));
t.length=SPI_BUF_SIZE_SMALL*8;
t.tx_buffer=spi_tx_buf;
t.rx_buffer=NULL;
while(1){
memset(&spi_tx_buf,0x78,sizeof(spi_tx_buf));
xSemaphoreTake(rdySem, portMAX_DELAY); //Wait until slave is ready - handshake
t1.tx_buffer = spi_tx_buf;
esp_err_t ret = spi_device_queue_trans(handle, &t, portMAX_DELAY);
if(ret!=ESP_OK){
ESP_LOGW(SPI_TAG,"error master queue %d",ret);
}
}
}
and
Code: Select all
static void SPI_RX (void *arg){
spi_transaction_t t;
memset(&t, 0, sizeof(t));
t.length=SPI_BUF_SIZE_SMALL*8;
t.tx_buffer=NULL;
t.rx_buffer=spi_rx_buf;
while(1){
esp_err_t ret = spi_device_get_trans_result(handle, &t, portMAX_DELAY);
if(ret!=ESP_OK){
ESP_LOGW(SPI_TAG,"error master queue");
}
for(int i=0;i<SPI_BUF_SIZE_SMALL;i++){
printf("%d",spi_rx_buf[i]);
}
taskYIELD();
}
}
Somehow like this it doesn't work anymore. I thought I need the
to wait for the handshake of the slave with
Code: Select all
xSemaphoreTake(rdySem, portMAX_DELAY);
However, it even works without.
Most likely I didn't understand the functions well enough. Could someone enlighten me?
I don't get any error messages, I just reveive garbage from the SPI_RX function.
Re: SPI Master Slave handshake with spi_device_queue_trans
Posted: Thu Jan 30, 2020 9:00 am
by NevynSelby
Are you using DMA ? If so what is the value for SPI_BUF_SIZE_SMALL ?
From the SPI Slave documentation:
The ESP32 DMA hardware has a limit to the number of bytes sent by a Host and received by a Device. The transaction length must be longer than 8 bytes and a multiple of 4 bytes; otherwise, the SPI hardware might fail to receive the last 1 to 7 bytes.
Regards,
Mark
Re: SPI Master Slave handshake with spi_device_queue_trans
Posted: Thu Jan 30, 2020 10:10 am
by rosenrot
I'm using DMA exactly the same as in the original example.
so it should be fine.
Can someone tell me if I can run this two commands in different tasks?
Where do I get my received data back? From t_send or from t_recv?
Do I have to access the spi_rx_buf in the spi_device_get_trans_result task to get the received data?
Unfortunately, I never found an example grabbing data back.
Code: Select all
spi_transaction_t t_send;
memset(&t_send, 0, sizeof(t_send));
t_send.length=SPI_BUF_SIZE_SMALL*8;
t_send.tx_buffer=spi_tx_buf;
t_send.rx_buffer=spi_rx_buf;
esp_err_t ret = spi_device_queue_trans(handle, &t_send, portMAX_DELAY);
Code: Select all
spi_transaction_t t_recv;
memset(&t_recv, 0, sizeof(t_recv));
//t_recv.length=SPI_BUF_SIZE_SMALL*8;
//t_recv.tx_buffer=spi_tx_buf;
//t_recv.rx_buffer=spi_rx_buf;
esp_err_t ret = spi_device_get_trans_result(handle, &t_recv, portMAX_DELAY);
//here data access via spi_rx_buf?
Re: SPI Master Slave handshake with spi_device_queue_trans
Posted: Thu Jan 30, 2020 11:10 pm
by rosenrot
Ok, I was able to move a step furter.
Can someone tell me at which point I can grab received data?
option1:
esp_err_t ret = spi_device_queue_trans(handle, &t_send, portMAX_DELAY);
esp_err_t ret = spi_device_get_trans_result(handle, &t_recv, portMAX_DELAY);
HERE from t_send rx_buffer?
option2:
esp_err_t ret = spi_device_queue_trans(handle, &t_send, portMAX_DELAY);
esp_err_t ret = spi_device_get_trans_result(handle, &t_recv, portMAX_DELAY);
HERE from t_recv rx_buffer?
Re: SPI Master Slave handshake with spi_device_queue_trans
Posted: Fri Jan 31, 2020 12:20 pm
by ESP_Sprite
The hardware is allowed to do the transmission/reception anywhere between when queue_trans is called and get_trans_result returns. Note that in both your examples, get_trans_result should set t_recv to be a pointer to t_send (unless you queued up other transactions earlier) so you can use both/either to get to the data.
Re: SPI Master Slave handshake with spi_device_queue_trans
Posted: Mon Feb 03, 2020 12:23 am
by rosenrot
This is what I understood as well. However, it doesn't seem to work.
Should it work if I feed the same &t_send into queue_trans or do I need to create an array of t_send? I ask because queue_trans might get called 4 times while get_trans_result returns only 2 times.
I found an example syncing both function calls by a queue. However, I don't need a synced information transfer.
If I use the same &t_send, it will get overwritten once queue_trans is called twice and get_trans_queue is called only once, right?
Re: SPI Master Slave handshake with spi_device_queue_trans
Posted: Mon Feb 03, 2020 9:36 am
by ESP_Sprite
You cannot use the same t_send; by queuing the transfer, you effectively hand over ownership of that memory to the SPI subsystem (and as such, you shouldn't in any way) until it gets returned to you using get_trans_result.
Re: SPI Master Slave handshake with spi_device_queue_trans
Posted: Mon Feb 03, 2020 10:44 am
by rosenrot
I see.
So, what is the most efficient scenario of using
queue_trans and
get_trans_result ?
Do you have a better idea than what I show below (using queues to have a ringbuffer for transmissions)? Can I just create new t_send or trans for each transmission, will the memory be released once the
get_trans_result returns it?
Scenario:
Doing this, sometimes the tx task is called more often than the rx task and therefor the tx task has to wait for the queue to get the entry from the rx task. This results in the
Queue is full but shouldn't be message, is it a design failure or what do I not get correctly?
TX task
Code: Select all
static void spi_slave_tx_task(){
for(int i = 0; i < RX_N; i++){
trans_desc[i].tx_buffer = send_buffer[i];
trans_desc[i].rx_buffer = receive_buffer[i];
trans_desc[i].length = SPI_BUF_SIZE_SMALL*8;
//trans_desc[i].flags &= ~SPI_TRANS_USE_TXDATA;
((char*)trans_desc[i].rx_buffer)[0] = 0;
spi_slave_transaction_t * ptr = &trans_desc[i];
xQueueSendToBack(empty_buffer_queue, &ptr, portMAX_DELAY);
}
spi_slave_transaction_t * trans = NULL;
while(1){
if(xQueueReceive(empty_buffer_queue, &trans, 10)){
memcpy(trans->tx_buffer,&data, sizeof(data)); //fill TX
if(ESP_OK != spi_slave_queue_trans(RCV_HOST, trans, 1)){
ESP_LOGE(TAG, "Delay queueing transaction, shouldn't happen");
spi_slave_queue_trans(RCV_HOST, trans, portMAX_DELAY);
}
}else{
if(spi_tx_error%10==0){
ESP_LOGE(TAG, "Queue empty, nowhere to write incoming spi occured %d",spi_tx_error);
}
spi_tx_error++;
}
}
RX task
Code: Select all
static void spi_slave_rx_task(){
data_t data;
spi_slave_transaction_t * trans = NULL;
while(1){
if(ESP_OK != spi_slave_get_trans_result(RCV_HOST, &trans, portMAX_DELAY)){
ESP_LOGW(SPI_TAG,"error slave queue");
}
memcpy(&data,trans->rx_buffer,sizeof(data_t)); //get RX
if(data[4]>0){
xQueueSend(queue_spi_input, &data, portMAX_DELAY); //usage RX
}
if(!xQueueSendToBack(empty_buffer_queue, &trans, 1)){
ESP_LOGE(TAG, "Queue is full but shouldn't be");
}
}
}
Re: SPI Master Slave handshake with spi_device_queue_trans
Posted: Mon Feb 03, 2020 4:00 pm
by ESP_Sprite
You could in theory create t_send items dynamically, using malloc() and free(), but you'd probably still be limited by the fact that your SPI queue has a limited amount if items it can contain, so it won't really help here.
Can you expand a bit on why you have a separate Tx and Rx task? Does this code run as SPI slave or SPI master?
Re: SPI Master Slave handshake with spi_device_queue_trans
Posted: Mon Feb 03, 2020 5:37 pm
by rosenrot
This code runs as SPI Slave.
Let me explain what I want to do. I want to establish a bidirectional SPI communication between two ESP32s. Therefore, I used the master/slave example from the idf examples (first post). This example uses spi_transmit in one task to do so, also a handshake line is used to tell the master when he should transmit data in order for the slave's data to get picked up.
My idea was to have two tasks to send and receive data. One for loading the data to be send via
queue_trans and one to receive data by
get_trans_result . Maybe this is already a design failure. However, otherwise I wouldn't see the reason why to use these two functions and not
spi_transmit. Again, I might be wrong here already. If there are no data available, I send empty data, so that the receiver knows that this is just dummy data.
Currently, I'm trying to understand
https://github.com/espressif/esp-idf/is ... -351595974 because I think it might help me.
Any idea how to make a bidirectional SPI communication work realiably is welcome.