Summary of PIC16F877 up down counter code and Proteus simulation
This article explains implementing an up/down counter and a digital clock on a PIC16F877(A) using HI-TECH C and Proteus for simulation. Timer0 generates 1 ms interrupts to build ms, sec, min, and hr counters; LCD displays time. Time is configured via three buttons (Set Time, Up, Down) on PORTE (RE0–RE2). A 4 MHz crystal is used. The code uses a simple state machine (NORMAL_STATE, CONFIG_HR_STATE, CONFIG_MIN_STATE, CONFIG_SEC_STATE) to handle normal counting and time configuration.
Parts used in the PIC16F877 up down counter and digital clock:
- PIC16F877A microcontroller
- 16x2 LCD module
- 4 MHz crystal oscillator
- Three push buttons for Set Time, Up, Down (connected to RE0, RE1, RE2)
- One push button for Count/Up-Down switch (SW1)
- Resistors for button pull-ups/pull-downs as required
- Proteus simulator (for circuit simulation)
- MPLAB IDE with HI-TECH C compiler (for code development)
- Power supply for PIC16F877A (Vcc and ground)
This PIC16F877 microcontroller tutorial answers the question,
” How to implement a up down counter using PIC16F877 ? ”
Using PIC16 simulator (Proteus) you can verify this counter code and change it according to your needs. Using one push button (Labeled as Count, as shown in figure below) you can increment (When SW1 switch is towards up position) or decrement (When SW1 switch is towards down position) count value (displayed on LCD) as you desire. This code is written in C language using MPLAB with HI-TECH C compiler. You can download this code from the ‘Downloads‘ section at the bottom of this page.
In this article, it is assumed that you know, how to interface LCD with PIC16F877 microcontroller. If you don’t then please read this page.
The following diagram (made in Proteus) shows the PIC microcontroller circuit diagram.
The above figure was taken after setting time to 03:59:38, timer0 is used as the base for digital clock generation. Timer0 is used here to generate 1msec interrupts. After every 1msec a global variable named msCounter increments. When msCounter reaches a value of 1000 then another global variable named secCounter increments and this process repeats itself. Similarly, when secCounter reaches 60, then minCounter increments. And when minCounter reaches 60 then hrCounter increments. This process continues until hrCounter reaches 24 then all of these variables reset their values. LCD is updated with the new values of hrCounter, minCounter and secCounter after every second.
You can set time using three push buttons attached on RE0, RE1 and RE2 pins (As shown in the above figure). By pressing ‘Set Time‘ button one time, code enters in configuration state. Hours value starts to blink and you can modify it using Up and Down buttons. Pressing Up button increments the value and pressing Down button decrements the value. When you are done setting Hours value, press ‘Set Time‘ button again, then Minutes value will start to blink and you can adjust this value using Up and Down buttons. Similarly, after setting Minutes value, you can press ‘Set Time‘ button again, then Seconds value will start to blink and you can adjust this value using Up and Down buttons. When you are done adjusting the time, then press ‘Set Time‘ button for the last time and this clock will start to work normally.
A crystal of 4MHz value is used in this circuit, which makes this PIC16F877A run at a speed of 1MIPS (Million of instructions per second).
Code
The main function code is shown below.
![]() |
| Figure 2. Main function code for digital clock on PIC16F877A |
In the main function, firstly PORTE pins are made input and ADC is turned off on these pins. Then LCD is initialized using InitLCD() function. Then Timer0 is initialized to generate 1msec interrupts.
After that, in the while(1) loop, there is a state machine running. A variable named State is used to keep track of the current state of the clock. When this code starts, then State has a value of NORMAL_STATE. In the normal state, clock value is incremented after every second and whenever msCounter reaches a value of zero[1], then new values of hrCounter, minCounter and secCounter are updated on the LCD. UpdateTimeCounters() function is called every time and it corrects the values of every counter variable depending upon the value of msCounter, which is incremented in the ISR function of timer0.
In the normal state, code keeps checking RE0 pin value, if ‘Set Time‘ button is pressed then RE0 pin value becomes zero. This is detected using if(!RE0) statement in the code. When ‘Set Time‘ button is pressed then state is changed to ‘CONFIG_HR_STATE‘ (i-e configure hours value state). In this state, Up and Down buttons are constantly monitored and hrCounter value can be adjusted. Similarly, by pressing ‘Set Time’ button again, state is changed to ‘CONFIG_MIN_STATE‘ (i-e configure minutes value state). In this state minCounter value can be adjusted using Up and Down buttons and by pressing ‘Set Time‘ button again, state is changed to ‘CONFIG_SEC_STATE‘ (i-e configure seconds value state). In this state secCounter value can be adjusted using Up and Down buttons. By pressing ‘Set Time‘ button again, state is changed to ‘NORMAL_STATE‘ and clock starts to run normally.
Source : PIC16F877 up down counter code and Proteus simulation
- How does the timer generate time base for the clock?
Timer0 is configured to generate 1 ms interrupts; a msCounter increments each interrupt and when it reaches 1000 a secCounter increments. - Can I set the time using buttons?
Yes; pressing the Set Time button cycles configuration states for hours, minutes, and seconds, and Up and Down buttons adjust values. - What microcontroller clock speed is used?
A 4 MHz crystal is used, giving the PIC16F877A a speed of 1 MIPS. - Does the code use a state machine?
Yes; a State variable tracks NORMAL_STATE, CONFIG_HR_STATE, CONFIG_MIN_STATE, and CONFIG_SEC_STATE to manage operation. - How is the LCD updated?
The LCD is updated every second with hrCounter, minCounter, and secCounter values when msCounter reaches zero condition in the main loop. - How are button inputs configured?
PORTE pins are set as inputs and ADC is turned off on those pins before using them for buttons on RE0, RE1, and RE2. - What happens when secCounter, minCounter, or hrCounter reach their limits?
When secCounter reaches 60 it increments minCounter; when minCounter reaches 60 it increments hrCounter; when hrCounter reaches 24 all counters reset. - How is increment/decrement of counters detected?
Up and Down buttons are monitored in configuration states; pressing Up increments and pressing Down decrements the respective counter.

