SPI DMA based on teensy DmaSpi library
This is proof of concept, porting the teensy 3 DmaSpi library to mbed MK64F. Program does 8-bit DMA SPI transfers. Verified with jumper between MOSI and MISO and with logic analyzer. With SPI clock @12MHz, data rate for 1024 bytes is 10.6 mbs. With SPI clock @30MHz, 22.6 mbs
main.cpp@0:03cb2b9cf5d3, 2016-02-21 (annotated)
- Committer:
- manitou
- Date:
- Sun Feb 21 16:52:03 2016 +0000
- Revision:
- 0:03cb2b9cf5d3
running version
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
manitou | 0:03cb2b9cf5d3 | 1 | // DMA SPI 8-bit frames based on teensy 3 |
manitou | 0:03cb2b9cf5d3 | 2 | // https://github.com/crteensy/DmaSpi |
manitou | 0:03cb2b9cf5d3 | 3 | // jumper MISO to MOSI for verification |
manitou | 0:03cb2b9cf5d3 | 4 | #include "mbed.h" |
manitou | 0:03cb2b9cf5d3 | 5 | |
manitou | 0:03cb2b9cf5d3 | 6 | #define PRREG(z) printf(#z" 0x%x\n",z) |
manitou | 0:03cb2b9cf5d3 | 7 | |
manitou | 0:03cb2b9cf5d3 | 8 | Timer tmr; |
manitou | 0:03cb2b9cf5d3 | 9 | DigitalOut CSpin(D10); |
manitou | 0:03cb2b9cf5d3 | 10 | SPI spi(D11,D12,D13); // mosi, miso, sclk SPI0 |
manitou | 0:03cb2b9cf5d3 | 11 | |
manitou | 0:03cb2b9cf5d3 | 12 | #define SPIHZ 12000000 |
manitou | 0:03cb2b9cf5d3 | 13 | #define TXCHNL 0 |
manitou | 0:03cb2b9cf5d3 | 14 | #define RXCHNL 1 |
manitou | 0:03cb2b9cf5d3 | 15 | #define DMAMUX_SPI0RX 14 |
manitou | 0:03cb2b9cf5d3 | 16 | #define DMAMUX_SPI0TX 15 |
manitou | 0:03cb2b9cf5d3 | 17 | |
manitou | 0:03cb2b9cf5d3 | 18 | #define NBYTES 1024 |
manitou | 0:03cb2b9cf5d3 | 19 | uint8_t rx_buffer[NBYTES]; |
manitou | 0:03cb2b9cf5d3 | 20 | uint8_t tx_buffer[NBYTES]; |
manitou | 0:03cb2b9cf5d3 | 21 | |
manitou | 0:03cb2b9cf5d3 | 22 | volatile int dmadone; |
manitou | 0:03cb2b9cf5d3 | 23 | |
manitou | 0:03cb2b9cf5d3 | 24 | void dmaisr() { |
manitou | 0:03cb2b9cf5d3 | 25 | /// RX dma interrupt |
manitou | 0:03cb2b9cf5d3 | 26 | DMA_CINT = RXCHNL; //clear interrupt |
manitou | 0:03cb2b9cf5d3 | 27 | SPI0_RSER = 0; |
manitou | 0:03cb2b9cf5d3 | 28 | SPI0_SR = 0xFF0F0000; |
manitou | 0:03cb2b9cf5d3 | 29 | dmadone = 1; |
manitou | 0:03cb2b9cf5d3 | 30 | |
manitou | 0:03cb2b9cf5d3 | 31 | } |
manitou | 0:03cb2b9cf5d3 | 32 | |
manitou | 0:03cb2b9cf5d3 | 33 | void dma_init(void) { |
manitou | 0:03cb2b9cf5d3 | 34 | // Enable clock for DMAMUX and DMA |
manitou | 0:03cb2b9cf5d3 | 35 | SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK; |
manitou | 0:03cb2b9cf5d3 | 36 | SIM_SCGC7 |= SIM_SCGC7_DMA_MASK; |
manitou | 0:03cb2b9cf5d3 | 37 | |
manitou | 0:03cb2b9cf5d3 | 38 | DMA_CERQ = RXCHNL; |
manitou | 0:03cb2b9cf5d3 | 39 | DMA_CERQ = TXCHNL; |
manitou | 0:03cb2b9cf5d3 | 40 | |
manitou | 0:03cb2b9cf5d3 | 41 | // Enable TX channel and set SPI0_Tx as DMA request source |
manitou | 0:03cb2b9cf5d3 | 42 | DMAMUX_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(DMAMUX_SPI0TX); |
manitou | 0:03cb2b9cf5d3 | 43 | DMA0->TCD[TXCHNL].DADDR = (uint32_t)&SPI0_PUSHR; |
manitou | 0:03cb2b9cf5d3 | 44 | DMA0->TCD[TXCHNL].SOFF = 1; |
manitou | 0:03cb2b9cf5d3 | 45 | DMA0->TCD[TXCHNL].DOFF = 0; |
manitou | 0:03cb2b9cf5d3 | 46 | DMA0->TCD[TXCHNL].ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0); |
manitou | 0:03cb2b9cf5d3 | 47 | DMA0->TCD[TXCHNL].NBYTES_MLNO = 1; |
manitou | 0:03cb2b9cf5d3 | 48 | DMA0->TCD[TXCHNL].DLAST_SGA=0; |
manitou | 0:03cb2b9cf5d3 | 49 | DMA0->TCD[TXCHNL].SLAST=0; |
manitou | 0:03cb2b9cf5d3 | 50 | DMA0->TCD[TXCHNL].CSR = DMA_CSR_DREQ_MASK; |
manitou | 0:03cb2b9cf5d3 | 51 | |
manitou | 0:03cb2b9cf5d3 | 52 | // Enable SPI0 RX channel |
manitou | 0:03cb2b9cf5d3 | 53 | DMAMUX_CHCFG1 |= DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(DMAMUX_SPI0RX); |
manitou | 0:03cb2b9cf5d3 | 54 | DMA0->TCD[RXCHNL].SADDR = (uint32_t)&SPI0_POPR; // recv |
manitou | 0:03cb2b9cf5d3 | 55 | DMA0->TCD[RXCHNL].SOFF = 0; |
manitou | 0:03cb2b9cf5d3 | 56 | DMA0->TCD[RXCHNL].DOFF = 1; |
manitou | 0:03cb2b9cf5d3 | 57 | DMA0->TCD[RXCHNL].ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0); |
manitou | 0:03cb2b9cf5d3 | 58 | DMA0->TCD[RXCHNL].NBYTES_MLNO = 1; |
manitou | 0:03cb2b9cf5d3 | 59 | DMA0->TCD[RXCHNL].DLAST_SGA=0; |
manitou | 0:03cb2b9cf5d3 | 60 | DMA0->TCD[RXCHNL].SLAST=0; |
manitou | 0:03cb2b9cf5d3 | 61 | DMA0->TCD[RXCHNL].CSR = DMA_CSR_DREQ_MASK | DMA_CSR_INTMAJOR_MASK; |
manitou | 0:03cb2b9cf5d3 | 62 | |
manitou | 0:03cb2b9cf5d3 | 63 | // setup RX ISR |
manitou | 0:03cb2b9cf5d3 | 64 | NVIC_EnableIRQ(DMA1_IRQn); |
manitou | 0:03cb2b9cf5d3 | 65 | NVIC_SetVector(DMA1_IRQn, (uint32_t)&dmaisr); |
manitou | 0:03cb2b9cf5d3 | 66 | } |
manitou | 0:03cb2b9cf5d3 | 67 | |
manitou | 0:03cb2b9cf5d3 | 68 | void spidma(void *txbuff, void *rxbuff, int bytes) { |
manitou | 0:03cb2b9cf5d3 | 69 | CSpin=0; |
manitou | 0:03cb2b9cf5d3 | 70 | dmadone = 0; |
manitou | 0:03cb2b9cf5d3 | 71 | SPI0_SR = 0xFF0F0000; |
manitou | 0:03cb2b9cf5d3 | 72 | SPI0_RSER = SPI_RSER_TFFF_RE_MASK | SPI_RSER_TFFF_DIRS_MASK | SPI_RSER_RFDF_RE_MASK | SPI_RSER_RFDF_DIRS_MASK; |
manitou | 0:03cb2b9cf5d3 | 73 | |
manitou | 0:03cb2b9cf5d3 | 74 | // Set memory address for source and destination |
manitou | 0:03cb2b9cf5d3 | 75 | DMA0->TCD[TXCHNL].SADDR = (uint32_t)txbuff; // xmit |
manitou | 0:03cb2b9cf5d3 | 76 | DMA0->TCD[RXCHNL].DADDR = (uint32_t)rxbuff; |
manitou | 0:03cb2b9cf5d3 | 77 | |
manitou | 0:03cb2b9cf5d3 | 78 | // Current major iteration count |
manitou | 0:03cb2b9cf5d3 | 79 | DMA0->TCD[TXCHNL].BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(bytes); |
manitou | 0:03cb2b9cf5d3 | 80 | DMA0->TCD[TXCHNL].CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(bytes); |
manitou | 0:03cb2b9cf5d3 | 81 | DMA0->TCD[RXCHNL].BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(bytes); |
manitou | 0:03cb2b9cf5d3 | 82 | DMA0->TCD[RXCHNL].CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(bytes); |
manitou | 0:03cb2b9cf5d3 | 83 | |
manitou | 0:03cb2b9cf5d3 | 84 | // Enable request signal for channels |
manitou | 0:03cb2b9cf5d3 | 85 | DMA_SERQ = RXCHNL; |
manitou | 0:03cb2b9cf5d3 | 86 | DMA_SERQ = TXCHNL; |
manitou | 0:03cb2b9cf5d3 | 87 | |
manitou | 0:03cb2b9cf5d3 | 88 | while (!dmadone); // wait for completion |
manitou | 0:03cb2b9cf5d3 | 89 | // while(!(DMA0->TCD[RXCHNL].CSR & BM_DMA_TCDn_CSR_DONE)); // wait |
manitou | 0:03cb2b9cf5d3 | 90 | |
manitou | 0:03cb2b9cf5d3 | 91 | CSpin=1; |
manitou | 0:03cb2b9cf5d3 | 92 | } |
manitou | 0:03cb2b9cf5d3 | 93 | |
manitou | 0:03cb2b9cf5d3 | 94 | void spiperf(int mhz) { |
manitou | 0:03cb2b9cf5d3 | 95 | int i, us; |
manitou | 0:03cb2b9cf5d3 | 96 | |
manitou | 0:03cb2b9cf5d3 | 97 | spi.frequency(mhz*1000000); |
manitou | 0:03cb2b9cf5d3 | 98 | CSpin=0; |
manitou | 0:03cb2b9cf5d3 | 99 | us = tmr.read_us(); |
manitou | 0:03cb2b9cf5d3 | 100 | for(i=0;i<NBYTES;i++) spi.write(tx_buffer[i]); // old school |
manitou | 0:03cb2b9cf5d3 | 101 | us = tmr.read_us()-us; |
manitou | 0:03cb2b9cf5d3 | 102 | CSpin=1; |
manitou | 0:03cb2b9cf5d3 | 103 | printf("spi %d mhz %d us %.2f mbs %d bytes %0x\n",mhz,us,8.*NBYTES/us,NBYTES,SPI0_CTAR0); |
manitou | 0:03cb2b9cf5d3 | 104 | } |
manitou | 0:03cb2b9cf5d3 | 105 | |
manitou | 0:03cb2b9cf5d3 | 106 | int main() { |
manitou | 0:03cb2b9cf5d3 | 107 | int i, errs; |
manitou | 0:03cb2b9cf5d3 | 108 | uint32_t us; |
manitou | 0:03cb2b9cf5d3 | 109 | |
manitou | 0:03cb2b9cf5d3 | 110 | printf("SystemCoreClock %d %s %s\n",SystemCoreClock,__TIME__,__DATE__); |
manitou | 0:03cb2b9cf5d3 | 111 | wait(2.0); |
manitou | 0:03cb2b9cf5d3 | 112 | tmr.start(); |
manitou | 0:03cb2b9cf5d3 | 113 | CSpin=1; |
manitou | 0:03cb2b9cf5d3 | 114 | spi.format (8, 0); |
manitou | 0:03cb2b9cf5d3 | 115 | spi.frequency (SPIHZ); |
manitou | 0:03cb2b9cf5d3 | 116 | |
manitou | 0:03cb2b9cf5d3 | 117 | // Load some exciting data into the buffers |
manitou | 0:03cb2b9cf5d3 | 118 | for (i=0; i<NBYTES; i++) tx_buffer[i] = i; |
manitou | 0:03cb2b9cf5d3 | 119 | |
manitou | 0:03cb2b9cf5d3 | 120 | dma_init(); |
manitou | 0:03cb2b9cf5d3 | 121 | |
manitou | 0:03cb2b9cf5d3 | 122 | |
manitou | 0:03cb2b9cf5d3 | 123 | while (1){ |
manitou | 0:03cb2b9cf5d3 | 124 | // spiperf(SPIHZ/1000000); |
manitou | 0:03cb2b9cf5d3 | 125 | memset(rx_buffer,0,NBYTES); |
manitou | 0:03cb2b9cf5d3 | 126 | us = tmr.read_us(); |
manitou | 0:03cb2b9cf5d3 | 127 | spidma(tx_buffer,rx_buffer,NBYTES); |
manitou | 0:03cb2b9cf5d3 | 128 | us = tmr.read_us()-us; |
manitou | 0:03cb2b9cf5d3 | 129 | errs=0; |
manitou | 0:03cb2b9cf5d3 | 130 | for (i=0; i<NBYTES; i++) if (rx_buffer[i] != i%256) errs++; // MISO to MOSI |
manitou | 0:03cb2b9cf5d3 | 131 | printf("dmaspi %d hz %d us %.2f mbs %d bytes errs %d %0x\n",SPIHZ,us,8.*NBYTES/us,NBYTES,errs,SPI0_CTAR0); |
manitou | 0:03cb2b9cf5d3 | 132 | wait(4.0); |
manitou | 0:03cb2b9cf5d3 | 133 | } |
manitou | 0:03cb2b9cf5d3 | 134 | } |