Ich versuche, ein einfaches Ohmmeter zu bauen, indem ich einen Spannungsteiler erstelle und dann einen analogen Pin an einem PIC18F2550 verwende, um die Ausgangsspannung zu lesen und den Ohmwert eines der Widerstände zu bestimmen. Ich verwende einen 20-MHz-Oszillator für die Uhr des PIC, und die gesamte Schaltung läuft von einem LM7805-Regler ab. Ich verwende einen permanenten 10K-Ohm-Widerstand als Widerstand 2 in meinem Teiler und löse nach dem Wert des ersten Widerstands auf. Ich lese den Wert von PIN AN0 ab. Das Problem ist, dass der gelesene Wert sehr ungenau ist und ich nicht sicher bin, was das Problem ist. Was kann ich tun, um genauere Messwerte zu erhalten?
Hier ist mein Code für den XC8-Compiler
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include <string.h>
#pragma config FOSC = HS // Oscillator Selection bits (HS oscillator (HS))
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
#pragma config IESO = OFF // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)
#pragma config PWRT = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOR = OFF // Brown-out Reset Enable bits (Brown-out Reset disabled in hardware and software)
#pragma config BORV = 0 // Brown-out Reset Voltage bits (Maximum setting)
#pragma config VREGEN = OFF // USB Voltage Regulator Enable bit (USB voltage regulator disabled)
#pragma config WDT = OFF // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))
#pragma config CCP2MX = OFF // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
#pragma config PBADEN = OFF // PORTB A/D Enable bit (PORTB<4:0> pins are configured as analog input channels on Reset)
#pragma config LPT1OSC = OFF // Low-Power Timer 1 Oscillator Enable bit (Timer1 configured for higher power operation)
#pragma config MCLRE = OFF // MCLR Pin Enable bit (RE3 input pin enabled; MCLR pin disabled)
#pragma config STVREN = OFF // Stack Full/Underflow Reset Enable bit (Stack full/underflow will not cause Reset)
#pragma config LVP = OFF // Single-Supply ICSP Enable bit (Single-Supply ICSP disabled)
#pragma config XINST = OFF // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))
#pragma config CP0 = OFF // Code Protection bit (Block 0 (000800-001FFFh) is not code-protected)
#pragma config CP1 = OFF // Code Protection bit (Block 1 (002000-003FFFh) is not code-protected)
#pragma config CP2 = OFF // Code Protection bit (Block 2 (004000-005FFFh) is not code-protected)
#pragma config CP3 = OFF // Code Protection bit (Block 3 (006000-007FFFh) is not code-protected)
#pragma config CPB = OFF // Boot Block Code Protection bit (Boot block (000000-0007FFh) is not code-protected)
#pragma config CPD = OFF // Data EEPROM Code Protection bit (Data EEPROM is not code-protected)
#pragma config WRT0 = OFF // Write Protection bit (Block 0 (000800-001FFFh) is not write-protected)
#pragma config WRT1 = OFF // Write Protection bit (Block 1 (002000-003FFFh) is not write-protected)
#pragma config WRT2 = OFF // Write Protection bit (Block 2 (004000-005FFFh) is not write-protected)
#pragma config WRT3 = OFF // Write Protection bit (Block 3 (006000-007FFFh) is not write-protected)
#pragma config WRTC = OFF // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) are not write-protected)
#pragma config WRTB = OFF // Boot Block Write Protection bit (Boot block (000000-0007FFh) is not write-protected)
#pragma config WRTD = OFF // Data EEPROM Write Protection bit (Data EEPROM is not write-protected)
#pragma config EBTR0 = OFF // Table Read Protection bit (Block 0 (000800-001FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR1 = OFF // Table Read Protection bit (Block 1 (002000-003FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR2 = OFF // Table Read Protection bit (Block 2 (004000-005FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR3 = OFF // Table Read Protection bit (Block 3 (006000-007FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTRB = OFF // Boot Block Table Read Protection bit (Boot block (000000-0007FFh) is not protected from table reads executed in other blocks)
#define _XTAL_FREQ 20000000
int main()
{
ADCON0 = 0b00000001;
ADCON1 = 0b00001110;
ADCON2 = 0b10001010;
TRISA0 = 1;
while(1)
{
GO_DONE = 1;
while(GO_DONE);
unsigned int adc = ((ADRESH<<2) | ADRESL);
const float maxAdcBits = 1023.0f;
const float vin = 5.0f;
const float resistance2 = 10000.0f;
float voltsPerBit = (vin / maxAdcBits);
float vout = adc * voltsPerBit;
float ohms = ((resistance2 * vin) - (resistance2 * vout)) / vout;
}
}
Sie verbinden Ihre HIGH- und LOW-Byte-Werte falsch miteinander:
unsigned int adc = ((ADRESH<<2) | ADRESL);
Sie haben ein 10-Bit-Ergebnis, 2 Bits in ADRESH und 8 Bits in ADRESL.
Angenommen, die beiden Werte sind
ADRESH = 0b00000010
ADRESL = 0b10101010
Sie haben die obere Position um 2 Stellen nach links verschoben, sodass daraus Folgendes wird:
ADRESH = 0b00001000
ADRESL = 0b10101010
Jetzt ODER die beiden Werte zusammen.
ADRESH = 0b00001000
ADRESL = 0b10101010
OR = 0b10101010
Kein Wunder, dass die Werte falsch sind.
Sie müssen zuerst die beiden Werte in 16-Bit konvertieren (um sicherzustellen, dass der Compiler weiß, dass er mit 16-Bit-Werten arbeiten soll, nicht mit 8):
unsigned int hval = ADRESH;
unsigned int lval = ADRESL;
Das macht die Werte:
hval = 0b0000000000000010
lval = 0b0000000010101010
Dann müssen Sie den hohen Teil um 8 Bit verschieben, damit er links vom niedrigeren Wert liegt:
hval = 0b0000001000000000
lval = 0b0000000010101010
Und dann schließlich ODER sie zusammen:
hval = 0b0000001000000000
lval = 0b0000000010101010
OR = 0b0000001010101010
Ihr Code zum Generieren des vollen Werts könnte also etwa so aussehen:
unsigned int hval = ADRESH;
unsigned int lval = ADRESL;
unsigned int adc = (hval << 8) | lval;
Das ist natürlich etwas verschwenderisch für Variablen, und Sie könnten es mit Casting in eine Zeile komprimieren, um genügend Verschiebungsraum zu gewährleisten:
unsigned int adc = ((unsigned int)ADRESH << 8) | ADRESL;
Oh, und wenn Sie schon dabei sind, lassen Sie Ihre Abhängigkeit von Gleitkommazahlen fallen. Es bewirkt, dass Ihr Programm sowohl sehr ressourcenhungrig als auch etwas langsam ist. Arbeiten Sie stattdessen mit Festkommawerten (ganzzahligen Werten).
Berechnen Sie zum Beispiel die Millivolt, nicht die Volt:
unsigned long mv = 5000 * adc / 1023;
Daraus berechnen Sie dann den Wert des Widerstands:
unsigned long ohms = ((10000 * 5000) - (10000 * mv)) / mv;
Und das alles ohne einen einzigen Fließkommawert.
5000 * adc
wird ein 16-Bit überlaufen, unsigned int
es sei denn adc
, es ist 13 oder weniger (und wird negativ, weil 5000
es eine vorzeichenbehaftete int
Konstante ist, es sei denn, es ist 6 oder weniger, was die Division wegen der Vorzeichenerweiterung durcheinander bringt). Die Verwendung 5000UL * adc
sollte es beheben.
Andi aka
Markovian8261
Peter Becker