Pic 16f688 adc

Thread Starter

nerdegutta

Joined Dec 15, 2009
2,689
Hi.

I got stuck and maybe blind. I have an LED connected to RA2, and a POT connected to RC3/AN7. The idea is that the LED will flash at different rates according the to ADC reading.

Rich (BB code):
// ADC test program

#include <htc.h>

#define _XTAL_FREQ 4000000
#define led RA2

unsigned int adc_raw;

// Configuration
__CONFIG(FOSC_XT &		// Crystal/resonator on RA4 & RA5
WDTE_OFF &				// Watchdog timer off
PWRTE_OFF & 			// POwer up timer enable off
MCLRE_OFF &				// MCLR pin function is digital input, MCLR internally tied to VDD
CP_OFF &				// Code protection off
CPD_OFF &				// Data code protection off
BOREN_OFF & 			// Brown out off
IESO_OFF & 				// Internat switchover off
FCMEN_ON);				// Fail-Safe clock monitor on

void adc_init(void)
{
ADCON0bits.ADFM = 0;		// Left justified
ADCON0bits.VCFG = 0;		// VDD as reference
ADCON0bits.CHS = 0b111;			// Analog channel set to AN7
ADCON0bits.ADON = 1;		// ADC is enabled
ADCON1bits.ADCS = 0b001;		// ADC Conversion Clock Select bit

} // End adc_init

void adc_read(void)
{

ADCON0bits.GO = 1;
__delay_us(10);
	while(ADCON0bits.nDONE);
} // End adc_read

void main (void)
{
CMCON0	= 0x07;				// Turn off comparators
ANSEL	= 0b00001000;		// Analog input on RC3/AN7
ADCON1	= 0b01000000;		// Conversion Clock

TRISA = 0b00000000;
TRISC = 0b00001000;

led = 0;

adc_init();

while (1)
{
	adc_read();
adc_raw = ADRESH << 8;
adc_raw |= ADRESL;

if (adc_raw <= 512)
	{
	led = 1;
	__delay_ms(500);
	led = 0;
	__delay_ms(500);
	}
else
{
	led = 1;
	__delay_ms(100);
	led = 0;
	__delay_ms(100);
}
}
}
When I run the code on the chip, the LED flashes at the fastest rate, regardless of the POT.

I need some advice.
 

JohnInTX

Joined Jun 26, 2012
4,787
adc_read(); adc_raw = ADRESH << 8; adc_raw |= ADRESL;
For a raw count of 512, ADRESH will be B'10000000' and ADRESHL will be B'00000000' which is 8000h. If you shift ADRESH, you lose the data and read 0000h.
 

JohnInTX

Joined Jun 26, 2012
4,787
Right justified range:
Each ADC count = 1
Range is 0000h-03ffh (0-1023 - 10 bits)

When left justified, you effectively multiply the two byte result by 2^6:
Each ADC count = 64 (because of the implied shift resulting from the left justify)
Range is 0000h - FFC0h

Regardless of how you justify the number you can put it into an unsigned int by
adc_raw = (unsigned int)ADRESH;
adc_raw = adc_raw *256 (or <<8); //shift it up 8 bit)
adc_raw += (unsigned int)ADRESL;

You now have the bit pattern from ADRESH:ADRESL in the int. What its value is is determined by whether its left (result*64) or right (result*1) justified. Note that the int must be unsigned for left justified because the MSbit is the sign in a signed int. Left justified ADC values more than half-scale will be negative in a signed int (because the MSbit==1).
 

MMcLaren

Joined Feb 14, 2010
861
Shouldn't the ADFM bit be set to '1' for right justification?

I ended up borrowing the sequence used in the library code for a couple different high level languages. Those functions turn the ADC module on before reading, wait for acquisition, start the conversion, turn the ADC module off, then copy the ADC result. Here's a snippet from a 16F688 BoostC program (below).

Good luck on your project.

Cheerful regards, Mike

Rich (BB code):
  /*                                                                *
   *  initialize ADC module for AN2(RA2)                            *
   *                                                                */
     ansel = 0b00000100;        // RA2/AN2 analog, others digital
     adcon0 = 0b10000000;       // right justified adc result
     adcon1 = 0b01010000;       // Fosc/16 conversion clock
Rich (BB code):
   int getadc(char channel)     // channel, 0..7
   { int result;                //
     adcon0.CHS0 = channel.0;   // select channel
     adcon0.CHS1 = channel.1;   //   "
     adcon0.CHS2 = channel.2;   //   "
     adcon0.ADON = 1;           // turn ADC on
     delay_us(20);              // acquisition delay
     adcon0.GO_DONE = 1;        // start conversion
     while(adcon0.GO_DONE);     // wait for conversion complete
     adcon0.ADON = 0;           // turn ADC off
     result = adresh << 8;      // collect ADC reading
     result |= adresl;          // 
     return result;             // 0..1023
   }
 

Thread Starter

nerdegutta

Joined Dec 15, 2009
2,689
Thanks a lot, guys.

I got it working, and posting the program for others to see.

Rich (BB code):
// ADC test program

#include <htc.h>

#define _XTAL_FREQ 4000000
#define led RA2

unsigned int adc_raw;

// Configuration
__CONFIG(FOSC_XT &		// Crystal/resonator on RA4 & RA5
WDTE_OFF &				// Watchdog timer off
PWRTE_OFF & 			// POwer up timer enable off
MCLRE_OFF &				// MCLR pin function is digital input, MCLR internally tied to VDD
CP_OFF &				// Code protection off
CPD_OFF &				// Data code protection off
BOREN_OFF & 			// Brown out off
IESO_OFF & 				// Internat switchover off
FCMEN_ON);				// Fail-Safe clock monitor on

// Functions
void adc_init(void)
{
ADCON0bits.ADFM = 1;		// Right justified
ADCON0bits.VCFG = 0;		// VDD as reference
ADCON0bits.CHS = 0b111;		// Analog channel set to AN7
ADCON0bits.ADON = 1;		// ADC is enabled
ADCON1bits.ADCS = 0b001;	// ADC Conversion Clock Select bit
} // End adc_init

void adc_read(void)
{
	ADCON0bits.GO = 1;
	__delay_us(10);
	while(ADCON0bits.nDONE);
} // End adc_read

// Main program
void main (void)
{
CMCON0	= 0x07;				// Turn off comparators
ANSEL	= 0b00001000;		// Analog input on RC3/AN7

TRISA = 0b00000000;
TRISC = 0b00001000;

led = 0;

adc_init();

while (1)
{
	adc_read();
	adc_raw = (unsigned int) ADRESH;
	adc_raw = adc_raw*256;
	adc_raw += (unsigned int) ADRESL;

if (adc_raw <= 512)
	{
	led = 1;
	__delay_ms(500);
	led = 0;
	__delay_ms(500);
	}
else
{
	led = 1;
	__delay_ms(100);
	led = 0;
	__delay_ms(100);
}
}
}
Have a wonderful Saturday evening. (I'm wondering if the Saturday Nite Live show from Fort Lauderdale is sending tonight?)
 

t06afre

Joined May 11, 2009
5,934
First Hi-Tech C is Little-endian. In a Little-endian system. A two byte variable like a typical ADC result the First byte (lowest address) will hold the least significant byte. So set the ADC result byte order correct. Second if you look at the 16f688 header file. I am quite sure that HI-Tech have defined a 16 bit variable for the ADC result. Given that ADFM bit is set to one. It should just be plug a play. I am not on a computer with HI-Tech installed so I can not help more than this. But if you are in doubt you can post a snippet of your PIC header file.
The common way to join bytes to say an int or long. Is to use the Union Declaration. I can see you have solved your problem. But it is kind of cycle wasting ;)
 

t06afre

Joined May 11, 2009
5,934
First Hi-Tech C is Little-endian. In a Little-endian system. A two byte variable like a typical ADC result the First byte (lowest address) will hold the least significant byte. So set the ADC result byte order correct. Second if you look at the 16f688 header file. I am quite sure that HI-Tech have defined a 16 bit variable for the ADC result. Given that ADFM bit is set to one. It should just be plug a play. I am not on a computer with HI-Tech installed so I can not help more than this. But if you are in doubt you can post a snippet of your PIC header file.
The common way to join bytes to say an int or long. Is to use the Union Declaration. I can see you have solved your problem. But it is kind of cycle wasting ;)
Sorry I was somewhat wrong here. Have not worked much with PIC16F lately and forgot that the ADRES registers in most 16F are NOT consecutive Like in the 18F series. They are most often placed different banks. So coding like this make no sense
Rich (BB code):
volatile unsigned short ADRES @ 0x09E;
So we are left with the union way joining the the two ADRES register to one variable. This is the least cycle consuming way of doing it. I post my code here
Rich (BB code):
// ADC test program
#include <htc.h>
#define _XTAL_FREQ 4000000
#define led RA2
union {unsigned short ADRES;
            char ad_hi_low[2];
          } ad_data;
 
// Configuration
__CONFIG(FOSC_XT &  // Crystal/resonator on RA4 & RA5
WDTE_OFF &    // Watchdog timer off
PWRTE_OFF &    // POwer up timer enable off
MCLRE_OFF &    // MCLR pin function is digital input, MCLR internally tied to VDD
CP_OFF &    // Code protection off
CPD_OFF &    // Data code protection off
BOREN_OFF &    // Brown out off
IESO_OFF &     // Internat switchover off
FCMEN_ON);    // Fail-Safe clock monitor on
void adc_init(void)
{
ADCON0bits.ADFM = 1;  // Right justified!!!!!
ADCON0bits.VCFG = 0;  // VDD as reference
ADCON0bits.CHS = 0b111;   // Analog channel set to AN7
ADCON0bits.ADON = 1;  // ADC is enabled
ADCON1bits.ADCS = 0b001;  // ADC Conversion Clock Select bit
} // End adc_init
void adc_read(void)
{
ADCON0bits.GO = 1;
__delay_us(10);
 while(ADCON0bits.nDONE);
} // End adc_read
void main (void)
{
CMCON0 = 0x07;    // Turn off comparators
ANSEL = 0b00001000;  // Analog input on RC3/AN7
ADCON1 = 0b01000000;  // Conversion Clock
TRISA = 0b00000000;
TRISC = 0b00001000;
led = 0;
adc_init();
while (1)
{
 adc_read();
  ad_data.ad_hi_low[0]=ADRESL;
 ad_data.ad_hi_low[1]=ADRESH;
if ( ad_data.ADRES <= 512)
 {
 led = 1;
 __delay_ms(500);
 led = 0;
 __delay_ms(500);
 }
else
{
 led = 1;
 __delay_ms(100);
 led = 0;
 __delay_ms(100);
}
}
}
 
Top