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.

5438 SPI and DMA

I want to interface an SD/MMC card using SPI interface. on teh 1611 I had no problems doing so by manually servicing the hardware SPI.

On the 5438 I want to use the DMA feature at least for the 512 byte block transfers, so transfers are executed as fast as possible and maybe completely in the background. After all, this is what DMA was designed for. And with the SPI hardware running at MCLK, it takes only 8 MCLK cycles to transfer a single byte.

Whil is is possible to write hand-optimized code that can feed the SPI at the desired speed, this code would fail if teh SPI speed is lower than MCLK. On the other side, any code that actually checks for the transmit/receive IFGs (and even more using an ISR for this job) would be significantly slow down teh transmission (in case of an ISR by a magnitude). This is where DMA comes to play.

BUT

Usage of the DMA controller is explained very poorly in the documentation. All information is spread over many different locations and seems to be incomplete. Also, my experiences with typos and wrong data due to copy/paste errors or whatever, made me question the correctness of any information I find in the datasheets. So if something does not work as intended, question is: is it my code or is it a typo in the datasheet?

The actual problem is the following: I set up the DMA controller and SPI as required by the datasheet, and nothing happens. No DMA is triggered.

When I set the DMA trigger to manual trigger (DMAREQ), check the SPIs IFGs manually (no, interrupts are not enabled - I found a small sentence somewhere stating that this will prohibit DMA triggering, and I don't use ISRs for SPI anyway) and then set DMAREQ, the transfers are executed as expected. Albeit sloooow of course.

I tried using USCI0A, 0B, 1A, all to no avail. I also tried (with USCI0B) all 32 triggers, also nothing worked.

The trigger table (when searching for MSP and DMA at TI and though the internet, I always found exactly the same table, has any device a different one?) does not list DMA triggers for USCI2 and 3 at all. Is it not possible to do DMA with them?

 

Posting my code would be of no use, as it is modular and spreads over several header and code files. But here is the basic setup:

DMA channel 0: source = (UCxy)RXBUF, destination = memory buffer, size=512, DMA0CTL=DMASBDB|DMADSTINCR_3|DMADT_SINGLE

DMA channel1: source = memory buffer+1, destination= TXBUF, size=511, DMA1CTL=DMASBDB|DMASRCINCR_3|DMADT_SINGLE

I make a dummy read from RXBUF to clear RXIFG, then set the DMAEN bits for both channels.

the transfer is started by writing the content of TX memory buffer to TXBUF. This clears and re-sets TXIFG After it has been transfered, RXIFG is flagged and nothing happens. No matter which triggers I set in DMACTL0.

I tried to manually trigger DMA channel 1 (TX) to test various triggers for channel 0 (RX) , but it gets never triggered when the first byte arrives in RXBUF and RXIFG is set.

The documentation only talks about I2C and DMA. Does this mean that DMA is only possible for I2C? But then, USCIAx does not have I2C, yet there are triggers for it listed in the trigger table.

So the question remains: how do I make USCIA/B0/1/2/3 RX/TXIFG trigger an DMA transfer?

In the last years, I wrote a LOT of low level code for several MSPs, Atmels and PICs, but this one is really killing me. And since this is DMA, neither debugging code nor a debugger is of any help. Perhaps an electron microscope... ;)

  • There is a F5438 code example that uses the DMA with SPI which you can start off with.

    http://www.ti.com/lit/zip/slac166

    Find for msp430x54xdma_03.c

    This code example leverages 2 DMA where 1 DMA is used to TX and the other to RX.

    Regards,

    William

  • Be aware tha not all USCI channels provide DMA triggers on the 5438.

    I am using UCA1 in SPI mode to transmit an array of 1024 bytes to a remote 128x64 graphical display via DMA.

    Here is my init code

    // Config UCA1 for Ground Level Display
        if( GLD_CONTROL ) { 
            UCA1CTL1  = UCSWRST;
         UCA1CTL1 |= UCSSEL_3;                  // SMCLK (7.995392 MHz) 
         UCA1CTL0  = UCPEN + UCPAR;        // 8E1 (MCG 1350)
         UCA1BR0   = 0x45;          // 115200 Baud (MCG 1350)
         UCA1BR1   = 0; 
         UCA1MCTL  = UCBRS_4 + UCBRF_0;                          // Modulation UCBRSx=4, UCBRFx=0
         UCA1CTL1 &= ~UCSWRST;                         // Initialize USCI state machine  

            GLD_RS485_DE_HI;
            GLD_RS485_RE_HI;

            // Setup DMA Channel 1 For LCD Array Transfer to GLD
            DMA1CTL  = 0;                                  // Disable DMA Channel 1
         DMACTL0 |= DMA1TSEL_21;                         // Select UCA1TXIFG As DMA Trigger

         DMA1CTL |= DMASRCINCR_3;                       // Increment Source Addr After DMA

            DMA1CTL |= DMADT_0;                                     // Single Transfer
         DMA1CTL |= ( DMADSTBYTE + DMASRCBYTE );          // Byte To Byte Transfer
         DMA1DA   = ( INT32U )&UCA1TXBUF;            // Set DMA Dest Address Register
        }

    My display update code


    void lcd_update(unsigned char top, unsigned char bottom)
    {
     unsigned char x, err, e;
     unsigned char y;
     unsigned char yt;
     unsigned char yb;
     unsigned char *colptr;
        unsigned int j;

        //  DMA Start to GLD
        if( GLD_CONTROL ) { 
            GLD_RS485_DE_HI;

            DMA1SA    = ( INT32U )l_display_array;              // Set DMA Source Address Register
         DMA1SZ    = 1024;                            // Block Size
         DMA1CTL  |= DMAEN;                               // Enable DMA Channel 1

            UCA1IFG &= ~UCTXIFG;          ATTENTION YOU MIGHT NEED THESE LINES TO PRIME THE PUMP SO TO SPEAK !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            UCA1IFG |= UCTXIFG; 
        }
    //  DMA End

     yt = top >> 3;    /* setup bytes of range */
     yb = bottom >> 3;
     
     LCD_CS_LO;
     
     for(y = yt; y <= yb; y++)
     { 
        LCD_RS_LO;              
       
      PutSerialToLCD( LCD_SET_PAGE+y ); /* set page */
      PutSerialToLCD( LCD_SET_COL_HI+0 ); /* set column 0 */
      PutSerialToLCD( LCD_SET_COL_LO+0 );
        
      colptr = &l_display_array[y][0];
      
      LCD_RS_HI;
      
      for (x=0; x < X_BYTES; x++)
      {
    //     PutSerialToLCD( *colptr++ ); 
                UCB1TXBUF = *colptr++;
      }
     }
     LCD_CS_HI;  


        if( GLD_CONTROL ) { 

            // Wait for GLD update to finish
            while( ( DMA1CTL & DMAIFG ) == 0 ); 

            while( UCA1STAT & UCBUSY );

    #if 1
            // Setup to Receive the IR Key from the GLD

            GLD_RS485_DE_LO;    // DE=0
            GLD_RS485_RE_LO;     // RE=0
            x = UCA1RXBUF;      // Dummy read JIC
            UCA1_RX_INT_ENABLE;

            e = OS_TRUE;
            for( j = 0; j < 250; j++ ) {
                if( ( x = CommGetInBufCnt( COMM2 ) ) == 2 ) {
                    x = CommGetChar( COMM2, 0, &err );
                    y = CommGetChar( COMM2, 0, &err );
                    if( x == y ) {
                        if( x <= 15 ) {
                            IR_KeyValg = x;
                            e = OS_FALSE;
                        }
                    }
                    break;
                }
            }

            if( e ) {
                IR_KeyValg = 9;          // NO-KEY Value
                CommInit( COMM2 );
            }

            GLD_RS485_RE_HI;      // RE=1
            UCA1_RX_INT_DISABLE;
    #endif

        }

     
    }

    Hope this helps

     

     

     

  • Correction I am using UART mode for the second LCD but SPI is very similar.

  •  

    Thanks for the answers. I think I pinned the problem down. It is the access to the DMACTL0 register.

    As I said, my code is modular. So you call a function to 'lock' a DMA channel. Depending on hardware and already locked dma channels, you get back a DMA channel number for further use.

    Next step is to initialize the DMA channel with all the required information. So you call DmaInit with the locked DMA channel nr. and the source/destination etc. data. And the trigger.

    Since you can program every DMA channel independently, DmaInit is not writing to DMACTL0, but rather to DMACTL0_L, DMACTL0_H etc, whose addresses are stored in an array.

    When compiling, teh resulting assembly code is

      39:../EPOS_COMMON_3/hwdma.c ****   *(DMATRIG[channel])=trigger;
     350 012a 0F46        mov r6, r15
     351 012c 0F5F        rla r15
     352 012e 1A4F 0000   mov DMATRIG(r15), r10
     353 0132 CA45 0000   mov.b r5, @r10

    where r5 holds the channel nr and DMATRIG is defined as

    volatile unsigned char * const DMATRIG[MAX_DMA]={&DMACTL0_L,&DMACTL0_H,&DMACTL1_L};

    which correctly shows up as

     207                .global DMATRIG
     208                 .p2align 1,0
     209                 .type DMATRIG,@object
     210                 .size DMATRIG,6
     211                DMATRIG:
     212 0018 0005        .short 0x500
     213 001a 0105        .short 0x500 + 0x01
     214 001c 0205        .short 0x500 +0x02

    so in the above code, for DMA channel 1, DMATRIG(r15) resolves to 0x501. But the next command, mov.b r5,@r10, actually moves the byte to 0x500, so the trigger for channel 1 will always overwrite the trigger for channel 0, and channel 1 will get DMAREQ as trigger.

    I know that any word access to an unaligned address accesses the aligned address below, silently ignoring the LSB of the address. This is (somewhat hidden) documented somewhere. But this is a BYTE access - to an uneven indirect address. i verified that r10 indeed contains 0x0501, but the byte gets written to 0x0500.

    So the mysterium of the non-working SPI has been resolve, uncovering another one:

    Does the processor too ignore the LSB of the address here? Or is the DMACTL0 register 16 bit access only, taking any byte-access as a write to its low-byte?

    In all examples, DMACTL0 is configured for DMA0 and DMA1 together (or for DMA0 only) with a single word access, so this problem was never hit.

    After replacing the indirect access by a swirch/case structure (which is BAD for modularity), it seems that any byte write access to DMACTL0_H instead goes to DMACTL0_L. While the documentation clearly states that all registers are byte and word access and the tables explicitely list _L and _H byte sub-registers.

    from SLAU208E:

     

     

    All registers have word or byte register access. For a generic register ANYREG, the suffix"_L" (ANYREG_L) refers to the lower byte of the register (bits 0 through 7). The suffix "_H"(ANYREG_H) refers to the upper byte of the register (bits 8 through 15).

    So either the documentation is wrong and must be altered, or it is a silicon bug for the errata sheet.

  • For what it's worth, I experienced the exact same problem trying to use DMA channel 1 and accessing DMACTL0 with byte access.  My solution was to adapt the code to use word addressing only.

  • It's not a big problem to circumvent it by reading the DMACTLx register, erase the desired 8 bits and add the new trigger. But it makes the code bigger, slower (sometimes every CPU cycle counts, this is why one wants to use DMA), looks ugly, and, most of all, you need to KNOW that it is necessary despite of the documentation telling otherwise. :(

  • Jens-Michael Gross said:

    I want to interface an SD/MMC card using SPI interface. on teh 1611 I had no problems doing so by manually servicing the hardware SPI.

    On the 5438 I want to use the DMA feature at least for the 512 byte block transfers, so transfers are executed as fast as possible and maybe completely in the background. After all, this is what DMA was designed for. And with the SPI hardware running at MCLK, it takes only 8 MCLK cycles to transfer a single byte.

    Whil is is possible to write hand-optimized code that can feed the SPI at the desired speed, this code would fail if teh SPI speed is lower than MCLK. On the other side, any code that actually checks for the transmit/receive IFGs (and even more using an ISR for this job) would be significantly slow down teh transmission (in case of an ISR by a magnitude). This is where DMA comes to play.

    BUT

    Usage of the DMA controller is explained very poorly in the documentation. All information is spread over many different locations and seems to be incomplete. Also, my experiences with typos and wrong data due to copy/paste errors or whatever, made me question the correctness of any information I find in the datasheets. So if something does not work as intended, question is: is it my code or is it a typo in the datasheet?

    The actual problem is the following: I set up the DMA controller and SPI as required by the datasheet, and nothing happens. No DMA is triggered.

    When I set the DMA trigger to manual trigger (DMAREQ), check the SPIs IFGs manually (no, interrupts are not enabled - I found a small sentence somewhere stating that this will prohibit DMA triggering, and I don't use ISRs for SPI anyway) and then set DMAREQ, the transfers are executed as expected. Albeit sloooow of course.

    I tried using USCI0A, 0B, 1A, all to no avail. I also tried (with USCI0B) all 32 triggers, also nothing worked.

    The trigger table (when searching for MSP and DMA at TI and though the internet, I always found exactly the same table, has any device a different one?) does not list DMA triggers for USCI2 and 3 at all. Is it not possible to do DMA with them?

     

    Posting my code would be of no use, as it is modular and spreads over several header and code files. But here is the basic setup:

    DMA channel 0: source = (UCxy)RXBUF, destination = memory buffer, size=512, DMA0CTL=DMASBDB|DMADSTINCR_3|DMADT_SINGLE

    DMA channel1: source = memory buffer+1, destination= TXBUF, size=511, DMA1CTL=DMASBDB|DMASRCINCR_3|DMADT_SINGLE

    I make a dummy read from RXBUF to clear RXIFG, then set the DMAEN bits for both channels.

    the transfer is started by writing the content of TX memory buffer to TXBUF. This clears and re-sets TXIFG After it has been transfered, RXIFG is flagged and nothing happens. No matter which triggers I set in DMACTL0.

    I tried to manually trigger DMA channel 1 (TX) to test various triggers for channel 0 (RX) , but it gets never triggered when the first byte arrives in RXBUF and RXIFG is set.

    The documentation only talks about I2C and DMA. Does this mean that DMA is only possible for I2C? But then, USCIAx does not have I2C, yet there are triggers for it listed in the trigger table.

    So the question remains: how do I make USCIA/B0/1/2/3 RX/TXIFG trigger an DMA transfer?

    In the last years, I wrote a LOT of low level code for several MSPs, Atmels and PICs, but this one is really killing me. And since this is DMA, neither debugging code nor a debugger is of any help. Perhaps an electron microscope... ;)

    Wow Gens-Michael! This is the first time seeing you asking a question! Always answering! ;)

  • CaEngineer said:
    Wow Gens-Michael! This is the first time seeing you asking a question! Always answering! ;)

    Wow, you found a 4 (!!!) year old thread. Just FYI, most guys start posting in a support forum because they need advice and switch to helping other ppl later.

  • CaEngineer said:
    Wow Gens-Michael! This is the first time seeing you asking a question! Always answering! ;)

    We can learn a lot from JMG - including how to correctly ask a question :)

  • CaEngineer said:
    Wow Gens-Michael! This is the first time seeing you asking a question!

    Actually, it was the first time, anyone has seen anything from me – this is my thread #1 :)

**Attention** This is a public forum