Messen der Batteriespannung vom Mikrocontroller mit ADC

Ich verwende derzeit einen PIC16f1826, um zu versuchen, die Spannung der 3,7-V-Batterie zu messen, die den Mikrocontroller mit Strom versorgt.

Nach meinem Verständnis kann der ADC von 0 V bis zur in 1024 Teile geteilten Referenzspannung messen, sodass ich die Batterie nicht als Referenzspannung verwenden kann. Stattdessen verwende ich eine feste Spannungsreferenz bei 2,048 V, die im Mikrocontroller generiert wird.

2,048 liegt jedoch unter der 3,7-V-Batterie, die ich zu messen versuche, also verwende ich einen Spannungsteiler, um den Eingang zu halbieren, damit ich aussagekräftige Messwerte erhalten kann. Wenn ich dies jedoch tue und versuche, meinen Messwert zu erhalten, erhalte ich einen Wert von 6-5/1023, was einfach falsch erscheint. Wenn ich die Batterie ohne Spannungsteiler anschließe bekomme ich die volle 1023/1023.

Meine Schaltung sieht wie folgt aus:

schematisch

Simulieren Sie diese Schaltung – Mit CircuitLab erstellter Schaltplan

Ich habe die UART-Verbindung weggelassen, da sie für die Frage nicht relevant ist. Mein Code für den ADC ist folgender:

#include <xc.h>
#include <stdlib.h>

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#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 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 LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)

// Define Oscillator Frecuency to use in delay_ms calculations.
#define _XTAL_FREQ   4000000  // 4MHz

// Function that initializes ADC parameters.
void initADC(void) {

    // Enable Fixed Voltage Reference.
    FVRCONbits.FVREN = 1;

    // Set Fixed Voltage Reference to 2.048V.
    FVRCONbits.ADFVR = 0b10;

    // Enable Analog input on RA1.
    TRISA1 = 0;
    ANSELAbits.ANSA1 = 0;

    // Define Conversion Clock to use FOSC/4.
    ADCON1bits.ADCS = 0b100;

    // Configure ADC Positive Reference Voltage to use FVR buffer.
    ADCON1bits.ADPREF = 0b11;

    // Configure ADC Negative Reference Voltage to use VSS.
    ADCON1bits.ADNREF = 0;

    // Output format as right justified.
    ADCON1bits.ADFM = 1;

    // Select Pin AN1 as Channel.
    ADCON0bits.CHS = 0b00001;

    // Start ADC Module.
    ADCON0bits.ADON = 1;

    // Start conversion.
    ADCON0bits.GO_nDONE = 1;
}
// Main function.
void main(void) {

    // Setup Internal Oscillator to run at 4MHz.
    OSCCONbits.IRCF = 0b1101;

    // Initialize ADC.
    initADC();

    while(1) {
        if(!ADCON0bits.GO_nDONE) {
            sendString("Ready: ");
            // Read ADC Result Register High and 
            // Register Low for full 10 bit scope.
            int adcResult = ((ADRESH)<<8)|(ADRESL);

            char percentToChar[5];
            itoa(percentToChar, adcResult, 10);
            sendString(percentToChar);
            sendString("\n");
            ADCON0bits.GO_nDONE = 1;

        } else {
            sendString("Not ready \n");
        }
        __delay_ms(300);
    }
}

Das Handbuch für den Mikrocontroller finden Sie unter: http://ww1.microchip.com/downloads/en/DeviceDoc/41391D.pdf Seite 135 behandelt die Festspannungsreferenz und Seite 140 behandelt ADC.

Warum erhalte ich den Messwert, den ich im ADC erhalte? Es ergibt für mich keinen Sinn, mache ich vielleicht etwas im Code falsch? Sollte ich dazu das Komparatormodul anstelle des ADC verwenden? Jede Hilfe, die Sie leisten können, um mir bei der Lösung dieses Problems zu helfen, wird sehr geschätzt!

Es sieht so aus, als ob Sie nicht itoarichtig verwenden. Das erste Argument sollte der zu konvertierende Wert sein, das zweite ist der Puffer.
Sie benötigen keine externen Komponenten, wenn Sie die interne Referenz als A/D-Eingang und die Vdd als Referenz verwenden. Ein Messwert von N entspricht einer Vdd von 1023 * Vref / N.
@EugenSch. Sie haben tatsächlich Recht, haben es geändert und erhalten jetzt andere Ergebnisse. Sie werden daran herumschrauben, um zu sehen, ob dies der Fehler war.
@WoutervanOoijen Sie sagen also, ich brauche keinen Eingangspin, um die Spannung abzulesen? Wie kann ich den ADC-Kanal so einstellen, dass er VDD verwendet? Im Handbuch scheint es nicht beschrieben zu sein.
Sie wählen eine von wenigen Möglichkeiten als Referenz aus, Vdd wird am häufigsten verwendet. Überprüfen Sie den A/D-Abschnitt des Datenblatts.
Sieht so aus, als hätten Sie eine Antwort, aber was bedeutet "6-5/1023" in diesem Zusammenhang?
@mcmahon Ich wollte sagen, dass ich entweder einen Wert von 6 von 1023 oder 5 von 1023 bekomme
@WoutervanOoijen hat es gefunden, ich werde meinen Code ändern, um es so zu machen, danke für die Hilfe!
Sie haben sich gerade 2 Widerstände und (vielleicht wichtiger) 0,2 mA gespart :)
@WoutervanOoijen Das habe ich tatsächlich getan, deine Antwort hat es am Ende für mich gelöst. Wenn du also die Antwort schreiben möchtest, werde ich sie akzeptieren, damit du die Punkte bekommen kannst :)

Antworten (1)

Das ist nicht meine Antwort, aber diese Frage sitzt seit Monaten hier mit der Antwort in den Kommentaren.

Wie Wouter van Ooijen in den Kommentaren feststellt, wird die interne Referenz als A / D-Eingang und Vdd als Referenz verwendet

Das Ablesen von N entspricht einer Vdd von 1023 * Vref/N

Ja, es wäre so ziemlich der gleiche Code, aber mit ADCON0bits.CHS = 0b11111;dem FVR als ADC-Kanal auszuwählen, ADCON1bits.ADPREF = 0b00;Vdd als positive Referenz zu verwenden und dannVdd = 2.048*1023/adcResult