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.

How do you DMA from RAM to FLASH on a MSP430?

Other Parts Discussed in Thread: MSP430FG4618, CC430F5137

I'm having problems writing a DMA routine that transfers data from a C array, held in RAM, to FLASH memory.

Here's some example code. I'm running it on the MSP430FG4618 on the experimenter board.

 

#include "msp430xG46x.h"

void main(void)
{
    unsigned short inData[16];
    unsigned int i;

    // create some dummy data
    for (i = 0; i < 16; i++)
    {
        inData[i] = i + 1;
    }

    WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT
    __data16_write_addr((unsigned short)&DMA0SA, (unsigned int)inData);
                                              // Start block address
    __data16_write_addr((unsigned short)&DMA0DA, 0x10000ul);
                                              // Destination block address
    DMA0SZ = 0x0010;                          // Block size
    DMA0CTL = DMADT_5 + DMASRCINCR_3 + DMADSTINCR_3 + DMAEN; // Rpt, inc, enable

    // Setup and erase Flash memory
    // (Rem.: This time is also used to wait for
    //        the voltages getting stabilized)
    FCTL2 = FWKEY + FSSEL1 + FN1;        // SMCLK/3 = ~333kHz
    FCTL3 = FWKEY;                       // Unlock Flash memory for write

    // Erase memory block
    FCTL1 = FWKEY + ERASE;
    __data20_write_char(0x1000ul, 0x00); // Dummy write activates segment erase

    FCTL1 = FWKEY + WRT;                 // Enable Flash write for copying

    DMA0CTL |= DMAREQ;                   // Trigger block transfer

    // Deactivate Flash memory write access
    FCTL1 = FWKEY;                       // Disable Flash write
    FCTL3 = FWKEY + LOCK;                // Lock Flash memory
}

 

It doesn't work. It only writes some random byte to the first word of FLASH.

Any ideas what I've done wrong or missed out?

I know that the x4xx Family User's Guide (SLAU056H) states that "write transfers to the Flash memory succeed if the Flash controller set--up is prior to the DMA transfer and if the Flash is not busy" in Section 10.2.14, but it doesn't seem to be as simple as that. Do I need to do single transfers rather than a block transfer?

Thanks!

Derek

  • There is a voice-recorder demo code for this device (see these files: http://www.ti.com/litv/zip/slac129a). Try taking the main Voice Recorder.c file and editing the DMA portion. As it is it will take triggers from the ADC12 and write to flash. You should be able to edit this to move from RAM -> Flash.

  • That's exactly how I wrote this example. I wanted to use a block transfer instead of Timer B to do the recording to make the code simpler since I have no data to sample and hence no sampling rate, so I took the RAM to RAM DMA block transfer example msp430xG46x_dma_01_CCE.c (slac118c.zip) and added the flash memory control code from Voice_Recorder.c, but making this change has broken the data transfer. If I just do the RAM to RAM transfer the code works fine.

    Does the DMA module still need a timer to control its transfer? My suspicion is that the DMA channel is transferring data faster than the flash clock rather than being controlled by the flash controller. The most elegant solution would be to use the flash controller, however, if this doesn't work than I will have to use another timer to trigger the DMA transfers.

    Derek

  • I think that is a good intuition about the DMA wanting to operate faster than the flash is written. The problem here is there is no flash trigger for the DMA, so if you use the DMAREQ software trigger you have to make sure the timing works out. Using one of the timers is the most common solutions.

  • I've re-implemented the example using a timer:

     

    #include "msp430xG46x.h"

    void main(void)
    {
      unsigned short inData[16];
      volatile unsigned int i;
     
      WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT

      FLL_CTL0 |= XCAP14PF;                     // Configure xtal load capacitance

      // LFXT1 startup delay
      do
      {
        IFG1 &= ~OFIFG;                         // Clear OSCFault flag
        for (i = 0x47FF; i > 0; i--);           // Time for flag to set
      }
      while (IFG1 & OFIFG);                     // OSCFault flag still set?
      __disable_interrupt();                    // Disable interrupts
     
      // create some dummy data
      for (i = 0; i < 16; i++)
      {
         inData[i] = i + 1;
      }  

      // Setup Timer_B for DMA to FLASH
      TBCTL = TBSSEL_2;                         // Use SMCLK as Timer_B source
      TBR = 0;
      TBCCR0 = 100;                             // Initialize TBCCR0
      TBCCR1 = 50;                              // Initialize TBCCR0
      TBCCTL1 = OUTMOD_7;                       // Reset OUT1 on EQU1, set on EQU0

      // set up DMA
       __data16_write_addr((unsigned short)&DMA0SA, (unsigned int)inData);
                                                // Start block address
       __data16_write_addr((unsigned short)&DMA0DA, 0x10000ul);
                                                // Destination block address
      DMA0SZ = 0x0010;                          // Block size
      DMA0CTL = DMADT_0 + DMASRCINCR_3 + DMADSTINCR_3 + DMAEN + DMAIE;
                                                // single xfer, inc s & d,
                                                // enable DMA & interrupt
      DMACTL0 = DMA0TSEL_8;                     // TBCCR0 triggers DMA0

      // Setup and erase Flash memory
      // (Rem.: This time is also used to wait for
      //        the voltages getting stabilized)
      FCTL2 = FWKEY + FSSEL1 + FN1;        // SMCLK/3 = ~333kHz
      FCTL3 = FWKEY;                       // Unlock Flash memory for write
     
      FCTL1 = FWKEY + ERASE;
      __data20_write_char(0x10000ul, 0x00); // Dummy write activates segment erase
      
      FCTL1 = FWKEY + WRT;                  // Enable Flash write for copying
      
      TBCTL |= MC0;                           // Start Timer_B in UP mode
                                              // (counts up to TBCL0)

      // Activate LPM during DMA transfer, wake-up when finished
      __bis_SR_register(LPM0_bits + GIE);     // Enable interrupts, enter LPM0
      __disable_interrupt();                  // Disable interrupts

      // Deactivate Flash memory write access
      FCTL1 = FWKEY;                       // Disable Flash write
      FCTL3 = FWKEY + LOCK;                // Lock Flash memory

      TBCTL = 0;                           // Disable Timer_B
    }

    //------------------------------------------------------------------------------
    // DMA interrupt handler
    //------------------------------------------------------------------------------
    #pragma vector=DMA_VECTOR
    __interrupt void DMA_ISR(void)
    {
      DMA0CTL &= ~DMAIFG;                       // Clear DMA0 interrupt flag
      __bic_SR_register_on_exit(LPM0_bits);     // Exit LPM0 on reti
    }
    //------------------------------------------------------------------------------

     

    Note that Timer B is used to produce a square wave with a period of 100 clock cycles (SMCLK = 1.048576 MHz). I tried to reduce this value but the DMA transfer still didn't work even if the period was 80 cycles. This shows how much faster the DMA transfer is compared with the FLASH memory!

    Also this version uses the DMA interrupt, which is fired when the transfer is complete, and the ISR to handle it.

    Derek

  •   // Setup Timer_B for DMA to FLASH
      TBCTL = TBSSEL_2;                         // Use SMCLK as Timer_B source
      TBR = 0;
      TBCCR0 = 100;                             // Initialize TBCCR0
      TBCCR1 = 50;                              // Initialize TBCCR0
      TBCCTL1 = OUTMOD_7;                       // Reset OUT1 on EQU1, set on EQU0

     

    This part of the code, why did you use Timer B? What is the reason? Can't we use Timer A?

  • ferhat said:

    This part of the code, why did you use Timer B? What is the reason? Can't we use Timer A?

     

    Why not using TimerB? Maybe he's using TimerA for smething else (e.g. a system timer tick)

    The key is the DMA trigger setting:

    DMACTL0 = DMA0TSEL_8;                     // TBCCR0 triggers DMA0

    Change it, and you can use TimerA or (on 54xx devices) TimerA0 or TImerA1.

    It's a shame that there is no FLASH trigger for DMA. It would make writing a block from ram to flash as fast as from a RAM based block writing function. Even a bit faster (no need for a busy-checking loop) Without need to place running code into RAM. I can also imagine some more DMA triggers that could be useful. Including more than one external one.

    p.s.: does anyone know what the DMA does when writing from a word source to a byte destination? Does it two byte writes? Could be useful for stuffing a 12 bit ADC result into the uart TX buffer (provided that there was enough idle time before to empty the TX buffer as well as the TX shift register)

  • Hi Derek ,

        Timer to trigger DMA is working perfectly, Thanks for solution.

    One more thing for your info I tried with 80 cycles ,it's working fine I done experiment on used CC430F5137 chip.It may be heaving improved flash.

    Which one is the best optimized solution  to transfer data from RAM to Flash

    1)using pointer.

    2)Using DMA .

    Please explain.

    Regards

    Kishor

**Attention** This is a public forum