Estoy tratando de construir un ohmímetro simple creando un divisor de voltaje y luego usando un pin analógico en un PIC18F2550 para leer el voltaje de salida y determinar el valor óhmico de una de las resistencias. Estoy usando un oscilador de 20 Mhz para el reloj del PIC, y todo el circuito funciona con un regulador LM7805. Utilizo una resistencia permanente de 10K ohmios como resistencia 2 en mi divisor y resuelvo el valor de la primera resistencia. Estoy leyendo el valor del PIN AN0. El problema es que el valor leído es muy impreciso y no estoy seguro de cuál es el problema. ¿Qué podría hacer para obtener lecturas más precisas?
Aquí está mi código para el compilador XC8
#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;
}
}
Estás uniendo mal los valores de byte ALTO y BAJO:
unsigned int adc = ((ADRESH<<2) | ADRESL);
Tiene un resultado de 10 bits, 2 bits en ADRESH y 8 bits en ADRESL.
Digamos que los dos valores son
ADRESH = 0b00000010
ADRESL = 0b10101010
Desplazaste a la izquierda el alto por 2 lugares, por lo que se convierte en:
ADRESH = 0b00001000
ADRESL = 0b10101010
Ahora usted O los dos valores juntos.
ADRESH = 0b00001000
ADRESL = 0b10101010
OR = 0b10101010
No es de extrañar que los valores estén equivocados.
Primero debe convertir los dos valores a 16 bits (para asegurarse de que el compilador sepa trabajar con 16 bits de valor, no con 8):
unsigned int hval = ADRESH;
unsigned int lval = ADRESL;
Eso hace que los valores:
hval = 0b0000000000000010
lval = 0b0000000010101010
Luego, debe cambiar la parte alta en 8 bits para que caiga a la izquierda del valor más bajo:
hval = 0b0000001000000000
lval = 0b0000000010101010
Y luego finalmente OR juntos:
hval = 0b0000001000000000
lval = 0b0000000010101010
OR = 0b0000001010101010
Entonces, su código para generar el valor completo podría verse así:
unsigned int hval = ADRESH;
unsigned int lval = ADRESL;
unsigned int adc = (hval << 8) | lval;
Por supuesto, eso es un desperdicio de variables, y podría comprimirlo en una sola línea utilizando la conversión para garantizar suficiente espacio de cambio:
unsigned int adc = ((unsigned int)ADRESH << 8) | ADRESL;
Ah, y ya que estás en eso, deja de depender del punto flotante. Hace que su programa consuma muchos recursos y sea algo lento. Trabaje en valores de punto fijo (enteros) en su lugar.
Por ejemplo, calcule los milivoltios, no los voltios:
unsigned long mv = 5000 * adc / 1023;
Luego, a partir de eso, calcula el valor de la resistencia:
unsigned long ohms = ((10000 * 5000) - (10000 * mv)) / mv;
Y todo sin un solo valor de punto flotante.
5000 * adc
se desbordará un 16 bits unsigned int
a menos que adc
sea 13 o menos (y será negativo, porque 5000
es una int
constante con signo, a menos que sea 6 o menos, lo que arruinará la división debido a la extensión del signo). Usar 5000UL * adc
debería arreglarlo.
Andy alias
markoviano8261
pete becker