/*
* File: vumetro2.c
* Author: nahueldiaz
*
* Created on 27 de enero de 2016, 21:20
*/
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include <plib.h>
#include <plib/adc.h>
#include <math.h>
#define _XTAL_FREQ 2000000
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = OFF // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital I/O, MCLR internally tied to VDD)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB0 // CCP1 Pin Selection bit (CCP1 function on RB0)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = ON // Internal External Switchover bit (Internal External Switchover mode enable
unsigned char high,low;
unsigned char result;
unsigned char cnt,msb,lsb ;
void interrupt alta(void){ //interrupcion de alta
INTCONbits.GIE = 0; //deshabilita las int. para nocausar varias int a la vez
if (PIR1bits.ADIF == 1){ // si salto flag de ADC entonces..
high = ADRESH; // rescatar valores de ADC
low = ADRESL;
PORTB = low;
__delay_ms(10);
ADRESH = 0x00;
ADRESL = 0x00;
PIR1bits.ADIF = 0;
}
INTCONbits.GIE = 1;
}
unsigned char display (unsigned char no){ //display 7 segmentos
unsigned char patern;
unsigned char segment[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
patern = segment[no];
return (patern);
}
void main (){
OSCCON = 0x50 ;// clock 2MHz
__delay_ms (10);
TRISA = 0X02;
TRISB = 0X00;
PORTA = 0X00;
PORTB = 0X00;
ANSEL = 0b00000010; // ADC en RA1
ADCON0 = 0b10001001; // clock 2 mhz dividido 64 = 31250
ADCON1 = 0b11000000; // 32us de tiempo de adquisicion
INTCON = 0b01000000; // interrupciones habilitadas ADC
PIR1bits.ADIF = 0;
PIE1bits.ADIE = 1;
INTCONbits.GIE = 1;
__delay_ms (30);
ADCON0 = 0b10001101 ;
while (1){
__delay_ms(10);
result = ADRESL * 1275;
ADCON0 = 0b10001101;
}
// el siguiente bloque se coloca si se quiere realizar manualmente la consulta del adc
//while (1){
//if (ADCON0 == 0b10001001) { // si completo la conversion , entra en el ciclo y toma los valores del ADC
//high = ADRESH;
//low = ADRESL;
//PORTB = low;
//}
//__delay_ms (30);
//ADCON0 = 0b10001101;
}//}
Creo que la variable "result" está mal definida... Es de tipo char y solo tiene 8 bits de extensión... Al multiplicarla por 1275 se desborda totalmente.
Podrías probar definiendo esta variable con:
unsigned long int result
Saludos.
Primero sacar lo de la interrupcion poniendo ADRESL y ADRESH a 0. Ya que es son 2 cosas que pueden hacer que te de 0. Eso y que leas efectivamente 0 del ADC. (Al final hay mas sobre esto)
Y ahora vamos con el tema de la multiplicacion.Código: C
result = ((unsigned short long int)ADRESL * 1275)>>8 ;
O si utilizas stdint, ( personalmente no me gusta la opcion anterior )Código: C
result = ((uint24_t)ADRESL * 1275)>>8 ;
Esto hace que ADRESL sea tomado como una variable de 24bits y cuando se multiplica el resultado entre por completo. luego el shift y se guarda.
De no haberlo realizado de esa forma ADRESL se promociona al tipo de 1275, es decir un int ( 16 bits ). Y se produce la multiplicacion en 16bits
Por ejemplo, con ADRESL = 250, y result siendo de 32bits sin signo para que pueda tener ese valorCódigo: C
result = (ADRESL * 1275);
Lo obtenido es : result = 0xFFFF.E418
Como la multipliacion en 16bits ( sin signo ) supera los 16bits se trunca a 16 quedando 0xE418, lo cual es con signo, al pasarlo a uno de 32 bits se promociona de nuevo pero se promociona a lo mas proximo y que pueda representar el valor, lo cual es: 24/32 bits con signo y se extiende el signo por eso las 0xFFFF, si repetimos el mismo trabajo con esta formula:Código: C
result = ((uint24_t)ADRESL * 1275);
El resultado es distinto, por que esta ves el mas grande es el de uint24_t, como resultado tenes que result tiene un valor de = 0x0003.E418.
Ahora si incorporamos todo.Código: C
result = ((uint24_t)ADRESL * 1275)>>8;
Queda en 0x03E4 , que es el valor buscado.
Optimizacion
- Y ahora llega el momento de la optimizacion... Es necesario hacer ese calculo ? 1275 / 256 = 4.98
¿Crees que le estarias errando por mucho al hacerlo directamente asi?:Código: C
result = ((uint16_t)ADRESL * 5); resultado = ((uint16_t)variable << 2) + variable;
Obviamente el maximo error se va a dar cuando ADRESL tenga un valor igual a 255.
Con tu formula : 0x4F6 - 350 ciclos en PIC sin multiplicador
Con 5 en la formula: 0x4FB ( Maximo error 5 ) - 219 ciclos
Con shift de 2 + ADRESL : 0x4FB (Mismo error que con 5) - 17 ciclos ( Lo que hago es multiplicar por 4 y sumarle 1, en resume igual que multiplicar por 5 )
Error en tomar el valor ?
- Y por otra parte .. por que estas tomando ADRESL cuando tu ADC es de 10 bits ? , si quisieras realmente reducir tu numero a un valor de 8 bits, deberias usar justificado izquierda y tomar ADRESH.
Error en activar el ADC ?
- Estaria bueno que dijeras el PIC que posees, por que normalmente el bit de GO ( que activa la conversion ) esta en el bit 1 el cual nunca activas:
ADCON0 = 0b10001101;
Igual podrias hacer:Código: C
ADCON0bits.GO = 1; // Activo conversion while(ADCON0bits.GO == 1); // Espero que termine.
Por lo que puede ser que no estes activando la conversion del ADC y por lo tanto siempre leas 0. Que es una de las 2 causas que dije al comienzo de por que puede dar 0.