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.

MSP430FR5994: DMA doesn't work after being re-enabled

Expert 3520 points
Part Number: MSP430FR5994

Hi,

I am having similar problem with what was described in this e2e thread.

The erratasheet doesn't mention anything about the DMA. So I wonder what I should do to make the DMA work after being stopped and re-enabled.

Context:

- Use ADC MEM0 to convert channel 1. The ADC is triggered by TimerA0 every 312.5us.

- Use DMA to move 64 consecutive results to a buffer in SRAM. The DMA works well.

- Disable the DMA after 64 transfers

- Use ADC MEM0 to convert channel 2 once

- Re-enable DMA

- Use ADC MEM0 to convert channel 1, again. Use DMA to move 64 consecutive results to a buffer in SRAM. This time the DMA doesn't work (no transfer happens).

Below is the code I used.

#include <msp430fr5994.h>
#include <stdint.h>
#define TRANSFER_SIZE   64
    unsigned int result_A3 = 0;
    unsigned int result_array[100];
void InitClock()
{
    // Clock System Setup
    CSCTL0_H = CSKEY_H;                     // Unlock CS registers
    CSCTL1 = DCOFSEL_0;                     // Set DCO to 1MHz
    //    CSCTL2 = SELA__LFMODCLK | SELS__DCOCLK | SELM__DCOCLK;
    // Set ACLK = 32kHz crystal, SMCLK = MCLK = DCO
    CSCTL2 = SELA__LFXTCLK | SELS__DCOCLK | SELM__DCOCLK;
    // Per Device Errata set divider to 4 before changing frequency to
    // prevent out of spec operation from overshoot transient
    CSCTL3 = DIVA__4 | DIVS__4 | DIVM__4;   // Set all corresponding clk sources to divide by 4 for errata
    CSCTL1 = DCOFSEL_3;                     // Set DCO to 4MHz

    // Delay by ~10us to let DCO settle. 60 cycles = 20 cycles buffer + (10us / (1/4MHz))
    __delay_cycles(100);
    CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1;   // MCLK = 4MHz, SMCLK = 4MHz, ACLK = 4MHz
    CSCTL0_H = 0;                           // Lock CS Registers
}

void InitTimer()
{
    /*
    TA0CCR0 = 1249;
    TA0CCTL1 = OUTMOD_3;    // TACCR1 output mode = set/reset
    TA0CCR1 = 1200;                             // TACCR1 PWM Duty Cycle
    TA0CTL = TASSEL__SMCLK | MC__UP;         // SMCLK, up mode*/

    TA0CCTL0 = CCIE;        // TACCR0 interrupt enabled
    TA0CCR0 = 1249;         // trigger cycle for ADC is 312.5us = (4MHz/3200)

    TA0CCTL1 = OUTMOD_3;    // TACCR1 output mode = set/reset
    TA0CCR1 = 1200;         // TACCR1 is the trigger source for ADC

    TA0CTL = TASSEL__SMCLK | MC__UP;         // SMCLK, up mode
}

void InitDMA()
{
    /*
    // Configure DMA channel 0
    __data20_write_long((uintptr_t) &DMA0SA,(uintptr_t) 0x1C20);
                                            // Source block address
    __data20_write_long((uintptr_t) &DMA0DA,(uintptr_t) 0x1C40);
                                            // Destination single address
    DMA0SZ = 16;                            // Block size
    DMA0CTL = DMADT_5 | DMASRCINCR_3 | DMADSTINCR_3; // Rpt, inc */

    DMACTL0 |= DMA0TSEL__ADC12IFG;
    __data16_write_addr((unsigned short) &DMA0SA,(unsigned long) &ADC12MEM0);
    __data16_write_addr((unsigned short) &DMA0DA,(unsigned long) &result_array[0]);
    DMA0SZ = TRANSFER_SIZE;                            // 64 conversions
    DMA0CTL = DMADT_4 + DMADSTINCR_3 + DMAEN;
}

void InitADC()
{
    P1SEL1 |= BIT3 + BIT4;       // Configure P1.3 (A3) for ADC light sensor, Configure P1.4 (A4) for ADC batt voltage
    P1SEL0 |= BIT3 + BIT4;

    ADC12CTL1 = 0;
    ADC12CTL0 = 0;

    ADC12CTL0 |= ADC12ON;    // Turn on. No sample or conversion starts

    ADC12CTL1 = ADC12PDIV__1
                + ADC12SHS_1    // TA0 CCR1 triggers conversion
                + ADC12SHP
                + ADC12CONSEQ_2;    // repeated single channel

    ADC12CTL2 = ADC12RES__12BIT;

    ADC12CTL3 = 0;  // start address = MEM0

    ADC12LO = 0;            // comparator hi and lo thresholds
    ADC12HI = 0x0FFF;

    ADC12IFGR0 = 0;         // clear all pending flags if any
    ADC12IFGR1 = 0;
    ADC12IFGR2 = 0;

    ADC12IER0 = 0; // interrupt disabled
    ADC12IER1 = 0;
    ADC12IER2 = 0;

    ADC12MCTL0 = ADC12EOS       // end of sequence
                + ADC12INCH_4;  // A4 single-ended

    ADC12CTL0 |= ADC12ENC;  // enable conversions
}

void InitIO()
{
    // Configure GPIO
    P1OUT = 0;
    P1DIR = BIT0|BIT5;                           // For LED

    // Disable the GPIO power-on default high-impedance mode to activate
    // previously configured port settings
    PM5CTL0 &= ~LOCKLPM5;
}

/**
 * main.c
 */
int main(void)
{
    WDTCTL = WDTPW | WDTHOLD;               // Stop WDT

    InitClock();
    InitIO();

    P1OUT|= BIT5;

    InitADC();
    InitTimer();
    InitDMA();

//    __bis_SR_register(LPM0_bits + GIE);     // Enter LPM0 w/ interrupt
//    while((ADC12IFGR0 & ADC12IFG0) == 0);    // wait for conversion done
    while((DMA0CTL & DMAIFG) == 0);    // wait for transfer done
    _NOP();
    DMA0CTL &= ~DMAEN;                       // Disable DMA0
    
    // stop any pending conversion
    ADC12CTL1 = 0;
    ADC12CTL0 = 0;
    _NOP();

    // select A3
    ADC12CTL0 |= ADC12ON;    // Turn on. No sample or conversion starts
    ADC12CTL1 = ADC12PDIV__1
                + ADC12SHS_0    // SW triggers conversion
                + ADC12SHP
                + ADC12CONSEQ_0;    // single channel, single conversion

    ADC12IFGR0 = 0;         // clear all pending flags if any
    ADC12IFGR1 = 0;
    ADC12IFGR2 = 0;

    ADC12IER0 = 0; // interrupt disabled
    ADC12IER1 = 0;
    ADC12IER2 = 0;

    ADC12MCTL0 = ADC12EOS       // end of sequence
                + ADC12INCH_3;  // A3 single-ended

    ADC12CTL0 |= ADC12ENC | ADC12SC;  // enable and start conversion

    while((ADC12IFGR0 & ADC12IFG0) == 0);    // wait for conversion done
    result_A3 = ADC12MEM0;
    _NOP();

    P1OUT&= ~BIT5;

    ADC12IFGR0 = 0;         // clear all pending flags if any
    ADC12IFGR1 = 0;
    ADC12IFGR2 = 0;

    // re-init DMA
    DMACTL0 |= DMA0TSEL__ADC12IFG;
    DMA0CTL = DMADT_4 + DMADSTINCR_3 + DMAEN;

    InitADC();

    while((DMA0CTL & DMAIFG) == 0);    // wait for transfer done
    _NOP();

    while(1);
}

  • This was discussed more recently over here, but I don't think there was a conclusion there either:

    https://e2e.ti.com/support/microcontrollers/msp430/f/166/p/919200/3426334

    As near as I could tell, the singleton conversion step (without DMA) caused some status to be routed to the DMA unit and stuck there, such that it was convinced that the trigger was high even after it had gone low (ergo no edge possible). The DMALEVEL trick allowed it to process the trigger and go back to "reality". I'm pretty sure that behavior is unintended; I don't know why it's not considered an Erratum.

    Outside of the DMALEVEL trick (which is pretty clumsy), I guess the only workaround is to avoid it, e.g. by doing the singleton conversion using DMA as well.

**Attention** This is a public forum