AnsweredAssumed Answered

No ADC interrupt after deep sleep on LPC845

Question asked by David Ingleby-Oddy on Nov 4, 2019
Latest reply on Nov 6, 2019 by Alice_Yang

Hello,

 

We have an LPC845 application that uses the internal  ADC and this is working without a problem until we enter and exit deep sleep mode, whereupon no further ADC interrupts are generated.

 

The following code is used to enter deep sleep with a WKT interrupt waking the device after a variable period of time:

 

void sleep(uint32_t timeout)
{

// Setup the WKT timer and configure power down control
Chip_Clock_EnablePeriphClock(SYSCON_CLOCK_ADC);
Chip_Clock_EnablePeriphClock(SYSCON_CLOCK_GPIO0);
Chip_Clock_EnablePeriphClock(SYSCON_CLOCK_GPIO1);
Chip_Clock_EnablePeriphClock(SYSCON_CLOCK_GPIOINT);
Chip_Clock_EnablePeriphClock(SYSCON_CLOCK_IOCON);
Chip_Clock_EnablePeriphClock(SYSCON_CLOCK_SWM);
Chip_Clock_EnablePeriphClock(SYSCON_CLOCK_WKT);
Chip_PMU_SetPowerDownControl(LPC_PMU, PMU_DPDCTRL_LPOSCEN | PMU_DPDCTRL_LPOSCDPDEN | PMU_DPDCTRL_WAKEUPPAD | PMU_DPDCTRL_WAKEUPPAD2);
Chip_SYSCON_PeriphReset(RESET_WKT);
Chip_SYSCON_EnablePeriphWakeup(SYSCON_STARTER_WKT);
NVIC_EnableIRQ(WKT_IRQn);

// Record current peripheral power status ready for wakeup
// and configure power down configuration
LPC_SYSCON->PDAWAKECFG = LPC_SYSCON->PDRUNCFG;
Chip_SYSCON_SetDeepSleepPD((1<<SYSCON_PDRUNCFG_BOD) | (1<<SYSCON_PDRUNCFG_WDTOSC));
Chip_SYSCON_DisableBODReset();

// Turn all LEDs off
Chip_GPIO_SetPinState(LPC_GPIO_PORT, 0, GPIO_LED_RED, pinHigh);
Chip_GPIO_SetPinState(LPC_GPIO_PORT, 0, GPIO_LED_GREEN, pinHigh);
Chip_GPIO_SetPinState(LPC_GPIO_PORT, 0, GPIO_LED_BLUE, pinHigh);

// Turn off peripheral clocks to save power
Chip_Clock_DisablePeriphClock(SYSCON_CLOCK_ADC);
Chip_Clock_DisablePeriphClock(SYSCON_CLOCK_UART0);
Chip_Clock_DisablePeriphClock(SYSCON_CLOCK_UART1);
Chip_Clock_DisablePeriphClock(SYSCON_CLOCK_UART2);
Chip_Clock_DisablePeriphClock(SYSCON_CLOCK_IOCON);
Chip_Clock_DisablePeriphClock(SYSCON_CLOCK_SWM);
Chip_Clock_DisablePeriphClock(SYSCON_CLOCK_GPIO0);
Chip_Clock_DisablePeriphClock(SYSCON_CLOCK_GPIO1);

// Set the WKT to timeout * 10
Chip_WKT_Start(LPC_WKT, WKT_CLKSRC_10KHZ, timeout * 10);

// Enter sleep awaiting WKT interrupt
Chip_PMU_Sleep(LPC_PMU, PMU_MCU_POWER_DOWN);

// Stop the WKT on wakeup
Chip_WKT_Stop(LPC_WKT);

// Jumps the system timer forward by the sleep period
Platform_jumpTick(timeout);

// Re-initialise ADC
adc_init();

}

 

and this is the IRQ handler which re-enables the peripheral clocks upon WKT interrupt:

 

void WKT_IRQHandler(void)
{
Chip_WKT_ClearIntStatus(LPC_WKT);
SCB->SCR &= ~(1<<2);
Chip_Clock_EnablePeriphClock(SYSCON_CLOCK_ADC);
Chip_Clock_EnablePeriphClock(SYSCON_CLOCK_UART0);
Chip_Clock_EnablePeriphClock(SYSCON_CLOCK_UART1);
Chip_Clock_EnablePeriphClock(SYSCON_CLOCK_UART2);
Chip_Clock_EnablePeriphClock(SYSCON_CLOCK_GPIO0);
Chip_Clock_EnablePeriphClock(SYSCON_CLOCK_GPIO1);

}

 

The application also uses three UARTs and a number of other peripherals which work after deep sleep exit so its only the ADC that is the problem.

 

To ensure that the ADC is re-initialised and re-calibrated after deep sleep we also call adc_init() again after exit:

 

void adc_init(void)
{
// Setup ADC for 12-bit mode and normal power
Chip_ADC_Init(LPC_ADC, 0);

// Enable clock, and divide-by-1 at this clock divider
LPC_SYSCON->ADCCLKDIV = 1;
LPC_SYSCON->ADCCLKSEL = 0;

// Calibrate ADC
Chip_ADC_StartCalibration(LPC_ADC);
while (!(Chip_ADC_IsCalibrationDone(LPC_ADC))) {}

// Setup for maximum ADC clock rate using synchronous clocking
Chip_ADC_SetClockRate(LPC_ADC, 10000000);

// Setup a sequencer to read from ADC0
Chip_ADC_SetupSequencer(LPC_ADC, ADC_SEQA_IDX, (ADC_SEQ_CTRL_CHANSEL(0) | ADC_SEQ_CTRL_MODE_EOS));

// Enable the clock to the Switch Matrix before assigning pins
Chip_Clock_EnablePeriphClock(SYSCON_CLOCK_SWM);

// Configure the SWM for P0_7 as the input for the ADC0
Chip_SWM_FixedPinEnable(SWM_FIXED_ADC_0, 1);

// Disable the clock to the Switch Matrix to save power
Chip_Clock_DisablePeriphClock(SYSCON_CLOCK_SWM);

// Clear all pending interrupts
Chip_ADC_ClearFlags(LPC_ADC, Chip_ADC_GetFlags(LPC_ADC));

// Enable ADC overrun and sequence A completion interrupts
Chip_ADC_EnableInt(LPC_ADC, (ADC_INTEN_SEQA_ENABLE | ADC_INTEN_OVRRUN_ENABLE));

// Enable ADC NVIC interrupt
NVIC_EnableIRQ(ADC_SEQA_IRQn);

// Enable sequencer
Chip_ADC_EnableSequencer(LPC_ADC, ADC_SEQA_IDX);

// Manual start for ADC conversion sequence A
Chip_ADC_StartSequencer(LPC_ADC, ADC_SEQA_IDX);

}

 

We have checked all the PDRUNCFG, NVIC and SYSAHBCLK0CTRL registers after exit and these all indicate that the ADC is functional but we do not get any further ADC interrupts until we hard reset the device.

 

Any help would be very much appreciated.

 

David

Outcomes