KL27Z switching between ADC channels in polled mode - timing problem?

cancel
Showing results for 
Search instead for 
Did you mean: 

KL27Z switching between ADC channels in polled mode - timing problem?

Jump to solution
1,158 Views
robertpoor
Contributor V

I extended the sample application from

SDK_2.1_FRDM-KL27Z/boards/frdmkl27z/driver_examples/adc16/polling

to read multiple ADC channels.  What I observe is that the first channel to be polled gets a reasonable value, the other channels polled after that get a full-scale reading (0xffff).  After inserting a delay between readings, I get reasonable values on all the channels.

My question: is there some flag that I must honor, or some known delay I must insert, when switching between input channels on the ADC?

Here is the code in question:

#include "fsl_debug_console.h"
#include "board.h"
#include "fsl_adc16.h"

#include "pin_mux.h"
#include "clock_config.h"

#define DEMO_ADC16_BASE ADC0
#define DEMO_ADC16_CHANNEL_GROUP 0U
#define CH08_ADC16_USER_CHANNEL 8U  /* PTB0, A0-ADC0_SE8 */
#define CH09_ADC16_USER_CHANNEL 9U  /* PTB1, A0-ADC0_SE9 */
#define CH11_ADC16_USER_CHANNEL 11U /* PTC2, A0-ADC0_SE11 */

void delay(void) {
 for (uint32_t i = 0; i < 400000; i++) {
 __NOP();
 }
}

/**
 * Initialize a channel_config struct: no interrupts, no differential conversion.
 */
void initChannelConfig(adc16_channel_config_t *config, int channelNumber) {
  config->channelNumber = channelNumber;
  config->enableInterruptOnConversionCompleted = false;
#if defined(FSL_FEATURE_ADC16_HAS_DIFF_MODE) && FSL_FEATURE_ADC16_HAS_DIFF_MODE
  config->enableDifferentialConversion = false;
#endif /* FSL_FEATURE_ADC16_HAS_DIFF_MODE */
}

 /*
 When in software trigger mode, each conversion would be launched once calling the 
 "ADC16_ChannelConfigure()" function, which works like writing a conversion command 
 and executing it. For another channel's conversion, just to change the "channelNumber" 
 field in channel's configuration structure, and call "ADC16_ChannelConfigure() again.
 */

/**
 * Read a sample from the ADC on the provided channel using polling.
 */
uint32_t pollChannel(adc16_channel_config_t *config) {
  ADC16_SetChannelConfig(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP, config);
  while (0U == (kADC16_ChannelConversionDoneFlag &
         ADC16_GetChannelStatusFlags(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP))) {
 }
 return ADC16_GetChannelConversionValue(DEMO_ADC16_BASE, DEMO_ADC16_CHANNEL_GROUP);
}

int main(void) {
  adc16_config_t adc16ConfigStruct;
  adc16_channel_config_t ch08ChannelConfig;
  adc16_channel_config_t ch09ChannelConfig;
  adc16_channel_config_t ch11ChannelConfig;

  BOARD_InitPins();
  BOARD_BootClockRUN();
  BOARD_InitDebugConsole();

  PRINTF("\r\nADC16 polling example.\r\n");
  ADC16_GetDefaultConfig(&adc16ConfigStruct);
  adc16ConfigStruct.resolution=kADC16_ResolutionSE16Bit;
  adc16ConfigStruct.enableLowPower = true;
  adc16ConfigStruct.longSampleMode = kADC16_LongSampleCycle24;
  ADC16_Init(DEMO_ADC16_BASE, &adc16ConfigStruct);
  ADC16_EnableHardwareTrigger(DEMO_ADC16_BASE, false); /* Make sure the software trigger is used. */
#if defined(FSL_FEATURE_ADC16_HAS_CALIBRATION) && FSL_FEATURE_ADC16_HAS_CALIBRATION
  if (kStatus_Success == ADC16_DoAutoCalibration(DEMO_ADC16_BASE)) {
    PRINTF("ADC16_DoAutoCalibration() Done.\r\n");
  } else {
    PRINTF("ADC16_DoAutoCalibration() Failed.\r\n");
  }
#endif /* FSL_FEATURE_ADC16_HAS_CALIBRATION */
  PRINTF("Press any key to get user channel's ADC value ...\r\n");

  initChannelConfig, &ch08ChannelConfig, CH08_ADC16_USER_CHANNEL);
  initChannelConfig, &ch09ChannelConfig, CH09_ADC16_USER_CHANNEL);
  initChannelConfig, &ch11ChannelConfig, CH11_ADC16_USER_CHANNEL);

  while (1) {
    GETCHAR();

    uint32_t ch08_val = pollChannel(&ch08ChannelConfig);
    delay();  // without this delay, ch09_val reads as 0xffff
    uint32_t ch09_val = pollChannel(&ch09ChannelConfig);
    delay();  // without this delay, ch11_val reads as 0xffff
    uint32_t ch11_val = pollChannel(&ch11ChannelConfig);

    PRINTF("CH08: %d \tCH09: %d \tCH11: %d\r\n", ch08_val, ch09_val, ch11_val);
}

Example output WITHOUT the calls to delay():

CH08: 47808   CH09: 65535   CH11: 65535

Example output WITH the calls to delay():

CH08: 47808   CH09: 46678   CH11: 48726
Tags (2)
1 Solution
239 Views
robertpoor
Contributor V

Xiangjun:

You are right to suspect a problem with VREF!  However, the problems discussed in this thread are specific to the FRDM-KL27Z board, NOT the 32 pin MKL27Z64VFM4 part.  

We have solved the problem for the FRDM board -- please see Bug in adc16/polling driver example for an explanation.  In short, the VREFH input on the FRDM board is left floating, and the adc16 code example uses VREFH for VREF, leading to unpredictable results.  The solution is to configure the ADC to use the "alternate" reference source, in other word, VDD.  I've tested it, and it works reliably.

The problems that Hy has discussed with you regarding the 32 pin part are (probably) unrelated to this, and would best be split off into another thread.  To avoid any confusion, I will mark this thread as solved and start a separate thread for that purpose.

Thank you!

View solution in original post

11 Replies
239 Views
baerun
Contributor I

Internal Vref Closed sample code .

This code add your code . Problem fixed

vref_config_t vrefUserConfig;
VREF_GetDefaultConfig(&vrefUserConfig); /* Gets a default configuration. */
VREF_Init(VREF, &vrefUserConfig);
VREF_SetTrimVal(VREF,0);

0 Kudos
239 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, all

After communicating with Hy Mai, i see that customer uses MKL27Z64VFM4 with 32 pin LQFP package,and the ADC sample will saturate to 0xFFFF in 16 bits mode when the analog voltage exceeds 2.5V. In other words, the question now is what is the ADC voltage reference?

First of all, we should download the KL27P64M48SF2RM from the link for MKL27Z64VFM4 with 32 pin LQFP package:

ARM Cortex-M0+|Ultra-Low Power Kinetis KL2x USB MCU|NXP 

pastedImage_2.png

Based on the RM, this is the section to discuss the reference voltage of ADC:

pastedImage_3.png

Because I have not the board with MKL27Z64VFM4, pls have try for the follwoing setting:

1)connect a 0.1uF capacitor on the PTE30 pin, have a test without modifying any code.

2)change the VREFH setting with ADCx_SC2[REFSEL]=2b'01 and have the ADC sample.

BR

Xiangjun Rong

0 Kudos
240 Views
robertpoor
Contributor V

Xiangjun:

You are right to suspect a problem with VREF!  However, the problems discussed in this thread are specific to the FRDM-KL27Z board, NOT the 32 pin MKL27Z64VFM4 part.  

We have solved the problem for the FRDM board -- please see Bug in adc16/polling driver example for an explanation.  In short, the VREFH input on the FRDM board is left floating, and the adc16 code example uses VREFH for VREF, leading to unpredictable results.  The solution is to configure the ADC to use the "alternate" reference source, in other word, VDD.  I've tested it, and it works reliably.

The problems that Hy has discussed with you regarding the 32 pin part are (probably) unrelated to this, and would best be split off into another thread.  To avoid any confusion, I will mark this thread as solved and start a separate thread for that purpose.

Thank you!

View solution in original post

239 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, all

As I said I think the code is okay. The issue cames from the external hardware signal impedance, I see that you test the signal from PTB0/PTB1 pins, if you use ADC to sample analog signal,  the analog signal should have low impedance, if the analog signal have high impedance, the drifting, inaccurate sample... will happens.

Pls have the following tests:

1)connect the GND or VDD to the PTB0 or PTB1, what is the result? I think the drifting will disappear.

2)the analog signal is from sensor, pls tell us the ananlog signal output impedance. If it has high impedance, i suggest you connect an analog buffer, and connect the PTB0 or PTB1 to the analog buffer output. The analog buffer consists of an OP AMP, for example OP07/27/37, connect the inverting pin of AMP to the output pin of AMP, connect the sensor output to non-inverting pin, it is okay.

Pls have a try

BR

Xiangjun Rong

0 Kudos
239 Views
robertpoor
Contributor V

Xiangjun Rong:

It's interesting that you say the analog input should be driven by a low impedance such as an op amp.

The data sheet (KL27P64M48SF2) specifies that you should use an RC low-pass filter (section 6.3.1) with a maximum impedance of 5 k Ohms (section 5.3.6.1.1).  But I also note that the data sheet advises "the analog source resistance must be kept as low as possible."  Still, it seems like an impedance as high as 5 k should be permissible.   

Hy: Are you in a position to drive the input from a low-impedance voltage source (e.g. a power supply or an op amp voltage follower)?

0 Kudos
239 Views
bobpaddock
Senior Contributor II

A A/D input should always be driven with the lowest possible impedance.

Analog Devices goes into details as to why here:

http://www.analog.com/media/en/training-seminars/design-handbooks/Data-Conversion-Handbook/Chapter6.... 

A Simple R/C filter at the input serves several different functions.

Anti-Aliasing for sampled systems.

A stable fixed capacitance to the driver compared to the switching going on in the A/D.

Most often over looked it keeps switching noise from the A/D inside the chip from escaping to a large degree.



0 Kudos
239 Views
robertpoor
Contributor V

Bob:

You should know that our production application drives the A/D inputs directly from Op Amp voltage followers with RC filters on their inputs, so in the final version, yes, the A/D inputs will be driven from low impedance sources.

Also, as I mentioned, the original problem I observed went away when I drove all three inputs from a 1V source, the observed problems went away.  So I believe this issue is closed, but the engineers at NXP apparently want to investigate some other aspects of it.

0 Kudos
239 Views
dmcdaniel
Contributor I

To ALL:

Please refer to our schematics that were provided.  We direct drive the A/D converter with LMV341 op amps for all three signals fed to the a/d converter.

0 Kudos
239 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi,

i  have reviewed your code, especially,

uint32_t pollChannel(adc16_channel_config_t *config){}
I think the code is okay.
Maybe you use C++ to develop code and declare the variable as
    uint32_t ch09_val = pollChannel(&ch09ChannelConfig);
can you declare the variable
uint32_t ch09_val  as
void main(void)
{
uint32_t ch09_val;
uint32_t ch08_val;
uint32_t ch11_val;
..............

while(1)
{
ch08_val = pollChannel(&ch08ChannelConfig);
........
}
If you still have issue, can you have a debug by step by step and check the ADC register
and variable to get the cause?
BR
Xiangjun Rong

0 Kudos
239 Views
robertpoor
Contributor V

I believe I tracked down the source of the problem: in the original run, the inputs were floating and not driven from a voltage source.  In that mode, they become sensitive to the charge time (somehow).  When I tied all three ADC inputs to a 1V source, they all read the same values with or without the delay.

Thank you for your help.

0 Kudos
239 Views
HeMyFAE
NXP Employee
NXP Employee

Hi Xiangjun,

I sent you an email describing the offset/drifting problem with the ADC16_polling demo code from KSDK2.0.

Please let me know if you have received it.

Regards,

hy

0 Kudos