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();
}
}