AnsweredAssumed Answered

LPC4370 HSADC does not work correctly with clocking from PLLAUDIO.

Question asked by Vitaliy Livnov on Jan 7, 2019
Latest reply on Jan 14, 2019 by jeremyzhou

We are trying to figure out how to configure the HSADC microcontroller LPC4370. We took the project periph_hsadc as a basis. When clocking ADC from MAINPLL and USBPLL everything works fine, but when using PLLAUDIO tuned to 80 MHz, the ADC reads some noise that is not related to the input signal. How can we solve this problem? Thanks.

Here is our code:

static void HsadcInit(void) {
     uint32_t freqHSADC = 0;

     /* Setting up the HSADC clock is more complex than other peripherals.
        The HSADC clock is driven directly from the CGU/CCU and has limited
        source and divider options. Because the HSADC clocking is entirely
        handled outside the HSADC peripheral, example code for setting up
        the CGU/CCU to get a rough HSADC clock rate is included in this
        example. */

     setupClock(HSADC_CLOCK_RATE);

     /* Initialize HSADC */
     Chip_HSADC_Init(LPC_ADCHS);

     /* Show the actual HSADC clock rate */
     freqHSADC = Chip_HSADC_GetBaseClockRate(LPC_ADCHS);
     DEBUGOUT_S("HSADC sampling rate = %dKHz\r\n", freqHSADC / 1000);

     /* Setup FIFO trip points for interrupt/DMA to 8 samples, no packing */
     Chip_HSADC_SetupFIFO(LPC_ADCHS, 8, false);

     /* Software trigger only, 0x90 recovery clocks, add channel IF to FIFO entry */
     Chip_HSADC_ConfigureTrigger(LPC_ADCHS, HSADC_CONFIG_TRIGGER_SW,
                                        HSADC_CONFIG_TRIGGER_RISEEXT, HSADC_CONFIG_TRIGGER_NOEXTSYNC,
                                        HSADC_CHANNEL_ID_EN_ADD, 0x90);

     /* Select both positive and negative DC biasing for input 3 */
     Chip_HSADC_SetACDCBias(LPC_ADCHS, 0, HSADC_CHANNEL_DCBIAS, HSADC_CHANNEL_NODCBIAS);
     //Chip_HSADC_SetACDCBias(LPC_ADCHS, 2, HSADC_CHANNEL_DCBIAS, HSADC_CHANNEL_DCBIAS);

     /* Set low A threshold to 10% and high A threshold to 90% */
     Chip_HSADC_SetThrLowValue(LPC_ADCHS, 0, ((HSADC_MAX_SAMPLEVAL * 1) / 10));
     Chip_HSADC_SetThrHighValue(LPC_ADCHS, 0, ((HSADC_MAX_SAMPLEVAL * 9) / 10));

     /* Set low B threshold to 40% and high B threshold to 60% */
     Chip_HSADC_SetThrLowValue(LPC_ADCHS, 1, ((HSADC_MAX_SAMPLEVAL * 4) / 10));
     Chip_HSADC_SetThrHighValue(LPC_ADCHS, 1, ((HSADC_MAX_SAMPLEVAL * 6) / 10));

     /* Setup data format for offset binary and update clock settings. This function
        should be called whenever a clock change is made to the HSADC */

     Chip_HSADC_SetPowerSpeed(LPC_ADCHS, false);

     /* Enable HSADC power */
     Chip_HSADC_EnablePower(LPC_ADCHS);

     /* Setup HSADC table 0 descriptors */
     /* Descriptor entries are mapped as follows */
     /* 0 : mapped to input 0, branch to next descriptor after sample, match time
        is 0x90 clocks for the initial sample (must be greater than or equal to
           recovery clocks for auto power-up), test against threshold A */

     Chip_HSADC_SetupDescEntry(LPC_ADCHS, 0, 0, (HSADC_DESC_CH(0) |
                                                            HSADC_DESC_BRANCH_NEXT | HSADC_DESC_MATCH(0x95) | HSADC_DESC_THRESH_A |
                                                            HSADC_DESC_RESET_TIMER));

     Chip_HSADC_SetupDescEntry(LPC_ADCHS, 0, 1, (HSADC_DESC_CH(0) |
                                                            HSADC_DESC_BRANCH_NEXT | HSADC_DESC_MATCH(0x01) | HSADC_DESC_THRESH_A |
                                                            HSADC_DESC_RESET_TIMER));
     Chip_HSADC_SetupDescEntry(LPC_ADCHS, 0, 2, (HSADC_DESC_CH(0) |
                                                            HSADC_DESC_BRANCH_NEXT | HSADC_DESC_MATCH(0x01) | HSADC_DESC_THRESH_A |
                                                            HSADC_DESC_RESET_TIMER));
     Chip_HSADC_SetupDescEntry(LPC_ADCHS, 0, 3, (HSADC_DESC_CH(0) |
                                                            HSADC_DESC_BRANCH_NEXT | HSADC_DESC_MATCH(0x01) | HSADC_DESC_THRESH_A |
                                                            HSADC_DESC_RESET_TIMER));
     Chip_HSADC_SetupDescEntry(LPC_ADCHS, 0, 4, (HSADC_DESC_CH(0) |
                                                            HSADC_DESC_BRANCH_NEXT | HSADC_DESC_MATCH(0x01) | HSADC_DESC_THRESH_A |
                                                            HSADC_DESC_RESET_TIMER));
     Chip_HSADC_SetupDescEntry(LPC_ADCHS, 0, 5, (HSADC_DESC_CH(0) |
                                                            HSADC_DESC_BRANCH_NEXT | HSADC_DESC_MATCH(0x01) | HSADC_DESC_THRESH_A |
                                                            HSADC_DESC_RESET_TIMER));
     Chip_HSADC_SetupDescEntry(LPC_ADCHS, 0, 6, (HSADC_DESC_CH(0) |
                                                            HSADC_DESC_BRANCH_NEXT | HSADC_DESC_MATCH(0x01) | HSADC_DESC_THRESH_A |
                                                            HSADC_DESC_RESET_TIMER));

     /* 1 : mapped to input 0, branch to next descriptor after sample, match time
        is 1, test against threshold A */

     //Добавил HSADC_DESC_HALT и HSADC_DESC_INT после данного дескриптора
     Chip_HSADC_SetupDescEntry(LPC_ADCHS, 0, 7, (HSADC_DESC_CH(0) | HSADC_DESC_BRANCH_NEXT
                                                            | HSADC_DESC_HALT | HSADC_DESC_INT
                                                            | HSADC_DESC_MATCH(1) | HSADC_DESC_THRESH_A | HSADC_DESC_RESET_TIMER));

     Chip_HSADC_EnableInts(LPC_ADCHS, 0, (HSADC_INT0_DSCR_DONE));

     /* Enable HSADC interrupts in NVIC */
     NVIC_EnableIRQ(ADCHS_IRQn);

     /* Update descriptor tables - needed after updating any descriptors */
     Chip_HSADC_UpdateDescTable(LPC_ADCHS, 0);

     /* Setup periodic timer to perform software triggering */
     timer_setup();
}


/* Clock setup function for generating approximate HSADC clock. Trim this
   example function as needed to get the size down in a production system. */

static void setupClock(uint32_t rate)
{
//----PLLAUDIO----//
     SetUpPLL0audio(100, 15, 1);
     Chip_Clock_SetBaseClock(CLK_BASE_ADCHS, CLKIN_AUDIOPLL, true, false); /* HSADC base clock = divider A input */

//----MAINPLL----//
//     Chip_Clock_SetDivider(CLK_IDIV_A, CLKIN_MAINPLL, 2); /* Setup divider A for main PLL rate divided by 3 */
//     Chip_Clock_SetBaseClock(CLK_BASE_ADCHS, CLKIN_IDIVA, true, false);

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


     uint32_t frequency = ClockSourceFreqMeasure(CLKIN_AUDIOPLL);
     DEBUGOUT_S("Measured frequency=%d\r\n", frequency);

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

/**************************************************************************//**
*
* @brief Setup PLL0Audio as XTAL*msel/(nsel*psel)
*
* @param [in] msel  Multiple value
* @param [in] nsel  Pre-divider value
* @param [in] psel  Post-divider value
*
* @return  void
*
*****************************************************************************/

void SetUpPLL0audio(uint32_t msel, uint32_t nsel, uint32_t psel)
{

  /* disable clock, disable skew enable, power down pll,
  * (dis/en)able post divider, (dis/en)able pre-divider,
  * disable free running mode, disable bandsel,
  * enable up limmiter, disable bypass
  */

  LPC_CGU->PLL[CGU_AUDIO_PLL].PLL_CTRL = (6 << 24)   /* source = XTAL OSC 12 MHz */
                            | _BIT(0);  /* power down */

  /* set NDEC, PDEC and MDEC register */
  LPC_CGU->PLL[CGU_AUDIO_PLL].PLL_NP_DIV = (NDEC_Encode(nsel) << 12) | PDEC_Encode(psel);
  LPC_CGU->PLL[CGU_AUDIO_PLL].PLL_MDIV = MDEC_Encode(msel);

  LPC_CGU->PLL[CGU_AUDIO_PLL].PLL_CTRL = (6 << 24)   /* source = XTAL OSC 12 MHz */
                                      | (6<< 12);     // fractional divider off and bypassed

  /* wait for lock */
  while (!(LPC_CGU->PLL[CGU_AUDIO_PLL].PLL_STAT & 1));

  /* enable clock output */
  LPC_CGU->PLL[CGU_AUDIO_PLL].PLL_CTRL |= (1<<4); /* CLKEN */
}

Outcomes