SPI DMA based on teensy DmaSpi library

Dependencies:   mbed

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

Committer:
manitou
Date:
2016-02-21
Revision:
0:03cb2b9cf5d3

File content as of revision 0:03cb2b9cf5d3:

// DMA SPI 8-bit frames based on teensy 3
// https://github.com/crteensy/DmaSpi
//  jumper MISO to MOSI for verification
#include "mbed.h"

#define PRREG(z) printf(#z" 0x%x\n",z)

Timer tmr;
DigitalOut CSpin(D10);
SPI spi(D11,D12,D13); // mosi, miso, sclk  SPI0

#define SPIHZ 12000000
#define TXCHNL 0
#define RXCHNL 1
#define DMAMUX_SPI0RX 14
#define DMAMUX_SPI0TX 15

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

volatile int dmadone;

void dmaisr() {
    /// RX dma interrupt 
    DMA_CINT = RXCHNL;  //clear interrupt
    SPI0_RSER = 0;
    SPI0_SR = 0xFF0F0000;
    dmadone = 1;
    
}

void dma_init(void) {
    // Enable clock for DMAMUX and DMA
    SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK;
    SIM_SCGC7 |= SIM_SCGC7_DMA_MASK; 

    DMA_CERQ = RXCHNL;
    DMA_CERQ = TXCHNL;
          
    // Enable TX channel and set SPI0_Tx as DMA request source 
    DMAMUX_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(DMAMUX_SPI0TX);
    DMA0->TCD[TXCHNL].DADDR = (uint32_t)&SPI0_PUSHR;
    DMA0->TCD[TXCHNL].SOFF = 1; 
    DMA0->TCD[TXCHNL].DOFF = 0; 
    DMA0->TCD[TXCHNL].ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0);
    DMA0->TCD[TXCHNL].NBYTES_MLNO = 1;
    DMA0->TCD[TXCHNL].DLAST_SGA=0;
    DMA0->TCD[TXCHNL].SLAST=0;
    DMA0->TCD[TXCHNL].CSR = DMA_CSR_DREQ_MASK;
    
    // Enable SPI0 RX channel 
    DMAMUX_CHCFG1 |= DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(DMAMUX_SPI0RX);
    DMA0->TCD[RXCHNL].SADDR = (uint32_t)&SPI0_POPR;     // recv
    DMA0->TCD[RXCHNL].SOFF = 0; 
    DMA0->TCD[RXCHNL].DOFF = 1; 
    DMA0->TCD[RXCHNL].ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0);
    DMA0->TCD[RXCHNL].NBYTES_MLNO = 1;
    DMA0->TCD[RXCHNL].DLAST_SGA=0;
    DMA0->TCD[RXCHNL].SLAST=0;
    DMA0->TCD[RXCHNL].CSR = DMA_CSR_DREQ_MASK | DMA_CSR_INTMAJOR_MASK;

    // setup RX ISR
    NVIC_EnableIRQ(DMA1_IRQn);
    NVIC_SetVector(DMA1_IRQn, (uint32_t)&dmaisr);
}

void spidma(void *txbuff, void *rxbuff, int bytes) {
    CSpin=0;
    dmadone = 0;
    SPI0_SR = 0xFF0F0000;
    SPI0_RSER = SPI_RSER_TFFF_RE_MASK | SPI_RSER_TFFF_DIRS_MASK | SPI_RSER_RFDF_RE_MASK | SPI_RSER_RFDF_DIRS_MASK;

    // Set memory address for source and destination 
    DMA0->TCD[TXCHNL].SADDR = (uint32_t)txbuff;            // xmit
    DMA0->TCD[RXCHNL].DADDR = (uint32_t)rxbuff;
        
    // Current major iteration count
    DMA0->TCD[TXCHNL].BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(bytes);
    DMA0->TCD[TXCHNL].CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(bytes);
    DMA0->TCD[RXCHNL].BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(bytes);
    DMA0->TCD[RXCHNL].CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(bytes);
    
    // Enable request signal for channels
    DMA_SERQ = RXCHNL;
    DMA_SERQ = TXCHNL;
   
   while (!dmadone);  // wait for completion
 //   while(!(DMA0->TCD[RXCHNL].CSR & BM_DMA_TCDn_CSR_DONE)); // wait

    CSpin=1;
}

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

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

int main() {
    int i, errs;
    uint32_t us;
    
    printf("SystemCoreClock %d  %s %s\n",SystemCoreClock,__TIME__,__DATE__);
    wait(2.0);
    tmr.start();
    CSpin=1;
    spi.format (8, 0);
    spi.frequency (SPIHZ);
 
// Load some exciting data into the buffers
    for (i=0; i<NBYTES; i++) tx_buffer[i] = i;

    dma_init();


    while (1){
      //  spiperf(SPIHZ/1000000);
        memset(rx_buffer,0,NBYTES);
        us = tmr.read_us();
        spidma(tx_buffer,rx_buffer,NBYTES);
        us = tmr.read_us()-us;
        errs=0;
        for (i=0; i<NBYTES; i++) if (rx_buffer[i] != i%256) errs++;  // MISO to MOSI
        printf("dmaspi %d hz %d us %.2f mbs %d bytes  errs %d %0x\n",SPIHZ,us,8.*NBYTES/us,NBYTES,errs,SPI0_CTAR0);
        wait(4.0);
    }
}