This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

nRF52840 SPI EasyDMA Double Buffering

Hello,

I am trying to determine whether or not the SPI EasyDMA functionality of the nrf52840 supports double buffering in the sense that the DMA controller will switch buffers without intervention from the processor. In other words, can you start one DMA transfer and then immediately queue the next DMA transfer so that the following scenario happens:

Example:

255 Byte DMA buffer x 2

1. Start DMA transfer from buffer1 and immediately queue DMA transfer from buffer2.

2. Receive DMA transfer complete interrupt from buffer1. The DMA controller has already automatically switched to buffer2. Load buffer1 with new data and queue this transfer.

3. Receive DMA transfer complete interrupt from buffer2. The DMA controller has already automatically switched to buffer1. Load buffer2 with new data and queue this transfer.

4. etc...

I ask because I am using the SoftDevice while receiving ADC data over SPI sampled at 250 Hz and I want to make sure that if the DMA interrupt is delayed by any BLE activity that I do not miss a sample from my ADC's. If my understanding of this is correct, is there an example I can reference?

SoftDevice: S140

SDK: 15.0.0

Thanks,

Derek

  • I believe you've understood it correctly. 

    In essence it's just the RXD and TXD registers that are double buffered, which means that they can be updated immediately after an EVENTS_STARTED has fired. You can use as many buffers as you like. 

    You should check out the EasyDMA array list feature, as it allows you to queue multiple buffers at once without having to manually set the pointer registers each time after an EVENTS_STARTED. 

  • Thanks for the response!

    Let me preface this with what I am trying to now that I have gotten my hands on my DAC hardware. The DAC requires 2 bytes to be written while toggling chip select in-between transfers. I am trying to store an entire waveform in RAM and kick off a DMA SPI transfer to loop over this data indefinitely or until stopped by the application without intervention from the processor because the DAC writes cannot be interrupted. At the same time, the SoftDevice will be "listening" for a stop command to halt the DAC updates. My concern is that the SoftDevice interrupts will corrupt the output waveform.

    After looking at this thread: https://devzone.nordicsemi.com/f/nordic-q-a/18638/easydma-array-list, it looks like this can be done using Array Lists, hardware timers, and PPI. However, does this method also toggle the chip select line between transfers of each buffer in the Array List?

    My idea was to store each 2-byte value into a very large ArrayList as shown below and loop over this using SPI DMA, hardware timers, and PPI with no processor usage. Is this possible? If not, how can I achieve this? Are there any examples for SPI?

    Thanks!

    typedef struct ArrayList
    {
    uint8_t buffer[2]; // 2 DAC bytes
    } ArrayList_type;
    
    ArrayList_type MyArrayList[SIZEOF_WAVEFORM];

  • The SPIM0-2 peripherals does not have HW Chip Select, but the new SPIM3/QSPI peripheral does.

    If you need the QSPI peripheral for other devices I suggest you used SPIM0-2 and control the CS pin via PPI and GPIOTE. SPI slaves will usually have timing requirements for when the CS pin is pulled low and high, therefore I suggest that you control both the CS and the SPIM's TASKS_START with a TIMER and PPI. 

    You'll need to connect the TIMER's Compare0 event to the a GPIOTE TASKS_OUT, where the GPIOTE task is set up to pull the CS low. Then you'll need to connect TIMER's Compare1 event to the SPIM's TASKS_START, and the SPIM's EVENTS_END event to the GPIOTE TASKS_OUT[1], where the GPIOTE task is set up to pull the CS line high between each transfer. You will also need to fork the SPIM's EVENTS_END event to the TIMER's TASKS_CLEAR to restart the cycle. 

  • From what I have read on the Devzone and the datasheet, what you describe makes sense. Is there a working SPI driver example that implements this or similar functionality that I can reference? Specifically in regards to setting up the timers, tasks, forking, and PPI? I greatly appreciate your help.

    Thanks!

  • See Timer ExamplePPI ExampleGPIOTE Example, SPI Master ExampleGPIOTE Driver description, SPI master Driver description, GPIOTE Driver and HAL API, PPI Driver and HAL API, and TIMER Driver and HAL API.

    I'd start by playing with the TIMER and GPIOTE via HAL. Set up some GPIOTE tasks like pin toggles that are triggered by a TIMER's compare tasks. Use a digital analyzer to see how the GPIOs behave. This will teach you the basics of the PPI system (EVENT --> TASK) and how to set up the TIMER and GPIOTE. 

    The PPI system uses the register address of an EVENT and couples it to the register address of a TASK. All drivers or HALs should have a function for getting the address of an EVENT or TASK. Those addresses are also given in the Registers chapter of a peripheral's technical specification. 

    You can use the SPIM driver to initialize the SPIM peripheral and the HAL API to enable the linked list feature with a call tonrf_spim_tx_list_enable

Related