La función de lectura ADC no funciona correctamente

1

Estoy trabajando en un proyecto escolar donde necesito leer un grupo de valores analógicos y convertirlos a digital con PIC16F1788 ( hoja de datos ). Escribí la función usando el compilador C y xc8. El problema es que cuando lo pruebo no se comporta como debería. Cuando quito el potenciómetro, el LED permanece encendido aunque todo debería estar apagado ya que ya no hay entrada analógica. Aquí está mi código, gracias por su ayuda.

 #include <pic16f1788.h>
 #include <stdio.h>
 #include <stdlib.h>

 #include <xc.h>

// Config word
#define _XTAL_FREQ 32000000


/*config1 and config2 settings*/
void InitADC(void)
 {
 // CONFIG1
#pragma config FOSC = INTOSC // Oscillator Selection (INTOSC oscillator: I/O f   unction on CLKIN pin)
#pragma config WDTE = ON // Watchdog Timer Enable (WDT enabled)
#pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
 #pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)
 #pragma config VCAPEN = OFF // Voltage Regulator Capacitor Enable bit (Vcap functionality is disabled on RA6.)
 #pragma config PLLEN = ON // PLL Enable (4x PLL enabled)
 #pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
 #pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
 #pragma config LPBOR = OFF // Low Power Brown-Out Reset Enable Bit (Low power brown-out is disabled)
 #pragma config LVP = ON // Low-Voltage Programming Enable (Low-voltage programming enabled)





     //** Initalise Ports FOR ADC **//
     PORTA = 0x00; //Set ports to low
     PORTB = 0x00;
     TRISA = 0xFF; //Port A is all inputs. (set to 1)
     //** Set Up ADC Parameters **//
     ANSELA =0x2F ; // (0b00101111)All AN0-AN4(RA4 and RA7 have no ADC) of register A are set to analog input

     //ANSELH = 0x00; //Set the analog high bits to 0
     ADCON1 = 0x00; // Sets ADRESL to contain the first 7 bits of conversion, ADRESH will have the final 3 bits. And all the rest to default (FOSC/2 2 prescaler)
 } // void InitADC(void)



// Do the ADC convertion only for the channel indicated
// result of ADC is returned
 unsigned int ReadADC(unsigned char channel)
{
  unsigned int AN_Val;

     // automaticaly determines the input channel it will read the value from
     switch (channel)
         {
         case 1: // AN1 1000 0101
             ADCON0 = 0x85;
             break;
         case 2: // AN2 1000 1001
             ADCON0 = 0x89;
             break;
         case 3: //AN3 1000 1101
             ADCON0 = 0x8D;
             break;
  case 4: //AN4 1001 0001
             ADCON0 = 0x91;
             break;
         default: // Any other value will default to AN0
             ADCON0 = 0x81; // (1000 0001) - AN1 set up ADC ADCON0

 // sets the ADCON0 register for each port depending on the value of channel.
 // the result is 10-bit , The ADC is set to on, No convertion is in progress

         } // switch (channel)

     // Channel selected proceed with ADC convertion
     __delay_us(10); // sampling time
     ADCON0 = ADCON0 | 0x02; //This sets the go/!done bit that starts conversion. Bit will be cleared when ADC is complete
     while (ADCON0 & 0x02); //wait here until conversion is complete
    AN_Val = ((ADRESH << 8) + ADRESL) & 0x03ff; //result is 16 bits with 10-bits for measurement. Shift upper 8 bits left 8 bits into high byte and add low byte.
     return AN_Val;
} // int ADC(unsigned char channel)


 void main (void){

 //** Initalise Ports FOR ADC **//


  unsigned int AN1_Result;
  unsigned int AN0_Result;
  unsigned int AN2_Result;

   PORTC = 0x00;
   TRISC = 0x00; //Port C all output
  // TRISB = 0x00;

// in the following function I am trying to test and see if the code works by using a POT and changing the value and see if LEDs in port C would react to that. 
  do{
    AN0_Result = ReadADC(0);
    PORTC = AN0_Result; 

    }while(1);
}

Actualización # 1:

basado en algunas de las sugerencias, actualicé mi código de la siguiente manera:

Los problemas que sigo teniendo son que, incluso cuando no hay una entrada analógica, el led conectado a PORTC permanece encendido. Incluso cuando conecto a tierra la entrada usando una resistencia a tierra. Eso solo me dice que hay algo mal con mi función ReadADC. Volví a revisar mi código y sospecho que hay algo mal en las siguientes líneas que aún no entiendo:

ADCON0 = ADCON0 | 0x02; //This sets the go/!done bit that starts conversion. Bit will be cleared when ADC is complete
while (ADCON0 & 0x02);  //wait here until conversion is complete
AN_Val = ((ADRESH << 6) + ADRESL)& 0x03ff ;  // shift the ADRESh register by 6 bits to get rid of extra 0's and then add the bottom 8bits to it which gives the whole 10bit answer. (somebody suggested to & with 0x03ff still dont know why this could be wrong)

además de que cambié el código como algunos de ustedes notaron y movieron adc init dentro de la función ReadADC y agregué un par de líneas que tenían sentido para mí, aquí está

 #include <xc.h>

// Config word
#define _XTAL_FREQ   500000 // set it to match internal oscillator
#DEVICE ADC=10  // set ADC to 10 bit resolution

/*config1 and config2 settings*/
 // CONFIG1
#pragma config FOSC = INTOSC // Oscillator Selection (INTOSC oscillator: I/O f   unction on CLKIN pin)
#pragma config WDTE = OFF // Watchdog Timer Enable (WDT enabled)
#pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
 #pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)
 #pragma config VCAPEN = OFF // Voltage Regulator Capacitor Enable bit (Vcap functionality is disabled on RA6.)
 #pragma config PLLEN = ON // PLL Enable (4x PLL enabled)
 #pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
 #pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
 #pragma config LPBOR = OFF // Low Power Brown-Out Reset Enable Bit (Low power brown-out is disabled)
 #pragma config LVP = ON // Low-Voltage Programming Enable (Low-voltage programming enabled)


// Do the ADC convertion only for the channel indicated
// result of ADC is returned
 unsigned int ReadADC(unsigned char channel)
{
  unsigned int AN_Val;
  unsigned int bit_val;

    //** Initalise Ports FOR ADC **//
     PORTA = 0x00; //Set ports to low
     PORTB = 0x00;
     TRISA = 0xFF; //Port A is all inputs. (set to 1)
     //** Set Up ADC Parameters **//
     ANSELA =0x2F ; // (0b00101111)All AN0-AN4(RA4 and RA7 have no ADC) of register A are set to analog input

     ADCON1 = 0x00; // Sets ADRESL to contain the first 7 bits of conversion, ADRESH will have the final 3 bits. And all the rest to default (FOSC/2 2 prescaler)
     // automaticaly determines the input channel it will read the value from
     switch (channel)
         {
         case 1: // AN1 1000 0101
             ADCON0 = 0x85;
             break;
         case 2: // AN2 1000 1001
             ADCON0 = 0x89;
             break;
         case 3: //AN3 1000 1101
             ADCON0 = 0x8D;
             break;
  case 4: //AN4 1001 0001
             ADCON0 = 0x91;
             break;
         default: // Any other value will default to AN0
             ADCON0 = 0x81; // (1000 0001) - AN1 set up ADC ADCON0

 // sets the ADCON0 register for each port depending on the value of channel.
 // the result is 10-bit , The ADC is set to on, No convertion is in progress

         } // switch (channel)

     // Channel selected proceed with ADC convertion
     __delay_us(10); // sampling time
     ADCON0 = ADCON0 | 0x02; //This sets the go/!done bit that starts conversion. Bit will be cleared when ADC is complete
     while (ADCON0 & 0x02); //wait here until conversion is complete
    bit_val= ADRESH
    AN_Val = ((bit_val << 8) | ADRESL); //store the ADRESH into the 16 bit int then shit it up 8 spaces after that add the ADRESL values using the and operator.
     return AN_Val;
} // int ADC(unsigned char channel)


 void main (void){

 //** Initalise Ports FOR ADC **//


  unsigned int AN1_Result;
  unsigned int AN0_Result;
  unsigned int AN2_Result;

   PORTC = 0x00;
   TRISC = 0x00; //Port C all output


// in the following function I am trying to test and see if the code works by using a POT and changing the value and see if LEDs in port C would react to that. 
  do{
    AN0_Result = ReadADC(0);
    LATC = AN0_Result; 

    }while(1);
}

ACTUALIZACIÓN # 2

Ahora el ADC funciona, pero la salida no es del todo correcta. Lo probé utilizando 3 LEDs. Dos leds se encenderían cuando el voltaje esté por encima de los 2,5 voltios y un solo LED se iluminaría si está por debajo de 2.5v. El problema es que ocurre lo contrario. Dos leds se encenderían si el voltaje está por debajo y uno solo se iluminaría si el voltaje es superior a 2.5V. También revisé mi circuito y todo parece correcto.

#include <xc.h>

 // Config word
  #define _XTAL_FREQ   2000000 // set it to match internal oscillator

 // Do the ADC convertion only for the channel indicated
 // result of ADC is returned
  unsigned int ReadADC(unsigned char channel)
  {
 unsigned int AN_Val;
 unsigned int bit_val;

//** Initalise Ports FOR ADC **************************************************************//
 PORTA = 0x00; //Set ports to low
 PORTB = 0x00;
 TRISA = 0xFF; //Port A is all inputs. (set to 1)

 //** Set Up ADC Parameters **//
 ANSELA =0x2F ; // (0b00101111)All AN0-AN4(RA4 and RA7 have no ADC) of register A are set to analog input


 ADCON1 = 0xD0; // (0b11010000)
                //format setup see page 182 of datasheet
                // bit7: set for 2'complement format
                // bit6-4 : set FOSC/16
    //********************************************************************************************//

 // automaticaly determines the input channel it will read the value from
 switch (channel)
     {
     case 1: // AN1 1000 0101
         ADCON0 = 0x85;                  // bit0: ADC enabled
                                        //bit6-2: AN1 enabled for analog input
                                       //bit7: set for a 10-bit result(when its 1)
         break;
     case 2: // AN2 1000 1001
         ADCON0 = 0x89;
         break;
     case 3: //AN3 1000 1101
         ADCON0 = 0x8D;
         break;
     case 4: //AN4 1001 0001
         ADCON0 = 0x91;
         break;
     default: // Any other value will default to AN0
         ADCON0 = 0x81; // (1000 0001) - AN1 set up ADC ADCON0

    // sets the ADCON0 register for each port depending on the value of channel.
    // the result is 10-bit , The ADC is set to on, No convertion is in progress

     } // switch (channel)

 // Channel selected proceed with ADC convertion
__delay_us(10); // sampling time
ADCON0 = ADCON0 | 0x02; //This sets the go/!done bit that starts conversion. Bit will be cleared when ADC is complete
while (ADCON0 & 0x02); //wait here until conversion is complete
bit_val= ADRESH; // store upper 2 bits in a 16 bit int
AN_Val = ((bit_val << 8) | ADRESL); //store the ADRESH into the 16 bit int then shit it up 8 spaces after that add the ADRESL values using the and operator.
return AN_Val;
} // int ADC(unsigned char channel)



void main (void){

//** Initalise Ports FOR ADC **//


 unsigned int AN1_Result;
 unsigned int AN0_Result;
 unsigned int AN2_Result;

 PORTC = 0x00;
 TRISC = 0x00; //Port B all output
 //TRISB = 0x00;
 //ANSELB = 0x00; // all as port b digital as in/out
 ANSELC = 0x00;
while(1){
AN0_Result = ReadADC(1);
if(AN0_Result > 512){    // when the voltage is passed 2.5 volts these two LEDS should come on.
            /*Turn these 2 LED on*/
            LATCbits.LATC2 = 1;
            LATCbits.LATC3 = 1;
            LATCbits.LATC4 = 0;
            }
 if (AN0_Result < 512){
            /*turn one LED on*/
            LATCbits.LATC2 = 0;
            LATCbits.LATC3 = 0;
            LATCbits.LATC4 = 1;
            }
}
}
    
pregunta sam brosteain

2 respuestas

2

Un par de problemas con tu código:

  1. Por alguna razón, sus ajustes de configuración están definidos dentro de InitADC (). De acuerdo con el manual del usuario de XC8, esos deben ir al principio de su código (debajo de #include xc.h, creo).

  2. En realidad nunca llamas a InitADC () en ningún lugar.

  3. Usted dejó el temporizador de vigilancia (WDT) activado, pero no borra el temporizador en su código. Esto hará que el 16F1788 se reinicie constantemente. Apague la WDT.

  4. Sus ajustes de configuración están configurados para usar el oscilador interno. La frecuencia predeterminada del oscilador interno es de 500 kHz. Sin embargo, usted definió _XTAL_FREQ como 32MHz. Creo que XC8 utiliza el valor _XTAL_FREQ para calcular el tiempo de la función __delay_us (), por lo que sus retrasos serán extremadamente inexactos. En otras palabras, el compilador piensa que el programa se está ejecutando a 32MHz, pero en realidad, se está ejecutando a 500kHz.

  5. Siempre use los registros LAT para escribir valores en los puertos. Utilice solo los registros PORT para leer desde los puertos. Los registros LAT se introdujeron para superar el problema de lectura-modificación-escritura.

  6. El compilador define ADRESH como un char sin signo de 8 bits. Desplazar hacia la izquierda un valor de 8 bits 8 veces hará que el valor sea igual a 0x00 cada vez. Por lo tanto, su asignación de AN_Val siempre será incorrecta.

actualización por Sam Brosteain:

  1. Lo más importante es establecer el registro ADCON2. El nibble inferior del registro ADCON2 selecciona la fuente del canal negativo, que es la referencia de voltaje negativo (Vref-) para el A / D. El valor predeterminado de POR es usar el voltaje del pin AN0 como Vref-. ADCON2 = 0xF configure Vref- a la seleccionada por el bit ADNREF del registro ADCON1, que en su código está configurado para conectar Vref- a Vss. En otras palabras, está convirtiendo una señal de entrada analógica de un solo extremo que hace referencia a tierra (Vss).
respondido por el Dan Laks
2

Siempre hay una entrada analógica.

Cuando el potenciómetro está conectado, el valor de entrada es lo que está configurado en el potenciómetro.

Cuando se desconecta el potenciómetro, el valor de entrada es lo que se desee. Por lo general, en algún lugar alrededor de la mitad del rango, quizás un poco más abajo.

Con la entrada desconectada solo está captando el ruido del área circundante y usándolo como entrada.

Es necesario reemplazar el potenciómetro con una resistencia a tierra cuando el potenciómetro no está conectado.

    
respondido por el Majenko

Lea otras preguntas en las etiquetas