This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430F5308: Handling SPI with DMA

Other Parts Discussed in Thread: MSP430F5308

Hello all,

to achieve a 1 MBit/s SPI data rate with the MSP430F5308, I would rather use DMA to fill the transmit buffer and fetch the data from the receive buffer instead of going the normal way via an ISR. The ISR would have a hard time, given that at 25 MHz, it would run every 200 MCLK ticks. What´s more: I need to transfer 16 bit SPI data (i.e. 2x8 bits), and I understand from the slave´s datasheet that they should come without interruption between them.

I can transfer data via SPI (using UCB1) when writing directly to the transmit register. The IE bit for UCB is not set. The uC is running smoothly, it handles the UART which is also running just fine (i.e. no lockup due to unhandled interrupt).

To get started, I had intended to write two bytes to the TX register using DMA channel 1. What I want is to trigger DMA every time the transmit buffer is empty until DMA1SZ transfers have taken place and then to call the DMA ISR. Unfortunately, this doesn´t work - I get neither SPI transmission nor is the ISR ever called.

Init:

    DMACTL0 |= DMA1TSEL_23;  // UCB1TXIFG as trigger
    DMA1CTL = DMADT_4 + DMASRCINCR_3 + DMADSTBYTE + DMASRCBYTE + DMAIE + DMAEN;
    __data16_write_addr((unsigned short) &DMA1SA,(unsigned long) spi_buffer_send);
    __data16_write_addr((unsigned short) &DMA1DA,(unsigned long) UCB1TXBUF);
    DMA1SZ = 2;

Transmit (called in the main loop):

spi_buffer_send[0] = 0x5A;
spi_buffer_send[1] = 0xA5;
if (!(DMA1CTL & DMAEN)) {
    DMA1SZ = 2;
    DMA1CTL |= DMAEN;
}
if (DMA1CTL & DMAIFG) {
    rxtotal = DMA1CTL;
}

ISR:

#pragma vector=DMA_VECTOR
__interrupt void dma_isr (void) {
    if (DMAIV & DMAIV_DMA0IFG) {
        return;
    }
    if (DMAIV & DMAIV_DMA1IFG) {
        return;
    }
    if (DMAIV & DMAIV_DMA2IFG) {
        return;
    }
    
}

Am I overlooking something, or is the MSP430 simply not able to do what I want?

I have already gone through Google and various code examples, but not come across something that actually helped. My last idea is to switch to level instead of edge triggering, then I am at the end of my wisdom.

Any help is appreciated. Thank you.

Max

  • Hi Max,

    Yes, the MSP430 can do what you want.  And yes, what you want to do makes perfect sense.  Practically speaking, DMA is the only way to go for high-speed SPI.

    Because the DMA trigger is edge sensitive, I use this sequence to kick off a DMA-driven SPI burst:

    UCB1IFG &= ~UCTXIFG;

    UCB1IFG |=  UCTXIFG;

    Seems to me that's all you are missing.

    Also I noticed a small bug in the DMA1DA value you are writing.  You need the address of UCB1TXBUF, not the contents of that register.  In my code, I use the symbol with the underscore appended, as in "UCB1TXBUF_".  You could probably also type cast "&UCB1TXBUF" too.

    If you don't like directly manipulating the TXIFG bit as I do, you can also put the first character into TXBUF yourself and let the DMA channel handle the remaining characters.

    Jeff

  • Hi Jeff,

    you saved my day! With the kickoff sequence, it seems to work - at least the code reaches the breakpoint in the DMA´s ISR. The &UCBTXBUF bug is also fixed, thank you for pointing this out.

    I owe you some coffee...

    Max

  • Hi Jeff,

    you saved my day! With the kickoff sequence, it seems to work - at least the code reaches the breakpoint in the DMA´s ISR. The &UCBTXBUF bug is also fixed, thank you for pointing this out.

    I owe you a cup of coffee...

    Max

  • Maximilian Gauger said:
    I need to transfer 16 bit SPI data (i.e. 2x8 bits), and I understand from the slave´s datasheet that they should come without interruption between them.

    Well, what is the minimum SPI frequency?

    Since SPI is synchronous, and the SPI master generates one clock pulse per bit, any delay between first and second byte could be considered a decrease in bit clock frequency rather than a gap/delay.
    Also, due to the double-buffering of the USCI, the next byte can already be in the TXBUF while the previous byte is still sending. Other than with the older USI module, the USCI allows for seamless SPI operation of multiple bytes.

    Personally, I use busy-waiting for short SPI transfers. Wait for TXIFG is set and then stuff the next byte. I use DMA transfer only for sending/receiving the 512 bytes of an SD card sector. But for the command exchange, I use busy waiting code. With 16MHz SPI clock. :)

**Attention** This is a public forum