ADC-Lesefunktion funktioniert nicht richtig

Ich arbeite an einem Schulprojekt, bei dem ich eine Reihe analoger Werte lesen und mit PIC16F1788 ( Datenblatt ) in digitale umwandeln muss. Ich habe die Funktion mit C und dem xc8-Compiler geschrieben. Das Problem ist, wenn ich es teste, verhält es sich nicht so, wie es sollte. Wenn ich das Potentiometer entferne, bleibt die LED an, obwohl alles aus sein sollte, da kein analoger Eingang mehr vorhanden ist. Hier ist mein Code, danke für deine Hilfe.

 #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);
}

Aktualisierung Nr. 1:

Basierend auf einigen der Vorschläge habe ich meinen Code wie folgt aktualisiert:

Die Probleme, die ich immer noch habe, sind, dass selbst wenn kein analoger Eingang vorhanden ist, die an PORTC angeschlossene LED eingeschaltet bleibt. Auch wenn ich den Eingang mit einem Widerstand gegen Masse erde. Das sagt mir nur, dass mit meiner ReadADC-Funktion etwas nicht stimmt. Ich habe meinen Code noch einmal durchgesehen und vermute, dass mit den folgenden Zeilen etwas nicht stimmt, die ich noch nicht verstehe:

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)

Abgesehen davon, dass ich den Code geändert habe, wie einige von Ihnen bemerkt haben, und die adc-Init in die ReadADC-Funktion verschoben und ein paar Zeilen hinzugefügt haben, die für mich sinnvoll waren, hier ist es

 #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);
}

UPDATE Nr. 2

Jetzt funktioniert der ADC, aber die Ausgabe ist nicht ganz korrekt. Ich habe es mit 3 LEDs getestet. Zwei LEDs würden aufleuchten, wenn die Spannung über 2,5 Volt liegt, und eine einzelne LED würde aufleuchten, wenn sie unter 2,5 V liegt. Das Problem ist, dass das Gegenteil passiert. Zwei LEDs würden aufleuchten, wenn die Spannung unter 2,5 V liegt, und eine einzelne würde aufleuchten, wenn die Spannung über 2,5 V liegt. Ich habe auch meine Schaltung überprüft und alles scheint korrekt zu sein.

#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;
            }
}
}
Warum sind Ihre Konfigurationseinstellungen innerhalb von InitADC()?
Hast du das schon hinbekommen? Ich habe noch ein paar Kuriositäten in deinem Code gefunden.
Die Konvertierung funktioniert, aber die Ausgabe ist nicht korrekt. Ich gehe es durch und habe auch ein paar Fehler gefunden. Lassen Sie mich wissen, welche Fehler Sie gefunden haben, danke.
Gehen wir zurück in den Chatroom
Welche Werte lesen Sie vom ADC, wenn Sie den Eingang erden (und erwarten, dass der ADC-Messwert nahe Null liegt)? Nach meiner Erfahrung ist es nicht ungewöhnlich, einen Wert von wenigen Zählern (0-10 bei einem 10-Bit-ADC) zu erhalten, wenn der Eingang angeblich auf Massepotential liegt.

Antworten (2)

Ein paar Probleme mit Ihrem Code:

  1. Ihre Konfigurationseinstellungen werden aus irgendeinem Grund innerhalb von InitADC() definiert. Laut XC8-Benutzerhandbuch müssen diese ganz oben in Ihrem Code stehen (unter #include xc.h, glaube ich).

  2. Sie rufen InitADC() eigentlich nie irgendwo auf.

  3. Sie haben den Watchdog-Timer (WDT) eingeschaltet gelassen, aber den Timer in Ihrem Code nicht gelöscht. Dadurch wird der 16F1788 ständig zurückgesetzt. Schalten Sie den WDT aus.

  4. Ihre Konfigurationseinstellungen sind auf die Verwendung des internen Oszillators eingestellt. Die Standardfrequenz des internen Oszillators beträgt 500 kHz. Dennoch haben Sie _XTAL_FREQ auf 32 MHz definiert. Ich glaube, dass XC8 den _XTAL_FREQ-Wert verwendet, um das Timing für die __delay_us()-Funktion zu berechnen, sodass Ihre Verzögerungen extrem ungenau sein werden. Mit anderen Worten, der Compiler denkt, dass das Programm mit 32 MHz läuft, aber in Wirklichkeit läuft es mit 500 kHz.

  5. Verwenden Sie immer die LAT-Register, um Werte an die Ports zu schreiben. Verwenden Sie nur die PORT-Register, um von den Ports zu lesen. Die LAT-Register wurden eingeführt, um das Read-Modify-Write-Problem zu lösen.

  6. Der Compiler definiert ADRESH als 8-Bit-Zeichen ohne Vorzeichen. Wenn Sie einen 8-Bit-Wert achtmal nach links verschieben, wird der Wert jedes Mal gleich 0x00. Daher wird Ihre Zuweisung von AN_Val immer falsch sein.

Update von Sam Brosteain:

  1. Am wichtigsten ist das ADCON2-Register zu setzen. Das untere Halbbyte des ADCON2-Registers wählt die Quelle des negativen Kanals aus, die die negative Spannungsreferenz (Vref-) für den A/D ist. Die POR-Standardeinstellung ist die Verwendung der Spannung vom AN0-Pin als Vref-. ADCON2 = 0xF setzt Vref- auf das, was durch das ADNREF-Bit des ADCON1-Registers ausgewählt wird, das in Ihrem Code so konfiguriert ist, dass es Vref- mit Vss verbindet. Mit anderen Worten, Sie wandeln ein Single-Ended-Analogeingangssignal um, das auf Masse (Vss) verweist.
Ich habe meinen Beitrag aktualisiert, indem ich berücksichtigt habe, was Sie gesagt haben, danke
Wie ich sehe, haben Sie 1, 4 und 5 korrigiert. Sie haben jedoch noch immer nicht die InitADC()-Funktion irgendwo in Ihrem Code aufgerufen, noch das WDT deaktiviert oder die Zuweisung von AN_Val korrigiert. All diese Dinge sind einzeln tödlich.
Ok, ich sehe, Sie haben gerade den Inhalt von InitADC() kopiert und in ReadADC() eingefügt. Nicht wirklich der beste Weg, aber es wird Ihren Code nicht brechen. Die anderen Probleme in meinem Kommentar oben sind immer noch gültig.
Entschuldigung für das WDT, ich habe es in meinem Code gemacht und vergessen, es hier wiederzugeben, da ich es für den initADC entfernt und den ADC innerhalb der ReadADC-Funktion initialisiert habe, ist das in Ordnung? Außerdem bin ich im AN_Val-Teil wirklich verwirrt. Ich habe die Bits um 6 verschoben, um die oberen 6 0 loszuwerden. Dann habe ich die unteren 8 Bits hinzugefügt, ist das richtig?
Wenn Sie einen 8-Bit-Wert um 6 nach links verschieben und dann das niedrigere Byte hinzufügen, erhalten Sie nicht den 10-Bit-Wert, den Sie erhalten möchten. Ihr ursprünglicher Code war größtenteils korrekt. Der einzige Teil, den Sie vermisst haben, war, ADRESH zuerst in einen 16-Bit-Wert zu konvertieren und dann nach links zu verschieben.
würde es funktionieren, wenn ich es folgendermaßen mache: AN_Val = ((ADRESH << 8) & ADRESL); // Speichern Sie die ADRESH in die 16-Bit-Ganzzahl und fügen Sie sie dann 8 Leerzeichen hinzu, und fügen Sie die ADRESL-Werte mit dem Operator and hinzu. oder sollte ich zuerst die ADRESH in das int verschieben und dann die bitweisen Operationen durchführen? Danke
Hey, dan, ich habe es zum Laufen gebracht, aber es gibt mir das Gegenteil von dem, was die Ausgabe sein soll. Ich habe es so getestet, indem ich 3 LEDs eingesetzt habe. Zwei LEDs würden aufleuchten, wenn mein Ergebnis größer als 512 (2,5 Volt) ist, und die einzelne LED würde aufleuchten, wenn der Eingang kleiner ist. Ich arbeite, aber das Gegenteil passiert. Ich dachte, es wäre meine Schaltung, aber ich habe alles überprüft. Ich werde den aktualisierten Code veröffentlichen, damit Sie ihn sich ansehen können, wenn Sie etwas Zeit haben. Danke
@sambrosteain, was sind die Werte von ADRESH und ADRESL jetzt im Debugger?
Hallo Dan, aus irgendeinem Grund kann ich nur die Werte der Dinge innerhalb meiner Hauptfunktion abrufen. Beim Debuggen des Codes habe ich die Ausgabewerte von AN0_Result gelesen und sie mit dem Wert verglichen, den ich basierend auf der angelegten Spannung erwartet hatte, und ich muss sagen, dass sie mit meinen Berechnungen übereinstimmen. Der Code funktioniert also zu 100% (ziemlich genau), aber jetzt weiß ich, dass es die Schaltung ist. Ich werde die gesamte Schaltung morgen wiederholen und prüfen, wo ich einen Fehler gemacht habe. Ich kann Ihnen nicht genug für Ihre Hilfe danken! Wirklich !. Sie haben mir diesen Debugger gezeigt und er macht mein Leben jetzt so viel einfacher. Danke Dan!! Möge Gott Sie für Ihre Hilfe belohnen!
Ich gebe morgen Bescheid, wenn ich die Schaltung zum Laufen bekomme.
Ich habe es endlich zum Laufen gebracht. Ich habe vergessen, das ADCON2-Register einzurichten. Ich brauchte das, um die Referenzspannung auf Vref- einzustellen. Deshalb funktionierte alles rückwärts. Vielen Dank für Ihre Hilfe! Dan

Es gibt immer einen analogen Eingang.

Wenn das Potentiometer angeschlossen ist, ist der Eingangswert der Wert, auf den das Potentiometer eingestellt ist.

Wenn das Potentiometer getrennt ist, ist der Eingangswert, was auch immer es sich vorstellt. Normalerweise irgendwo in der Mitte des Bereichs, vielleicht etwas darunter.

Wenn der Eingang getrennt ist, werden nur Geräusche aus der Umgebung aufgenommen und als Eingang verwendet.

Sie müssen das Potentiometer durch einen Widerstand gegen Masse ersetzen, wenn das Potentiometer nicht angeschlossen ist.