Need Help on PIC Functions for A/D converter! Help please :)

Thread Starter

GammaRay86

Joined Dec 9, 2007
14
Hey guys,

I want to know some things about some PIC registers and hopefully some of you can help me.

I'm working on the A/D conversion on the PIC16F684. Here is my code so far:

Rich (BB code):
#pragma chip PIC16F684
#include "delay.h"

void main(void){

int adval, a, b;	// adval is the ADC value
TRISA.0 = 1; 		// Set RA0/AN0 (pin 13) as I/P
TRISC.2 = 0; 		// Set RC2/AN6 (pin 8) as a dig O/P
ANSEL.0 = 1; 		// Set RA0/AN0 (pin 13) as analog I/P
ADCON1 = 0b.0011.0000;  // Clock set to internal Frc

while(1){
//Do the sensor conversion
delay_us(50);			// Set delay of 50 us for sampling (TAD)
ADCON0 = 0b.1000.0011;		// Right justified, Vref = Vdd, 
				// Read from AN0 (pin 13), Go/Done = 1, ADC Enabled
while(ADCON0.1 == 1){}		// A/D conversion started, continue to check Go/Done, when 0 it is completed
				// Bit 1 of ADCON0 is cleared when finished
a = ADRESH.1;			// Read bit 1 of ADRESH into variable a
a = a * 512;			// Multiply "a" by the position of that bit (2^9)
b = ADRESH.0;			// Read bit 0 of ADRESH into variable b
b = b * 256;			// Multiply "b" by the position of that bit (2^8)
adval = ADRESL + a + b;		// ADC value is the sum of ADRESL, a, and b

if(adval <369){			// LED on at  RC2 (pin 8) when AN0 < 1.8V, off > 1.8V
	PORTC = 0b.0000.0100;
}
else {
PORTC = 0b.0000.0000;
}
}
}
This code does not seem to be working. It kinda works in that the LED blinks on and off but not at the 1.8 V like it should. It seems to turn off ONLY at values of like 2 V. But is on ABOVE and BELOW that which my code clearly does not say. So I need to ask some questions.

1. Do I need to set port A to be initially 0? e.g. PORTA = 0?

2. What is the difference between the ANSEL register and the CHS bits on ADCON0?

3. Do I need to set a delay between turning the ADC on and making the Go/Done bit 1?

4. Am I doing the AD value calculation correct assuming left justification? I assume the ADRESH bits should be multiplied by 2^8 (256) and 2^9 (512)?

5. I set the clock to internal RC. Is this okay?

5. ANYTHING anyone can see that is wrong? I saw some code where a comparator was shut off before the ADC. Is this required?

Thanks for any help
 

Markd77

Joined Sep 7, 2009
2,806
1:I don't think so but it would only affect the first conversion, because it isn't turned off. It only needs turning off if you want to save a few microwatts.
(just checked and there is a calculation for Taqc but the example they gave was only 8 microseconds)
2:ANSEL selects which pins are analog input rather than digital input or output, CHS bits choose which of the analog pins the ADC is connected to - there is only 1 ADC but it can be connected to any analog input.
3:No
4:No idea, I don't understand C.
5:probably safe, but see other post
5:Yes, set CMCON0 to 7 to turn off comparators
 
Last edited:

Markd77

Joined Sep 7, 2009
2,806
Oops, answer to the second 5 should be no.
The bits default to 0 and that sets the comparator off but the pins as analog.
7 would set them to digital.
 

Thread Starter

GammaRay86

Joined Dec 9, 2007
14
Thanks for the help Mark!

I wish I could get this to work though, I don't know what's wrong.

Do you know if I have to connect the MCLR pin to the Vdd? I hear this is required, I have tried it but it still doesn't get it to work. But I'd still like to know.

In my TRISC.2 = 0 line, does that really change the RC0 pin to digital output? Because it does change it to output but is it digital by default?
 

Markd77

Joined Sep 7, 2009
2,806
MCLR depends on the configuration, it should be connected to VDD by a resistor if it is configured as a reset.
I think you need a clrf Ansel before your ansel.0 =1 line, the default for ansel is b'11111111'. I should have spotted it earlier.
The bold in the below table is how defaults are shown.
REGISTER 4-3: ANSEL: ANALOG SELECT REGISTER
R/W-1 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1 R/W-1
ANS7 ANS6 ANS5 ANS4 ANS3 ANS2 ANS1 ANS0
bit 7 bit 0
Legend:
R = Readable bit W = Writable bit U = Unimplemented bit, read as ‘0’
-n = Value at POR ‘1’ = Bit is set ‘0’ = Bit is cleared x = Bit is unknown
 

Thread Starter

GammaRay86

Joined Dec 9, 2007
14
If ANSEL is b'11111111' by default, why would I even need to put ANSEL = 1? Wouldn't it be fine to keep as all analog since the datasheet says it does not affect digital output?

Edit: Also, you posted on my other thread:

You don't need a delay before setting the go/done bit. TAD is the clock that the A/D converter runs from. You need to set bits 2:0 of ADCON1 depending on table 9.1.
When the device frequency is greater than 1 MHz, the FRC clock source is only recommended if the
conversion will be performed during Sleep.
The FRC clock is separate from the main system clock.
The while loop checking the go/done bit is correct.
How can I check what my device frequency is? Because if it's greater than 1 MHz it says I should avoid using the FRC clock source. How would I know which one to choose if I don't use the FRC clock source? Thanks again!
 

Thread Starter

GammaRay86

Joined Dec 9, 2007
14
Nevermind, the default device frequency is 4 MHz. I'm going to try the ADC clock source at Fosc/8 and Fosc/16 to see if they make a difference.

My question about the ANSEL still stands though, I don't understand why I need to set it at all.
 

Markd77

Joined Sep 7, 2009
2,806
If ANSEL is b'11111111' by default, why would I even need to put ANSEL = 1? Wouldn't it be fine to keep as all analog since the datasheet says it does not affect digital output?
Sorry, there is no need. I am used to using a 12F675 where ANSEL affects outputs.
 

spinnaker

Joined Oct 29, 2009
7,830
I am just getting started in this whole thing myself so I am a bit ignorant on the subject but perhaps we can stumble through this together.

I don't see you setting ADCON2 anywhere. It makes it a lot easier to set to right justification.

Here is some sample code to read an ADC in high precision:

Rich (BB code):
unsigned int ADC_Convert(void)
{ // start an ADC conversion and return the 8 most-significant bits of the result
    
    unsigned i;

    ADCON0bits.GO_DONE = 1;             // start conversion
    while (ADCON0bits.GO_DONE == 1);    // wait for it to complete
    i = (ADRESH * 256);
    i = ADRESL + i; 
    return  i;               // return high byte of result
}
You might want o pick up one of the PIC demo boards. It makes learning this stuff a lot easier.
 

Thread Starter

GammaRay86

Joined Dec 9, 2007
14
In that code, if you're only wanting an 8-bit result, why do you need both ADRESH and ADRESL. Assuming a left justification, you would only need ADRESH*256 for 8-bits no?
 

spinnaker

Joined Oct 29, 2009
7,830
In that code, if you're only wanting an 8-bit result, why do you need both ADRESH and ADRESL. Assuming a left justification, you would only need ADRESH*256 for 8-bits no?
You do not need to multiply for 8 bits. Just read ADRESH.


Rich (BB code):
unsigned char ADC_Convert(void)
{ // start an ADC conversion and return the 8 most-significant bits of the result
    ADCON0bits.GO_DONE = 1;             // start conversion
    while (ADCON0bits.GO_DONE == 1);    // wait for it to complete
    return ADRESH;                      // return high byte of result
}
You will need to multiply to obtain your actual voltage.


l = ADC_IN * VoltsPerBit

VoltsPerBit = VMaxIn / 256 (or 1024 for 10 bit)
You will then want to multiply that result by 100, 1,000, 10,000 or pretty much whatever you want.

When you display your result, remember to adjust for appropriate decimal point.
 

Thread Starter

GammaRay86

Joined Dec 9, 2007
14
If I wanted to only use the highest 7 bits, would adval = ADRESH - ADRESH.0 work?

Would my VoltsPerBit = VmaxIn / (2^7) in this case?
 

spinnaker

Joined Oct 29, 2009
7,830
Sorry just getting started myself so I am not sure.

Why do you want to use only 7 bits? Why make it so complicated? If you want to use 7 bits then for your formula to work, you would need to shift 1.

Otherwise, I would think it is still 2^ 8.
 
Top