Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

PIC16F877 ADC stalled?

Status
Not open for further replies.

MizuRyuu

New Member
Hi. I am trying to make an ADC program to test the ADC function of the PIC16F877 microcontroller. I have got the code to compile, but it doesn't seem to work. The code seem to enter the while(GO) loop but it doesn't exit. This is strange as the Go bit from ADCON0 is suppose to be cleared once the ADC process is finished. So I am wondering either something is stalling the ADC process or the ADC process take over 2 min to run...

#include <system.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

//Target PIC16F877 configuration word
#pragma DATA _CONFIG, _PWRTE_ON & _BODEN_OFF & _WDT_OFF & _LVP_ON & _CPD_OFF & _DEBUG_OFF & _HS_OSC & _CP_OFF

//Set clock frequency
#pragma CLOCK_FREQ 20000000


void interrupt( void )
{


//Handle timer0 interrupt
if( intcon & (1<<T0IF) )
{
clear_bit( intcon, T0IF ); //clear timer 0 interrupt bit
}

//Handle timer1 interrupt
if( pir1 & (1<<TMR1IF) )
{
clear_bit( pir1, TMR1IF ); //clear timer 1 interrupt bit
}

//Handle timer2 interrupt
if( pir1 & (1<<TMR2IF) )
{
clear_bit( pir1, TMR2IF ); //clear timer 2 interrupt bit
}

}

int convert_table[10] = {512,256,128,64,32,16,8,4,2,1};

void main( void )
{
//Configure port A
trisa = 0x10;
//Configure port B
trisb = 0x00;
//Configure port C
trisc = 0x00;
//Configure port D
trisd = 0x00;
//Configure port E
trise = 0x00;

//Configure A/D pins
adcon0 = 0x04;
adcon1 = 0x8E;


//Initialize port A
porta = 0x00;
//Initialize port B
portb = 0x00;
//Initialize port C
portc = 0x00;
//Initialize port D
portd = 0x00;
//Initialize port E
porte = 0x00;
//Set Timer0 mode
clear_bit( option_reg, T0CS ); //configure timer0 as a timer
//Set prescaler assignment
clear_bit( option_reg, PSA ); //prescaler is assigned to timer0
//Set prescaler rate
clear_bit( option_reg, PS2 ); //prescaler rate 1:2
clear_bit( option_reg, PS1 );
clear_bit( option_reg, PS0 );
//Set timer0 source edge selection
set_bit( option_reg, T0SE ); //increment on high-to-low transition on RA4/T0CKI pin

//Set timer 1 prescaler rate
clear_bit( t1con, T1CKPS1 ); //prescaler rate 1:1
clear_bit( t1con, T1CKPS0 );
//Set timer 1 mode
clear_bit( t1con, TMR1ON ); //disable timer 1

//Set timer 2 prescaler rate
clear_bit( t2con, T2CKPS1 ); //prescaler rate 1:1
clear_bit( t2con, T2CKPS0 );
//Set timer 2 postscaler rate
clear_bit( t2con, TOUTPS3 ); //postscaler rate 1:1
clear_bit( t2con, TOUTPS2 );
clear_bit( t2con, TOUTPS1 );
clear_bit( t2con, TOUTPS0 );
//Set timer 2 mode (enable or disable)
clear_bit( t2con, TMR2ON ); //enable timer 2


//Enable interrupts (Timer0)
intcon = 0xA0;
int check;

//Endless loop
while( 1 )
{
adcon0 = 11000101b; //turn on ADC
while (NOT_DONE) //waiting until ADC conversion is finished
{
portd = 11111111b;
}
portd = 00000000b; //test that ADC exits since Go bit suppose to change to 0 when ADC finishes
if ((ADRESL & 0x01b)== 0x01b) check += convert_table[0]; // convert binary to digital
if ((ADRESL & 0x02b)== 0x02b) check += convert_table[1];
if ((ADRESL & 0x04b)== 0x04b) check += convert_table[2];
if ((ADRESL & 0x08b)== 0x08b) check += convert_table[3];
if ((ADRESL & 0x10b)== 0x10b) check += convert_table[4];
if ((ADRESL & 0x20b)== 0x20b) check += convert_table[5];
if ((ADRESL & 0x40b)== 0x40b) check += convert_table[6];
if ((ADRESL & 0x80b)== 0x80b) check += convert_table[7];
if ((ADRESL & 0x01b)== 0x01b) check += convert_table[8];
if ((ADRESL & 0x02b)== 0x02b) check += convert_table[9];
if (check > 511) //if equal or bigger than 2.5 Volts
{
portb = 11111111b;
}
else
{
portb = 00000000b;
}
}
}
 
Code:
adcon0 = 11000101b; //turn on ADC
while (NOT_DONE) //waiting until ADC conversion is finished
{
     portd = 11111111b;
}
I just glanced at the code.

What is NOT_DONE defined as ? I do not see it in the code.
Do not see the While(GO) mentioned in the text.

Create a test case/program with just the ADC setup and the conversion loop.
Then try it. Just want to be sure you do not have an unwanted interaction between the ADC code and the interrupt driven code.

If it still fails post the new code and we can take a look at it.
 
Last edited:
Both GO and NOT_DONE are assigned to bit 2 of ADCON0. It shouldn't be the issue.

The main problem is that RA0, which he's using as an analog input, is not configured as input in the TRISA register.

Also, the configuration of the analog module could be far better.
 
adcon0 = 11000101b; //turn on ADC
The GO/DONE bit should NOT be set in the same instruction that turns on the A/D.
Here's the sequence you should use:
Configure the IO port for analog inputs.
Configure the A/D module.
Wait the required acquisition time if using multiple A/D inputs.
Start conversion by setting GO/DONE bit in ADCON0
Wait for A/D conversion to complete by polling for the GO/DONE bit to be cleared.
 
kchriste said:
The GO/DONE bit should NOT be set in the same instruction that turns on the A/D.
Here's the sequence you should use:
Configure the IO port for analog inputs.
Configure the A/D module.
Wait the required acquisition time if using multiple A/D inputs.
Start conversion by setting GO/DONE bit in ADCON0
Wait for A/D conversion to complete by polling for the GO/DONE bit to be cleared.

The GO bit is the instruction that turns on the A/D

I have changed port A so it is input, but I am still unable to get it to work.
I have checked Port B and Port D, and they are giving me 0V at all time. So it doesn't seem like the code is even entering the do loop... which is weird as it should go through that loop at least once before exiting....

#include <system.h>

//Target PIC16F877 configuration word
#pragma DATA _CONFIG, _PWRTE_ON & _BODEN_OFF & _WDT_OFF & _LVP_ON & _CPD_OFF & _DEBUG_OFF & _HS_OSC & _CP_OFF

//Set clock frequency
#pragma CLOCK_FREQ 20000000


void interrupt( void )
{


//Handle timer0 interrupt
if( intcon & (1<<T0IF) )
{
clear_bit( intcon, T0IF ); //clear timer 0 interrupt bit
}

//Handle timer1 interrupt
if( pir1 & (1<<TMR1IF) )
{
clear_bit( pir1, TMR1IF ); //clear timer 1 interrupt bit
}

//Handle timer2 interrupt
if( pir1 & (1<<TMR2IF) )
{
clear_bit( pir1, TMR2IF ); //clear timer 2 interrupt bit
}

}

void main( void )
{
//Configure port A
trisa = 0x2F;
//Configure port B
trisb = 0x00;
//Configure port C
trisc = 0x00;
//Configure port D
trisd = 0x00;
//Configure port E
trise = 0x17;

//Configure A/D pins
adcon0 = 0x41;
adcon1 = 0x80;

//Set Timer0 mode
clear_bit( option_reg, T0CS ); //configure timer0 as a timer
//Set prescaler assignment
clear_bit( option_reg, PSA ); //prescaler is assigned to timer0
//Set prescaler rate
clear_bit( option_reg, PS2 ); //prescaler rate 1:2
clear_bit( option_reg, PS1 );
clear_bit( option_reg, PS0 );
//Set timer0 source edge selection
set_bit( option_reg, T0SE ); //increment on high-to-low transition on RA4/T0CKI pin

//Set timer 1 prescaler rate
clear_bit( t1con, T1CKPS1 ); //prescaler rate 1:1
clear_bit( t1con, T1CKPS0 );
//Set timer 1 mode
clear_bit( t1con, TMR1ON ); //disable timer 1

//Set timer 2 prescaler rate
clear_bit( t2con, T2CKPS1 ); //prescaler rate 1:1
clear_bit( t2con, T2CKPS0 );
//Set timer 2 postscaler rate
clear_bit( t2con, TOUTPS3 ); //postscaler rate 1:1
clear_bit( t2con, TOUTPS2 );
clear_bit( t2con, TOUTPS1 );
clear_bit( t2con, TOUTPS0 );
//Set timer 2 mode (enable or disable)
clear_bit( t2con, TMR2ON ); //enable timer 2


//Enable interrupts (Timer0)
intcon = 0xA0;
int check;

//Endless loop
while( 1 )
{
adcon0 = 01000101b; //turn on ADC
do //waiting until ADC conversion is finished
{
portd = 11111111b;
}while (NOT_DONE);
portd = 00000000b; //test that ADC exits since Go bit suppose to change to 0 when ADC finishes
binary to digital
check = (ADRESH<<8) | ADRESL;
if (check.9 == 1) //if equal or bigger than 2.5 Volts
{
portb = 11111111b;
}
else
{
portb = 00000000b;
}
}
}
 
Last edited:
The GO bit is the instruction that turns on the A/D
Wrong. The GO/DONE bit (bit2) starts the A/D conversion. Bit0 (ADON) of adcon0 turns the A/D converter on/off.
So instead of:
//Configure A/D pins
adcon0 = 0x41;
adcon1 = 0x80;
You should:
Code:
//Configure A/D pins
adcon0 = 0x81;
adcon1 = 0xC0;
Because you are using a 20Mhz clock you must divide it by 64 or the ADC will run with too fast of a clock.
And instead of:
while( 1 )
{
adcon0 = 01000101b; //turn on ADC
do //waiting until ADC conversion is finished
{
You should do this:
Code:
while( 1 )
{
adcon0 = 10000101b; //start ADC conversion
do //waiting until ADC conversion is finished
{
 
Nigel Goodwin said:
Can't help you with C, but the ADC is fully explained in my tutorials using assembler.

Heck, the ADC is pretty well explained in Microchip's own datasheet. I find that their products are pretty well documented, wouldn't you agree? They are nice enough to tell you the steps to use a function and sometimes provide sample code. Nice of them.
 
ArtemisGoldfish said:
Heck, the ADC is pretty well explained in Microchip's own datasheet. I find that their products are pretty well documented, wouldn't you agree? They are nice enough to tell you the steps to use a function and sometimes provide sample code. Nice of them.

But not very clearly documented, and a LOT of people struggle using the A2D, my tutorial goes through the design process step by step to make it easy.
 
kchriste said:
Wrong. The GO/DONE bit (bit2) starts the A/D conversion. Bit0 (ADON) of adcon0 turns the A/D converter on/off.
So instead of:

You should:
Code:
//Configure A/D pins
adcon0 = 0x81;
adcon1 = 0xC0;
Because you are using a 20Mhz clock you must divide it by 64 or the ADC will run with too fast of a clock.
And instead of:

You should do this:
Code:
while( 1 )
{
adcon0 = 10000101b; //start ADC conversion
do //waiting until ADC conversion is finished
{
About your first advise, the adcon1 bit 7 to 5 are unimplmented, so changing 0x80 to 0xC0 would not actually affect the ADC

Thank you for the Clock tip...
 
About your first advise, the adcon1 bit 7 to 5 are unimplmented, so changing 0x80 to 0xC0 would not actually affect the ADC
Are you using the 16F877 or the 16F877A? In both, bit7 is implemented in adcon1 as the ADC result justification. Bit6 is implemented on the A version only and effects the ADC clock rate.
 
kchriste said:
Are you using the 16F877 or the 16F877A? In both, bit7 is implemented in adcon1 as the ADC result justification. Bit6 is implemented on the A version only and effects the ADC clock rate.

sry... i meant bit 6 to 4.... and i am using the 16F877....


The code seem to go through the while(1) loop once, but it won't loop back...
 
Last edited:
I think at this point you should simplify your code. Disable interrupts. Instead of the fancy shift and compare; (if (check.9 == 1) //if equal or bigger than 2.5 Volts) just do something simple like:
portb = ADRESL
Also use another port, such as port C or D, for your code debugging so you can see where in your code it really stops rather than sharing this port with your ADC data output on portb.
 
this is my current code: (using SourceBoost C in case i haven't mentioned that yet... )

#include <system.h>

//Target PIC16F877 configuration word
#pragma DATA _CONFIG, _PWRTE_OFF & _BODEN_OFF & _WDT_OFF & _LVP_ON & _CPD_OFF & _DEBUG_OFF & _HS_OSC & _CP_OFF

//Set clock frequency
#pragma CLOCK_FREQ 20000000

void main( void )
{
//Configure port A - E
trisa = 0x2F;
trisb = 0x00;
trisc = 0x00;
trisd = 0x00;

//Initialize port A - E
portd = 0x00;
porta = 0x00;
portc = 0x00;
portb = 0x00;

//Configure A/D pins
adcon0 = 0x89; //Set clock Fosc/32, ch1(AN0), GO = 0, ADC Operating
adcon1 = 0x80; //Right Justified, All ADC pins on

volatile unsigned int ADC_VALUE;

while( 1 ) //Endless loop
{
adcon0 = 0x8D; //turn on ADC
do //waiting until ADC conversion is finished
{
portd = 11111111b;
}while ((adcon0 & 0x04) == 0x04);
portd = 00000000b; //test that ADC exits since Go bit suppose to change to 0 when ADC finishes
ADC_VALUE = ADRESL; /* Getting HSB of CCP1 */
ADC_VALUE += (ADRESH << 8); /* Getting LSB of CCP1 */
if (ADC_VALUE.9 == 1) //if equal or bigger than 2.5 Volts
{
portb = 00000000b;
}
else
{
portb = 11111111b;
}
portc = 11111111b;
//portb = ADRESH;
portc = 00000000b;
}
}

I got it to loop correctly now, and it also seem to enter and exit the do while loop correctly too. However, the port b assignment doesn't seem to be working, regardless of whether i set my input voltage above or below 2.5V.... All it does is stay 0s... My guess is that something went wrong with the conversion process, or the process didn't happen at all. However, I couldn't see why it shouldn't. I have set up the ADC, and then enable the GO bit to start to process....
 
Try adding a delay for the acquisition time:

while( 1 ) //Endless loop
{
Delay100uS
adcon0 = 0x8D; //turn on ADC
do //waiting until ADC conversion is finished
{

What happenes when you copy to portb with the results from ADRESL only?
 
Last edited:
when i copy the ADRESL to port B, the output is different, but it still stay constant regardless of what i change the analog input to...

and i don't get what you mean for the delay thing.... In your example, shouldn't the delay be after open while loop bracket??

I tried changing it to:

while( 1 ) //Endless loop
{
delay_ms(1);
adcon0 = 0x8D; //turn on ADC
do //waiting until ADC conversion is finished
{
portd = 11111111b;
}while ((adcon0 & 0x04) == 0x04);

but it still doesn't work...

i tried adding delays after pretty much every ADC step too... that didn't seem to do anything either...
 
Last edited:
when i copy the ADRESL to port B, the output is different, but it still stay constant regardless of what i change the analog input to..
.
What do you get on the output? Are all bits high, low, or are some high and some low?
and i don't get what you mean for the delay thing.... In your example, shouldn't the delay be after open while loop bracket??
Opps. That was a typo. Yes, after the while loop bracket. :eek:
 
kchriste said:
.
What do you get on the output? Are all bits high, low, or are some high and some low?

Opps. That was a typo. Yes, after the while loop bracket. :eek:
Some high some low...

tried that... didn't work either...

i'm going insane... i really need to get the adc working for a project i am doing.... but it doesn't even seem like i can even turn it on ><;;;
 
Some high some low...
Did they stay steady as the input voltage on AN0 was varied? (Oops edit: I see that they did above) I assume you measured the voltage directly on pin 2 of the F877 and it varies from 0 to 5V via a pot or something.
ADC_VALUE = ADRESL; /* Getting HSB of CCP1 */
Is this just a typo? Why does the comment refer to CCP1 which is the Capture/Compare/PWM Register1 of timer1? I assume ADRESL etc are defined in system.h
Can you post a pic/schematic of your hardware setup?
 
Last edited:
kchriste said:
Did they stay steady as the input voltage on AN0 was varied? (Oops edit: I see that they did above) I assume you measured the voltage directly on pin 2 of the F877 and it varies from 0 to 5V via a pot or something.

Is this just a typo? Why does the comment refer to CCP1 which is the Capture/Compare/PWM Register1 of timer1? I assume ADRESL etc are defined in system.h
Can you post a pic/schematic of your hardware setup?

I am supplying the input through a power supply unit where i can adjust the voltage...

sry... the whole CCP1 was from a sample program i found on the web when i was looking for help... however, the ADRESH and ADRESL is defined in both the .h files and also the datasheet.....

my pic is currently in a development board... this should be the schematic for the board... these are the only schematic that came with the development board...


**broken link removed**

**broken link removed**

thank you for your help so far....
 
Last edited:
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top