Skip to content

AVR Digital Inputs: Build a 4-Bit Binary Counter from Scratch!

Are you interested in learning how to create a simple 4-bit binary counter using the ATmega8 microcontroller? This project is perfect for beginners who want to dive into embedded systems and microcontroller programming. In this tutorial, I’ll guide you step-by-step to build a 4-bit binary counter that counts from 0000 to 1111 (0 to 15 in decimal) and displays the output on 4 LEDs.

A 4-bit binary counter is a fundamental digital circuit with a wide range of applications in electronics, embedded systems, and computer science. A 4-bit binary counter is an excellent tool for learning the basics of digital electronics and microcontroller programming. It helps you understand:

• Binary number systems (counting from 0000 to 1111).
• How counters work in digital circuits.
• Microcontroller I/O operations (e.g., controlling LEDs).

Components Required

• ATmega8 Microcontroller
• 4 LEDs
• 4 Resistors (220Ω)
• Push Button
• Resistor (10kΩ)
• Breadboard
• Jumper Wires
• Power Supply (5V)
• AVR programmer (USBasp or similar) for uploading code to the Atmega8

Hardware Setup

Programming Diagram

Circuit Diagram

ATmega8 PinConnection
PD0 (Pin 2)LED1 Anode
PD1 (Pin 3)LED2 Anode
PD2 (Pin 4)LED3 Anode
PD3 (Pin 5)LED4 Anode
PB1(Pin 15)Button with pull-up Configuration
GNDAll LED Cathode (through 220Ω resistor)

Code

#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>

int main(void) {

	DDRD = 0x0F; // Set PD0 to PD3 as output
	DDRB &=~(1<<PB1); // Set PB1 as input
	uint8_t count = 0;

	while (1) {
	
		if (!(PINB & (1 << PB1))) { // Button Pressed
			_delay_ms(200); // Debounce delay
			
			count++;
			
			if (count > 15) {
				count = 0; // Reset counter
			}
			
			PORTD = (PORTD & 0xF0) | (count & 0x0F); // Output binary to LEDs
			while (!(PINB & (1 << PB1))); // Wait for button release
			
		}
		
	}
	
}

Pin Configuration

DDRD = 0x0F; // Set PD0 to PD3 as output
DDRB &=~(1<<PB1); // Set PB1 as input

• DDRD = 0x0F → Sets PD0 to PD3 (lower 4 bits of PORTD) as output for LED connection.

   Binary: 0b00001111

Meaning:

PinMode
PD0Output
PD1Output
PD2Output
PD3Output
PD4-PD7Input (Unused)

• DDRB &= ~(1<<PB1) → Sets PB1 as input for the push button.

Counter Variable

uint8_t count = 0;

This variable will store the binary count (0-15).

Button Press Detection

if (!(PINB & (1 << PB1))) {

This checks if the PB1 button is pressed.

👉 PINB & (1 << PB1) reads the PB1 pin state.
👉 !() inverts the result, so:

0 means the button is pressed.
1 means the button is released.

Debounce Delay

_delay_ms(200);

It waits 200 ms to avoid multiple signals from the button due to mechanical noise (button bouncing).

Counter Increment & Reset

count++;
if (count > 15) {
	count = 0;
}

The counter increases by 1 every button press.
If the counter exceeds 15 (0b1111), it resets to 0.

Binary Output to LEDs

PORTD = (PORTD & 0xF0) | (count & 0x0F);

This line sends the lower 4 bits of count to PD0-PD3.

1. (PORTD & 0xF0)

• 0xF0 in binary → 11110000
• This masks the lower 4 bits (D0-D3) and keeps the upper 4 bits unchanged.
• Suppose PORTD = 10101101

Then:

PORTD    = 10101101
0xF0        = 11110000
———————————————-
Result      = 10100000  (D0D3 become 0, D4D7 remain same)

2. (count & 0x0F)

• 0x0F in binary → 00001111
• This takes only the lower 4 bits of count and ignores the upper bits.

3. Combine with | (OR Operator)

• The upper 4 bits stay the same from (PORTD & 0xF0).
• The lower 4 bits get updated with (count & 0x0F).

Example: If:

PORTD  = 10101101
count     = 00000111

Result:

(PORTD & 0xF0)  = 10100000
(count & 0x0F)     = 00000111
———————————————————-
PORTD                    = 10100111

Button Release Wait

while (!(PINB & (1 << PB1)));

This line holds the program until the button is released.
Without this line, the counter would increase continuously while the button is held down.

Leave a Reply

Your email address will not be published. Required fields are marked *