I am working on a school project where I need to read a bunch of analog values and convert them to digital with PIC16F1788(datasheet). I wrote the function using C and xc8 compiler. The problem is when I test it it doesn't behave as It should. When I remove the Potentiometer the LED stays on eventhough everything should be off since there is no analog input anymore. Here is my code thank you for your help.
#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);
}
Update #1 :
based on some of the suggestions I updated my code as follows:
The problems that I am still having are that even when there are no analog iput the led connected to PORTC is stays on. Even when I ground the input using a resistor to ground. That just tells me that there is something wrong with my ReadADC function. I went over my code again and I suspect that there is something wrong with the following lines that I dont get yet:
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)
other then that I changed the code as some of you noted and moved the adc init inside the ReadADC function and I added a couple of lines that made sense to me here it is
#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 # 2
Now The ADC works, but the output is not quite correct. I tested it using 3 LED's Two leds would come on when the voltage is above 2.5 volts and a single LED would light up if it is under 2.5v. The problem is the opposite happens Two leds would come on if the voltage is under and a single one would light up if the voltage is over 2.5V. I also checked my circuit and everything seems correct.
#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;
}
}
}