SSP (SPI) + DMA on LPC1768

15 Feb 2018

Hi all,

I have a doubt about using DMA and SSP (SPI) to get data from an ADC. I have a version working that uses SSP (SPI), and reads the data from the ADC. The data that comes from the ADC consists of two 16 bits (4 status + 12 data), so 32 bits. The CS select should be 0 (enabled) during those 32 incoming bits.

The reason I am doing SPI with DMA is that I am seeing there is a lag between consecutive spi.write(0x00) calls, so I wanted to shorten it and use a DMA with interrupts.

Right now I am configuring the DMA as follows: - DMA Channel 0 as a Memory to Peripheral, sending a buffer of 4 bytes (0x00) to the SPI (Data Register) - DMA Channel 1 as a Peripheral to Memory, sending from the DR (Data Register) to another 4 bytes buffer.

The problem is that the processor stops responding after enabling the DMA_CH_1.

Do you guys have any thoughts? Below you'll find the configuration. And thanks in advance :)

config_dma

void setupDma() {
    setupDmaTx();
    setupDmaRx();
    LPC_SSP0->DMACR |= 
                    (1UL << 0) // Enable RX DMA
                    | (1UL << 1) // Enable TX DMA)
                    ;
    NVIC_SetVector(DMA_IRQn, (uint32_t)irqDma);
    NVIC_EnableIRQ(DMA_IRQn);
}

config_dma_tx

void setupDmaTx() {
    int channel = DMA_CHANNEL_TX; // DMA_CHANNEL_TX = 0
    // Initialize the GPDMA corresponding channel
    dmaChannelTx = (LPC_GPDMACH_TypeDef *) LPC_GPDMACH0;
    
    // reset GPDMA interrupts for the specific channel
    LPC_GPDMA->DMACIntTCClear = (1UL << channel) & 0xFF;
    LPC_GPDMA->DMACIntErrClr = (1UL << channel) & 0xFF;
    
    // Clear channel control and configuration registers
    dmaChannelTx->DMACCControl = 0x00;
    dmaChannelTx->DMACCConfig = 0x00;

    // set source address to the buffer of 0x00
    dmaChannelTx->DMACCSrcAddr = (uint32_t)&spi_to_send;
    // set the destination address as the SSP0 (SPI) Data Register
    dmaChannelTx->DMACCDestAddr = (uint32_t)&LPC_SSP0->DR;

    // LLI
    dmaChannelTx->DMACCLLI = 0;

    // Setup the Control Register
    dmaChannelTx->DMACCControl = 
                            (sizeof(spi_to_send) & 0xFFF)   // Transfer size (11:0)
                            | ((0x00 & 0x7) << 12)          // Source burst size (SPI is 4)
                            | ((0x00 & 0x7) << 15)          // Destination burst size (SPI is 4)
                            | ((0x00 & 0x7) << 18)          // Source transfer width (SPI is byte)
                            | ((0x00 & 0x7) << 21)          // Destination transfer width (SPI is byte)
                            | (1UL << 26)                   // Source increment after each transfer
                            | (1UL << 31)                   // Terminal count interrupt enable
                            ;
    // Enable GPDMA channel
    LPC_GPDMA->DMACConfig |= 1UL;
    // while (!(LPC_GPDMA->DMACConfig & 1UL));

    // Calculate the absolute value for connection number
    uint32_t tmpDst = 0UL;
    tmpDst = ((tmpDst > 15) ? (tmpDst - 8) : tmpDst);
    

    dmaChannelTx->DMACCConfig = 
                            ((0x00 & 0x1F) << 1)        // Connection of source peripheral (0)
                            | ((tmpDst & 0x1F) << 6)    // Connection of destination peripheral (0) SSP0 TX
                            | ((0x01 & 0x03) << 11)     // Transfer type: Memory to Peripheral
                            | (1UL << 14)               // Enable interrupt mask
                            | (1UL << 15)               // Enable Interrupt error mask
                            ;
}

config_dma_rx

void setupDmaRx() {
   int channel = DMA_CHANNEL_RX; //  DMA_CHANNEL_RX = 1
    // Initialize the GPDMA corresponding channel
    LPC_GPDMACH_TypeDef *dmaChannelRx = (LPC_GPDMACH_TypeDef *) LPC_GPDMACH1;
    
    // reset GPDMA interrupts for the specific channel
    LPC_GPDMA->DMACIntTCClear |= (1UL << channel) & 0xFF;
    LPC_GPDMA->DMACIntErrClr |= (1UL << channel) & 0xFF;
    
    // Clear channel control and configuration registers
    dmaChannelRx->DMACCControl = 0x00;
    dmaChannelRx->DMACCConfig = 0x00;

    // set source address as the SSP0 (SPI) Data Register
    dmaChannelRx->DMACCSrcAddr = (uint32_t)&LPC_SSP0->DR;
    // set the destination address to the buffer
    dmaChannelRx->DMACCDestAddr = (uint32_t)&buffer;

    // LLI
    dmaChannelRx->DMACCLLI = 0;

    // Setup the Control Register
    dmaChannelRx->DMACCControl = 
                            (sizeof(buffer) & 0xFFF)        // Transfer size (11:0)
                            | ((0x00 & 0x7) << 12)          // Source burst size (SPI is 4) // TODO: 0x01 & 0x7 for source burst
                            | ((0x00 & 0x7) << 15)          // Destination burst size (SPI is 4)
                            | ((0x00 & 0x7) << 18)          // Source transfer width (SPI is byte)
                            | ((0x00 & 0x7) << 21)          // Destination transfer width (SPI is byte)
                            | (1UL << 27)                   // Destination increment after each transfer
                            | (1UL << 31)                   // Terminal count interrupt enable
                            ;
    // Enable DMA channel
    LPC_GPDMA->DMACConfig |= 1;
    // while (!(LPC_GPDMA->DMACConfig & 1UL));

    dmaChannelRx->DMACCConfig = 
                            ((0x01 & 0x1F) << 1)        // Connection of source peripheral (1) SSP0_Rx
                            | ((0x00 & 0x1F) << 6)      // Connection of destination peripheral (0)
                            | ((0x02 & 0x03) << 11)     // Transfer type: Peripheral to Memory
                            | (1UL << 14)               // Enable interrupt mask
                            | (1UL << 15)               // Enable Interrupt error mask
                            ;
}