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.

Change DMA Address While ADC Conversion



Hi,

My code is somehow not working.

What I want is, I have an array (adcDataCollected[3][512]) that ADC conversion data is written via DMA. When DMA interrupt occurs, I would like to change address of DMA destination, so previous ADC data will not be lost.

While changing DMA destination address inside the interrupt, it always writes to a different place.

CRC data shows data is written like:

Writes to adcDataCollected [1] (should be 0), [0] (should be 1), [1] (should be 2), [2] (should be 0), [0] (should be 2), ...

Sometimes it goes like

Writes to adcDataCollected [2] (should be 0), [0] (should be 1), [1] (should be 2), [2] (should be 0), [0] (should be 2), ...

I checked DMA0DA address and even though it is the correct address, data is written to another address in the array.

Can anyone figure out what I'm doing wrong?

Thank you in advance,

 

#include <msp430.h> 
#include "lcd/lcd.h"

// ADC data timer, which is used to collect data in 1 second
const unsigned int adcDatatimer = (unsigned int)((unsigned long)1048576 / (unsigned long)256);
// Data Collected from ADC
volatile int adcDataCollected[3][512] = {0};
// Counter of data collected from ADC
unsigned int adcDataCollectedCounter = 0;

// DMA Interrupt Handler
#pragma vector=DMA_VECTOR
__interrupt void _dmaInterruptHandler(void)
{
  switch(__even_in_range(DMAIV,16))
  {
    case 0: break;
    case 2:                                 // DMA0IFG = DMA Channel 0
      // Stop conversion
      ADC12CTL0 &= ~ADC12ENC;
      
      adcDataCollectedCounter++;
      if (adcDataCollectedCounter >= 3)
        adcDataCollectedCounter -= 3;
        
      __data16_write_addr((unsigned short)&DMA0DA, (unsigned long)adcDataCollected[adcDataCollectedCounter]);
      
      // Start conversion again
      ADC12CTL0 |= ADC12ENC;
      
      __low_power_mode_off_on_exit();
      break;
    case 4: break;                          // DMA1IFG = DMA Channel 1
    case 6: break;                          // DMA2IFG = DMA Channel 2
    case 8: break;                          // DMA3IFG = DMA Channel 3
    case 10: break;                         // DMA4IFG = DMA Channel 4
    case 12: break;                         // DMA5IFG = DMA Channel 5
    case 14: break;                         // DMA6IFG = DMA Channel 6
    case 16: break;                         // DMA7IFG = DMA Channel 7
    default: break;
  }
}

void updateLCD(int a, int b, int c, int d)
{
  lcdBufferClear(0);
  lcdBufferPrintString(0, "A", 0, eLcdPage1);
  lcdBufferPrintString(0, " : ", 50, eLcdPage1);
  lcdBufferPrintInt(0, a, 70, eLcdPage1);
  lcdBufferPrintString(0, "B", 0, eLcdPage3);
  lcdBufferPrintString(0, " : ", 50, eLcdPage3);
  lcdBufferPrintInt(0, b, 70, eLcdPage3);
  lcdBufferPrintString(0, "C", 0, eLcdPage5);
  lcdBufferPrintString(0, " : ", 50, eLcdPage5);
  lcdBufferPrintInt(0, c, 70, eLcdPage5);
  lcdBufferPrintString(0, "D", 0, eLcdPage7);
  lcdBufferPrintString(0, " : ", 50, eLcdPage7);
  lcdBufferPrintInt(0, d, 70, eLcdPage7);
  lcdSendBuffer(0);
}

unsigned short getCRC(unsigned char* buffer, unsigned int length)
{
  // Loop variable
  unsigned long i = 0;
  // CRC
  unsigned short crc = 0;
  // Temp buffer
  unsigned char* pBuffer = buffer;
  
  for (i = length; i; i--, pBuffer++)
  {
    crc += *pBuffer;
  }

  return crc;
}

int main(void)
{
  unsigned short crcs[4] = {0};

  WDTCTL = WDTPW | WDTHOLD;	// Stop watchdog timer
  
  // Disable global interrupts
  __disable_interrupt();
  
  lcdInit();
  
  // ******************************
  // ADC init
  // Select P6.4
  P6SEL |= BIT4;
  // P6.4 input
  P6DIR &= ~BIT4;
  
  //Setup Timer B0
  TB0CCR0 = adcDatatimer;
  TB0CCR1 = adcDatatimer >> 1;
  // Set/Reset Mode
  TB0CCTL1 = OUTMOD_3;
  // SMCLK, Up mode
  TB0CTL = TBSSEL__SMCLK + MC__UP + TBCLR;
  
  // Turn on ADC12, Sampling time, set multiple sample conversion
  ADC12CTL0 = ADC12ON | ADC12SHT0_2 | ADC12MSC;
  // Use sampling timer, set mode Repeat-single-channel
  ADC12CTL1 = ADC12SHS_3 | ADC12CONSEQ_2 | ADC12SSEL_3 | ADC12DIV_0;
  
  // Vr+=Avcc and Vr-=AVss
  // Select channel 4
  ADC12MCTL0 = ADC12SREF_0 | ADC12INCH_4;
  
  // Write dis
  DMACTL4 = DMARMWDIS;

  // Set trigger register channel
  DMACTL0 = DMA0TSEL__ADC12IFG;
  // Set control register options
  DMA0CTL = DMADT_4 | DMADSTINCR_3 | DMASRCINCR_0;
  
  // Enable DMA and interrupt on specified channel
  DMA0CTL &= ~DMAIFG;
  DMA0CTL |= DMAEN + DMAIE;
  
  DMA0SZ = 512;
  __data16_write_addr((unsigned short)&DMA0SA, (unsigned long)&ADC12MEM0);
  __data16_write_addr((unsigned short)&DMA0DA, (unsigned long)adcDataCollected);
  
  // Enable conversions
  ADC12CTL0 |= ADC12ENC;
  
  // Enter LPM3, enable interrupts
  __enable_interrupt();
	
  // Super loop
  while (1)
  {
    // Low power mode
    // __low_power_mode_3();
    
    crcs[0] = adcDataCollectedCounter;
    crcs[1] = getCRC((unsigned char*)adcDataCollected[0], 1024);
    crcs[2] = getCRC((unsigned char*)adcDataCollected[1], 1024);
    crcs[3] = getCRC((unsigned char*)adcDataCollected[2], 1024);
    
    updateLCD(crcs[0], crcs[1], crcs[2], crcs[3]);
  }
  
	return 0;
}

int _system_pre_init()
{
  // Stop watchdog timer (prevent timeout reset)
  WDTCTL = WDTPW | WDTHOLD;
  
  return 1;
}

 

 

  • You can only change the DMA destination address while the DMA is stopped.
    you can do:

    Program two DMA channels to fill your array with 4 conversion results. Use single transfer mode. Don’t use round-robin priority.
    DMA channel 0 will fill the destination with 4 results. Then it will trigger an interrupt. In the meantime, DMA channel 1 will take over. You can re-program DMA0 for the next block. However, you can’t start it yet due to the higher priority of DMA0. When DMA1 is done, it will too trigger an interrupt and here you instantly enable DMA0 (so it will immediately take over) then reprogram DMA1, and so on. Not perfect, but close.

**Attention** This is a public forum