Hello, I have used the config tool to setup LPC55S69 ADC channel but the result is always 32768 (or half of the 16 bit total value) I have moved the voltage on the pin between 0 - 3.3V but the result is always the same. Included below is a scope trace showing the time to run the autocal (green) and then sampling (Pink), the yellow trace is the ADV input voltage around 2V. Also the code used to obtain the measurements.
uint32_t read_adc()
{
lpadc_conv_result_t config;
GPIO_PinWrite(GPIO, 1, Debug_1, 1);
LPADC_DoAutoCalibration(ADC0);
LPADC_DoSoftwareTrigger(ADC0, 2); //trigger ADC read
GPIO_PinWrite(GPIO, 1, Debug_1, 0);
GPIO_PinWrite(GPIO, 0, Debug_4, 1);
while (!LPADC_GetConvResult(ADC0, &config, 0U)){}; //wait for result
GPIO_PinWrite(GPIO, 0, Debug_4, 0);
return (uint32_t) config.convValue; //return data
}
Solved! Go to Solution.
Thank you very much @xiangjun_rong for your help and support, just to detail here in case anyone else as the same issue, the fix was to execute
void ADC_INIT_PREP(void)
{
POWER_DisablePD(kPDRUNCFG_PD_LDOGPADC);
ANACTRL_Init(ANACTRL);
ANACTRL_EnableVref1V(ANACTRL, true);
}
Before the ADC setup.
Looking at the .h files this turns off the ADC and turns on the 1V internal bandgap, presuming this is required for calibration?
My user manual is Rev 1.9 Nov 2019 and has no reference to the register value AUX_BIAS, I will download the latest and check this detail.
The version of IAR I am using is 8.50.1 and has no details in the register view for AUX_BIAS, I will also get the latest version of this IDE to see if the information as been updated.
Regards Sanders7284
Thank you very much @xiangjun_rong for your help and support, just to detail here in case anyone else as the same issue, the fix was to execute
void ADC_INIT_PREP(void)
{
POWER_DisablePD(kPDRUNCFG_PD_LDOGPADC);
ANACTRL_Init(ANACTRL);
ANACTRL_EnableVref1V(ANACTRL, true);
}
Before the ADC setup.
Looking at the .h files this turns off the ADC and turns on the 1V internal bandgap, presuming this is required for calibration?
My user manual is Rev 1.9 Nov 2019 and has no reference to the register value AUX_BIAS, I will download the latest and check this detail.
The version of IAR I am using is 8.50.1 and has no details in the register view for AUX_BIAS, I will also get the latest version of this IDE to see if the information as been updated.
Regards Sanders7284
Hi, all,
Maybe only the line is required:
POWER_DisablePD(kPDRUNCFG_PD_LDOGPADC);
BR
XiangJun Rong
Maybe only the line is required:
POWER_DisablePD(kPDRUNCFG_PD_LDOGPADC);
Dear @xiangjun_rong ,
why are you writing "maybe"? As an NXP employee you should rather know exactly whether this call is required or not.
I'd appreciate if you'd please discuss internally how to properly initialize this ADC and how to enable which bias power. Thanks.
Daniel
Hi, Daniel,
Sorry, yesterday, I was busy, I did not test on EVK board
I have tested on my EVK board today, for the three lines:
POWER_DisablePD(kPDRUNCFG_PD_LDOGPADC);
ANACTRL_Init(ANACTRL);
ANACTRL_EnableVref1V(ANACTRL, true);
Only the line POWER_DisablePD(kPDRUNCFG_PD_LDOGPADC); is prerequisite.
Hope it can help you
BR
XiangJun Rong
All I can tell you is, I comment that line out and PDEN_AUXBIAS is set to 1 but the ADC is still running, I have the local watch and scope with debug to confirm.
The other way around sets AUXBIAS to 0 as expected.
I stand by the comment, I made a few days ago, it is like there is another location in the ANACTRL register that enables the 1V reference as well.
Hi @sanders7284 ,
I do not doubt your findings. However, they conflict with what I see here with my board.
Did you reset / power off your board after flashing the code? I'm asking the usual code-compile-flash-debug-procedure does not reset the peripherals (at least not on my board).
I ran into this issue while testing whether the call POWER_DisablePD(kPDRUNCFG_PD_LDOGPADC); is required. First I ran code that included this call. Then I commented out this call, compiled, flashed and ran the changed code. Still, AUXBIAS in register PDRUNCFG0 was powered on (== 0x0). See this screenshot:
Only after I pressed the reset button on my board, the AUXBIAS returned to reset defaults (== 0x1 == not powered). Then I saw the "half rail" ADC values again.
Thanks for the reply, I have tried a reset and a power cycle and get the same results, with either register manipulation the ADC runs, without any is the only way I can get back to measuring half rail? this is very strange, but I am happy I can move forward with my project. You can see on my previous post AUXBAIS set and unset and in both case the ADC is running. Unfortunately there is no register entry in my debugger for the location in ATACLTR, so I can't see what that code is actually doing to the register (if anything).
Thanks, @xiangjun_rong .
Your findings correspond with the code provided in this article.
However, the example "lpadc_interrupt" (from drivers section of LPC5528 SDK) does use all three calls.
I wonder why this is?
Hi @sanders7284 ,
I'm coming back to this issue as it just hit me, too, and your solution got the ADC working. Thanks for sharing.
I figured that the call to
POWER_DisablePD(kPDRUNCFG_PD_LDOGPADC);
is sufficient to get the ADC working. It seems to me that output of 1V reference voltage does not need to be enabled in register AUX_BIAS. Can you confirm this?
Note, the API call POWER_DisablePD() manipulates the PDRUNCFG0 register which controls the power to various analog blocks. Here, bit 19 (aka PDEN_AUXBIAS) is cleared. Contrary to what the name insinuates, this call powers up the peripheral.
I wonder whether this call should be issued before the ADC is being initialized or calibrated. At least it also works if I issue it after initialization and calibration and just before triggering the first conversion.
Daniel
Yes you are correct just calling
POWER_DisablePD(kPDRUNCFG_PD_LDOGPADC);
Allows the ADC to function as you say
static inline void POWER_DisablePD(pd_bit_t en)
{
/* PDRUNCFGCLR */
PMC->PDRUNCFGCLR0 = (uint32_t)en;
}
//taken from define
//kPDRUNCFG_PD_LDOGPADC = (1UL << 19),
However what is strange is calling
ANACTRL_Init(ANACTRL);
ANACTRL_EnableVref1V(ANACTRL, true);
Also allows the ACD to run correctly
static inline void ANACTRL_EnableVref1V(ANACTRL_Type *base, bool enable)
{
if (enable)
{
base->AUX_BIAS |= ANACTRL_AUX_BIAS_VREF1VENABLE_MASK;
}
else
{
base->AUX_BIAS &= ~ANACTRL_AUX_BIAS_VREF1VENABLE_MASK;
}
}
#define ANACTRL_AUX_BIAS_VREF1VENABLE_MASK (0x2U)
It's almost like there are 2 registers and if either one is set then the 1V AUX_BIAS is enabled.
This can't be the case, can it?
@sanders7284 , I agree.
According to @xiangjun_rong , only the call to
POWER_DisablePD(kPDRUNCFG_PD_LDOGPADC);
is required. It's confusing why your test results differ. 🤷
No description of register AUX_BIAS? That sounds familiar. Others have struggled with this register as well, see here: forum article.
That being said, the current documentation (UM11126 Rev. 2.4) now covers this register in chapter 11.5.12, Table 287.
Further, ADC startup seems not straight forward. Even the user manual calls the startup procedure a "work-around" (section 39.7.1.1 "ADC start-up sequence software work-around").
Personally, I find the "pseudocode" in this section not helpful at all. I wonder which API it is using.
@danielholala thank you for the links, yes this looks like the same issue, I never searched for AUX_BIAS as I did not know it existed or the previous article may have helped me.
I have the latest user manual and yes the register is detailed, but I agree the workaround section is not very clear nor complete, it talks about powering down the ADC and changing settings in the ADC register but still not mention of AUX_BIAS.
The presumption from NXP seems to be that everyone is starting with the SDK and examples in xpresso, as we use IAR and have bespoke life safety applications this is not always our starting point, we are very dependent on the written materials for our applications.
@xiangjun_rong maybe this is worth raising with the documents team, to make sure the work around and ADC section make reference to the requirements for AUX_BIAS?
Hi,
I have read your read_adc() api function, I do not know where you configure the ADC pin, which you set up the command register, where you assign the ADC channel.
Anyway, I suggest you download SDK package from the link:
https://mcuxpresso.nxp.com/en/welcome
and try to test the ADC example based on MCUXpressoLPC55S69-EVK board.
You can read the readme.txt file to know where the test Analog pin is, you can connect VDD or GND to the analog channel.
In general, the ADC analog channel pin is multiplexed with GPIO, but you can not configure the pin as GPIO output and toggle the GPIO and have the ADC to sample the same pin.
Hope it can help you
BR
XiangJun Rong
Thank you for the reply, the channel setup code is auto generated from the config tools as below:-
/***********************************************************************************************************************
* ADC0 initialization code
**********************************************************************************************************************/
/* clang-format off */
/* TEXT BELOW IS USED AS SETTING FOR TOOLS *************************************
instance:
- name: 'ADC0'
- type: 'lpadc'
- mode: 'LPADC'
- custom_name_enabled: 'false'
- type_id: 'lpadc_ddcc12878b96237847ab78b571214e1c'
- functional_group: 'BOARD_InitPeripherals'
- peripheral: 'ADC0'
- config_sets:
- fsl_lpadc:
- lpadcConfig:
- clockSource: 'AsynchronousFunctionClock'
- clockSourceFreq: 'BOARD_BootClockRUN'
- enableInDozeMode: 'true'
- conversionAverageMode: 'kLPADC_ConversionAverage16'
- offsetCalibration: 'yes'
- autoCalibrate: 'true'
- enableAnalogPreliminary: 'true'
- powerUpDelay: '0x80'
- referenceVoltageSource: 'kLPADC_ReferenceVoltageAlt2'
- powerLevelMode: 'kLPADC_PowerLevelAlt4'
- triggerPriorityPolicy: 'kLPADC_TriggerPriorityPreemptSubsequently'
- enableConvPause: 'false'
- convPauseDelay: '0'
- FIFO0Watermark: '0'
- FIFO1Watermark: '0'
- FIFO0WatermarkDMA: 'false'
- FIFO1WatermarkDMA: 'false'
- lpadcConvCommandConfig:
- 0:
- user_commandId: 'Sounder_Monitoring'
- commandId: '7'
- chainedNextCommandNumber: '0'
- sampleChannelMode: 'kLPADC_SampleChannelSingleEndSideA'
- channelNumber: 'CH.4'
- enableAutoChannelIncrement: 'false'
- loopCount: '0'
- hardwareAverageMode: 'kLPADC_HardwareAverageCount16'
- sampleTimeMode: 'kLPADC_SampleTimeADCK3'
- conversionResoultuionMode: 'kLPADC_ConversionResolutionHigh'
- enableWaitTrigger: 'false'
- lpadcConvTriggerConfig:
- 0:
- user_triggerId: 'Sounder_Monitoring_Trigger'
- triggerId: '1'
- targetCommandId: '7'
- delayPower: '15'
- priority: 'false'
- channelAFIFOSelect: '0'
- channelBFIFOSelect: '0'
- enableHardwareTrigger: 'false'
- IRQ_cfg:
- interrupt_type: ''
- enable_irq: 'false'
- adc_interrupt:
- IRQn: 'ADC0_IRQn'
- enable_interrrupt: 'enabled'
- enable_priority: 'false'
- priority: '0'
- enable_custom_name: 'false'
* BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS **********/
/* clang-format on */
const lpadc_config_t ADC0_config = {
.enableInDozeMode = true,
.conversionAverageMode = kLPADC_ConversionAverage16,
.enableAnalogPreliminary = true,
.powerUpDelay = 0x80UL,
.referenceVoltageSource = kLPADC_ReferenceVoltageAlt2,
.powerLevelMode = kLPADC_PowerLevelAlt4,
.triggerPriorityPolicy = kLPADC_TriggerPriorityPreemptSubsequently,
.enableConvPause = false,
.convPauseDelay = 0UL,
.FIFO0Watermark = 0UL,
.FIFO1Watermark = 0UL
};
lpadc_conv_command_config_t ADC0_commandsConfig[1] = {
{
.sampleChannelMode = kLPADC_SampleChannelSingleEndSideA,
.channelNumber = 4U,
.chainedNextCommandNumber = 0,
.enableAutoChannelIncrement = false,
.loopCount = 0UL,
.hardwareAverageMode = kLPADC_HardwareAverageCount16,
.sampleTimeMode = kLPADC_SampleTimeADCK3,
.conversionResolutionMode = kLPADC_ConversionResolutionHigh,
.enableWaitTrigger = false
}
};
lpadc_conv_trigger_config_t ADC0_triggersConfig[1] = {
{
.targetCommandId = 7,
.delayPower = 15UL,
.channelAFIFOSelect = 0,
.channelBFIFOSelect = 0,
.priority = 1,
.enableHardwareTrigger = false
}
};
static void ADC0_init(void) {
/* Initialize LPADC converter */
LPADC_Init(ADC0_PERIPHERAL, &ADC0_config);
/* Perform offset calibration */
LPADC_DoOffsetCalibration(ADC0_PERIPHERAL);
/* Perform auto calibration */
LPADC_DoAutoCalibration(ADC0_PERIPHERAL);
/* Configure conversion command 7. */
LPADC_SetConvCommandConfig(ADC0_PERIPHERAL, ADC0_SOUNDER_MONITORING, &ADC0_commandsConfig[0]);
/* Configure trigger 1. */
LPADC_SetConvTriggerConfig(ADC0_PERIPHERAL, ADC0_SOUNDER_MONITORING_TRIGGER, &ADC0_triggersConfig[0]);
}
/***********************************************************************************************************************
* Initialization functions
**********************************************************************************************************************/
void BOARD_InitPeripherals(void)
{
/* Initialize components */
ADC0_init();
}
also from pin_mux.c
IOCON->PIO[1][8] =
((IOCON->PIO[1][8] &
/* Mask bits to zero which are setting */
(~(IOCON_PIO_FUNC_MASK | IOCON_PIO_MODE_MASK | IOCON_PIO_SLEW_MASK | IOCON_PIO_DIGIMODE_MASK | IOCON_PIO_ASW_MASK)))
/* Selects pin function.
* : PORT18 (pin 24) is configured as ADC0_4. */
| IOCON_PIO_FUNC(PIO1_8_FUNC_ALT0)
/* Selects function mode (on-chip pull-up/pull-down resistor control).
* : Inactive.
* Inactive (no pull-down/pull-up resistor enabled). */
| IOCON_PIO_MODE(PIO1_8_MODE_INACTIVE)
/* Driver slew rate.
* : Standard-mode, output slew rate is slower.
* More outputs can be switched simultaneously. */
| IOCON_PIO_SLEW(PIO1_8_SLEW_STANDARD)
/* Select Digital mode.
* : Disable digital mode.
* Digital input set to 0. */
| IOCON_PIO_DIGIMODE(PIO1_8_DIGIMODE_ANALOG)
/* Analog switch input control.
* : For all pins except PIO0_9, PIO0_11, PIO0_12, PIO0_15, PIO0_18, PIO0_31, PIO1_0 and PIO1_9 analog
* switch is closed (enabled). */
| IOCON_PIO_ASW(PIO1_8_ASW_VALUE1));
Hi,
This is what you defined:
- user_triggerId: 'Sounder_Monitoring_Trigger'
- triggerId: '1'
Based on above configuration, pls try to change your ADC code like:
uint32_t read_adc()
{
lpadc_conv_result_t config;
GPIO_PinWrite(GPIO, 1, Debug_1, 1);
LPADC_DoAutoCalibration(ADC0);
//LPADC_DoSoftwareTrigger(ADC0, 2); //trigger ADC read
LPADC_DoSoftwareTrigger(ADC0, Sounder_Monitoring_Trigger); //Rong wrote trigger ADC read
GPIO_PinWrite(GPIO, 1, Debug_1, 0);
GPIO_PinWrite(GPIO, 0, Debug_4, 1);
while (!LPADC_GetConvResult(ADC0, &config, 0U)){}; //wait for result
GPIO_PinWrite(GPIO, 0, Debug_4, 0);
return (uint32_t) config.convValue; //return data
}
Pls have a try.
BR
XiangJun Rong
Thanks I have tried this with a slight difference as there was no definition for sounder_monitoring_trigger.
Instead I was able to find:-
/* Trigger 1 - Sounder_Monitoring_Trigger */
#define ADC0_SOUNDER_MONITORING_TRIGGER 1U
Giving code like this.
uint32_t read_adc()
{
lpadc_conv_result_t config;
GPIO_PinWrite(GPIO, 1, Debug_1, 1);
LPADC_DoAutoCalibration(ADC0);
//LPADC_DoSoftwareTrigger(ADC0, 2); //trigger ADC read
LPADC_DoSoftwareTrigger(ADC0, ADC0_SOUNDER_MONITORING_TRIGGER); //trigger ADC read
GPIO_PinWrite(GPIO, 1, Debug_1, 0);
GPIO_PinWrite(GPIO, 0, Debug_4, 1);
while (!LPADC_GetConvResult(ADC0, &config, 0U)){}; //wait for result
GPIO_PinWrite(GPIO, 0, Debug_4, 0);
return (uint32_t) config.convValue; //return data
}
This now gets stuck in the while loop never gaining a result.
After looking at the user manual I believe the code I had originally with a value of 2U is correct, as the register is bitwise, so a 1U as now set up will trigger event 0, so now the wrong event.
The passed value is not shifted, it is used directly, as below.
/*!
* @brief Do software trigger to conversion command.
*
* @Param base LPADC peripheral base address.
* @Param triggerIdMask Mask value for software trigger indexes, which count from zero.
*/
static inline void LPADC_DoSoftwareTrigger(ADC_Type *base, uint32_t triggerIdMask)
{
/* Writes to ADCx_SWTRIG register are ignored while ADCx_CTRL[ADCEN] is clear. */
base->SWTRIG = triggerIdMask;
}
Hi,
Pls pay attention to the color variable.
Anyway, I suggest you use the SDK example code, because the SDK example code is fully tested.
Hope it can help you
BR
XiangJun Rong
/* Set conversion CMD configuration. */
LPADC_GetDefaultConvCommandConfig(&mLpadcCommandConfigStruct);
mLpadcCommandConfigStruct.sampleChannelMode=kLPADC_SampleChannelSingleEndSideB;
mLpadcCommandConfigStruct.channelNumber = 4; //sample CH4B
#if defined(DEMO_LPADC_USE_HIGH_RESOLUTION) && DEMO_LPADC_USE_HIGH_RESOLUTION
mLpadcCommandConfigStruct.conversionResolutionMode = kLPADC_ConversionResolutionHigh;
#endif /* DEMO_LPADC_USE_HIGH_RESOLUTION */
LPADC_SetConvCommandConfig(DEMO_LPADC_BASE, DEMO_LPADC_USER_CMDID, &mLpadcCommandConfigStruct);
/* Set trigger configuration. */
LPADC_GetDefaultConvTriggerConfig(&mLpadcTriggerConfigStruct);
mLpadcTriggerConfigStruct.targetCommandId = DEMO_LPADC_USER_CMDID; /* CMD15 is executed. */
mLpadcTriggerConfigStruct.enableHardwareTrigger = false;
#define TRIGGERID 0
LPADC_SetConvTriggerConfig(DEMO_LPADC_BASE, TRIGGERID, &mLpadcTriggerConfigStruct); /* Configurate the trigger0. */
while (1)
{
GETCHAR();
LPADC_DoSoftwareTrigger(DEMO_LPADC_BASE, 1<<TRIGGERID); /* 1U is trigger0 mask. */
while (!g_LpadcConversionCompletedFlag)
{
}
PRINTF("ADC value: %d\r\nADC interrupt count: %d\r\n",
((g_LpadcResultConfigStruct.convValue) >> g_LpadcResultShift), g_LpadcInterruptCounter);
g_LpadcConversionCompletedFlag = false;
}