LPC546xx Internal ADC Threshold Interrupt Lockup

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

LPC546xx Internal ADC Threshold Interrupt Lockup

Jump to solution
797 Views
koreycyrus
Contributor I

So I've been using the ADC0 - channel 9 on the LPC546xx to try and setup a threshold comparison interrupt - triggering above roughly 2 volt. This triggers fine but I can not seem to clear this triggered status, the IRQ handler always seems to fire no matter what (resultantly halting the program), even when removing the power supplies VDDA, ADC0, VD2_ANA, VREFP, TS - and the ADC clock (causing ADC_GetStatusFlags(ADC0) used in the interrupt handler to return 0) and still the interrupt fires, ive tried manipulating the relevant bits in the NVIC registers: clearing the NVIC->ISPR[0] (only the correct bit) and setting NVIC->ICPR[0] (only the correct bit) etc, from what i can determine some internal sequence or hardware routine must stay alive and keep updating NVIC registers, that or i cant overwrite them for some reason (never tried before), same with ADC0->FLAGS (which holds the threshold compare int trigger bit) and ADC0->GDAT[0] (ADC_SEQ_GDAT_RESULT holds the last result and ADC_SEQ_GDAT_CHN is the corresponding channel - which seem valid) registers, but I can set all the other ADC0 registers (CTRL, SEQ_CTRL etc) to zero. Any ideas would be appreciated, all im trying to do is trigger on a threshold, remove the physical trigger condition, then clear the interrupt so it can be triggered again.

 

Note: I extracted this code from a more complex project and used keypresses to fire different test routines (so i could change the voltage and stuff and try different combinations), testing was more indepth than outlined below but this is the general idea of what i tried

 

adc_conv_seq_config_t a2dConvSeqConfigStruct;
adc_result_info_t a2dResultInfoStruct, a2dGlobalResultInfoStruct;
bool stopInterruptPlease = false;

#define CHANGE_THRESHOLD 0
#define CONFIG_ZERO 0
#define KILL_ADC 1
#define KEEP_READING_ONCE_INTERRUPTED 1

void main() {
while(1) {

if (stopInterruptPlease) {
#if KILL_ADC
SysTick_DelayTicks(100); // let die
#endif

// test methods to stop interrupt
#if CHANGE_THRESHOLD
// change threshold stuff - this should not trigger
ADC_SetThresholdPair0(ADC0, 4095, 4096); // shouldnt be in this range
ADC_SetChannelWithThresholdPair0(ADC0, (1 << A2D_CHANNEL_9));
ADC_EnableThresholdCompareInterrupt(ADC0, A2D_CHANNEL_9, kADC_ThresholdInterruptDisabled/*kADC_ThresholdInterruptOnCrossing*/); // neither should trigger
#elif CONFIG_ZERO

ADC0->CTRL = 0;
ADC0->INSEL = 0;
ADC0->SEQ_CTRL[0] = 0;
ADC0->SEQ_CTRL[1] = 0;
ADC0->SEQ_GDAT[0]; // read only - fsl driver says this should clear
ADC0->SEQ_GDAT[1]; // read only - fsl driver says this should clear
// ADC0->DAT[0 - 11]; // read-only & dont care i guess
ADC0->THR0_LOW = 0;
ADC0->THR1_LOW = 0;
ADC0->THR0_HIGH = 0;
ADC0->THR1_HIGH = 0;
ADC0->CHAN_THRSEL = 0;
ADC0->INTEN = 0;
ADC0->FLAGS = 1; // fsl_adc says 1
ADC0->STARTUP = 0;
ADC0->CALIB = 0;
#endif


#if KILL_ADC
// already dead need to recover ADC
POWER_DisablePD(kPDRUNCFG_PD_VDDA);
POWER_DisablePD(kPDRUNCFG_PD_ADC0);
POWER_DisablePD(kPDRUNCFG_PD_VD2_ANA);
POWER_DisablePD(kPDRUNCFG_PD_VREFP);
POWER_DisablePD(kPDRUNCFG_PD_TS);
CLOCK_EnableClock(kCLOCK_Adc0);
#else
NVIC_EnableIRQ(ADC0_THCMP_IRQn);
#endif

#if KEEP_READING_ONCE_INTERRUPTED
A2D_Read(A2D_CHANNEL_9); // triggered - testing with and without keep reading, makes no difference
#endif
} else
A2D_Read(A2D_CHANNEL_9); // not triggered yet - keep reading to keep threshold checks happening (is this correct?)
}
}

void ADC0_THCMP_IRQHandler() {
uint32_t flags = ADC_GetStatusFlags(ADC0);
ADC_ClearStatusFlags(ADC0, 1); // fsl_adc driver says write 1 to clear (also tried 0x0)

if (NVIC_GetPendingIRQ(ADC0_THCMP_IRQn))
NVIC_ClearPendingIRQ(ADC0_THCMP_IRQn); // once threshold has triggered, this interrupt seems to continuously trigger - tried clearing, currently active but tried this outside interrupt stack also - makes no difference as continuously triggered
// NVIC->ICPR &= ~(1 << ADC0_THCMP_IRQn);

if ((1 << A2D_CHANNEL_9) & flags)
stopInterruptPlease = true;
//todo else if // ... other channel callbacks
// else
// PrintErrorADC("*** Could not match source channel ***\n");


#if KILL_ADC
// kill ADC
POWER_EnablePD(kPDRUNCFG_PD_VDDA);
POWER_EnablePD(kPDRUNCFG_PD_ADC0);
POWER_EnablePD(kPDRUNCFG_PD_VD2_ANA);
POWER_EnablePD(kPDRUNCFG_PD_VREFP);
POWER_EnablePD(kPDRUNCFG_PD_TS);
CLOCK_DisableClock(kCLOCK_Adc0);
#else
NVIC_DisableIRQ(ADC0_THCMP_IRQn);
// memory barriers required
__DSB();
__ISB();
#endif
}

void A2D_Read(uint8_t channel) {
a2dConvSeqConfigStruct.channelMask = (1U << channel);
ADC_SetConvSeqAConfig(ADC0, &a2dConvSeqConfigStruct);
ADC_DoSoftwareTriggerConvSeqA(ADC0);
while (!ADC_GetChannelConversionResult(ADC0, channel, &a2dResultInfoStruct));
ADC_GetConvSeqAGlobalConversionResult(ADC0, &a2dGlobalResultInfoStruct);
}

void A2D_Init() {
// give necessary supplies and a clock
POWER_DisablePD(kPDRUNCFG_PD_VDDA);
POWER_DisablePD(kPDRUNCFG_PD_ADC0);
POWER_DisablePD(kPDRUNCFG_PD_VD2_ANA);
POWER_DisablePD(kPDRUNCFG_PD_VREFP);
POWER_DisablePD(kPDRUNCFG_PD_TS);
CLOCK_EnableClock(kCLOCK_Adc0);

if (!ADC_DoSelfCalibration(ADC0))
;//PrintErrorADC("*** ADC calibration failure ***\n");

adc_config_t adcConfigStruct;
// adcConfigStruct.clockDividerNumber = 19; // ~ 5MHz
adcConfigStruct.clockDividerNumber = 4000; // main clock is 96MHz, so ADC should run at like 24,000KHz right?
#if defined(FSL_FEATURE0_ADC_HAS_CTRL_ASYNMODE) & FSL_FEATURE_ADC_HAS_CTRL_ASYNMODE
adcConfigStruct.clockMode = kADC_ClockSynchronousMode;
#endif
#if defined(FSL_FEATURE_ADC_HAS_CTRL_RESOL) & FSL_FEATURE_ADC_HAS_CTRL_RESOL
adcConfigStruct.resolution = kADC_Resolution12bit;
#endif
#if defined(FSL_FEATURE_ADC_HAS_CTRL_BYPASSCAL) & FSL_FEATURE_ADC_HAS_CTRL_BYPASSCAL
adcConfigStruct.enableBypassCalibration = false;
#endif
#if defined(FSL_FEATURE_ADC_HAS_CTRL_TSAMP) & FSL_FEATURE_ADC_HAS_CTRL_TSAMP
adcConfigStruct.sampleTimeNumber = 0U;
#endif
#if defined(FSL_FEATURE_ADC_HAS_CTRL_LPWRMODE) & FSL_FEATURE_ADC_HAS_CTRL_LPWRMODE
adcConfigStruct.enableLowPowerMode = false;
#endif
#if defined(FSL_FEATURE_ADC_HAS_TRIM_REG) & FSL_FEATURE_ADC_HAS_TRIM_REG
adcConfigStruct.voltageRange = kADC_HighVoltageRange;
#endif
ADC_Init(ADC0, &adcConfigStruct);

#if !(defined(FSL_FEATURE_ADC_HAS_NO_INSEL) && FSL_FEATURE_ADC_HAS_NO_INSEL)
ADC_EnableTemperatureSensor(ADC0, false);
#endif

a2dConvSeqConfigStruct.channelMask = (1U << A2D_CHANNEL_9);
a2dConvSeqConfigStruct.triggerMask = 0U; // not hardware group interrupt triggered
a2dConvSeqConfigStruct.triggerPolarity = kADC_TriggerPolarityPositiveEdge;
a2dConvSeqConfigStruct.enableSingleStep = false /*true*/;
a2dConvSeqConfigStruct.enableSyncBypass = false;
a2dConvSeqConfigStruct.interruptMode = kADC_InterruptForEachConversion /*kADC_InterruptForEachSequence*/;
ADC_SetConvSeqAConfig(ADC0, &a2dConvSeqConfigStruct);
ADC_DoSoftwareTriggerConvSeqA(ADC0);
while (!ADC_GetChannelConversionResult(ADC0, A2D_CHANNEL_9, &a2dResultInfoStruct));
ADC_GetConvSeqAGlobalConversionResult(ADC0, &a2dGlobalResultInfoStruct);

ADC_SetThresholdPair0(ADC0, 0, 2500 /*respect to 2^12=4096*/); // no trigger between 0V and ~2V
ADC_SetChannelWithThresholdPair0(ADC0, (1 << A2D_CHANNEL_9));
ADC_EnableThresholdCompareInterrupt(ADC0, A2D_CHANNEL_9, kADC_ThresholdInterruptOnOutside);
NVIC_EnableIRQ(ADC0_THCMP_IRQn); // threshold compare interrupt
}

Labels (1)
0 Kudos
1 Solution
790 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Korey,

I suggest you check each THCMPx bit in the following ADC FLAGS register, if one bit is set, clearing the bit by writing a 1 to the bit. I think it is okay to write 0x3FF to the ADC FLAGS register so that you can clear all bits. Pls have a try.

BR

Xiangjun Rong

 

xiangjun_rong_0-1601432538840.png

 

View solution in original post

0 Kudos
2 Replies
791 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Korey,

I suggest you check each THCMPx bit in the following ADC FLAGS register, if one bit is set, clearing the bit by writing a 1 to the bit. I think it is okay to write 0x3FF to the ADC FLAGS register so that you can clear all bits. Pls have a try.

BR

Xiangjun Rong

 

xiangjun_rong_0-1601432538840.png

 

0 Kudos
777 Views
koreycyrus
Contributor I

Yeah that did it, thank you!

0 Kudos