Configuring the PIC18F4550 10-bit ADC module
Jul 20 2011 06:57 PM | Robotics Guy in PIC18 Microcontrollers
As always, make sure you have a copy of the datasheet for your PIC. Here's a link to the PIC18F4550 datasheet.
The Analog to Digital Converter on your PIC microcontroller is an incredibly useful tool. Using the ADC, you can interface all sorts of wonderful peripherals, such as potentiometers, gyroscopes, accelerometers, and many other types of sensors. The ADC takes a VSS-VDD (0-5V on my PIC mcu) DC signal as its input and then converts the voltage to a digital value. Using the 10-bit ADC, this means that a voltage level of 0V should be read as 0, and 5V as 1023 (2^10 - 1). Some PIC microcontrollers also have 12-bit ADC modules, allowing for a much higher resolution. Depending on the number of pins on the microcontroller, it has between 10 and 13 analog inputs.
The ADC module on the PIC18F4550 has five registers associated with it (page 267 in the datasheet):
- Result High Register (ADRESH)
- Result Low Register (ADRESL)
- Control Register 0 (ADCON0)
- Control Register 1 (ADCON1)
- Control Register 2 (ADCON2)
ADCON0bits.ADON = 0; // Disable A/D module ADCON0bits.CHS0 = 0; // Select channel 0 (AN0) ADCON0bits.CHS1 = 0; ADCON0bits.CHS2 = 0; ADCON0bits.CHS3 = 0;
Only one channel (I/O pin) may be sampled at one time. The CHS3:CHS0 (CHannel Select) bits are used to select the channel that will be sampled when the GO_DONE bit is set. I'm only using one analog input and so I decided to use channel 0 (AN0), as this will leave the rest of the I/O pins available as digital inputs or outputs. I choose channel 0 by clearing bits CHS3:CHS0 and, since I'm not using any other channels, I will always leave them cleared. If you use multiple channels, just change the CHS3:CHS0 bits to select the channel you want to sample, then set the GO_DONE bit to start the sample/conversion process.
Next is the ADCON1 register. This register controls which I/O pins are analog and which are digital. As previously metioned, I chose to use channel 0 so that the rest of the pins would be available as digital I/O. Two bits in the register also determine what the voltage reference source is. A table in the datasheet shows the different options:

ADCON1 configuration:
ADCON1bits.VCFG1 = 0; // Use VSS for Vref- source ADCON1bits.VCFG0 = 0; // Use VDD for Vref+ source ADCON1bits.PCFG0 = 0; // Make AN0 pin analog and all others digital ADCON1bits.PCFG1 = 1; ADCON1bits.PCFG2 = 1; ADCON1bits.PCFG3 = 1;
The last configuration register is ADCON2. The ADFM bit determines whether the result in the ADRESH:ADRESL byte pair is right or left justified.

I prefer to make the result right justified. Everything else is little endian, so why shouldn't the A/D result be? Actually, there is a valid reason for having left justification. If you only wanted 8-bits from the 10-bit result, then you could use left justification and only use the ADRESH byte, effectively discarding the two least significant bits.
Now the A/D conversion clock needs to be set. The time to convert one bit is called TAD and must be greater than the minimum TAD time, shown below in Table 28-29:

As you can see, the minimum TAD time is 0.8 μs. TAD can be configured to equal:
- 2 TOSC
- 4 TOSC
- 8 TOSC
- 16 TOSC
- 32 TOSC
- 64 TOSC
If you don't feel like doing the math, then table 21-1 is an easy way to determine what the A/D conversion clock bits should be set to:

Finally, the three bits selecting the acquisition time need to be configured. You can find the formula to calculate the acquisition time in the datasheet, but it's probably sufficient to say that it needs to be about 2.5 μs. The acquisition time determines how long the channel is sampled before starting the conversion. The acquisition time can be one of the following options:
- 0 TAD
- 2 TAD
- 4 TAD
- 6 TAD
- 8 TAD
- 12 TAD
- 16 TAD
- 20 TAD
ADCON2 configuration:
ADCON2bits.ADFM = 1; // A/D result is right justified ADCON2bits.ACQT0 = 1; // Acquisition time ADCON2bits.ACQT1 = 0; ADCON2bits.ACQT2 = 0; ADCON2bits.ADCS0 = 0; // A/D conversion clock ADCON2bits.ADCS1 = 1; ADCON2bits.ADCS2 = 1;
Don't forget to set the TRIS bits corresponding to your analog input pins:
TRISAbits.TRISA0 = 1; // input
The last step is to enable the A/D module:
ADCON0bits.ADON = 1; // Enable A/D module
Now the A/D module on your PIC18F4550 should be configured! All that's left is to actually sample the analog input by setting the GO_DONE bit, waiting for the GO_DONE bit to be cleared, and save the result by reading the ADRESH:ADRESL pair (or ADRES).
One final thing that should be noted is that the maximum recommended input impedance is 2.5KΩ . So if you're using a 10K or 100K potentiometer, for instance, you need to put a buffer amplifier (also called a "line follower") between the input pin and the signal source.












0 Comments