exactitud ADC pic16f887

0

Estoy intentando leer un sensor de temperatura (Lm35) de mi ADC pic16f887. La cosa es que estoy obteniendo una diferencia de aproximadamente +/- 15 ticks en los resultados. ¿Se espera esto? ¿Debo estar haciendo el medio de varias adquisiciones?

El Lm35 da la temperatura en mv, donde 10mv = 1 grado celsius. El rango va desde (20mv a 1.5v), aproximadamente 2º a 150º. Debido a que en teoría el ADC tiene una precisión de 0.5º. (1024 ticks = 5v de los cuales obtenemos 307 ticks = 1.5v y como 150 grados 150/307 = 0.5º).

Para mejorar esta precisión de medio grado, he agregado un diodo Zener de 2.5v a vref + con un capacitor 10uc. Así, el circuito final que tengo es este.

simular este circuito : esquema creado usando CircuitLab

Pero todavía estoy obteniendo variaciones de + -15 tic. variatons, ¡lo cual es mucho en grados!

Y el código que estoy usando es este.

#include <xc.h>
#include <pic16f887.h>

#define _XTAL_FREQ 8000000

#pragma config FOSC = INTRC_CLKOUT, WDTE = OFF, PWRTE = OFF      
#pragma config MCLRE = ON, CP = OFF, CPD = OFF, BOREN = OFF      
#pragma config IESO = ON, FCMEN = ON, LVP = OFF        

typedef unsigned int uint;
typedef unsigned char uchar;
typedef unsigned short ushort;

void uart_init(const long int baudrate) {
    SPBRG = 12;
    TXEN = 1;
    SPEN = 1;
    SYNC = 0;
    TRISC6 = 0;
    TRISC7 = 1;
}

void uart_write(char data) {
    while (!TRMT) {}
    TXREG = data;
}

uchar uart_tx_empty() {
    return (uchar)TRMT;
}

void uart_write_l(char *text, uint size) {
    for (int i = 0; i < size; i++) {
        uart_write(text[i]);
    }
}

ushort read_adc() {
    ADON = 1;
    __delay_ms(100); 
    GO_DONE = 1;
    while (GO_DONE) {}
    ADON = 0;
    ushort res = 0;
    res = ADRESH << 8;
    res |= ADRESL;
    return res;
}

void main(void) {
    uart_init(9600);
    ANS0 = 1;
    TRISA0 = 1;
    PORTD = 0; // Set led port
    TRISD = 0; // LED Output
    OSCCON = 0x70; // 8mhz internal clock
    ADFM = 1;  // right justified
    VCFG0 = 1; // Vref+
    ADCON0 = 0b11000000; 
    while (1) {
        ushort result = read_adc();
        char *arr = (char *)&result;
        uart_write_l(arr, 2);  

        RD1 = 1;
        __delay_ms(500);
        RD1 = 0;
        __delay_ms(500);

    }
    return;
}
    
pregunta Aram

3 respuestas

4

En realidad, debe leer la hoja de datos , en particular el capítulo sobre el A / D. Dos problemas obvios de un vistazo rápido al código son que no le está dando el tiempo A / D para estabilizar después de encenderlo, y no le está dando tiempo de adquisición.

Intenta dejar el A / D encendido todo el tiempo. Esto soluciona ambos problemas, asumiendo que deja suficiente tiempo después de una conversión antes de comenzar la siguiente. Parece que hay algunos retrasos allí, por lo que se cumple esta condición.

Nuevamente, cómo usar el A / D correctamente está bien descrito en la hoja de datos. Léelo.

Una vez que esté funcionando básicamente, puede hacer un filtrado de paso bajo en las lecturas para reducir los uno o dos recuentos de ruido restantes sobre el promedio.

Normalmente ejecuto el A / D más rápido de lo que necesito desde el código de interrupción. Este paso bajo también filtra la secuencia de lecturas, y luego deja el valor filtrado en una variable global que el código de primer plano puede capturar cuando quiera. De esa manera, las lecturas A / D y el uso de los valores se desacoplan, lo que permite que cada parte del código haga lo que hace bien y es razonablemente independiente de otras partes del sistema. Todo lo que se entrelaza, como en su código, le causará problemas cuando intente hacer crecer el sistema más allá de las demostraciones más simples.

    
respondido por el Olin Lathrop
2

Dos cosas para probar:

Agregue un retraso entre ADON = 1 y GO_DONE = 1. Esto permitirá que los interruptores internos se estabilicen antes de leer el ADC.

Si esto resuelve su problema, aún puede ver uno o dos "tics" de incertidumbre. Puede reducir aún más esto tomando múltiples lecturas y promediando. Por lo general sumo 8 lecturas y cambio a la derecha los 3 bits de respuesta.

Además, a partir de la hoja de datos, parece que el LM35 tiene una salida de seguidor de emisor y no le gustan las cargas capacitivas. Recomiendan agregar una carga como se muestra:

    
respondido por el John Birckhead
0
  

Para mejorar esta precisión de medio grado, he agregado un zener de 2.5v   diodo

Los diodos Zener por debajo de 5V tienden a tener poca regulación y estabilidad de temperatura.

El TL431B 'regulador de derivación de precisión' sería Una opción mucho mejor. Tiene una precisión del 0,5% y una desviación muy baja a temperaturas ambiente normales.

  

Estoy obteniendo una diferencia de aproximadamente +/- 15 ticks en los resultados. Es esto   esperado?

Suena un poco alto, pero no es sorprendente si hay algún ruido en la señal, así como la referencia y la fuente de alimentación. Probablemente pueda filtrarlo tomando un promedio de varias lecturas.

Mi técnica habitual es acumular 64 lecturas de 10 bits (produciendo un resultado de 16 bits sin desbordamiento) y luego dividir el total de nuevo a la resolución que quiero. A menudo es posible aumentar la resolución efectiva más allá de los 10 bits, ya que el ruido "atenúa" las lecturas para producir valores intermedios cuando se promedian.

    
respondido por el Bruce Abbott

Lea otras preguntas en las etiquetas