Has anyone else noticed a large discontinuity while using the LPC845 ADC near mid-value? I am seeing a range of missing codes immediately below 2048 ticks.
I can replicate this on my own boards along with the LPCXpresso845MAX dev board using minimally modified example code. By changing the adc_basic example to use ADC2 and connecting a bench supply with 2K/22K resistor divider to Analog header A0, I can replicate the issue by dialing the supply voltage. Here are the missing code ranges I observe on my three LPCXpresso845MAX boards:
2034-2047
2037-2047
2041-2047
The problem exists independent of sample rate and is present at much lower input impedances as well. This is rather annoying for my application since I have bidirectional current sensors which idle at half ADC value.
Solved! Go to Solution.
XiangJun,
This does appear to resolve the problem. The FSL driver provided in the example code (2.3.1) is already trying to do this but there appear to be two problems which the new code resolves:
This means that ADC_DoSelfCalibration shows the outward appearance of calibration succeeding when it's not actually running at all or is clocked too fast. I also noted that, as of SDK 3.5.0, this problem still exists in the FSL driver. I have updated my fls_adc driver to 3.5.0 and applied the following changes:
bool ADC_DoSelfCalibration(ADC_Type *base, uint32_t frequency) {
uint32_t tmp32;
uint32_t ctrl_value;/* Store the current contents of the ADC CTRL register. */
tmp32 = base->CTRL;/* Ensure the ADC clock is enabled in SYSCON. */
SYSCON->SYSAHBCLKCTRL0 |= SYSCON_SYSAHBCLKCTRL0_ADC_MASK;
ctrl_value = base->CTRL;
/* Divide the system clock to yield an ADC clock of about 500 kHz. */
ctrl_value &= ~ADC_CTRL_CLKDIV_MASK;
ctrl_value |= ADC_CTRL_CLKDIV((frequency / 500000U) - 1U);
/* Clear the LPWR bit. */
ctrl_value &= ~ADC_CTRL_LPWRMODE_MASK;
/* Start ADC self-calibration. */
ctrl_value |= ADC_CTRL_CALMODE_MASK;/* Set the new CTRL value in a single write as recommended by the datasheet. */
base->CTRL = ctrl_value;/* Delay for 200 uSec @ 500KHz ADC clock */
SDK_DelayAtLeastUs(200U);/* Check the completion of calibration. */
if (ADC_CTRL_CALMODE_MASK == (base->CTRL & ADC_CTRL_CALMODE_MASK)) {
/* Restore the contents of the ADC CTRL register. */
base->CTRL = tmp32;
return false; /* Calibration timeout. */
}
/* Restore the contents of the ADC CTRL register. */
base->CTRL = tmp32;return true;
}
I hope that it will be patched ASAP to avoid further confusion. Thank you for the assistance in getting this figured out.
Evan Buchanan
Hi, Evan,
With the design engineers involved, the problem is solved. when you do calibration for the ADC, you should set the ADC clock as a slower ADC clock frequency for example 0.5MHz. After the calibration, you can recover the ADC clock frequency to 30MHz at most.
I attach the code which you gave and modified by us, pls have a try.
BR
XiangJun Rong
bool ADC_DoSelfCalibration(ADC_Type *base, uint32_t frequency)
{
uint32_t tmp32;
#if 0
/* Store the current contents of the ADC CTRL register. */
tmp32 = base->CTRL;
/* Start ADC self-calibration. */
base->CTRL |= ADC_CTRL_CALMODE_MASK;
/* Divide the system clock to yield an ADC clock of about 500 kHz. */
base->CTRL &= ~ADC_CTRL_CLKDIV_MASK;
base->CTRL |= ADC_CTRL_CLKDIV((frequency / 500000U) - 1U);
/* Clear the LPWR bit. */
base->CTRL &= ~ADC_CTRL_LPWRMODE_MASK;
/* Delay for 200 uSec @ 500KHz ADC clock */
SDK_DelayAtLeastUs(200U);
/* Check the completion of calibration. */
if (ADC_CTRL_CALMODE_MASK == (base->CTRL & ADC_CTRL_CALMODE_MASK))
{
/* Restore the contents of the ADC CTRL register. */
base->CTRL = tmp32;
return false; /* Calibration timeout. */
}
/* Restore the contents of the ADC CTRL register. */
base->CTRL = tmp32;
#else
/* Store the current contents of the ADC CTRL register. */
SYSCON->SYSAHBCLKCTRL0|=1<<24;
tmp32 = base->CTRL;
/* Divide the system clock to yield an ADC clock of about 500 kHz. */
base->CTRL &= ~ADC_CTRL_CLKDIV_MASK;
base->CTRL |= ADC_CTRL_CLKDIV((frequency / 500000U) - 1U);
/* Clear the LPWR bit. */
base->CTRL &= ~ADC_CTRL_LPWRMODE_MASK;
/* Start ADC self-calibration. */
base->CTRL |= ADC_CTRL_CALMODE_MASK;
/* Check the completion of calibration. */
while (base->CTRL & ADC_CTRL_CALMODE_MASK);
/* Restore the contents of the ADC CTRL register. */
base->CTRL = tmp32;
#endif
return true;
}
bool ADC_DoSelfCalibration(ADC_Type *base, uint32_t frequency)
{
uint32_t tmp32;
#if 0
/* Store the current contents of the ADC CTRL register. */
tmp32 = base->CTRL;
/* Start ADC self-calibration. */ // <== Shouldn't this be done after the step below (changing clock to 500 kHz)???
base->CTRL |= ADC_CTRL_CALMODE_MASK;
/* Divide the system clock to yield an ADC clock of about 500 kHz. */
base->CTRL &= ~ADC_CTRL_CLKDIV_MASK;
base->CTRL |= ADC_CTRL_CLKDIV((frequency / 500000U) - 1U);
...
The code you are referencing is defined out (#if 0) and is from the broken FSL driver. The updated code from their FAE (after the #else) does move the calibration enable after the change of the CLKDIV.
However, changing the placement of the calibration start alone doesn't fix the problem unless the ADC clock has been previously enabled in SYSCON->SYSAHBCLKCTRL0. Further, I prefer setting CTRL in a single write as recommended in the user manual section 27.3.4 step 2.
XiangJun,
This does appear to resolve the problem. The FSL driver provided in the example code (2.3.1) is already trying to do this but there appear to be two problems which the new code resolves:
This means that ADC_DoSelfCalibration shows the outward appearance of calibration succeeding when it's not actually running at all or is clocked too fast. I also noted that, as of SDK 3.5.0, this problem still exists in the FSL driver. I have updated my fls_adc driver to 3.5.0 and applied the following changes:
bool ADC_DoSelfCalibration(ADC_Type *base, uint32_t frequency) {
uint32_t tmp32;
uint32_t ctrl_value;/* Store the current contents of the ADC CTRL register. */
tmp32 = base->CTRL;/* Ensure the ADC clock is enabled in SYSCON. */
SYSCON->SYSAHBCLKCTRL0 |= SYSCON_SYSAHBCLKCTRL0_ADC_MASK;
ctrl_value = base->CTRL;
/* Divide the system clock to yield an ADC clock of about 500 kHz. */
ctrl_value &= ~ADC_CTRL_CLKDIV_MASK;
ctrl_value |= ADC_CTRL_CLKDIV((frequency / 500000U) - 1U);
/* Clear the LPWR bit. */
ctrl_value &= ~ADC_CTRL_LPWRMODE_MASK;
/* Start ADC self-calibration. */
ctrl_value |= ADC_CTRL_CALMODE_MASK;/* Set the new CTRL value in a single write as recommended by the datasheet. */
base->CTRL = ctrl_value;/* Delay for 200 uSec @ 500KHz ADC clock */
SDK_DelayAtLeastUs(200U);/* Check the completion of calibration. */
if (ADC_CTRL_CALMODE_MASK == (base->CTRL & ADC_CTRL_CALMODE_MASK)) {
/* Restore the contents of the ADC CTRL register. */
base->CTRL = tmp32;
return false; /* Calibration timeout. */
}
/* Restore the contents of the ADC CTRL register. */
base->CTRL = tmp32;return true;
}
I hope that it will be patched ASAP to avoid further confusion. Thank you for the assistance in getting this figured out.
Evan Buchanan
I'm having less than stellar results with NXP support in getting this resolved or even confirmed.
I'm including a link to sample code for the LPCXpresso845MAX should someone here want to verify the behavior. You can do this by programming the firmware to your board and then attaching to the running device via LinkServer in MCUXpresso. You can see the configuration by restarting and then resuming. Pins J6-6 and J1-6 must be jumpered to carry signal from DAC0 to ADC0.
The firmware generates random noise (default) or a sine wave at ~1.65V on DAC0. It simultaneously samples ADC0 to generate a histogram of all ADC codes present. It periodically checks for the presence of codes between 1998 and 2097 outputting the number of missing codes. If the count is less than 20, a list will be printed as shown here:
I'm interested in seeing if the problem is reproducible on newer chip revisions. I will be checking on a newer LPC845-BRK shortly.
The code is available on my Google Driver here: https://drive.google.com/open?id=1eOXafzRHB2a7cj2CsgvzIe1x20INcBPq
Hi, Evan
I have downloaded your code, I will test the code later and tell you the result next week.
BR
XiangJun Rong
Shortly after I posted my last message support was able to reproduce the problem on the dev board I sent them. This is a positive development, but more data never hurts, especially if there is a production date component to the problem.
Thanks for the additional effort,
Evan
Hi, Evan,
I can reproduce your issue on my LPCXpresso845MAX board, I have created a ticket for AE team, I will help AE team to duplicate the issue and hear their opinion for the root cause, then determine if we list the issue in the errata.
BR
Xiangjun Rong
Hi, Evan,
For the ADC missing code issue of LPC84x, I have notified the AE team. As you know there is not any workaround or whatever to avoid this issue. Sorry for not helping you
BR
Xiangjun Rong
Hi all,
this case of missinge codes is less annoying than the one I encountered with an acceleration sensor. There were large intervals of acceleration values mapped onto the same codes, adjacent to gaps of missing codes. In this case, however, there is no large interval of voltages mapped to the same code. This allows a workaround that is ~100% effective (decrease in resolution from 12 bit to 11.995 bit) and "easy" in the sense of now extra components on the board and only a few lines of extra code: Calibrate away the gap by using different offset values for the code ranges above and below the gap.
If you planned to do a calibration anyway, the extra cost is minimal.
If not, check whether 11 bit resolution is sufficient and if so, map the input range to half the code range.
If not, chose another chip.
Rainald
Rainald Koch wrote:
In this case, however, there is no large interval of voltages mapped to the same code. This allows a workaround that is ~100% effective (decrease in resolution from 12 bit to 11.995 bit) and "easy" in the sense of now extra components on the board and only a few lines of extra code: Calibrate away the gap by using different offset values for the code ranges above and below the gap.
This does not seem like a viable approach given the gap varies with each part. If one were to try and calibrate this out in production, it would take too much test time to be precise.
And what if the code gap varies with temperature, voltage, or other variables?
If not, check whether 11 bit resolution is sufficient and if so, map the input range to half the code range.
It isn't "11 bits". The code gap is as much as 15 codes, which is 4 bits.
Engineers don't choose a microcontroller with a 12 bit ADC and then are happy with it acting like an unpredictable 8 bit ADC.
If not, chose another chip.
If the NXP response is that the LPC845 ADC code gap is acceptable, then, yes, I'd steer away from NXP microcontrollers. Having a gap like that right in mid range is clearly unacceptable and doesn't meet datasheet spec for INL and DNL.
JB
:-o Using half the range of a 12-bit ADC yields 11-bit resolution, not 8 bit.
Xiangjun,
We have also contacted support through our sales rep referencing this post. I would be more than happy for this to be a software or configuration issue but your reply implies that is not the case. If this is a known hardware issue, the lack of an Errata is unacceptable. Thank you for running this up the chain on your end.
xiangjun.rong wrote:
Hi, Evan,
For the ADC missing code issue of LPC84x, I have notified the AE team. As you know there is not any workaround or whatever to avoid this issue. Sorry for not helping you
BR
Xiangjun Rong
Xiangjun,
Your reply, given you know there is no workaround, suggests this is a known issue, that Evan wasn't the first to encounter it. Yet there is no entry in the errata list for the LPC84x. Will the Errata be updated? When was this issue first discovered?
Also, does this issue afflict other LPC8xx chips? For example, I have a design with an LPC804 which also has a 12 bit ADC which seems similar to the LPC84x series. I haven't tested the ADC, but I am worried it also has this defect. I was considering moving the project to an LPC84x series, but this ADC issue has taken the motivation out of that. There's a lot of choice in the low cost Cortex-M microcontroller market, so this ADC issue needs to get fixed.
JB
Xiangjun,
I've done a bit more testing to better quantify the problem.
First, I ran a quick test with a 500Hz ~50mV sine wave from a 50 ohm signal generator on my worst LPCXpresso845MAX board. The sample plot makes it easy to visualize the gap in codes:
Second, I collected date codes and more precise code gap sizes for the LPC845 chips I am looking at:
LPCXpresso845MAX Boards:
Date Code | Missing Code Count |
---|---|
17-27 | 13 |
17-27 | 9 |
17-44 | 7 |
My Custom Board:
Date Code | Missing Code Count |
---|---|
17-44 | 15 |
I'm seeing the problem in both batches of chips I have on hand.
Lastly, I ran a test over night with a source at 1.65V. This results in readings toggling back and forth across the discontinuity due to noise. Over many hours and millions of samples, not once did I see a missing code re-appear.
So to summarize:
Other engineers on my end are interested to see if the problem can be replicated. We suspect an unexpected error term in the lower half of the successive approximation DAC or similar issue.
Thanks,
Evan Buchanan
Xiangjun,
Using a 50 ohm waveform generator as a source, I still see roughly the same number of missing codes (+/-1). The problem doesn't appear to be source impedance dependent.
Thanks for the assistance,
Evan
Hi, Evan,
if you use a low impedance voltage source to have a test, for example connect the waveform generator output(have it output DC voltage) to ADC analog channel, then have the output DC voltage to about 1.6V, does the missing code appear?
Xiangjun Rong