Kinetis K60 ADC readings ar way off

cancel
Showing results for 
Search instead for 
Did you mean: 

Kinetis K60 ADC readings ar way off

719 Views
GaryOlmstead
Contributor V

Hi --

I am designing a Kinetis K60 into a project.  I have two boards that both basically work, but the ADC readings don't match.  For simplicity I will focus on the readings from the bandgap, but all the channels are similar. I am using both ADCs, and I am having MCUxpresso generate the self-calibration code for both ADCs.  I have tested it, and the self-calibration routine runs to completion without any errors.  All the ADCs are set for 16 bits single ended and 16 bits differential, 32 readings with hardware averaging into a single sample, 20 extra sampling cycles, and input clock divided by 8.  I am also taking 64 sets of samples, and dividing the result by 64, so I am confident that noise is not the biggest problem.

Vref-hi is 3.3V, and the data sheet says that the bandgap value is 1.0V so I would expect readings around 19859.  One board reads 27516, and the other reads 1128.  For each board, the bandgap reading is the same for both ADCs.  The self-calibration routine claims that calibration was successful for both ADCs on the second board.

Any suggestions would be appreciated.

Best Regards
Gary Olmstead

10 Replies

536 Views
kerryzhou
NXP TechSupport
NXP TechSupport

Hi GARY OLMSTEAD,

   Thanks for Myke Predko help with this post.

   Do you also check your VDDA and the VSS, whether it is stable in another board.

   Do you try to input the GND or the other specific voltage to the ADC channel, any difference?

   The internal bandgap have the error rate:

pastedImage_1.png

  But your two board test result difference is really very large, I think you may also need to check your hardware, if you are using totally the same code.

Best Regards,

Kerry

 

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

 

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos

536 Views
GaryOlmstead
Contributor V

Hi Kerry --

Actually, there's an issue with the power supply board layout, and power is very noisy,  But I have set both ADCs to add 20 cycles of sample time, hardware averaging 32 samples, and on top of that, reading each input 64 times, for a total of 2048 samples to get one value to display to the user.  Believe me, the readings are very stable.  They are just very wrong.

0 Kudos

536 Views
kerryzhou
NXP TechSupport
NXP TechSupport

Hi GARY OLMSTEAD

   Clean Analog power supply is very important to the correct and stable ADC conversion data.

   Myke Predko lastest reply is valuable and professional, please refer to that suggest, try to connect a new clean analog power, then test it again on your side.

   If you still have issues, just kindly let us know.

Best Regards,

Kerry

 

-------------------------------------------------------------------------------
Note:
- If this post answers your question, please click the "Mark Correct" button. Thank you!

 

- We are following threads for 7 weeks after the last post, later replies are ignored
Please open a new thread and refer to the closed one, if you have a related question at a later point in time.
-------------------------------------------------------------------------------

0 Kudos

536 Views
GaryOlmstead
Contributor V

I took Myke's suggestion about disabling the main power (I took out the inductor), and powering it from two AA batteries.  I wrote a custom program that read one otherwise unconnected input for ADC0, and the same again for ADC1.  I also read the bandgap for both ADC0 and ADC1.  The board now has about 40mV p-p of noise.

Code:

#include "board.h"
#include "peripherals.h"
#include "pin_mux.h"
#include "clock_config.h"
#include "MK60D10.h"
#include "fsl_debug_console.h"
/* TODO: insert other include files here. */
#define ADC0_CHANNEL_GROUP 0U
#define ADC1_CHANNEL_GROUP 0U
adc16_config_t adc16ConfigStruct;
adc16_channel_config_t adc16ChannelConfigStruct;

/* TODO: insert other definitions and declarations here. */

/*
* @brief Application entry point.
*/
int main(void)
{

   int32_t rawADC0 = 0; // Raw DC bus voltage input
   int32_t rawADC0_Bandgap = 0;
   int32_t rawADC1 = 0;
   int32_t rawADC1_Bandgap = 0;
   uint8_t loop_counter;

   /* Init board hardware. */
   BOARD_InitBootPins();
   BOARD_InitBootClocks();
   BOARD_InitBootPeripherals();
   /* Init FSL debug console. */
   BOARD_InitDebugConsole();
   PRINTF("ADC Test ADC0 SE18 and ADC1 SE8\n");
   while(1)
   {

         rawADC0 = 0;
         rawADC0_Bandgap = 0;
         rawADC1 = 0;
         rawADC1_Bandgap = 0;
         for(loop_counter = 0; loop_counter < 64; loop_counter++)
         {
            ADC16_SetChannelConfig(ADC0, ADC0_CHANNEL_GROUP, &ADC0_channelsConfig[0]);
            while (0U == (kADC16_ChannelConversionDoneFlag &
                     ADC16_GetChannelStatusFlags(ADC0, ADC0_CHANNEL_GROUP)))
            {;}
            rawADC0 += ADC16_GetChannelConversionValue(ADC0, ADC0_CHANNEL_GROUP);

            ADC16_SetChannelConfig(ADC0, ADC0_CHANNEL_GROUP, &ADC0_channelsConfig[1]);
            while (0U == (kADC16_ChannelConversionDoneFlag &
            ADC16_GetChannelStatusFlags(ADC0, ADC0_CHANNEL_GROUP)))
            {;}
            rawADC0_Bandgap += ADC16_GetChannelConversionValue(ADC0, ADC0_CHANNEL_GROUP);

///////////////////////////////////////////////////
            ADC16_SetChannelConfig(ADC1, ADC1_CHANNEL_GROUP, &ADC1_channelsConfig[0]);   
           while (0U == (kADC16_ChannelConversionDoneFlag &
            ADC16_GetChannelStatusFlags(ADC1, ADC0_CHANNEL_GROUP)))
            {;}
            rawADC1 += ADC16_GetChannelConversionValue(ADC1, ADC1_CHANNEL_GROUP);

            ADC16_SetChannelConfig(ADC1, ADC1_CHANNEL_GROUP, &ADC1_channelsConfig[1]);
            while (0U == (kADC16_ChannelConversionDoneFlag &
                        ADC16_GetChannelStatusFlags(ADC1, ADC0_CHANNEL_GROUP)))
           {;}
           rawADC1_Bandgap += ADC16_GetChannelConversionValue(ADC1, ADC1_CHANNEL_GROUP);

         }
         rawADC0 /= 64;
         rawADC0_Bandgap /= 64;
         rawADC1 /= 64;
         rawADC1_Bandgap /= 64;
         PRINTF("ADC0 pin 53 = %i ADC0 bandgap = %i ADC1 pin 26 = %i ADC1 bandgap = %i\n", rawADC0,          rawADC0_Bandgap, rawADC1, rawADC1_Bandgap);
   }
return 0 ;
}

Both ADCs are set for 16 bit conversions, single ended, clock divided by 8, 32 samples with hardware averaging.  I only tested the worst board.

The results:

ADC0 reading SE8 (pin53): 1.235V applied read 60744. 

ADC0 reading the Bandgap 1540

ADC0 reading SE8               3.116V applied read 63889

ADC1 reading SE18 (pin 26): 1.03V applied read 1664

ADC1 reading the Bangap 1512.

ADC1 reading SE18              2.934 V applied read 1583

Absent any other ideas, I will set the ADCs for their other conversion modes, and see if there are any differences.  There aren't any ADC errata that I know of for this mask (5N22D).


Thanks for your help.

Gary

0 Kudos

536 Views
myke_predko
Senior Contributor III

Hi GaryOlmstead‌,

Your measurement code looks fine - you're using the same APIs as I am in one of my programs.  The only thing I'm not sure of is whether or not you have to put a delay in between the readings - I side step this issue by calling my code from a 100Hz timer interrupt.  

In your program - I don't see the initialization code.  I'm guessing that it's in "BOARD_InitBootPeripherals();", correct?  I'm guessing you're using SDC 2.7.0 with MCUXpresso, so the ADCs are setup using the Peripherals Tool?

Have you checked the "BOARD_InitBootPins" method and verified that the pin muxes are being set correctly?  Years ago, I *think* I saw problems with the ADC when I wasn't setting the Pin Muxes correctly but I can't remember if they are the same issues as you are seeing.  

Along with that, I don't see the declarations for:

- ADC0_channelsConfig

ADC1_channelsConfig

I'm guessing their automatically generated by the Peripherals Tool?  Could you share the variables' declarations?

Could you explain what you mean by "bandgap" and how you get your summary values?  When I look at the code you perform one measurement which is added to "rawADC#" and then a second one, with a different "ADC#_channelsConfig" element and add the value to "rawADC#_Bandgap". 

Finally, you print the values (divided by 64, which is the number of samples) but what you put in your post, is not what I would expect from the source code - why isn't there a "bandgap" value for the second voltage reading?  I'm not quite sure what is your test set up - are you running it twice (once with 1.235V, 1.030V and then with 3.116V, 2.934V inputs, respective to ADC0 and ADC1)?

Have you tried this code on a Freedom/Tower board?  By powering from the batteries you've eliminated one variable (the noisy supply) but there still is the question as to whether the issue is with the hardware or with the software (I don't know how you validated the ADC initialization code).  

myke

0 Kudos

536 Views
GaryOlmstead
Contributor V

Hi Myke --

Well, I hope that BOARD_InitBootPeripherals() is setting the ADCs correctly, but the lack of any meaningful changes in the output, even when the input changes from ground to 3.3V might mean something rather basic is wrong.  Since my last message, I have changed the resolution to 8 bits, and dropped all the hardware averaging, and multiple sampling.  It didn't make any difference.  I don't have this chip on a Tower or Freedom board, but I do have a K64 on both Tower and Freedom boards.  I will give that a try. 

"Bandgap" is channel 27 on each ADC.  There was a reading for the second test, but I ignored it because it was the same as the first.  I shouldn't have been so lazy.  The actual output looks like this:

ADC0 pin 53 = 252 ADC0 bandgap = 6 ADC1 pin 26 = 6 ADC1 bandgap = 6
ADC0 pin 53 = 252 ADC0 bandgap = 6 ADC1 pin 26 = 6 ADC1 bandgap = 6
ADC0 pin 53 = 252 ADC0 bandgap = 6 ADC1 pin 26 = 6 ADC1 bandgap = 6
ADC0 pin 53 = 252 ADC0 bandgap = 6 ADC1 pin 26 = 6 ADC1 bandgap = 6
ADC0 pin 53 = 251 ADC0 bandgap = 6 ADC1 pin 26 = 6 ADC1 bandgap = 6

In the above sample, ADC1 (pin26) measures 1.677V, and ADC0 (pin 53) measures 0.754V.  I didn't do anything to get the summary value, this is just the decimal value returned by ADC16_GetChannelConversionValue().

My setup is that I have two, AA batteries in series, with a 10K, ten turn pot across the terminals, with the wiper of the pot going to the processor's pin 26 (should be ADC1, SE18, and two more AA batteries tied to a different 10K, ten turn pot tied to the processor's pin 53, which should be ADC0, SE8.  The negative side of the "lower" battery in each of the pairs  is tied to system ground.  Board power is from yet another pair of AA batteries.

Here are the contents of board.c:

/***********************************************************************************************************************
* BOARD_InitPeripherals functional group
**********************************************************************************************************************/
/***********************************************************************************************************************
* ADC0 initialization code
**********************************************************************************************************************/


/* clang-format off */
/* TEXT BELOW IS USED AS SETTING FOR TOOLS *************************************
instance:
- name: 'ADC0'
- type: 'adc16'
- mode: 'ADC'
- custom_name_enabled: 'false'
- type_id: 'adc16_7d827be2dc433dc756d94a7ce88cbcc5'
- functional_group: 'BOARD_InitPeripherals'
- peripheral: 'ADC0'
- config_sets:
- fsl_adc16:
- adc16_config:
- referenceVoltageSource: 'kADC16_ReferenceVoltageSourceVref'
- clockSource: 'kADC16_ClockSourceAsynchronousClock'
- enableAsynchronousClock: 'true'
- clockDivider: 'kADC16_ClockDivider8'
- resolution: 'kADC16_ResolutionSE8Bit'
- longSampleMode: 'kADC16_LongSampleDisabled'
- enableHighSpeed: 'false'
- enableLowPower: 'false'
- enableContinuousConversion: 'false'
- adc16_channel_mux_mode: 'kADC16_ChannelMuxA'
- adc16_hardware_compare_config:
- hardwareCompareModeEnable: 'false'
- doAutoCalibration: 'false'
- offset: '0'
- trigger: 'false'
- hardwareAverageConfiguration: 'kADC16_HardwareAverageDisabled'
- adc16_pga_config:
- adc16PgaEnable: 'false'
- enable_dma: 'false'
- enable_irq: 'false'
- adc_interrupt:
- IRQn: 'ADC0_IRQn'
- enable_priority: 'false'
- priority: '0'
- enable_custom_name: 'false'
- adc16_channels_config:
- 0:
- enableDifferentialConversion: 'false'
- channelNumber: 'SE.1'
- enableInterruptOnConversionCompleted: 'false'
- channelGroup: '0'
- initializeChannel: 'false'
- 1:
- enableDifferentialConversion: 'false'
- channelNumber: 'SE.27'
- enableInterruptOnConversionCompleted: 'false'
- channelGroup: '0'
- initializeChannel: 'false'
* BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS **********/
/* clang-format on */
adc16_channel_config_t ADC0_channelsConfig[2] = {
{
.channelNumber = 1U,
.enableDifferentialConversion = false,
.enableInterruptOnConversionCompleted = false,
},
{
.channelNumber = 27U,
.enableDifferentialConversion = false,
.enableInterruptOnConversionCompleted = false,
}
};
const adc16_config_t ADC0_config = {
.referenceVoltageSource = kADC16_ReferenceVoltageSourceVref,
.clockSource = kADC16_ClockSourceAsynchronousClock,
.enableAsynchronousClock = true,
.clockDivider = kADC16_ClockDivider8,
.resolution = kADC16_ResolutionSE8Bit,
.longSampleMode = kADC16_LongSampleDisabled,
.enableHighSpeed = false,
.enableLowPower = false,
.enableContinuousConversion = false
};
const adc16_channel_mux_mode_t ADC0_muxMode = kADC16_ChannelMuxA;
const adc16_hardware_average_mode_t ADC0_hardwareAverageMode = kADC16_HardwareAverageDisabled;

void ADC0_init(void) {
/* Initialize ADC16 converter */
ADC16_Init(ADC0_PERIPHERAL, &ADC0_config);
/* Make sure, that software trigger is used */
ADC16_EnableHardwareTrigger(ADC0_PERIPHERAL, false);
/* Configure hardware average mode */
ADC16_SetHardwareAverage(ADC0_PERIPHERAL, ADC0_hardwareAverageMode);
/* Configure channel multiplexing mode */
ADC16_SetChannelMuxMode(ADC0_PERIPHERAL, ADC0_muxMode);
}

/***********************************************************************************************************************
* ADC1 initialization code
**********************************************************************************************************************/
/* clang-format off */
/* TEXT BELOW IS USED AS SETTING FOR TOOLS *************************************
instance:
- name: 'ADC1'
- type: 'adc16'
- mode: 'ADC'
- custom_name_enabled: 'false'
- type_id: 'adc16_7d827be2dc433dc756d94a7ce88cbcc5'
- functional_group: 'BOARD_InitPeripherals'
- peripheral: 'ADC1'
- config_sets:
- fsl_adc16:
- adc16_config:
- referenceVoltageSource: 'kADC16_ReferenceVoltageSourceVref'
- clockSource: 'kADC16_ClockSourceAlt1'
- enableAsynchronousClock: 'true'
- clockDivider: 'kADC16_ClockDivider8'
- resolution: 'kADC16_ResolutionSE8Bit'
- longSampleMode: 'kADC16_LongSampleDisabled'
- enableHighSpeed: 'false'
- enableLowPower: 'false'
- enableContinuousConversion: 'false'
- adc16_channel_mux_mode: 'kADC16_ChannelMuxA'
- adc16_hardware_compare_config:
- hardwareCompareModeEnable: 'false'
- doAutoCalibration: 'false'
- offset: '0'
- trigger: 'false'
- hardwareAverageConfiguration: 'kADC16_HardwareAverageDisabled'
- adc16_pga_config:
- adc16PgaEnable: 'false'
- enable_dma: 'false'
- enable_irq: 'false'
- adc_interrupt:
- IRQn: 'ADC1_IRQn'
- enable_priority: 'false'
- priority: '0'
- enable_custom_name: 'false'
- adc16_channels_config:
- 0:
- enableDifferentialConversion: 'false'
- channelNumber: 'SE.4a'
- enableInterruptOnConversionCompleted: 'false'
- channelGroup: '0'
- initializeChannel: 'false'
- 1:
- enableDifferentialConversion: 'false'
- channelNumber: 'SE.27'
- enableInterruptOnConversionCompleted: 'false'
- channelGroup: '0'
- initializeChannel: 'false'
* BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS **********/
/* clang-format on */
adc16_channel_config_t ADC1_channelsConfig[2] = {
{
.channelNumber = 4U,
.enableDifferentialConversion = false,
.enableInterruptOnConversionCompleted = false,
},
{
.channelNumber = 27U,
.enableDifferentialConversion = false,
.enableInterruptOnConversionCompleted = false,
}
};
const adc16_config_t ADC1_config = {
.referenceVoltageSource = kADC16_ReferenceVoltageSourceVref,
.clockSource = kADC16_ClockSourceAlt1,
.enableAsynchronousClock = true,
.clockDivider = kADC16_ClockDivider8,
.resolution = kADC16_ResolutionSE8Bit,
.longSampleMode = kADC16_LongSampleDisabled,
.enableHighSpeed = false,
.enableLowPower = false,
.enableContinuousConversion = false
};
const adc16_channel_mux_mode_t ADC1_muxMode = kADC16_ChannelMuxA;
const adc16_hardware_average_mode_t ADC1_hardwareAverageMode = kADC16_HardwareAverageDisabled;

void ADC1_init(void) {
/* Initialize ADC16 converter */
ADC16_Init(ADC1_PERIPHERAL, &ADC1_config);
/* Make sure, that software trigger is used */
ADC16_EnableHardwareTrigger(ADC1_PERIPHERAL, false);
/* Configure hardware average mode */
ADC16_SetHardwareAverage(ADC1_PERIPHERAL, ADC1_hardwareAverageMode);
/* Configure channel multiplexing mode */
ADC16_SetChannelMuxMode(ADC1_PERIPHERAL, ADC1_muxMode);
}

/***********************************************************************************************************************
* Initialization functions
**********************************************************************************************************************/
void BOARD_InitPeripherals(void)
{
/* Initialize components */
ADC0_init();
ADC1_init();
}

/***********************************************************************************************************************
* BOARD_InitBootPeripherals function
**********************************************************************************************************************/
void BOARD_InitBootPeripherals(void)
{
BOARD_InitPeripherals();
}

And for good measure, pin_mux.c

void BOARD_InitBootPins(void)
{
BOARD_InitPins();
}

/* clang-format off */
/*
* TEXT BELOW IS USED AS SETTING FOR TOOLS *************************************
BOARD_InitPins:
- options: {callFromInitBoot: 'true', coreID: core0, enableClock: 'true'}
- pin_list:
- {pin_num: '26', peripheral: ADC1, signal: 'SE, 18', pin_signal: VREF_OUT/CMP1_IN5/CMP0_IN5/ADC1_SE18}
- {pin_num: '53', peripheral: ADC0, signal: 'SE, 8', pin_signal: ADC0_SE8/ADC1_SE8/TSI0_CH0/PTB0/LLWU_P5/I2C0_SCL/FTM1_CH0/RMII0_MDIO/MII0_MDIO/FTM1_QD_PHA}
- {pin_num: '100', peripheral: GPIOD, signal: 'GPIO, 7', pin_signal: PTD7/CMT_IRO/UART0_TX/FTM0_CH7/FTM0_FLT1}
* BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS ***********
*/
/* clang-format on */

/* FUNCTION ************************************************************************************************************
*
* Function Name : BOARD_InitPins
* Description : Configures pin routing and optionally pin electrical features.
*
* END ****************************************************************************************************************/
void BOARD_InitPins(void)
{
/* Port B Clock Gate Control: Clock enabled */
CLOCK_EnableClock(kCLOCK_PortB);
/* Port D Clock Gate Control: Clock enabled */
CLOCK_EnableClock(kCLOCK_PortD);

/* PORTB0 (pin 53) is configured as ADC0_SE8 */
PORT_SetPinMux(PORTB, 0U, kPORT_PinDisabledOrAnalog);

/* PORTD7 (pin 100) is configured as PTD7 */
PORT_SetPinMux(PORTD, 7U, kPORT_MuxAsGpio);
}

0 Kudos

536 Views
myke_predko
Senior Contributor III

Hi GaryOlmstead‌,

Just did a quick scan of the pin_mux.c file above.  There is no PORT_SetPinMux for ADC1_SE18, but when I look at the datasheet it's the default so I'm guessing that's not required.  

When I compare the code in the "void ADC#_init(void)" methods to mine, I have an "ADC16_DoAutoCalibration" rather than the "ADC16_SetHardwareAverage" in board.c.  I've never used "ADC16_SetHardwareAverage" so I can't comment here.  

Honestly, I don't know what to say regarding the software - it looks like it was system generated for the most part so I'd be willing to trust it.  

I would still push you to run this code in a Freedom/Tower board and see what happens.  You still have two variables and by running it on known good hardware you can eliminate one of the variables.  

Good luck,

myke

0 Kudos

536 Views
myke_predko
Senior Contributor III

Hi GaryOlmstead‌,

Could you please provide some more information? 

What is the software you're using with the ADCs, what are they wired to and how are they wired?  As part of this, could you explain how you wired VDDA, VREFH, VSSA & VREFL?

I've been using Kinetis ADCs in a variety of situations without any issues.  The values are accurate and repeatable.  

myke

536 Views
GaryOlmstead
Contributor V

Hi Myke --

Well, it's my custom board, but I've been doing this sort of thing for a long time (with other processors), so I don't think it's a total disaster.  VDDA and VREFH are wired to a power plane, which is the regular 3.3V supply, which I know isn't great and will fix on the next go around.  VSSA and VREFL are wired to a ground plane.

Since it's my board, none of the supplied demo programs work, and it's all my software.

Gary

0 Kudos

536 Views
myke_predko
Senior Contributor III

Hey GaryOlmstead‌,

I see in your reply to Kerry, you're saying that the power supply is very noisy - that's going to throw things off on any processor/ADC.  

Could I suggest that you disconnect/disable your power supply and run your processor with something like two "AA" batteries (which should guarantee a very clean supply) and run your software?  

I run Kinetis ADCs with:

  • Custom code
  • KSDK drivers
  • SDK drivers

Without any issues and, just checking on a board I can run all three on, I get identical results (repeatable to within 10 units) - I was checking as a follow up to KV46 ADC accuracy issue.  

Now, I'm very anal about power and distribution.  I use the NXP recommended caps for decoupling and noise reduction.  VDDA & VREFH as well as VSSA & VREFL all come through a 100MHz common mode choke and I run with a single ground plane (no analog ground plane) - VDDA & VREFH have a 100nF & a 2.2uF for noise filtering.  If you have power supply noise or a wonky analog ground plane you're going to get what you are seeing.  

I'm probably more cautious in terms of how I have power and ground wired than is absolutely necessary but experience has taught me that good clean power and a single ground goes a long way in eliminating potential operating issues.  

myke

0 Kudos