Read a Potentiometer with the PIC16F877A ADC in PICSimLab
Analog-to-digital conversion is where a simulator earns its keep: you can turn a virtual knob and watch the digital result change instantly. PICSimLab’s Potentiometers spare part provides four potentiometers, and the PIC16F877A has a built-in ADC — this tutorial connects one to the other and shows the converted value on LEDs.
What you need
- PICSimLab 0.9.3 — see the download page.
- MPLAB X IDE and the XC8 compiler.
- Board: McLab2 or PICGenios, microcontroller PIC16F877A.
- One Potentiometers spare part (Inputs category) and one LEDs spare part (Outputs category).
Wiring used in this tutorial (example)
- Potentiometer 1 output → AN0 (pin RA0), the ADC channel 0 input
- LEDs spare part → PORTD, showing the top 8 bits of the 10-bit result
Steps
1. Generate the project and add the parts
Create the MPLAB X project for the PIC16F877A with the Code Project Wizard. In PICSimLab open Modules → Spare parts, add Inputs → Potentiometers and Outputs → LEDs, and connect them in Properties as listed above. PICSimLab also offers a Potentiometers (Rotary) variant — either works for this exercise.
2. Configure the ADC
Two registers matter. ADCON1 = 0x8E selects right-justified results and configures AN0 as an analog input with VDD/VSS as references on the PIC16F877A. ADCON0 selects the ADC clock (Fosc/32 in the example), channel AN0 and switches the module on. RA0 must stay an input, so TRISA keeps bit 0 set.
3. Convert and display
Setting GO/DONE starts a conversion; the firmware polls the bit until it clears, then combines ADRESH:ADRESL into a 10-bit value. The example shows the top 8 bits on PORTD LEDs, which is enough to see the value sweep from 0x00 to about 0xFF as you drag the potentiometer.
4. Build, load and run
Build in MPLAB X, load the .hex and start the simulation. Drag the first potentiometer in the spare-parts window: the LED pattern should follow the knob smoothly.
Example code (XC8)
// Example: read AN0 with the ADC, show top 8 bits - PIC16F877A, XC8
#pragma config FOSC = HS
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config BOREN = ON
#pragma config LVP = OFF
#pragma config CPD = OFF
#pragma config WRT = OFF
#pragma config CP = OFF
#include <xc.h>
#define _XTAL_FREQ 20000000 // Example: match the board clock
unsigned int adc_read_an0(void) {
__delay_us(30); // acquisition time
ADCON0bits.GO = 1; // start conversion
while (ADCON0bits.GO) { } // wait for GO/DONE to clear
return (unsigned int)((ADRESH << 8) | ADRESL);
}
void main(void) {
TRISA = 0x01; // RA0/AN0 input, rest of PORTA outputs
TRISD = 0x00; // PORTD outputs (LEDs)
ADCON1 = 0x8E; // right-justified, AN0 analog, Vref = VDD/VSS
ADCON0 = 0x81; // Fosc/32, channel AN0, ADC on
while (1) {
unsigned int value = adc_read_an0();
PORTD = (unsigned char)(value >> 2); // top 8 bits to the LEDs
__delay_ms(50);
}
}
Try next
- HD44780 LCD — print the numeric ADC value instead of a bit pattern.
- Oscilloscope basics — probe AN0 and watch the analog level the ADC sees.
- 7-segment display — show the reading as a 0–999 count.
Troubleshooting
The LEDs never change (stuck at 0 or full scale). The potentiometer output is almost certainly not connected to AN0/RA0 in the part’s Properties, or TRISA bit 0 was cleared by other code. Also confirm ADCON1 leaves AN0 as an analog pin — with the wrong PCFG bits the pin reads as digital.
The value jitters by a few counts. A small amount of movement between readings is normal for a 10-bit converter. Average several conversions in code if you need a steadier display.
