LPC-Link2 High Speed ADC and DMA Interrupt?

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

LPC-Link2 High Speed ADC and DMA Interrupt?

737 Views
act642
Contributor II

Hi there, 

I am currently using two LPC-Link2's (one as debug probe and one as the eval board).

My goal is to continuously send data from the ADC FIFO to a DMA buffer. Once the DMA buffer is full, I also want to use the DMA interrupt to send data through USB. 

However, I am not able to enter the DMA interrupt at all, and I was wondering if anyone else has gotten this to work? Please find my code for reference:

#include "board.h"
#include <stdio.h>
#include "libusbdev.h"
#ifdef __USE_CMSIS
#include "LPC43xx.h"
#endif
// code from forum: https://community.nxp.com/t5/LPC-Microcontrollers/How-to-achieve-80MHz-using-DMA-from-LPC4370-s-HSADC/m-p/531525/page/1
// DMA buffer -> 4095 -> 32-bits -> packed -> 4095*2
// ADC FIFO
#define VADC_DMA_WRITE  7
#define VADC_DMA_READ   8
#define DMA_TRANSFER_SIZE 100 // max. 4095
uint32_t sample[DMA_TRANSFER_SIZE];

//DMA_TransferDescriptor_t arrayLLI[1];
//static GPDMA_LLI_Type DMA_Stuff[1];

/*
 * High speed ADC is set up appropriately
 */
void ADC_init(void) {
	Chip_Clock_SetDivider(CLK_IDIV_A, CLKIN_USBPLL, 2); /* Source DIV_A from USB0PLL, and set divider to 2 (Max div value supported is 4) [IN 480 MHz; OUT 240 MHz */
	Chip_Clock_SetDivider(CLK_IDIV_B, CLKIN_IDIVA, 3); /* Source DIV_B from DIV_A, [IN 240 MHz; OUT 80 MHz */
	Chip_Clock_SetBaseClock(CLK_BASE_ADCHS, CLKIN_IDIVB, true, false); /* Source ADHCS base clock from DIV_B */

	Chip_HSADC_FlushFIFO(LPC_ADCHS);
	Chip_HSADC_DeInit(LPC_ADCHS);

	NVIC_DisableIRQ(ADCHS_IRQn);
	LPC_ADCHS->INTS[0].CLR_EN = 0x7F; 			// disable interrupt 0
	LPC_ADCHS->INTS[0].CLR_STAT = 0x7F; 		// clear interrupt status
	while (LPC_ADCHS->INTS[0].STATUS & 0x7D); 	// wait for status to clear, have to exclude FIFO_EMPTY

	LPC_ADCHS->INTS[1].CLR_EN = 0x7F;
	LPC_ADCHS->INTS[1].CLR_STAT = 0x7F;
	while (LPC_ADCHS->INTS[1].STATUS & 0x7D);

	LPC_ADCHS->POWER_DOWN = 0; // disable power down
	LPC_ADCHS->FLUSH = 1;	   // clear fifo

	/* Initialize HSADC */
	Chip_HSADC_Init(LPC_ADCHS);	   // initialize

	LPC_ADCHS->FIFO_CFG = (0x0 << 0) | /* UNPACKED */
						  (0x8 << 1);  /* FIFO_LEVEL */

	LPC_ADCHS->DSCR_STS = (0 << 0) |   /* ACT_TABLE:        0=table 0 is active, 1=table 1 is active */
							(0 << 1);  /* ACT_DESCRIPTOR:   ID of the descriptor that is active */


	LPC_ADCHS->DESCRIPTOR[0][0] = (2 << 0) | /* CHANNEL_NR:    0=convert input 0, 1=convert input 1, ..., 5=convert input 5 */
									(0 << 3) | /* HALT:          0=continue with next descriptor after this one, 1=halt after this and restart at a new trigger */
									(0 < 4)  | /* INTERRUPT:     1=raise interrupt when ADC result is available */
									(0 << 5) | /* POWER_DOWN:    1=power down after this conversion */
									(1 << 6) | /* BRANCH:        0=continue with next descriptor (wraps around after top) */
									/*                1=branch to the first descriptor in this table */
									/*                2=swap tables and branch to the first descriptor of the new table */
									/*                3=reserved (do not store sample). continue with next descriptor (wraps around the top) */
									(0x95 <<  | /* MATCH_VALUE:   Evaluate this descriptor when descriptor timer value is equal to match value */
									(0 << 22) 	| /* THRESHOLD_SEL: 0=no comparison, 1=THR_A, 2=THR_B */
									(1 << 24) 	| /* RESET_TIME:    1=reset descriptor timer */
									(1 << 31); 	  /* UPDATE_TABLE:  1=update table with all 8 descriptors of this table */

	LPC_ADCHS->CONFIG = /* configuration register */
							(1 << 0) | /* TRIGGER_MASK:     0=triggers off, 1=SW trigger, 2=EXT trigger, 3=both triggers */
							(0 << 2) | /* TRIGGER_MODE:     0=rising, 1=falling, 2=low, 3=high external trigger */
							(0 << 4) | /* TRIGGER_SYNC:     0=no sync, 1=sync external trigger input */
							(0 << 5) | /* CHANNEL_ID_EN:    0=don't add, 1=add channel id to FIFO output data */
							(0x90 << 6); /* RECOVERY_TIME:    ADC recovery time from power down, default is 0x90 */

	uint8_t DGEC = 0xE; // must match CRS (Table 1133) 0x4 = 80 MSPS thus 0xE

	// set all DGEC to 0xE
	LPC_ADCHS->ADC_SPEED = (DGEC << 16)
						| (DGEC << 12)
						| (DGEC << 8)
						| (DGEC << 4)
						| (DGEC);

	//  For AC coupling, the DC biasing is generated internally by setting DCINNEG= 1 and DCINPOS= 1.
	//	generated internally by setting DCINNEG = 1 and DCINPOS = 1.
	LPC_ADCHS->POWER_CONTROL = (0x4) /* CRS, current power vs speed programming*/;
							(1 << 4) |      /* DCINNEG:      0=no dc bias, 1=dc bias on vin_neg slide */
							(0 << 10) |     /* DCINPOS:      0=no dc bias, 1=dc bias on vin_pos slide */
							(0 << 16) |     /* TWOS:         0=offset binary, 1=two's complement */
							(1 << 17) |     /* POWER_SWITCH: 0=ADC is power gated, 1=ADC is active */
							(1 << 18);      /* BGAP_SWITCH:  0=ADC bandgap reg is power gated, 1=ADC bandgap is active */
	NVIC_EnableIRQ(ADCHS_IRQn);

}

/*
 * Set up GPDMA
 */
void GPDMA_init(void) {
//    /* DMA controller configuration */
	Chip_GPDMA_DeInit(LPC_GPDMA);
	Chip_GPDMA_Init(LPC_GPDMA);
	
	NVIC_DisableIRQ(DMA_IRQn);
	LPC_GPDMA->CONFIG = 0x00;	// Disable configuration

	/* Clear all DMA interrupt and error flag */
	LPC_GPDMA->INTTCCLEAR = 0xFF; //clears channel terminal count interrupt
	LPC_GPDMA->INTERRCLR = 0xFF; //clears channel error interrupt.

	uint8_t ch_no = Chip_GPDMA_GetFreeChannel(LPC_GPDMA, 0);
	Chip_GPDMA_ChannelCmd(LPC_GPDMA, ch_no, ENABLE);

	/* Setup the DMAMUX */
	LPC_CREG->DMAMUX &= ~(0x3 << (VADC_DMA_WRITE * 2));
	LPC_CREG->DMAMUX |= 0x3 << (VADC_DMA_WRITE * 2); /* peripheral 7 vADC Write(0x3) */
	LPC_CREG->DMAMUX &= ~(0x3 << (VADC_DMA_READ * 2));
	LPC_CREG->DMAMUX |= 0x3 << (VADC_DMA_READ * 2); /* peripheral 8 vADC read(0x3) */

	LPC_GPDMA->CONFIG = 0x01; // Enable configuration
	while (!(LPC_GPDMA->CONFIG & 0x01)); //Wait until GPDMA is enabled

	// Initialize LPC_GPDMA, ch_no = 0
	// Source, ADC FIFO
	// Destination -> sample buffer
	// LLI back to 0 -> circular?
	LPC_GPDMA->CH[ch_no].SRCADDR = (uint32_t) &LPC_ADCHS->FIFO_OUTPUT[0];
	LPC_GPDMA->CH[ch_no].DESTADDR = ((uint32_t) &sample);
	LPC_GPDMA->CH[ch_no].LLI = 0; //Configuration registers for channel 0

	// Refer to page 525 in the manual
	LPC_GPDMA->CH[ch_no].CONTROL = (DMA_TRANSFER_SIZE)   // transfer size
								| (0x03 << 12)  // src burst size = 16
								| (0x03 << 15)  // dst burst size = 16
								| (0x2 << 18)  	// src transfer width - 32-bit
								| (0x2 << 21)  	// dst transfer width - 32 bit
								| (0x1 << 24)  	// src AHB master select
								| (0x1 << 25)  	// dst AHB master select
								| (0x0 << 26) 	// src increment: 0, src address not increment after each trans
								| (0x1 << 27) 	// dst increment: 1, dst address NOT increment after each trans
								| (0x1 << 31); 	// terminal count interrupt enable bit: 1, enabled

	LPC_GPDMA->CONFIG = (0x1 << 0) |          	// Enable bit
						(VADC_DMA_READ << 1) |  // SRCPERIPHERAL - set to 8 - VADC (0x8)
						(0x0 << 6) |        	// Destination peripheral - memory - no setting
						(0x6 << 11) |  			// Flow control - peripheral to memory - DMA control
						(0x1 << 14) |          	// Int error mask
						(0x1 << 15);            // ITC - term count error mask

	NVIC_SetPriority(DMA_IRQn, 0x02);
	NVIC_EnableIRQ(DMA_IRQn);
	LPC_GPDMA->CH[ch_no].CONFIG = (0x1 << 0); 	// enable bit, 1 enable, 0 disable
}

void DMA_IRQHandler(void) {
	Board_LED_Toggle(0);
}

int main(void) {
	SystemCoreClockUpdate();
	Board_Init();
	Chip_USB0_Init(); /* Initialize the USB0 PLL to 480 MHz */
	ADC_init();
	GPDMA_init();
	libusbdev_init(USB_STACK_MEM_BASE, USB_STACK_MEM_SIZE);

	// Initialize sample array to 32 1's, F = 1111
	for (int i = 0; i < DMA_TRANSFER_SIZE; i++) {
		sample[i] = 0xFFFFFFFF;
	}

	Chip_HSADC_SWTrigger(LPC_ADCHS); // start DMA

	while (libusbdev_Connected() == 0);
	while (1) {
		__WFI();
	}
}

 

Labels (2)
0 Kudos
3 Replies

692 Views
Miguel04
NXP TechSupport
NXP TechSupport

HI @act642 

I am Miguel.

I've been investigating your issue and the problem with the interrupt could the initialization process, please check chapter 21.8.1.7 Programming a DMA channel step 2 and the 21.8.2.1 Peripheral-to-memory or memory-to-peripheral DMA flow on the LPC4370 user manual.

You could also use the DMA example from LPCOpen. The path on your system should be like this:

"MCUXpressoIDE_11.6.0_8187\ide\plugins\com.nxp.mcuxpresso.tools.wizards_11.6.0.202112161359\Examples\LPCOpen"

Let me know if this resolve your issue.

Best Regards, Miguel.

0 Kudos

687 Views
act642
Contributor II

Hi Miguel,

Unfortunately, this did not resolve my issue. I have reviewed the user manual as well and it did not work. From LPCOpen, I tried numerous examples such as periph_hsadc and periph_dma_timertrig. Both examples did not help with my progress either.

Please let me know what you discover!

0 Kudos

667 Views
Miguel04
NXP TechSupport
NXP TechSupport

Hi  @act642 

I've compared your code with the initialization of the examples and I don’t see you restart the CONFIG of the channel, could you verify if this is the issue?

Also, Could you provide me more details, please.

How do you want to trigger de DMA?

Is the DMA getting triggered (on registers)?

Does the examples from LPCOpen worked?

I'll let you know if I find something else.

Miguel.

0 Kudos