Error no lineal en las lecturas de ADC

3

Estoy midiendo voltajes de hasta 20 V con mi MCU ATmega2650 (ADC de 10 bits).

Estoy usando una referencia de voltaje de precisión de 5V (LT1021 - 0.05%).

Los divisores de voltaje están configurados con resistencias Panasonic del 1%.

Vcc > 10kOhm- > Measure- > 3.3kkOhm- > GND.

Relación de división: 3.3 / 13.3 = 0.248

Lo que he notado son los siguientes errores cada vez mayores al medir voltajes más grandes:

Vmeas   ADC Err(%)
3.05    152 -0.013382929
4.09    205 -0.0075968
5.02    253 -0.002075695
6.08    308 0.003057305
7.07    359 0.0054141
8.07    410 0.00595279
9.07    461 0.00637229
10.02   510 0.007764232
11.05   563 0.008777353
12.05   615 0.01046932
13.05   665 0.008925735
14.05   717 0.010366242
15.06   769 0.010955198
16.05   820 0.011495804
17.07   872 0.011368671
18.06   923 0.011826103
19.04   973 0.011739502
19.51   998 0.01271154
19.94   1020    0.012715509

¿Puede alguien explicar qué está causando esa no linealidad?

¿Alguna sugerencia sobre matemáticas para estimar esto (características de ADC en lugar de ajuste múltiple)? Cualquier referencia a modelos matemáticos ayudaría.

EDITAR - errores en todo el rango de voltaje:

La metodología de cálculo:

#define PSU_ANALOG_CHANNELS     3
#define PSU_ANALOG_MEASURES     5
#define PSU_ANALOG_MEASURE_DELAY 1

//apply vRef to each pin to measure post-divided ADC reading
const int psu_adc_corr[PSU_ANALOG_CHANNELS] =  {250,251,251};

int psu_volts_raw[PSU_ANALOG_CHANNELS];//stores ADC readings
float psu_volts[PSU_ANALOG_CHANNELS] = {0}; //stores final values
float mvAdc[PSU_ANALOG_CHANNELS]; //stores mV per each ADC-channel (to avoid division)

void calcMvADC(){
  for (int i=0; i<PSU_ANALOG_CHANNELS; i++) {
      mvAdc[i] = 5.0 / psu_adc_corr[i];
  }
}

//returns averaged reading for each ADC channel
int readAnalog(int ch) {
  int val = 0;
  for (int i=0; i<PSU_ANALOG_MEASURES; i++) {
    val += analogRead(ch);
    delay(PSU_ANALOG_MEASURE_DELAY);
  }
  return val/PSU_ANALOG_MEASURES;
}

void readADC() {
  for (int i=0; i<PSU_ANALOG_CHANNELS; i++) {
    psu_volts_raw[i]=readAnalog(i);
  }
}

/*
>6 <=7 : -1.1%
>7 <=9: -1.14%
>9 <=13: -1.25%
>13 -1.7%:
*/
float corrVoltage(float V) {
    if (V<6) return V;
    if (V>6 && V<=7) return V*0.989;
    if (V>7 && V<=9) return V*0.9886;
    if (V>9 && V<=13) return V*0.9875;
    if (V>13) return V*0.983;
    return V;
}

void calcVoltages() {
  for (int i=0; i<PSU_ANALOG_CHANNELS; i++) {
    psu_volts[i] = psu_volts_raw[i] * mvAdc[i];
    psu_volts[i] = corrVoltage(psu_volts[i]);
  }
}

void setup (){
  analogReference(EXTERNAL);
  calcMvADC();
  Serial.begin(115200);
}

void loop (){
  readADC();
  calcVoltages();
  for (int i=0; i<PSU_ANALOG_CHANNELS; i++) {
    Serial.println(psu_volts[i]);
  }
  delay(500);
}
    
pregunta FlegmatoidZoid

4 respuestas

3

Los ADC son naturalmente no lineales. Aproximadamente, la función de transferencia comienza en 0, luego aumenta más rápido que la función de transferencia lineal esperada hasta que alcanza # bits / 2 y luego vuelve a la curva donde debería estar. Puedo dibujar un diagrama si esta explicación no es clara.

El principal problema es que está asumiendo que el convertidor tiene la función de transferencia lineal con Voltaje = 5V / 250 * (valor de ADC). No lo hace y el error ni siquiera es lineal, como ya ha observado. Dada la forma de la función de transferencia real, los datos proporcionados por Andy y la forma en que está calculando los errores a voltajes más altos, el patrón que está viendo es el esperado.

No creo que haya dicho nada, pero permítame hacer una conjetura basada en este análisis: su ADC sobreestima constantemente los voltajes. Esto se debe a que la pendiente (5V / 250) está cerca del máximo de la pendiente de la función de transferencia real.

Editado para agregar: Tal vez quedó claro en tu publicación que se están sobreestimando ya que Err% siempre es > 1. Está claro si Err% = (lectura ATMega) / (valor real)

2da edición: en realidad, lo que puedes hacer fácilmente y ver cómo cambian los resultados: coloca 20 V a través del divisor de voltaje. Obtendrá un número, diga $ N $ y luego defina psu_adc_corr = N y myAdc = 20 / psu_adc_corr = 20 / N. Me interesaría ver lo que obtienes.

    
respondido por el SomeEE
2

aquí es probablemente la mejor explicación que he encontrado del proveedor

Tiene una sección dedicada a la no linealidad con la siguiente conclusión: " La no linealidad no se puede compensar con cálculos simples. Las aproximaciones polinómicas o las búsquedas de tablas se pueden utilizar para ese propósito. "

Así que hice una matriz int8_t de 14 valores de error de ADC (que representan incrementos de 1 voltio). ¡He aplicado esas correcciones en la lectura de ADC y yay! - Ahora tengo las lecturas de voltaje con un error de 0 a 1 mV.
Además, ahora puedo usar un solo valor de mV_per_ADC_step (con respecto a mi versión anterior para la cual he tenido un valor mV dedicado para cada canal ADC).

    
respondido por el FlegmatoidZoid
0

Se especifica que el ADC en la MCU tiene una precisión absoluta de hasta 2.5 LSbs a una velocidad de muestreo de 200 kHz. Esto se especifica en aVref y voltaje Vcc de 4V, pero supongo que va a ser muy similar a 5V. Si su referencia es 5V, 2.5 LSbs es aproximadamente 12mV. Esto puede manifestarse positiva o negativamente para un voltaje de entrada particular.

No estoy seguro de cómo se obtienen sus valores, pero a 0.248 * 7V en una entrada, 12 mV puede ser un error de 0.69% que se encuentra en algún lugar del reino que está viendo.

Pero si está utilizando un canal de ADC diferencial, esto puede aumentar a aproximadamente 18 LSbs en una ganancia de unidad porque hay un amplificador interno que agrega un error.

    
respondido por el Andy aka
0

No estoy muy familiarizado con el convertidor ADC de Atmel, pero sugiero que tal vez el tiempo de adquisición para el S & H no sea lo suficientemente largo. Creo que debería ser 15usec o mayor.

La constante de tiempo máxima es 100K \ $ \ Omega \ cdot 14 \ $ pF o 1.4usec, por lo que 10 constantes de tiempo son aproximadamente 15usec.

Alternativamente, ¿tienes algo como un diodo Zener de 5.1 V en la entrada? Eso podría fácilmente causar errores en ese rango.

    
respondido por el Spehro Pefhany

Lea otras preguntas en las etiquetas