How to achieve 80MHz using DMA from LPC4370's HSADC?

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

How to achieve 80MHz using DMA from LPC4370's HSADC?

12,521 Views
benjaminartes
Contributor I

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).

Issues:

  • Unable to achieve 80MHz from HSADC.  Using a 2 MHz sine wave and reading for 4095 samples we see ~20 samples per cycle showing that ADC and DMA are, combined, storing 40MHz bandwidth of data.  I assumed this was due to DMA configuration and that ADC was dropping samples due to a full FIFO.  Tried using burst reads from DMA and ending up reading an empty FIFO ~every other sample.  I believe HSADC is currently running at 40MHz even though it reports that it's clock is set to 80MHz.  Code is included below: What could possibly be limiting the sample-rate?
  • As discussed in previous issue we have tried using burst DMA to no avail.  User Manual is pretty clear that you have to use a FIFO LEVEL smaller than 15 so that src burst size and FIFO LEVEL will be the same, but no combination of FIFO LEVEL and src burst has yielded increased performance.  What are the circumstances / settings that DMA bursts will succeed?
  • We have also tried using Packed mode and run into issues.  Packed mode works fine for ~15 samples after which the packed sample is ALWAYS 0.  What are the limitations on using Packed mode?

 

Configuration:

Using Cortex M4 processor clocked at 204MHz.

AHB is set for 204MHz.

HSADC:

80Mhz.

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.

DMA:

Configured for transfer of 4095 samples from FIFO_OUTPUT[0].

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.

 

Thanks,

Ben

 

(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

uint32_t sample[NUM_SAMPLE];

 

int main(void)

{

       SystemCoreClockUpdate();

       Board_Init();

 

 

       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[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);

 

 

       /* Initialize HSADC */

       LPC_ADCHS->POWER_DOWN = 0;

       LPC_ADCHS->FLUSH = 1;

       Chip_HSADC_Init(LPC_ADCHS);

       LPC_ADCHS->FIFO_CFG      = (15 << 1) /* FIFO_LEVEL*/ | (1) /* PACKED_READ*/;

       LPC_ADCHS->DSCR_STS = 1;

       LPC_ADCHS->DESCRIPTOR[0][0] = (1 << 31) /* UPDATE TABLE*/ | (1 << 24) /* RESET_TIMER*/ | (0 << 22) /* THRESH*/ | (0xA00 << 8) /* MATCH*/ | (0x10 << 6) /* BRANCH*/;

       LPC_ADCHS->DESCRIPTOR[1][0] = (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 ///////////////////////////////////////////////////////////////////

       Chip_GPDMA_Init(LPC_GPDMA);

       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[0];

       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 //////////////////////////////////////////////////////

       Chip_HSADC_SWTrigger(LPC_ADCHS);

       // 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

       }

 

       Chip_HSADC_FlushFIFO(LPC_ADCHS);

       sts = Chip_HSADC_GetFIFOLevel(LPC_ADCHS);

       Chip_HSADC_DeInit(LPC_ADCHS);

       Chip_GPDMA_DeInit(LPC_GPDMA);

 

 

}

*/

Labels (1)
Tags (1)
40 Replies

5,510 Views
vitaliylivnov
Contributor III

In addition to the errors described above, you have an error in the DMA configuration.

LPC_GPDMA->CH[DMA_CH].CONFIG = (0x1 << 0); // enable bit, 1 enable, 0 disable‍

This line is superfluous, since it erases the previous configuration.

Here is the correct DMA channel configuration:

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
   | (0x2 << 11)  //flow control: peripheral to memory - DMA control
   | (0x1 << 14)  // IE  - interrupt error mask
   | (0x1 << 15);  // ITC - terminal count interrupt mask‍‍‍‍‍‍‍‍‍‍‍‍

5,510 Views
a_bet
Contributor IV

Hi vitaliylivnov

I'm still working on this. Would share your full DMA configuration?

If you are not allowed I can well understand...
Thank you

0 Kudos

5,510 Views
vitaliylivnov
Contributor III

Hello, a_bet

We are using modification of LPC Open library from this post: https://community.nxp.com/thread/422128 

0 Kudos

4,675 Views
ehsan
Contributor I

Hello,

The attachment link is not working anymore. I would appreciate it if you can provide the attachment here.

Thanks!

Ehsan

Tags (1)
0 Kudos

5,510 Views
a_bet
Contributor IV

OK thanks, I'm also using this. I also think I found another small bug in the libraries.

in the file gpdma_18xx43xx.c

case GPDMA_TRANSFERTYPE_P2M_CONTROLLER_PERIPHERAL:
        ctrl_word = GPDMA_DMACCxControl_TransferSize((uint32_t) GPDMAChannelConfig->TransferSize)
                    | GPDMA_DMACCxControl_SBSize(GPDMA_LUTPerBurstSrcConn)
                    | GPDMA_DMACCxControl_DBSize(GPDMA_LUTPerBurstSrcConn)
                    | GPDMA_DMACCxControl_SWidth(GPDMA_LUTPerWidSrcConn)
                    | GPDMA_DMACCxControl_DWidth(GPDMA_LUTPerWidSrcConn)
                    | GPDMA_DMACCxControl_SrcTransUseAHBMaster1
                    | GPDMA_DMACCxControl_DI
                    | GPDMA_DMACCxControl_I;

I think the line in bold should be

 | GPDMA_DMACCxControl_DBSize(GPDMA_LUTPerBurstDstConn)

Since it should specify DBSize, which is relative tu Dst and not Src,

What do you think?

0 Kudos

5,506 Views
vitaliylivnov
Contributor III

Hi, a_bet

I think this is not a mistake. In the DMA settings, the size of the packet required by the periphery (source) is used, and the memory (destination) can easily adapt to this size.

0 Kudos

5,506 Views
a_bet
Contributor IV

Maybe I'm a bit confused then.

The UM10503 DBSIZE field description reads:

Destination burst size. Indicates the number of transfers that
make up a destination burst transfer request. This value must
be set to the burst size of the destination peripheral or, if the
destination is memory, to the memory boundary size. The burst
size is the amount of data that is transferred when the BREQ
signal goes active in the destination peripheral.

And the AMBA specification says that memory boundary size should be 1024 bytes.

0 Kudos

5,505 Views
Ray_V
Contributor V

I agree, it is a bug. It will affect some peripheral to peripheral transfers and many memory to peripheral transfers. However peripheral to memory transfers may not be a problem.
These settings are for optimizing transfers, but having different settings for RAM is not necessary unless you cross page boundaries in paged memory as may be the case for some external RAM. In any case, tranfering from peripheral to memory(RAM) should not fail, because any of the burst settings for peripherals will not be likely to cross the page boundaries for RAM in one burst. However, I did not use the library for the transfers since I needed to use a burst size of 16 to meet the transfer speed requirement, but I did use it as reference for my code .

21.8.4 Address generation

Address generation can be either incrementing or non-incrementing (address wrapping is
not supported).

Some devices, especially memories, disallow burst accesses across certain address
boundaries. The DMA controller assumes that this is the case with any source or
destination area, which is configured for incrementing addressing. This boundary is
assumed to be aligned with the specified burst size. For example, if the channel is set for
16-transfer burst to a 32-bit wide device then the boundary is 64-bytes aligned (that is
address bits [5:0] equal 0). If a DMA burst is to cross one of these boundaries, then,
instead of a burst, that transfer is split into separate AHB transactions.

Note: When transferring data to or from the SDRAM, the SDRAM access must always be
programmed to 32 bit accesses. The SDRAM memory controller does not support
AHB-INCR4 or INCR8 bursts using halfword or byte transfer-size. Start address in
SDRAM should always be aligned to a burst boundary address.

21.8.4.1 Word-aligned transfers across a boundary

The channel is configured for 16-transfer bursts, each transfer 32-bits wide, to a
destination for which address incrementing is enabled. The start address for the current
burst is 0x0C000024, the next boundary (calculated from the burst size and transfer
width) is 0x0C000040.

The transfer will be split into two AHB transactions:

a 7-transfer burst starting at address 0x0C000024
a 9-transfer burst starting at address 0x0C000040.

0 Kudos

5,531 Views
Ray_V
Contributor V

I know this was a while ago, but I did not see a clear answer. I have this working at 80MHz on my board using LPC Open.

Just looking at your code:

LPC_ADCHS->DSCR_STS = 1;

Selects Table 1; Descriptor 0

LPC_ADCHS->DESCRIPTOR[0][0] = (1 << 31) /* UPDATE TABLE*/ | (1 << 24) /* RESET_TIMER*/ | (0 << 22) /* THRESH*/ | (0xA00 << /* MATCH*/ | (0x10 << 6) /* BRANCH*/;

Sets table 0, descriptor 0 Measure when counter is at Match=0xA00; Reset Counter and branch 0x10 is invalid as this field is only 2 bits long

LPC_ADCHS->DESCRIPTOR[1][0] = (1 << 31) /* UPDATE TABLE*/ | (1 << 24) /* RESET_TIMER*/ | (0 << 22) /* THRESH*/ | (0x01 << /* MATCH*/ | (0x01 << 6) /* BRANCH*/;

Sets Table 1, descriptor 0. Measure when counter is at Match= 1; Reset Counter and branch to first descriptor when done

This will just run the second descriptor (Table 1, Descriptor 0), and since the counter resets to 0 and measures at 1, you are sampling at 1/2 of the 80MHz. And you may not have enough recovery clocks when first measurement is taken.

try the following

/*  Select Table 0 desccriptor 1 */
LPC_ADCHS->DSCR_STS = (1 << 1)| 0;
/* Set descriptor 0 to take a measurement at every clock and branch to itself*/
LPC_ADCHS->DESCRIPTOR[0][0] = (1 << 24) /* RESET_TIMER*/ | (0 << 22) /* THRESH*/ | (0x00 << 8) /* MATCH*/ | (1 << 6) /* BRANCH to First*/;
/* Set descriptor 1 to take a measurement after 0x9A clocks and branch to first descriptor*/
LPC_ADCHS->DESCRIPTOR[0][1] = (1 << 31) /* UPDATE TABLE*/ | (1 << 24) /* RESET_TIMER*/ | (0 << 22) /* THRESH*/ | (0x9A << 8) /* MATCH*/ | (0x01 << 6) /* BRANCH to first*/;
‍‍‍‍‍‍‍‍‍‍‍‍‍

5,531 Views
a_bet
Contributor IV

Hi raymundovelarde‌ thanks a lot for the corrections.
I'm trying to use your configuration. To test ADC only what I do is: initialize the peripheral and send the software trigger.

My main is:

    ADCHS_init();

   /* START HSADC*/
   Chip_HSADC_SWTrigger(LPC_ADCHS); /*SW Trigger to the ADCHS*/

    while(1){}

My init file:

void ADCHS_init(void)
{
    /*ADCHS CLOCK: derived from USB0 PLL clock - don't use*/
    Chip_USB0_Init();                                                     /* Initialize the USB0 PLL to 480 MHz */
    Chip_Clock_SetDivider(CLK_IDIV_A, CLKIN_USBPLL, 3);            /* Source DIV_A from USB0PLL
    Chip_Clock_SetDivider(CLK_IDIV_B, CLKIN_IDIVA, 2);                 /* Source DIV_B from DIV_A
    Chip_Clock_SetDivider(CLK_IDIV_C, CLKIN_IDIVB, 1);                 /* Source DIV_C from DIV_B

    Chip_Clock_SetBaseClock(CLK_BASE_ADCHS, CLKIN_IDIVC, true, false);     /* Source ADHCS base clock from DIV_C */
    Chip_Clock_EnableOpts(CLK_ADCHS, true, true, 1);                     /* Enable the clock */

    /*Disable and reset Interrupt 0*/
    LPC_ADCHS->INTS[0].CLR_EN   = 0x7F;
    LPC_ADCHS->INTS[0].CLR_STAT = 0x7F;
    /* wait for status to clear, have to exclude FIFO_EMPTY*/
    while(LPC_ADCHS->INTS[0].STATUS & 0x7D);

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

    /* Initialize HSADC */
    Chip_RGU_TriggerReset(RGU_ADCHS_RST);
    LPC_ADCHS->POWER_DOWN = 0;
    LPC_ADCHS->FLUSH = 1;
    /*FIFO's and descriptors' setup*/
    LPC_ADCHS->FIFO_CFG      = (15 << 1) /* FIFO_LEVEL*/
                             | (0); /* 1 = PACKED_READ ENABLED*/

    /*  Select Table 0 descriptor 1 */
    LPC_ADCHS->DSCR_STS = (1 << 1)| 0;
    /* Set descriptor 0 to take a measurement at every clock and branch to itself*/
    LPC_ADCHS->DESCRIPTOR[0][0] = (1 << 24) /* RESET_TIMER*/
                                | (0 << 22) /* THRESH*/
                                | (0x01 << 8) /* MATCH*/
                                | (1 << 6) /* BRANCH to First*/;

    /* Set descriptor 1 to take a measurement after 0x9A clocks and branch to first descriptor*/
    LPC_ADCHS->DESCRIPTOR[0][1] = (1 << 31) /* UPDATE TABLE*/
                                | (1 << 24) /* RESET_TIMER*/
                                | (0 << 22) /* THRESH*/
                                | (0x9A << 8) /* MATCH*/
                                | (0x01 << 6) /* BRANCH to first*/;


    LPC_ADCHS->CONFIG = (0x01)          /* TRIGGER_MASK: sw trigger only */
                      | (0x90 << 6)     /* RECOVERY_TIME */
                      | (0 << 5)     /* CHANNEL ID EN */;


    uint8_t DGEC = 0xE;
    LPC_ADCHS->ADC_SPEED     = (DGEC << 20)
                             | (DGEC << 16)
                             | (DGEC << 12)
                             | (DGEC << 8)
                             | (DGEC << 4)
                             | (DGEC);

    LPC_ADCHS->POWER_CONTROL = (1 << 18) /* BGAP*/
                             | (1 << 17) /* POWER*/
                             | (0 << 4)  /* DCINNEG in ADC0*/
                             | (0 << 10) /* DCINPOS in ADC0*/
                             | (0x4) /* CRS*/
                             | (0 << 16)/*1 = Output in TWOS - 0 = Output in Offset Binary*/;


    /* Enable HSADC sample clock */
    Chip_Clock_Enable(CLK_ADCHS);
}

What I am doing is pausing the execution and check the content of the ADC fifo registers.

Any hints?

0 Kudos

5,532 Views
Ray_V
Contributor V

This shouldn't matter but I don't see why you use divider C, you can get clock directly from divider B.
My setup of the dividers is the opposite of yours on A and B dividers, again this shouldn't matter.

// Initialize USB PLL clock to 80MHz for use with ADCHS and SGPIO
Chip_Clock_EnablePLL(CGU_USB_PLL);
/* Initialize the USB0 PLL to 480 MHz */
Chip_USB0_Init();
Chip_Clock_SetDivider(CLK_IDIV_A, CLKIN_USBPLL, 2); /* Source DIV_A from USB0PLL/2 : IN 480 MHz; OUT 240 MHz */
Chip_Clock_SetDivider(CLK_IDIV_B, CLKIN_IDIVA, 3); /* Source DIV_B from DIV_A/3, : IN 240 MHz; OUT 80 MHz */
Chip_Clock_SetBaseClock(CLK_BASE_ADCHS, CLKIN_IDIVB, true, false);
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

One thing I see you don't enable the HSADC register clock this will cause it to stop since it can't write to the registers

/* Enable HSADC register clock */
Chip_Clock_EnableOpts(CLK_MX_ADCHS, true, true, 1);
/* Enable HSADC sample clock */
Chip_Clock_Enable(CLK_ADCHS);
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Descriptor 0 should have a match of 0 otherwise you are taking measurements at 40MHz, not 80MHz and should have reset timer enabled else the clock would have to wrap around before you get the next match. I assume you are using HSADC channel 0.

/* Set descriptor 0 to take a measurement at every clock and branch to itself*/
LPC_ADCHS->DESCRIPTOR[0][0] = (1 << 24) /* RESET_TIMER*/
   | (0 << 22) /* THRESH*/
   | (0 << 8) /* MATCH*/
   | (1 << 6) /* BRANCH to First*/;
/* Set descriptor 1 to take a measurement after 0x9A clocks and branch to first descriptor*/
LPC_ADCHS->DESCRIPTOR[0][1] = (1 << 31) /* UPDATE TABLE*/
   | (1 << 24) /* RESET_TIMER*/
   | (0 << 22) /* THRESH*/
   | (0x9A << 8) /* MATCH*/
   | (0x01 << 6) /* BRANCH to first*/;
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

5,532 Views
a_bet
Contributor IV

Hi raymundovelarde‌,
thanks for your precious hints: I found out that a big problem was the clock settings. DIV_C should be avoided.
Now my clk config looks like this:

    /*ADCHS CLOCK: derived from USB0 PLL clock - don't use*/
    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,
                                                                           (Max div value supported is 16) [IN 240 MHz; OUT 80 MHz] */
    /*Chip_Clock_SetDivider(CLK_IDIV_C, CLKIN_IDIVB, 1);*/                 /* Source DIV_C from DIV_B,
                                                                           (Max div value supported is 16) [IN 240 MHz; OUT 80 MHz] */

    Chip_Clock_SetBaseClock(CLK_BASE_ADCHS, CLKIN_IDIVB, true, false);     /* Source ADHCS base clock from DIV_C */

    Chip_Clock_EnableOpts(CLK_MX_ADCHS, true, true, 1);                    /* Enable HSADC register clock */

    Chip_Clock_EnableOpts(CLK_ADCHS, true, true, 1);                     /* Enable the clock */

    /* Enable HSADC sample clock */
    Chip_Clock_Enable(CLK_ADCHS);

I don't know why I have

Chip_Clock_EnableOpts(CLK_ADCHS, true, true, 1);

This code is a bit Frankestein, but I tried to remove it and it seems to be useless.

The problem is that I seems to loose samples.
What I do is: feed to the adc a 11MHz sine wave and start the debug session. I then pause it, export memory from 0x400F0200 to 0x400F0240 (these are the fifout regs). Then I process and parse it in matlab. I spent quit a lot of time on the matlab side and I believe is doing its work correctly.
Here's a picture describing my problem (note that I'm in packed mode).
Since I don't event initialize the DMA in my application, I think this is due to ADC misconfiguration. What do you think?
Figure 1_179.png

0 Kudos

5,507 Views
navidfanaee
Contributor III

Hi Bettati 

I am also doing the same process and keep getting meaningless data such as yours. did you make the ADC working properly ? could you pls share your code if it is OK with you ? thanks in advance for your help. 

0 Kudos

5,507 Views
Ray_V
Contributor V

Andrea Bettati,

the difference between Chip_Clock_EnableOpts() and Chip_Clock_Enable() is hat the first function sets up some flags that are not needed to be set on CLK_ADCHS.

Before starting the conversions make sure you flush the FIFO

LPC_ADCHS->FLUSH = 1;

After pausing your program, disable CLK_ADCHS so no conversions are occurring while you read the FIFO.

You could setup something like this

Chip_HSADC_SWTrigger(LPC_ADCHS); /*SW Trigger to the ADCHS*/
{
  bool wait = true;
  while(wait){}
}
Chip_Clock_Disable(CLK_ADCHS);

You can then, after you pause the program, set wait to false and single step to disable the clock.

Then you have to remember that the FIFO only has room for 16 words, since you are enabling packed mode, you have 2 measurements in each word, you may already be doing this but I mention it just in case.

As you read the FIFO, make sure you look at  bits 15-12 to verify that the samples are valid. In your case all should be 0

pastedImage_1.png

5,507 Views
a_bet
Contributor IV

Hi raymundovelarde‌ I did what you suggested and it makes sense to me. I thought peripherals were frozen when the core is, but you say it's not the case so I learned a lot.
I'm able to step through this code (wait is global so that I can change its value):

Chip_HSADC_SWTrigger(LPC_ADCHS); /*SW Trigger to the ADCHS*/
while(1)
{

    /* After pausing your program, disable CLK_ADCHS so no conversions
     * are occurring while you read the FIFO.*/
    while(wait){}

    /* Set wait to false and single step to disable the clock */
    Chip_Clock_Disable(CLK_ADCHS);
    Chip_Clock_Enable(CLK_ADCHS);

}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

And I tried with different signals as input, and I tried to pause both on Disable and Enable  (isn't it that the debugger stops on a line it has still to be executed?),but I still get corrupted results as before unfortunately.
Here my full adc configuration:

void ADCHS_init(void)
{
    /*ADCHS CLOCK: derived from USB0 PLL clock - don't use*/
    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,
                        (Max div value supported is 16) [IN 240 MHz; OUT 80 MHz] */
    /*Chip_Clock_SetDivider(CLK_IDIV_C, CLKIN_IDIVB, 1);*/        /* Source DIV_C from DIV_B,
                           (Max div value supported is 16) [IN 240 MHz; OUT 80 MHz] */

    Chip_Clock_SetBaseClock(CLK_BASE_ADCHS, CLKIN_IDIVB, true, false);  /* Source ADHCS base clock from DIV_C */

    Chip_Clock_EnableOpts(CLK_MX_ADCHS, true, true, 1);     /* Enable HSADC register clock */

    /* Enable HSADC sample clock */
    Chip_Clock_Enable(CLK_ADCHS);

 /*Disable and reset Interrupt 0*/
 LPC_ADCHS->INTS[0].CLR_EN   = 0x7F;
    LPC_ADCHS->INTS[0].CLR_STAT = 0x7F;
    /* wait for status to clear, have to exclude FIFO_EMPTY*/
    while(LPC_ADCHS->INTS[0].STATUS & 0x7D);

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

    /* Initialize HSADC */
    Chip_RGU_TriggerReset(RGU_ADCHS_RST);
    LPC_ADCHS->POWER_DOWN = 0;

    /*FIFO's and descriptors' setup*/
    LPC_ADCHS->FIFO_CFG      = (8 << 1) /* FIFO_LEVEL*/
           | (1); /* 1 = PACKED_READ ENABLED*/



    /*  Select Table 0 descriptor 1 */
    LPC_ADCHS->DSCR_STS = (1 << 1)| 0;
    /* Set descriptor 0 to take a measurement at every clock and branch to itself*/
    LPC_ADCHS->DESCRIPTOR[0][0] = (1 << 24) /* RESET_TIMER*/
              | (0 << 22) /* THRESH*/
        | (0 << 8) /* MATCH*/
                                | (1 << 6) /* BRANCH to First*/;

    /* Set descriptor 1 to take a measurement after 0x9A clocks and branch to first descriptor*/
    LPC_ADCHS->DESCRIPTOR[0][1] = (1 << 31) /* UPDATE TABLE*/
              | (1 << 24) /* RESET_TIMER*/
                                | (0 << 22) /* THRESH*/
                                | (0x9A << 8) /* MATCH*/
                                | (0x01 << 6) /* BRANCH to first*/;


    LPC_ADCHS->CONFIG = (0x01)    /* TRIGGER_MASK: sw trigger only */
                      | (0x90 << 6)  /* RECOVERY_TIME */
       | (0 << 5)  /* CHANNEL ID EN */;


    uint8_t DGEC = 0xE;
    LPC_ADCHS->ADC_SPEED     = (DGEC << 20)
           | (DGEC << 16)
        | (DGEC << 12)
        | (DGEC << 8)
        | (DGEC << 4)
        | (DGEC);

    LPC_ADCHS->POWER_CONTROL = (1 << 18) /* BGAP*/
                             | (1 << 17) /* POWER*/
                             | (0 << 4)  /* DCINNEG in ADC0*/
        | (0 << 10) /* DCINPOS in ADC0*/
                             | (0x4) /* CRS*/
        | (1 << 16)/*1 = Output in TWOS - 0 = Output in Offset Binary*/;

    /*Flush the FIFO before starting the conversion */
    LPC_ADCHS->FLUSH = 1;

}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

I don't know what I could check next, the data seem ok and as you mentioned I hav bits 12-15 to 0 and also 28-31. One ting that I noticed is that data makes sense almost always by groups of 4 samples.

0 Kudos

5,507 Views
Ray_V
Contributor V

The only obvious problem I can see is that you are not waiting for the ADCHS to reset before programming it. Try:

Chip_RGU_TriggerReset(RGU_ADCHS_RST);
while (Chip_RGU_InReset(RGU_ADCHS_RST)){}

Also, you don't really need to set POWER_DOWN to 0.

This controls what the ADCHS does when not in use. When set to 1 (default) it will power down when not in use which will require the recovery time before starting a session. Setting it to 0 will not power down the ADCHS between sessions even when in sleep mode allowing you to start quickly on subsequent sessions.

Deep Sleep, Power Down and Deep Power Down will still power it off.

5,507 Views
a_bet
Contributor IV

Hi raymundovelarde‌, few time these days to work on this project.
Thanks for the tip, I did what you suggested.
Unfortunately I still get bad data, missing sample as in the image I posted.
I tried to reduce the sampling frequency (both changing DIV A and B values, and changing the match value for the table's 0 desc 0) and basically nothing changes. Around 30 msps I saw some decently shaped sinewave once, but nothing more. I really don't understand what I am doing wrong at this point

0 Kudos

5,511 Views
Ray_V
Contributor V

I have not tried the way you are (just reading the FIFO after taking samples).

I have it working using the DMA and reading 1200 to 8100 samples of a 5MHz signal and have not seen the issues you are describing. As I understand it, the way you are running it, it should take 16 samples (fill the FIFO) and then start throwing away samples until you read the FIFO, but as I said I have not tried it that way.

try the following:

ADCHS_Init()
Chip_HSADC_SWTrigger(LPC_ADCHS); /*SW Trigger to the ADCHS*/
while(1)
{

    /* After pausing your program, disable CLK_ADCHS so no conversions
     * are occurring while you read the FIFO.*/
    while(wait){}

    LPC_ADCHS->POWER_CONTROL = 0;
    /* Read FIFO after stepping through above line */

    wait = true;
    ADCHS_Init()
    Chip_HSADC_SWTrigger(LPC_ADCHS); /*SW Trigger to the ADCHS*/
}

5,511 Views
a_bet
Contributor IV

Thank you a lot man, this little piece of code is very useful to check adc operation I believe.
So my results are good if I set match value to 1.
Here a screenshot: 2MHz signal sample at ~40msps
Figure 1_214.png

Unfortunately things screw up if I set the match to 0, and data start to make sense only 4 sample at a time.
Figure 1_215.png

I really don't know, but to me it's more likely to be a debug issue/limit than actual ADCHS misconfiguration. Thoughts?

0 Kudos

5,511 Views
Ray_V
Contributor V

You might be reading the FIFO wrong. I see you are displaying only 16 valid points. You have enabled Packed Read, so there should be 2 samples per 32-bit word read, so you should have 32 valid samples