I am currently trying to help a colleague get use the LPC4370's HSADC for a sensor project and am unable to get the advertised 80M samples from a single ADC channel (ADC0). We are using LPCXpresso and 2 LPClink2's. We have based the project off the example code in "periph_hsadc". We're using some ~2MHz test signals to verify performance (# of samples per cycle).
I've thoroughly read through the relevant sections of the Datasheet and User Manual (CGU, HSADC, DMA etc). I understand the operation of the ADC and it's associated FIFO and also have a good understanding of the DMA settings. I've reviewed many posts found through google (including every relevant post on here).
Using Cortex M4 processor clocked at 204MHz.
AHB is set for 204MHz.
Uses two descriptor entires: Table 0 entry 0 is first run with long match time to allow ADC to stabilize, then swap to Table 1 entry 0 with shortest match time and branch to self.
Uses no interrupts and has a FIFO Level of 15 (thought we have tried many other FIFO Levels, other settings depending).
Uses fastest CRS setting and has DGEC configured for 80MHz operation.
Configured for transfer of 4095 samples from FIFO_OUTPUT.
Uses src and dst burst size of 1 (whenever we have tried to change burst size DMA ends up reading empty FIFO ~50% of samples).
Doesn't use Linked List.
Tried using DMA as Flow Controller AND HSADC, performance doesn't change.
(And sorry for the code below, tried quoting with syntax highlighting and it hid a large number of the lines)
#define HSADC_DMA_READ 8
#define DMA_TRANSFER_SIZE 4095 // max. 4095
#define DMA_CH 7
#define NUM_SAMPLE DMA_TRANSFER_SIZE
Chip_USB0_Init(); /* Initialize the USB0 PLL to 480 MHz */
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 */
/////////////////////////////////////////// HSADC settings ////////////////////////////////////////////////////////////////
LPC_ADCHS->INTS.CLR_EN = 0x7F; // disable interrupt 0
LPC_ADCHS->INTS.CLR_STAT = 0x7F; // clear interrupt status
while(LPC_ADCHS->INTS.STATUS & 0x7D); // wait for status to clear, have to exclude FIFO_EMPTY
LPC_ADCHS->INTS.CLR_EN = 0x7F;
LPC_ADCHS->INTS.CLR_STAT = 0x7F;
while(LPC_ADCHS->INTS.STATUS & 0x7D);
/* Initialize HSADC */
LPC_ADCHS->POWER_DOWN = 0;
LPC_ADCHS->FLUSH = 1;
LPC_ADCHS->FIFO_CFG = (15 << 1) /* FIFO_LEVEL*/ | (1) /* PACKED_READ*/;
LPC_ADCHS->DSCR_STS = 1;
LPC_ADCHS->DESCRIPTOR = (1 << 31) /* UPDATE TABLE*/ | (1 << 24) /* RESET_TIMER*/ | (0 << 22) /* THRESH*/ | (0xA00 << 8) /* MATCH*/ | (0x10 << 6) /* BRANCH*/;
LPC_ADCHS->DESCRIPTOR = (1 << 31) /* UPDATE TABLE*/ | (1 << 24) /* RESET_TIMER*/ | (0 << 22) /* THRESH*/ | (0x01 << 8) /* MATCH*/ | (0x01 << 6) /* BRANCH*/;
LPC_ADCHS->CONFIG = (0x90 << 6) /* RECOVERY_TIME*/
| (0 << 5) /* CHANNEL_ID_EN*/
| (0x01) /* TRIGGER_MASK*/;
uint8_t DGEC = 0xE;
LPC_ADCHS->ADC_SPEED = (DGEC << 16) | (DGEC << 12) | (DGEC << 8) | (DGEC << 4) | (DGEC);
//Didn't set threshold registers as they aren't used
LPC_ADCHS->POWER_CONTROL = (1 << 18) /* BGAP*/
| (1 << 17) /* POWER*/
| (1 << 10) /* DC in ADC0*/
| (1 << 4) | (0x4) /* CRS*/;
////////////////////////////////////////////////////// DMA settings ///////////////////////////////////////////////////////////////////
LPC_GPDMA->CONFIG = 0x01;
while( !(LPC_GPDMA->CONFIG & 0x01) );
/* Clear all DMA interrupt and error flag */
LPC_GPDMA->INTTCCLEAR = 0xFF; //clears channel terminal count interrupt
LPC_GPDMA->INTERRCLR = 0xFF; //clears channel error interrupt.
LPC_GPDMA->CH[DMA_CH].SRCADDR = (uint32_t) &LPC_ADCHS->FIFO_OUTPUT;
LPC_GPDMA->CH[DMA_CH].DESTADDR = ((uint32_t) &sample);
LPC_GPDMA->CH[DMA_CH].CONTROL = (DMA_TRANSFER_SIZE) // transfer size
| (0x0 << 12) // src burst size
| (0x0 << 15) // dst burst size
| (0x2 << 18) // src transfer width
| (0x2 << 21) // dst transfer width
| (0x1 << 24) // src AHB master select
| (0x0 << 25) // dst AHB master select
| (0x0 << 26) // src increment: 0, src address not increment after each trans
| (0x1 << 27) // dst increment: 1, dst address increment after each trans
| (0x1 << 31); // terminal count interrupt enable bit: 1, enabled
LPC_GPDMA->CH[DMA_CH].CONFIG = (0x1 << 0) // enable bit: 1 enable, 0 disable
| (HSADC_DMA_READ << 1) // src peripheral: set to 8 - HSADC
| (0x0 << 6) // dst peripheral: no setting - memory
| (0x6 << 11) // flow control: peripheral to memory - DMA control
| (0x1 << 14) // IE - interrupt error mask
| (0x1 << 15) // ITC - terminal count interrupt mask
| (0x0 << 16) // lock: when set, this bit enables locked transfer
| (0x1 << 18); // Halt: 1, enable DMA requests; 0, ignore further src DMA req
LPC_GPDMA->CH[DMA_CH].LLI = 0;
///////////////////////////////////////// start HSADC and GPDMA //////////////////////////////////////////////////////
// start DMA
LPC_GPDMA->CH[DMA_CH].CONFIG = (0x1 << 0); // enable bit, 1 enable, 0 disable
//need to add wait for DMA complete
for(int i =0; i<4094; i++)
;//Issues with DMA interrupt, wait for end of sampling
sts = Chip_HSADC_GetFIFOLevel(LPC_ADCHS);
It looks like the FIFO level is the number of samples, not the number of Words in the FIFO, and it would only need 16 locations when READ_PACKED is not enabled.
What frequency are you running the CPU at? this is where CLK_MX_ADCHS and the register AHB clock is derived from, it needs to be fast enough to transfer the results to the FIFO.
Also, according to the data sheet once the FIFO is full if it takes additional measurements it will overwrite the previous contents, so it would be almost impossible to get a clean waveform this way.
You could try using all 8 descriptors from both tables to take 16 samples.
The first descriptor would have 0x9A match value and BRANCH to next descriptor.
the rest would have match value of 0 and branch to next descriptor until the last descriptor of table 0 which would swap tables and branch to first descriptor.
On the second table the last descriptor would HALT after it's processed.
Good catch, I forgot to look at the datasheet for clock sources. But I think it's the opposite. IDIVA is the only divider that can be connected to other dividers (IDIVA must be the source).
I have just successfully configured my LPC4370's ADCHS to take 2048 samples at 60 MHz and transfer to SRAM memory using the GPDMA. The trick to getting the ADCHS and GPDMA to work beyond 30 ish MHz is to use the PACKED_READ option in the ADCHS's FIFO_CFG register.
This packs two samples into each 32bit FIFO word so the DMA is now transferring two samples at a time effectively doubling the transfer rate to memory.
If you don't use the PACKED function the DMA will not have enough AHB bus time to transfer 8 samples @ 60 MHz from peripheral to memory and you will loose samples when the FIFO overflows. It you think about it even at 200 MHz its only 2.5x faster than the FIFO is filling and you need to copy 8 samples from peripheral to memory.
I haven't tested at 80 MHz as my clock is not configured to generate fADC of 80 MHz (yet) but I expect the ADCHS and DMA to still function correctly. Assuming your M4_BASE clock is 200 MHz.
Here are a few tips when configuring the ADC and DMA;
I triggered my ADC sampling using the SCT CTOUT_0 signal.
I set the DMA to control the number of samples to take (remember DMA transfer size = 1/2 sample size).
To stop the sampling I used the DMA Transfer Complete Interrupt to inject 8 new Table 0 descriptors into the ADCHS that all have the HALT bit set - this will cause the ADCHS to stop running when the descriptors are re-loaded from the double buffered descriptor registers. My original descriptor last entry had the UPDATE_TABLE bit set to make sure it gets reloaded.
My ADCHS descriptor table had 8 entries to take 8 samples from Channel 0, the last descriptor just branched back to the start and repeated. The last descriptor also had UPDATE_TABLE set ready for the end of sampling. To get full speed samples MATCH = 0 and RESET_TIMER = 1.
My Core PLL1 clock rate is 180 Mhz.
My ADCHS Clock is 60 MHz.
Hope this helps!
I am using the internal SRAM to store my samples;
Located in SRAM1 (AHB) 0x20000000..
Have had a working, reliable system for a while now with the LPC Clock running at 200MHz, ADCHS Sampling Rate 80Mhz.
I have to say its been a tricky application to get reliably working at fs=80MHz, very little clear documentation from NXP.
Hi David, that's a very good news to me too.
So, are you experiencing dropping samples with your setup? The only other person I spoke to who achieved 80msps had problems with DMA reload time (even using internal SRAM). See HSADC with GPDMA Transfer Dropping Samples at 80MSps
I can confirm I can reliably sample at 80MHz to internal SRAM memory without loosing any samples, my system samples an ultrasound signal so any lost samples will be evident and cause a problem.
During sampling there is only the RTOS tick timer running and I halt all other bus transfers, my application code sits in SRAM (0x1000_0000) and sample memory is AHB SRAM (0x2000_0000). CPU clock is 200MHz and I use the Audio PLL to generate the ASCHS Sample Clock.
Because I need to take more than 4096 samples I don't use the DMA to control the ADCHS, instead I load a continuous sample sequence into the ADCHS descriptor table and use TIMER0 to time the sampling process, a TIMER0 interrupt halts sampling by injecting a HALT command into the descriptor table. I could not get DMA Linked Lists to work at fs=80 MHz as once the DMA tries to load the next List the ADCHS overruns its FIFO and the data gets corrupted.
I have also found you must clear the ADCHS FIFO before starting a sampling run otherwise data gets corrupted.
In summary the ADCHS can sample to SRAM at 80MHz but you must;
Unfortunately I don't have the HW at the moment to try the update right now. By the way, I plan to do it in the next weeks while on holiday. Do you know if using a thrashold-based interrupt on the ADCHS could cause any problem? I need to detect a threshold crossing in my application since I need to acquire and process a pulse shaped signal... at 40 msps it works fine.
Packing 2 samples in one 32-bit read cycle (PACKED_READ (bit0) of ADCHS FIFO_CFG register) allows for 8192 samples. It also improves reliability as the effective transfer rate at 80msps is 25ns instead of 12.5ns
Do you need all those samples in your application? or do you just need samples after a certain time? (after 120us for example). I am asking because if you don't need the first portion of the waveform you can always set the MATCH_VALUE in the descriptor to start sampling later. For example to start sampling 120us later you can set MATCH_VALUE to 9600 (9600 * 12.5ns) and then collect only the number of samples you actually need.
The LPC4370 ADCHS works well at 80 Msps. As I mentioned in this thread on April, 6 2018 I have it working.
It is reliable and not dropping any samples. Disable interrupts during sampling as any interrupts could cause loss of samples by interfering with the BUS traffic from the DMA. You can check the DMA Interrupt Terminal Count (register INTTCSTAT) to determine when the requested number of samples have been transferred.
As the DMA is used to transfer from ADCHS to a 'memory' address, if that address was external memory connected via the EMC it should work. Obviously your bandwidth will depend on memory speed and configuration, its probably going to be slower than internal SRAM using the AHB Bus.
Hi David, it is great to hear someone who is using this chip and its AD capabilities. I think it's quite underrated!
Right now I'm sampling @40 MHz and I'm fine with that, but still have a lot of questions about this micro (for example see here).
I'd be glad to collaborate if someone needs help and try to help each other.. there's a lack of support and resources (lpcopen support I believe is still incomplete for this chip), but we can join forces:)
Hi @benjamin, thanks for posting your code. I'm trying to use it and display some data in Matlab, but I'm having trouble understanding how do you format the data out put. I tried to force the 2 complement format for the ADC but I still get a dynamic range of 2.1475e+09 (which is absurd) and a non sense graph.
I'm just using your code with this little cycle at the end:
for(int i =0; i<4095; i++)
Let me know if you managed to reach the 80msps, I need to use this HSADC for a thesis and I'm highly interested.
Thanks in advance!