Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 3586

SDK • SPI Slave with DMA

$
0
0
Hi, I'm trying to implement a SPI Slave with DMA in a RP2040 to receive 16 bytes at 1MHz from a SPI Master.
The Master clears CSN before the transfer and sets it at the end. I cannot change the Master.

The RP2040 SPI slave example is working in single byte and in continous mode, both in polling (as per the example) and with interrupts.
The next step is now to have it feed a DMA and trigger an interrupt once all 16 bytes have been received.
Note that I only need to use it as slave RX, I don't need to output any data (TX unconnected). The 3 signals used are: RX, SCK and CSN.

The issue that I'm having is that although the DMA is triggered by the reception of a byte, it only copies a byte at once. This works when in single-byte mode, but not in continuous mode, where only the first byte is copied, see the console output below.

My test setup is as follows:
Arduino Mega 2560 setup as a SPI master and sending 10 bytes (00 to 09) every second, for test purposes. This is the code:

Code:

/*  SPI Master*/#include <SPI.h>#define BUF_LEN         10const int slaveSelectPin = 53;uint8_t out_buf[BUF_LEN];void setup() {  Serial.begin(115200);  Serial.println("SPI Master");  pinMode(slaveSelectPin, OUTPUT);  SPI.begin();}void loop() {  Serial.println("Sending");  // Must initialize buffer every time  for (int i = 0; i < BUF_LEN; i++)  {    out_buf[i] = i;  }  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));  digitalWrite(slaveSelectPin, LOW);  SPI.transfer(out_buf, BUF_LEN);  digitalWrite(slaveSelectPin, HIGH);  SPI.endTransaction();  delay(1000);}
Raspberry Pi Pico connected with its default GPIOs and setup as a SPI slave + DMA. This is the code:

Code:

// Example of using the SPI as a slave with DMA#include <stdio.h>#include <stdlib.h>#include "pico/stdlib.h"#include "pico/binary_info.h"#include "pico/sync.h"#include "hardware/spi.h"#include "hardware/dma.h"#include "hardware/irq.h"#define BUF_LEN 10uint dma_rx;static uint8_t rxbuf[BUF_LEN];bool dma_rx_done = false;void dma_rx_irq_handler() {    dma_channel_acknowledge_irq0(dma_rx);    // Re-initialize and trigger the DMA transfer    dma_channel_set_trans_count(dma_rx, BUF_LEN, true);    dma_channel_set_write_addr(dma_rx, rxbuf, true);    printf("RX DMA IRQ\n");    dma_rx_done = true;}int main() {    // Enable UART so we can print status output    stdio_init_all();    //sleep_ms(2000);    printf("SPI example\n");    // Enable SPI at 1 MHz and connect to GPIOs    spi_init(spi_default, 1000 * 1000);    spi_set_slave(spi_default, true);    gpio_set_function(PICO_DEFAULT_SPI_RX_PIN, GPIO_FUNC_SPI);    gpio_set_function(PICO_DEFAULT_SPI_SCK_PIN, GPIO_FUNC_SPI);    gpio_set_function(PICO_DEFAULT_SPI_TX_PIN, GPIO_FUNC_SPI);    gpio_set_function(PICO_DEFAULT_SPI_CSN_PIN, GPIO_FUNC_SPI);    // Grab some unused dma channels    dma_rx = dma_claim_unused_channel(true);    printf("Configure RX DMA\n");    // We set the inbound DMA to transfer from the SPI receive FIFO to a memory buffer paced by the SPI RX FIFO DREQ    // We configure the read address to remain unchanged for each element, but the write    // address to increment (so data is written throughout the buffer)        dma_channel_config c = dma_channel_get_default_config(dma_rx);    channel_config_set_transfer_data_size(&c, DMA_SIZE_8);    channel_config_set_dreq(&c, spi_get_dreq(spi_default, false)); // RX DREQ    channel_config_set_read_increment(&c, false);    channel_config_set_write_increment(&c, true);    dma_channel_configure(dma_rx, &c,                          rxbuf, // write address                          &spi_get_hw(spi_default)->dr, // read address                          BUF_LEN, // element count (each element is of size transfer_data_size)                          false); // don't start yet    //Enable IRQ for RX DMA    dma_channel_set_irq0_enabled(dma_rx, true);    irq_set_exclusive_handler(DMA_IRQ_0, dma_rx_irq_handler);    irq_set_enabled(DMA_IRQ_0, true);    dma_channel_start(dma_rx);    while(1)    {      if(dma_rx_done)      {        dma_rx_done = false;        printf("RX DMA done\n");        for(int i = 0; i < BUF_LEN; i++) {            printf("%02x ", rxbuf[i]);        }        printf("\n");      }      if(spi_get_const_hw(spi_default)->sr & SPI_SSPSR_RNE_BITS)      {        printf("RX FIFO not empty\n");      }      if(spi_get_const_hw(spi_default)->sr & SPI_SSPSR_RFF_BITS)      {        printf("RX FIFO full\n");      }      printf("DMA transfer count=%02x\n", dma_channel_hw_addr(dma_rx)->transfer_count);      sleep_ms(100);    }}
The output is this (duplicated lines removed):
DMA transfer count=0a
DMA transfer count=09
DMA transfer count=08
DMA transfer count=07
DMA transfer count=06
DMA transfer count=05
DMA transfer count=04
DMA transfer count=03
DMA transfer count=02
DMA transfer count=01
RX DMA IRQ
RX DMA done
00 00 00 00 00 00 00 00 00 00
Where I would expect something more like this:
DMA transfer count=0a
RX DMA IRQ
RX DMA done
00 01 02 03 04 05 06 07 08 09
I went through the RP2040 TRM and the PL022 TRM but I couldn't find the details of the DMA implementation.
Any idea how I could get the DMA to transfer more than 1 byte at a time, or what settings I need to change in the SPI peripheral?
I don't think I can change the SPI mode since I'm expecting 16 bytes at 1MHz from the Master, but all inputs are welcome!
Thanks

Statistics: Posted by romain145 — Sun Aug 25, 2024 7:45 pm — Replies 1 — Views 18



Viewing all articles
Browse latest Browse all 3586

Trending Articles