DMA SPI achieves wire speed, 44.3 mbs for block transfers Proof-of-concept

Dependencies:   mbed

Proof-of-concept SPI block transfer using DMA to achieve wire speeds. SPI1 runs at 45mhz, standard SPI write() achieves only 11mbs, DMA version does 44.3 mbs.

More/comparative SPI results at https://github.com/manitou48/DUEZoo/blob/master/SPIperf.txt

main.cpp

Committer:
manitou
Date:
2015-11-10
Revision:
0:6242d93d3c51

File content as of revision 0:6242d93d3c51:

//nucleo DMA SPI from pyboard SPI   proof of concept
// need DMA channel for TX and another for RX
//tx DMA2_Stream5, DMA_CHANNEL_3   rx DMA2_Stream2, DMA_CHANNEL_3
// SPI1 and 4 max 45mbs    SPI3 4  max 22.5mbs
#include "mbed.h"

#define PRREG(z) printf(#z" 0x%x\n",z)
Timer tmr;
DigitalOut CSpin(D10);
SPI spi(SPI_MOSI, SPI_MISO, SPI_SCK); // mosi, miso, sclk  SPI1

#define SPI_BUFF_SIZE 1024
uint8_t rx_buffer[SPI_BUFF_SIZE];
uint8_t tx_buffer[SPI_BUFF_SIZE];

void spiperf(int mhz) {
    int i, us;

    spi.frequency(mhz*1000000);
    CSpin=0;
    us = tmr.read_us();
    for(i=0;i<SPI_BUFF_SIZE;i++) spi.write(tx_buffer[i]);
    us = tmr.read_us()-us;
    CSpin=1;
    printf("spi %d mhz %d us %.2f mbs  %0x\n",mhz,us,8.*SPI_BUFF_SIZE/us,SPI1->CR1);
}

// need to re-create SPI firmware to access SPI handle
static SPI_HandleTypeDef SpiHandle;
  
static void spiInit() {
    SpiHandle.Instance = SPI1;

    __HAL_SPI_DISABLE(&SpiHandle);

    SpiHandle.Init.Mode              = SPI_MODE_MASTER;
    SpiHandle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;  // 2 is 45 mhz    32 is 2.81mhz
    SpiHandle.Init.Direction         = SPI_DIRECTION_2LINES;
    SpiHandle.Init.CLKPhase          = SPI_PHASE_1EDGE;  // mode 0
    SpiHandle.Init.CLKPolarity       = SPI_POLARITY_LOW;
    SpiHandle.Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLED;
    SpiHandle.Init.CRCPolynomial     = 7;
    SpiHandle.Init.DataSize          = SPI_DATASIZE_8BIT;
    SpiHandle.Init.FirstBit          = SPI_FIRSTBIT_MSB;
    SpiHandle.Init.NSS               = SPI_NSS_SOFT;
    SpiHandle.Init.TIMode            = SPI_TIMODE_DISABLED;

    if (HAL_SPI_Init(&SpiHandle) != HAL_OK) {
        error("Cannot initialize SPI");
    }

    __HAL_SPI_ENABLE(&SpiHandle);
}

DMA_HandleTypeDef tx_DMA_Handle, rx_DMA_Handle;

static void dmaInit() {
    __DMA2_CLK_ENABLE();
    tx_DMA_Handle.Instance = DMA2_Stream5;

    // Need to deinit DMA first
    tx_DMA_Handle.State = HAL_DMA_STATE_READY;
    HAL_DMA_DeInit(&tx_DMA_Handle);

    tx_DMA_Handle.Init.Channel = DMA_CHANNEL_3;
    tx_DMA_Handle.Init.Direction = DMA_MEMORY_TO_PERIPH;
    tx_DMA_Handle.Init.PeriphInc = DMA_PINC_DISABLE;
    tx_DMA_Handle.Init.MemInc = DMA_MINC_ENABLE;
    tx_DMA_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    tx_DMA_Handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    tx_DMA_Handle.Init.Mode = DMA_NORMAL;
    tx_DMA_Handle.Init.Priority = DMA_PRIORITY_LOW;
    tx_DMA_Handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    tx_DMA_Handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    tx_DMA_Handle.Init.MemBurst = DMA_MBURST_INC4;
    tx_DMA_Handle.Init.PeriphBurst = DMA_PBURST_INC4;
    HAL_DMA_Init(&tx_DMA_Handle);
    //__HAL_LINKDMA(spiHandle, DMA_Handle, tx_DMA_Handle);   // TODO macro
    tx_DMA_Handle.Parent = &SpiHandle;
    SpiHandle.hdmatx = &tx_DMA_Handle;
    SpiHandle.hdmarx = NULL;
}


static void  spiSend(uint8_t *data, uint16_t bytes) {
    // ? need SPI handle
    HAL_SPI_Transmit_DMA(&SpiHandle, data, bytes);  // TODO
    HAL_DMA_PollForTransfer(&tx_DMA_Handle, HAL_DMA_FULL_TRANSFER , 2000);
    // while (tx_DMA_Handle.Instance->CR & DMA_SxCR_EN);   // spin
}
  
int main() {
    uint32_t us, i;
    
    printf("SystemCoreClock %d  %s %s\n",SystemCoreClock,__TIME__,__DATE__);
    tmr.start();
    CSpin =1;
    for(i=0;i<SPI_BUFF_SIZE;i++) tx_buffer[i]=i;
    PRREG(SPI1->CR1);
    PRREG(SPI1->CR2);
    PRREG(SPI1->SR);

    spiperf(1);
    spiperf(2);
    spiperf(4);
    spiperf(8);
    spiperf(15);
    spiperf(30);
    spiperf(45);

    spiInit();
    dmaInit();
    CSpin = 0;
    us = tmr.read_us();
    spiSend(tx_buffer,SPI_BUFF_SIZE);
    us = tmr.read_us()-us;
    CSpin=1;
    printf("DMAspi  %d us %.2f mbs  %0x\n",us,8.*SPI_BUFF_SIZE/us,SPI1->CR1);
}