DMA SPI achieves wire speed, 44.3 mbs for block transfers Proof-of-concept
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@0:6242d93d3c51, 2015-11-10 (annotated)
- Committer:
- manitou
- Date:
- Tue Nov 10 12:07:03 2015 +0000
- Revision:
- 0:6242d93d3c51
DMA SPI proof of concept, wire speed 44.3 mbs
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
manitou | 0:6242d93d3c51 | 1 | //nucleo DMA SPI from pyboard SPI proof of concept |
manitou | 0:6242d93d3c51 | 2 | // need DMA channel for TX and another for RX |
manitou | 0:6242d93d3c51 | 3 | //tx DMA2_Stream5, DMA_CHANNEL_3 rx DMA2_Stream2, DMA_CHANNEL_3 |
manitou | 0:6242d93d3c51 | 4 | // SPI1 and 4 max 45mbs SPI3 4 max 22.5mbs |
manitou | 0:6242d93d3c51 | 5 | #include "mbed.h" |
manitou | 0:6242d93d3c51 | 6 | |
manitou | 0:6242d93d3c51 | 7 | #define PRREG(z) printf(#z" 0x%x\n",z) |
manitou | 0:6242d93d3c51 | 8 | Timer tmr; |
manitou | 0:6242d93d3c51 | 9 | DigitalOut CSpin(D10); |
manitou | 0:6242d93d3c51 | 10 | SPI spi(SPI_MOSI, SPI_MISO, SPI_SCK); // mosi, miso, sclk SPI1 |
manitou | 0:6242d93d3c51 | 11 | |
manitou | 0:6242d93d3c51 | 12 | #define SPI_BUFF_SIZE 1024 |
manitou | 0:6242d93d3c51 | 13 | uint8_t rx_buffer[SPI_BUFF_SIZE]; |
manitou | 0:6242d93d3c51 | 14 | uint8_t tx_buffer[SPI_BUFF_SIZE]; |
manitou | 0:6242d93d3c51 | 15 | |
manitou | 0:6242d93d3c51 | 16 | void spiperf(int mhz) { |
manitou | 0:6242d93d3c51 | 17 | int i, us; |
manitou | 0:6242d93d3c51 | 18 | |
manitou | 0:6242d93d3c51 | 19 | spi.frequency(mhz*1000000); |
manitou | 0:6242d93d3c51 | 20 | CSpin=0; |
manitou | 0:6242d93d3c51 | 21 | us = tmr.read_us(); |
manitou | 0:6242d93d3c51 | 22 | for(i=0;i<SPI_BUFF_SIZE;i++) spi.write(tx_buffer[i]); |
manitou | 0:6242d93d3c51 | 23 | us = tmr.read_us()-us; |
manitou | 0:6242d93d3c51 | 24 | CSpin=1; |
manitou | 0:6242d93d3c51 | 25 | printf("spi %d mhz %d us %.2f mbs %0x\n",mhz,us,8.*SPI_BUFF_SIZE/us,SPI1->CR1); |
manitou | 0:6242d93d3c51 | 26 | } |
manitou | 0:6242d93d3c51 | 27 | |
manitou | 0:6242d93d3c51 | 28 | // need to re-create SPI firmware to access SPI handle |
manitou | 0:6242d93d3c51 | 29 | static SPI_HandleTypeDef SpiHandle; |
manitou | 0:6242d93d3c51 | 30 | |
manitou | 0:6242d93d3c51 | 31 | static void spiInit() { |
manitou | 0:6242d93d3c51 | 32 | SpiHandle.Instance = SPI1; |
manitou | 0:6242d93d3c51 | 33 | |
manitou | 0:6242d93d3c51 | 34 | __HAL_SPI_DISABLE(&SpiHandle); |
manitou | 0:6242d93d3c51 | 35 | |
manitou | 0:6242d93d3c51 | 36 | SpiHandle.Init.Mode = SPI_MODE_MASTER; |
manitou | 0:6242d93d3c51 | 37 | SpiHandle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16; // 2 is 45 mhz 32 is 2.81mhz |
manitou | 0:6242d93d3c51 | 38 | SpiHandle.Init.Direction = SPI_DIRECTION_2LINES; |
manitou | 0:6242d93d3c51 | 39 | SpiHandle.Init.CLKPhase = SPI_PHASE_1EDGE; // mode 0 |
manitou | 0:6242d93d3c51 | 40 | SpiHandle.Init.CLKPolarity = SPI_POLARITY_LOW; |
manitou | 0:6242d93d3c51 | 41 | SpiHandle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED; |
manitou | 0:6242d93d3c51 | 42 | SpiHandle.Init.CRCPolynomial = 7; |
manitou | 0:6242d93d3c51 | 43 | SpiHandle.Init.DataSize = SPI_DATASIZE_8BIT; |
manitou | 0:6242d93d3c51 | 44 | SpiHandle.Init.FirstBit = SPI_FIRSTBIT_MSB; |
manitou | 0:6242d93d3c51 | 45 | SpiHandle.Init.NSS = SPI_NSS_SOFT; |
manitou | 0:6242d93d3c51 | 46 | SpiHandle.Init.TIMode = SPI_TIMODE_DISABLED; |
manitou | 0:6242d93d3c51 | 47 | |
manitou | 0:6242d93d3c51 | 48 | if (HAL_SPI_Init(&SpiHandle) != HAL_OK) { |
manitou | 0:6242d93d3c51 | 49 | error("Cannot initialize SPI"); |
manitou | 0:6242d93d3c51 | 50 | } |
manitou | 0:6242d93d3c51 | 51 | |
manitou | 0:6242d93d3c51 | 52 | __HAL_SPI_ENABLE(&SpiHandle); |
manitou | 0:6242d93d3c51 | 53 | } |
manitou | 0:6242d93d3c51 | 54 | |
manitou | 0:6242d93d3c51 | 55 | DMA_HandleTypeDef tx_DMA_Handle, rx_DMA_Handle; |
manitou | 0:6242d93d3c51 | 56 | |
manitou | 0:6242d93d3c51 | 57 | static void dmaInit() { |
manitou | 0:6242d93d3c51 | 58 | __DMA2_CLK_ENABLE(); |
manitou | 0:6242d93d3c51 | 59 | tx_DMA_Handle.Instance = DMA2_Stream5; |
manitou | 0:6242d93d3c51 | 60 | |
manitou | 0:6242d93d3c51 | 61 | // Need to deinit DMA first |
manitou | 0:6242d93d3c51 | 62 | tx_DMA_Handle.State = HAL_DMA_STATE_READY; |
manitou | 0:6242d93d3c51 | 63 | HAL_DMA_DeInit(&tx_DMA_Handle); |
manitou | 0:6242d93d3c51 | 64 | |
manitou | 0:6242d93d3c51 | 65 | tx_DMA_Handle.Init.Channel = DMA_CHANNEL_3; |
manitou | 0:6242d93d3c51 | 66 | tx_DMA_Handle.Init.Direction = DMA_MEMORY_TO_PERIPH; |
manitou | 0:6242d93d3c51 | 67 | tx_DMA_Handle.Init.PeriphInc = DMA_PINC_DISABLE; |
manitou | 0:6242d93d3c51 | 68 | tx_DMA_Handle.Init.MemInc = DMA_MINC_ENABLE; |
manitou | 0:6242d93d3c51 | 69 | tx_DMA_Handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; |
manitou | 0:6242d93d3c51 | 70 | tx_DMA_Handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; |
manitou | 0:6242d93d3c51 | 71 | tx_DMA_Handle.Init.Mode = DMA_NORMAL; |
manitou | 0:6242d93d3c51 | 72 | tx_DMA_Handle.Init.Priority = DMA_PRIORITY_LOW; |
manitou | 0:6242d93d3c51 | 73 | tx_DMA_Handle.Init.FIFOMode = DMA_FIFOMODE_DISABLE; |
manitou | 0:6242d93d3c51 | 74 | tx_DMA_Handle.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; |
manitou | 0:6242d93d3c51 | 75 | tx_DMA_Handle.Init.MemBurst = DMA_MBURST_INC4; |
manitou | 0:6242d93d3c51 | 76 | tx_DMA_Handle.Init.PeriphBurst = DMA_PBURST_INC4; |
manitou | 0:6242d93d3c51 | 77 | HAL_DMA_Init(&tx_DMA_Handle); |
manitou | 0:6242d93d3c51 | 78 | //__HAL_LINKDMA(spiHandle, DMA_Handle, tx_DMA_Handle); // TODO macro |
manitou | 0:6242d93d3c51 | 79 | tx_DMA_Handle.Parent = &SpiHandle; |
manitou | 0:6242d93d3c51 | 80 | SpiHandle.hdmatx = &tx_DMA_Handle; |
manitou | 0:6242d93d3c51 | 81 | SpiHandle.hdmarx = NULL; |
manitou | 0:6242d93d3c51 | 82 | } |
manitou | 0:6242d93d3c51 | 83 | |
manitou | 0:6242d93d3c51 | 84 | |
manitou | 0:6242d93d3c51 | 85 | static void spiSend(uint8_t *data, uint16_t bytes) { |
manitou | 0:6242d93d3c51 | 86 | // ? need SPI handle |
manitou | 0:6242d93d3c51 | 87 | HAL_SPI_Transmit_DMA(&SpiHandle, data, bytes); // TODO |
manitou | 0:6242d93d3c51 | 88 | HAL_DMA_PollForTransfer(&tx_DMA_Handle, HAL_DMA_FULL_TRANSFER , 2000); |
manitou | 0:6242d93d3c51 | 89 | // while (tx_DMA_Handle.Instance->CR & DMA_SxCR_EN); // spin |
manitou | 0:6242d93d3c51 | 90 | } |
manitou | 0:6242d93d3c51 | 91 | |
manitou | 0:6242d93d3c51 | 92 | int main() { |
manitou | 0:6242d93d3c51 | 93 | uint32_t us, i; |
manitou | 0:6242d93d3c51 | 94 | |
manitou | 0:6242d93d3c51 | 95 | printf("SystemCoreClock %d %s %s\n",SystemCoreClock,__TIME__,__DATE__); |
manitou | 0:6242d93d3c51 | 96 | tmr.start(); |
manitou | 0:6242d93d3c51 | 97 | CSpin =1; |
manitou | 0:6242d93d3c51 | 98 | for(i=0;i<SPI_BUFF_SIZE;i++) tx_buffer[i]=i; |
manitou | 0:6242d93d3c51 | 99 | PRREG(SPI1->CR1); |
manitou | 0:6242d93d3c51 | 100 | PRREG(SPI1->CR2); |
manitou | 0:6242d93d3c51 | 101 | PRREG(SPI1->SR); |
manitou | 0:6242d93d3c51 | 102 | |
manitou | 0:6242d93d3c51 | 103 | spiperf(1); |
manitou | 0:6242d93d3c51 | 104 | spiperf(2); |
manitou | 0:6242d93d3c51 | 105 | spiperf(4); |
manitou | 0:6242d93d3c51 | 106 | spiperf(8); |
manitou | 0:6242d93d3c51 | 107 | spiperf(15); |
manitou | 0:6242d93d3c51 | 108 | spiperf(30); |
manitou | 0:6242d93d3c51 | 109 | spiperf(45); |
manitou | 0:6242d93d3c51 | 110 | |
manitou | 0:6242d93d3c51 | 111 | spiInit(); |
manitou | 0:6242d93d3c51 | 112 | dmaInit(); |
manitou | 0:6242d93d3c51 | 113 | CSpin = 0; |
manitou | 0:6242d93d3c51 | 114 | us = tmr.read_us(); |
manitou | 0:6242d93d3c51 | 115 | spiSend(tx_buffer,SPI_BUFF_SIZE); |
manitou | 0:6242d93d3c51 | 116 | us = tmr.read_us()-us; |
manitou | 0:6242d93d3c51 | 117 | CSpin=1; |
manitou | 0:6242d93d3c51 | 118 | printf("DMAspi %d us %.2f mbs %0x\n",us,8.*SPI_BUFF_SIZE/us,SPI1->CR1); |
manitou | 0:6242d93d3c51 | 119 | } |