We're using an EFM8BB1 dev kit to develop an application that uses the ADC as its main input system. We're currently looking at 4 channels in sequence, reading all 4 in turn, putting the data out the serial port and moving on to another set of input conditions. I've put it into 12-bit mode to get maximum resolution, using the following code:
// ADSC (SAR Clock Divider) = 1
// ((24.5MHz/1)/(5+1) = 4.08 MHz per optimal power recommendations)
// AD8BE (8-Bit Mode Enable) = NORMAL (ADC0 operates in 10-bit or 12-bit mode (normal operation).)
// ADGN (Gain Control) = GAIN_1 (The on-chip PGA gain is 1.)
// ADTM (Track Mode) = TRACK_NORMAL (Normal Track Mode. When ADC0 is
// enabled, conversion begins immediately following the start-of-
// conversion signal.)
ADC0CF = (1 << ADC0CF_ADSC__SHIFT) | ADC0CF_AD8BE__NORMAL | ADC0CF_ADGN__GAIN_1 | ADC0CF_ADTM__TRACK_NORMAL;
// [ADC0CF - ADC0 Configuration]$
// $[ADC0AC - ADC0 Accumulator Configuration]
* ADRPT ACC_4: Accumulate 4 readings (1 reading in 12-bit mode)
* ADSJST RIGHT_NO_SHIFT: Right-justify the result
* ADAE ACC_DISABLED: Latest result only held in accumulator until read
* AD12BE 12_BIT_ENABLED: Enable 12-bit mode
ADC0AC = ADC0AC_ADRPT__ACC_4 | ADC0AC_ADSJST__RIGHT_NO_SHIFT | ADC0AC_ADAE__ACC_DISABLED | ADC0AC_AD12BE__12_BIT_ENABLED;
// [ADC0AC - ADC0 Accumulator Configuration]$
// $[ADC0TK - ADC0 Burst Mode Track Time]
* AD12SM SAMPLE_ONCE: Sample once at the start of the cycle
* ADTK 52: Delay (64-52) = 12 HF oscillator cycles between consecutive conversions in bursts
* as per optimal power recommendations
ADC0TK = ADC0TK_AD12SM__SAMPLE_ONCE | (52 << ADC0TK_ADTK__SHIFT);
// [ADC0TK - ADC0 Burst Mode Track Time]$
// $[ADC0PWR - ADC0 Power Control]
* ADBIAS MODE3: Bias mode 3 (sample clock < 4MHz, but 4.08 is apparently OK)
* ADMXLP LP_MUX_VREF_ENABLED: Low power VREF and MUX buffers enabled
* ADLPM LP_BUFFER_ENABLED: Low power mode enabled
* ADPWR 4: Set the ADC power-up delay to (4 * 8) = 32 oscillator cycles
ADC0PWR = ADC0PWR_ADBIAS__MODE3 | ADC0PWR_ADMXLP__LP_MUX_VREF_ENABLED | ADC0PWR_ADLPM__LP_BUFFER_ENABLED | (4 << ADC0PWR_ADPWR__SHIFT);
// [ADC0PWR - ADC0 Power Control]$
// $[REF0CN - Reference Voltage Control]
* TEMPE TEMP_DISABLED: Not using the temperature sensor
* REFSL INTERNAL_VREF: Using the internal voltage reference
* GNDSL GND_PIN: Using the GND pin as the 0V reference
* IREFLVL 2P4: Reference voltage set to 2.4V
REF0CN = REF0CN_TEMPE__TEMP_DISABLED | REF0CN_REFSL__INTERNAL_VREF | REF0CN_GNDSL__GND_PIN | REF0CN_IREFLVL__2P4;
// [REF0CN - Reference Voltage Control]$
// $[ADC0GTH - ADC0 Greater-Than High Byte]
// [ADC0GTH - ADC0 Greater-Than High Byte]$
// $[ADC0GTL - ADC0 Greater-Than Low Byte]
// [ADC0GTL - ADC0 Greater-Than Low Byte]$
// $[ADC0LTH - ADC0 Less-Than High Byte]
// [ADC0LTH - ADC0 Less-Than High Byte]$
// $[ADC0LTL - ADC0 Less-Than Low Byte]
// [ADC0LTL - ADC0 Less-Than Low Byte]$
// $[ADC0CN0 - ADC0 Control 0]
* ADCM ADBUSY: Start a conversion by writing 1 to the ADBUSY bit
* ADBMEN BURST_ENABLED: ADC is in burst mode
* ADEN DISABLED: ADC is in low-power mode
ADC0CN0 = ADC0CN0_ADCM__ADBUSY | ADC0CN0_ADBMEN__BURST_ENABLED | ADC0CN0_ADEN__DISABLED;
// [ADC0CN0 - ADC0 Control 0]$
} /* AdcInit */
What we're seeing in the output data is interesting: Every so often, there's a discontinuity in the data for a set of input conditions and it appears to be "stuck" on the same value. Invariably, this value will have the following bit pattern in the lower bits: 11100
We can't find anything explicit in the code that might cause this. We're suspecting there might be a quirk in the hardware implementation of the oversampling calculation.
Has anybody else seen anything like this?
I can't post raw data without cleaning it up and removing some tell-tale commercial information. Possibly tomorrow if required (or I can email to a SiLabs person directly).
I've just realised, on re-reading the code in my message, that my comments on the clock divider don't line up with the actual code. For a 24.5MHz clock, the first active line of AdcInit() should be:
ADC0CF = (5 << ADC0CF_ADSC__SHIFT) | ADC0CF_AD8BE__NORMAL | ADC0CF_ADGN__GAIN_1 | ADC0CF_ADTM__TRACK_NORMAL;
I'll give that a whirl tomorrow.
If you'd like to provide information privately to a support engineer, please submit a support request here:
https://www.silabs.com/support > Email Support Request.
It may be helpful to the see a snippet of the runtime code that read the ADC value and ADC input mux change.
FYI - we also have an ADC example in Simplicity Studio (EFM8BB1_ADC_ExternalInput). Can you reproduce this behavior when adapting this example?
I've just realised, on re-reading the code in my message
soooo many threads have gone astray because of retyped code
ALWAYS use copy-paste and use the insert code button above
Hi Erik and Stephen,
Erik: I had actually used copy-and-paste. That misplaced "1" is from the original code, which has undergone some revision since I copied it from the example. I suspect that's the real source of my issue. I effectively have a 12.25MHz sample clock instead of the 4.08MHz clock used in the example (and recommended in the datasheet).
I was wondering how to format the code more cleanly: It took a moment of hovering the mouse over various buttons in the web interface here to work out which is the "Insert Code" button. I'll definitely use that in future. Thanks for pointing it out.
Stephen: Thanks for the link. If fixing the sample clock divider ratio doesn't do the trick, I'll post the spreadsheet that was generated in the testing, as well as the code.
Using Simplicity Configurator resolves 99% of problems like this. I'm so glad SiLabs created it.
Fixing the clock speed solved this particular problem.
There are several other problems now (mostly concerned with sensor drift), but they're not related to this issue.
Thanks for the sympathetic ears.