LPC1549 ADC with DMA problem

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

LPC1549 ADC with DMA problem

Jump to solution
1,821 Views
jespermadsen
Contributor III

I copied this example:

https://community.nxp.com/t5/LPC-Microcontrollers-Knowledge/DMA-Ping-Pong-application/ta-p/1120977 

for the LPCXpressor54114 Board and changed it to match the  LPC1549 LPCXpresso™ board.

As you can see there are a lot of pauses in the DMA sampling? Anybody knows what could be wrong?

The buffer size is 64

The sampling rate is 5000 Hz

The input signal is a 70 Hz triangle.

ADC-input:

ADC_input.png

Sampling without DMA:

ADC_samples_no_DMA.png

Samples with DMA:

ADC_samples.png

Source code:

 

#include "chip.h"

#include <cr_section_macros.h>

//#define NO_DMA

#define NUM_BUFFERS 4
#define DMA_TRANSFER_SIZE 64
#define ADC_INPUT_CHANNEL 0

#define SCT_PWM_RATE   5000          /* PWM frequency 5 KHz */
#define SCT_PWM_PIN_OUT    3          /* COUT3 Generate square wave */
#define SCT_PWM_OUT        3          /* Index of OUT PWM */

uint16_t adcOut;

DMA_CHDESC_T ADC_TransferDescriptors[NUM_BUFFERS] __attribute__ ((aligned(512)));


uint16_t CapturedData[256];

/**
 *
 * ADC IRQ not Used right now... Only for testing
 */
void ADC_SEQA_IRQHandler(void)
{
            /* If SeqA flags is set i.e. data in global register is valid then read it */
        Chip_ADC_ClearFlags(LPC_ADC0,0xFFFFFFFF);
}

uint32_t buf_index = 0, rawSample;
void ADC0A_IRQHandler(void)
{
	uint32_t pending;

	/* Get pending interrupts */
	pending = Chip_ADC_GetFlags(LPC_ADC0);

	/* Sequence A completion interrupt */
	if (pending & ADC_FLAGS_SEQA_INT_MASK) {
		rawSample = Chip_ADC_GetSequencerDataReg(LPC_ADC0, ADC_SEQA_IDX);
		CapturedData[buf_index++] = (uint16_t)(rawSample & ADC_SEQ_GDAT_RESULT_MASK);
		if(buf_index > 255)
		{
			buf_index = 0;
		}
	}

	/* Clear any pending interrupts */
	Chip_ADC_ClearFlags(LPC_ADC0, pending);
}



void DMA_IRQHandler(void)
{
        static uint16_t DMA_Sum=0;

        DMA_Sum++;

         if(DMA_Sum == 8)
         {
           DMA_Sum = 4;
         }

     Chip_GPIO_SetPinOutHigh(LPC_GPIO, 0, 0);

     /* Error interrupt on channel 0? */
     if ((Chip_DMA_GetIntStatus(LPC_DMA) & DMA_INTSTAT_ACTIVEERRINT) != 0)
     {
          /* This shouldn't happen for this simple DMA example, so set the LED
             to indicate an error occurred. This is the correct method to clear
             an abort. */
          Chip_DMA_DisableChannel(LPC_DMA, DMA_CH0);
          while ((Chip_DMA_GetBusyChannels(LPC_DMA) & (1 << DMA_CH0)) != 0) {}
          Chip_DMA_AbortChannel(LPC_DMA, DMA_CH0);
          Chip_DMA_ClearErrorIntChannel(LPC_DMA, DMA_CH0);
          Chip_DMA_EnableChannel(LPC_DMA, DMA_CH0);
     }

     Chip_GPIO_SetPinOutLow(LPC_GPIO, 0, 0);

     /* Clear DMA interrupt for the channel */
	Chip_DMA_ClearActiveIntAChannel(LPC_DMA, DMA_CH0);
}





     /***
      *      ____  __  __    _
      *     |  _ \|  \/  |  / \
      *     | | | | |\/| | / _ \
      *     | |_| | |  | |/ ___ \
      *     |____/|_|  |_/_/   \_\
      *     / ___|  ___| |_ _   _ _ __
      *     \___ \ / _ \ __| | | | '_ \
      *      ___) |  __/ |_| |_| | |_) |
      *     |____/ \___|\__|\__,_| .__/
      *                          |_|
      */
void DMA_Setup(void)
{
        DMA_CHDESC_T Initial_DMA_Descriptor;

     ADC_TransferDescriptors[0].source = (uint32_t)&LPC_ADC0->SEQ_GDAT[0];
     ADC_TransferDescriptors[1].source = (uint32_t)&LPC_ADC0->SEQ_GDAT[0];
     ADC_TransferDescriptors[2].source = (uint32_t)&LPC_ADC0->SEQ_GDAT[0];
     ADC_TransferDescriptors[3].source = (uint32_t)&LPC_ADC0->SEQ_GDAT[0];


     ADC_TransferDescriptors[0].dest = DMA_ADDR(&CapturedData[(0+1)*DMA_TRANSFER_SIZE-1]);
     ADC_TransferDescriptors[1].dest = DMA_ADDR(&CapturedData[(1+1)*DMA_TRANSFER_SIZE-1]);
     ADC_TransferDescriptors[2].dest = DMA_ADDR(&CapturedData[(2+1)*DMA_TRANSFER_SIZE-1]);
     ADC_TransferDescriptors[3].dest = DMA_ADDR(&CapturedData[(3+1)*DMA_TRANSFER_SIZE-1]);

     //The initial DMA desciptor is the same as the 1st transfer descriptor.   It
     //Will link into the 2nd of the main descriptors.

     ADC_TransferDescriptors[0].next = (uint32_t)&ADC_TransferDescriptors[1];
     ADC_TransferDescriptors[1].next = (uint32_t)&ADC_TransferDescriptors[2];
     ADC_TransferDescriptors[2].next = (uint32_t)&ADC_TransferDescriptors[3];

     //Link back to the 1st descriptor
     ADC_TransferDescriptors[3].next = (uint32_t)&ADC_TransferDescriptors[0];

     //For a test,  stop the transfers here.   The sine wave will look fine.
     //ADC_TransferDescriptors[3].next = 0;

     ADC_TransferDescriptors[0].xfercfg = (DMA_XFERCFG_CFGVALID |
                               DMA_XFERCFG_RELOAD  |
                               DMA_XFERCFG_SETINTA |
                               DMA_XFERCFG_WIDTH_16 |
                               DMA_XFERCFG_SRCINC_0 |
                               DMA_XFERCFG_DSTINC_1 |
                               DMA_XFERCFG_XFERCOUNT(DMA_TRANSFER_SIZE));

     ADC_TransferDescriptors[1].xfercfg = ADC_TransferDescriptors[0].xfercfg;
     ADC_TransferDescriptors[2].xfercfg = ADC_TransferDescriptors[0].xfercfg;

     ADC_TransferDescriptors[3].xfercfg = (DMA_XFERCFG_CFGVALID |
                               DMA_XFERCFG_RELOAD  |
                               DMA_XFERCFG_SETINTA |
                              DMA_XFERCFG_WIDTH_16 |
                              DMA_XFERCFG_SRCINC_0 |
                              DMA_XFERCFG_DSTINC_1 |
                              DMA_XFERCFG_XFERCOUNT(DMA_TRANSFER_SIZE));

     Initial_DMA_Descriptor.source = ADC_TransferDescriptors[0].source;
     Initial_DMA_Descriptor.dest =   ADC_TransferDescriptors[0].dest;
     Initial_DMA_Descriptor.next =  (uint32_t)&ADC_TransferDescriptors[1];
     Initial_DMA_Descriptor.xfercfg = ADC_TransferDescriptors[0].xfercfg;

     /* DMA initialization - enable DMA clocking and reset DMA if needed */
     Chip_DMA_Init(LPC_DMA);

     /* Enable DMA controller and use driver provided DMA table for current descriptors */
     Chip_DMA_Enable(LPC_DMA);
     Chip_DMA_SetSRAMBase(LPC_DMA, DMA_ADDR(Chip_DMA_Table));

     /* Setup channel 0 for the following configuration:
        - High channel priority
        - Interrupt A fires on descriptor completion */
     Chip_DMA_EnableChannel(LPC_DMA, DMA_CH0);
     Chip_DMA_EnableIntChannel(LPC_DMA, DMA_CH0);
     Chip_DMA_SetupChannelConfig(LPC_DMA, DMA_CH0,     //(DMA_CFG_PERIPHREQEN     |
                                   (DMA_CFG_HWTRIGEN        |
                                    DMA_CFG_TRIGBURST_BURST |
                                    DMA_CFG_TRIGTYPE_EDGE   |
                                    DMA_CFG_TRIGPOL_HIGH  |    //DMA_CFG_TRIGPOL_HIGH
                                    DMA_CFG_BURSTPOWER_1    |
                                    DMA_CFG_CHPRIORITY(0)
                                    )
                                    );


     //make sure ADC Sequence A interrupts is selected for for a DMA trigger
     Chip_INMUX_SetDMATrigger(DMA_CH0, DMATRIG_ADC0_SEQA_IRQ);

     /* Enable DMA interrupt */
     NVIC_EnableIRQ(DMA_IRQn);

     // The 1st descriptor is set up through the registers.

     /* Setup transfer descriptor and validate it */
     Chip_DMA_SetupTranChannel(LPC_DMA, DMA_CH0, &Initial_DMA_Descriptor);

     //Use the transfer configuration for our 4 main descriptors
     Chip_DMA_SetupChannelTransfer(LPC_DMA, DMA_CH0,     ADC_TransferDescriptors[0].xfercfg);
     Chip_DMA_SetValidChannel(LPC_DMA, DMA_CH0);
}

void SCT_PWM_Generate(void)
{
     /* Initialize the SCT2 as PWM and set frequency */
     Chip_SCTPWM_Init(LPC_SCT2);
     Chip_SCTPWM_SetRate(LPC_SCT2, SCT_PWM_RATE);

     /* Use SCT2_OUT3 pin */
     Chip_SCTPWM_SetOutPin(LPC_SCT2, SCT_PWM_OUT, SCT_PWM_PIN_OUT);

     /* Start with 50% duty cycle */
     Chip_SCTPWM_SetDutyCycle(LPC_SCT2, SCT_PWM_OUT, Chip_SCTPWM_PercentageToTicks(LPC_SCT2, 10));
     Chip_SCTPWM_Start(LPC_SCT2);
}


     /***
           *         _    ____   ____
           *        / \  |  _ \ / ___|
           *       / _ \ | | | | |
           *      / ___ \| |_| | |___
           *     /_/__ \_\____/ \____|
           *     / ___|  ___| |_ _   _ _ __
           *     \___ \ / _ \ __| | | | '_ \
           *      ___) |  __/ |_| |_| | |_) |
           *     |____/ \___|\__|\__,_| .__/
           *                          |_|
           */
void ADC_Setup(void)
{
     /* Initialization ADC to 12 bit and set clock divide to 1 to operate synchronously at System clock */
    Chip_ADC_Init(LPC_ADC0, ADC_CR_BITACC(0) | ADC_CR_CLKDIV(0)| ADC_CR_ASYNMODE);
	Chip_ADC_SetClockRate(LPC_ADC0, ADC_MAX_SAMPLE_RATE);
    //select ADC Channel 0 as input
    Chip_IOCON_PinMuxSet(LPC_IOCON, 0, 8, IOCON_FUNC0 | IOCON_ADMODE_EN | IOCON_S_MODE_0CLK | IOCON_MODE_INACT);

    Chip_SWM_Init();
	Chip_SWM_EnableFixedPin(SWM_FIXED_ADC0_0);	//P0_8

    Chip_ADC_SetADC0Input(LPC_ADC0, ADC_INSEL_ADC0);

    //Setup ADC0_SEQA_IRQ
    Chip_ADC_SetupSequencer(LPC_ADC0,ADC_SEQA_IDX,
                              ADC_SEQ_CTRL_SEQ_ENA |
                              ADC_SEQ_CTRL_CHANSEL(ADC_INPUT_CHANNEL) |
                              ADC0_SEQ_CTRL_HWTRIG_SCT2_OUT3 |
//                              ADC_SEQ_CTRL_HWTRIG_POLPOS |
//                              ADC_SEQ_CTRL_HWTRIG_SYNCBYPASS |
                              ADC_SEQ_CTRL_MODE_EOS);

    /* Enable Sequence A interrupt */
	Chip_ADC_EnableInt(LPC_ADC0, ADC_INTEN_SEQA_ENABLE);

	/* Calibrate ADC */
//	Chip_ADC_SetTrim(LPC_ADC0, ADC_TRIM_VRANGE_HIGHV);
	Chip_ADC_StartCalibration(LPC_ADC0);
	while (!(Chip_ADC_IsCalibrationDone(LPC_ADC0))) {}

	Chip_ADC_ClearFlags(LPC_ADC0, Chip_ADC_GetFlags(LPC_ADC0));

	NVIC_EnableIRQ(ADC0_SEQA_IRQn);

	Chip_ADC_EnableSequencer(LPC_ADC0, ADC_SEQA_IDX);

}

int main(void) {
    // Read clock settings and update SystemCoreClock variable
    SystemCoreClockUpdate();

	Chip_GPIO_Init(LPC_GPIO);
	Chip_GPIO_SetPinDIROutput(LPC_GPIO, 0, 0);

#ifndef NO_DMA
    DMA_Setup();
#endif

    ADC_Setup();
    SCT_PWM_Generate();


    while(1)
    {}
    return 0 ;
}

 

 

0 Kudos
Reply
1 Solution
1,794 Views
frank_m
Senior Contributor III

A ping-pong buffer would have been my suggested solution.

To be honest, I don't have much experience with the advanced DMA management on LPCs. I used a ADC/DMA chain regularly on other Cortex M devices, though. I usually had small buffers (like yours), and moved them in the DMA-ready interrupt - often with type conversion and scaling.

Probably DMA fills the buffers not in the order you think ...

I would read the DMA section of the MCU manual in that case.

View solution in original post

6 Replies
1,804 Views
frank_m
Senior Contributor III

Superficially looking at the images, it seems both methods (with and without ADC interrupt) seem messed up.

As xiangjun_rong suggested, I would restrict the code to one method. Either ADC interrupts or DMA.

Assuming you are sampling continuously, your problem might be buffer handling. Once one buffer is full, you need to either move the data, or swap the DMA buffers. Else the ADC/DMA chain will start to overwrite you data after one ADC sample time.

For low sampling rates (like 5kHz), buffer management can still be handled with ADC interrupts, without a large performance penalty.

1,797 Views
jespermadsen
Contributor III

Hello Frank Meyer.

Thank you for answering.

I'm using the ping-pong buffer for the DMA, witch means the DMA changes the buffer it self. Maybe the buffer changes too early and the samples gets mixed together?

I would like to get the DMA to work and then I can increase the frequency.

Best regards

Jesper

 

0 Kudos
Reply
1,795 Views
frank_m
Senior Contributor III

A ping-pong buffer would have been my suggested solution.

To be honest, I don't have much experience with the advanced DMA management on LPCs. I used a ADC/DMA chain regularly on other Cortex M devices, though. I usually had small buffers (like yours), and moved them in the DMA-ready interrupt - often with type conversion and scaling.

Probably DMA fills the buffers not in the order you think ...

I would read the DMA section of the MCU manual in that case.

1,789 Views
jespermadsen
Contributor III

Hello Frank Meyer.

Thanks.

The problem was this line:

Chip_DMA_SetSRAMBase(LPC_DMA, DMA_ADDR(Chip_DMA_Table));

it should have been this:

Chip_DMA_SetSRAMBase(LPC_DMA, DMA_ADDR(ADC_TransferDescriptors));

It was the wrong descriptors loaded into the DMA.

Best regards

Jesper

0 Kudos
Reply
1,815 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

 Hi, Jesper,

I suppose that you use both DMA and ADC Interrupt to read the ADC sample, how about disabling the ADC interrupt in ADC_Setup(); with removing the lines:

Chip_ADC_EnableInt(LPC_ADC0, ADC_INTEN_SEQA_ENABLE);
NVIC_EnableIRQ(ADC0_SEQA_IRQn);

 

I am not sure if it is the cause, anyway, pls have a try.

BR

XiangJun Rong

void ADC_Setup(void)
{
     /* Initialization ADC to 12 bit and set clock divide to 1 to operate synchronously at System clock */
    Chip_ADC_Init(LPC_ADC0, ADC_CR_BITACC(0) | ADC_CR_CLKDIV(0)| ADC_CR_ASYNMODE);
	Chip_ADC_SetClockRate(LPC_ADC0, ADC_MAX_SAMPLE_RATE);
    //select ADC Channel 0 as input
    Chip_IOCON_PinMuxSet(LPC_IOCON, 0, 8, IOCON_FUNC0 | IOCON_ADMODE_EN | IOCON_S_MODE_0CLK | IOCON_MODE_INACT);

    Chip_SWM_Init();
	Chip_SWM_EnableFixedPin(SWM_FIXED_ADC0_0);	//P0_8

    Chip_ADC_SetADC0Input(LPC_ADC0, ADC_INSEL_ADC0);

    //Setup ADC0_SEQA_IRQ
    Chip_ADC_SetupSequencer(LPC_ADC0,ADC_SEQA_IDX,
                              ADC_SEQ_CTRL_SEQ_ENA |
                              ADC_SEQ_CTRL_CHANSEL(ADC_INPUT_CHANNEL) |
                              ADC0_SEQ_CTRL_HWTRIG_SCT2_OUT3 |
//                              ADC_SEQ_CTRL_HWTRIG_POLPOS |
//                              ADC_SEQ_CTRL_HWTRIG_SYNCBYPASS |
                              ADC_SEQ_CTRL_MODE_EOS);

    /* Enable Sequence A interrupt */
	Chip_ADC_EnableInt(LPC_ADC0, ADC_INTEN_SEQA_ENABLE);

	/* Calibrate ADC */
//	Chip_ADC_SetTrim(LPC_ADC0, ADC_TRIM_VRANGE_HIGHV);
	Chip_ADC_StartCalibration(LPC_ADC0);
	while (!(Chip_ADC_IsCalibrationDone(LPC_ADC0))) {}

	Chip_ADC_ClearFlags(LPC_ADC0, Chip_ADC_GetFlags(LPC_ADC0));

	NVIC_EnableIRQ(ADC0_SEQA_IRQn);

	Chip_ADC_EnableSequencer(LPC_ADC0, ADC_SEQA_IDX);

}

 

1,798 Views
jespermadsen
Contributor III

Hello xiangjun rong

Thank you for answering and sorry for the late response.

If I remove the line:

  • Chip_ADC_EnableInt(LPC_ADC0, ADC_INTEN_SEQA_ENABLE);

I don't get any DMA interrupts. Could that be the problem?

And when I remove the line:

  • NVIC_EnableIRQ(ADC0_SEQA_IRQn);

It don't change the result.

Best regards

Jesper

0 Kudos
Reply