The ATmega8 is a popular microcontroller from the AVR family by Microchip (formerly Atmel). It has 23 GPIO (General Purpose Input/Output) pins divided across three ports: PortB, PortC, and PortD. Below is a basic tutorial on how to configure and use GPIO pins on the ATmega8.
1. GPIO Ports in ATmega8
• PortB (PB0-PB7): 8 GPIO pins
• PortC (PC0-PC6): 7 GPIO pins (Note: PC7 is not available on the ATmega8)
• PortD (PD0-PD7): 8 GPIO pins
Each port has three registers associated with it:
1. DDRx (Data Direction Register): Configures the direction of the pins (input or output).
2. PORTx (Port Data Register): Sets the output value or enables/disables pull-up resistors for input pins.
3. PINx (Port Input Register): Reads the input value of the pins.
2. Configuring GPIO Pins
Set Pin as Output
To set a pin as an output, write 1 to the corresponding bit in the DDRx register.
Example: Set PB0 as output:
DDRB |= (1 << PB0); // Set PB0 as output
Set Pin as Input
To set a pin as an input, write 0 to the corresponding bit in the DDRx register.
Example: Set PD2 as input:
DDRD &= ~(1 << PD2); // Set PD2 as input
Enable Pull-Up Resistor
For input pins, you can enable the internal pull-up resistor by writing 1 to the corresponding bit in the PORTx register.
Example: Enable pull-up on PD2:
PORTD |= (1 << PD2); // Enable pull-up on PD2
3. Writing to Output Pins
To write a value to an output pin, use the PORTx register.
Example: Set PB0 high:
PORTB |= (1 << PB0); // Set PB0 high
Example: Set PB0 low:
PORTB &= ~(1 << PB0); // Set PB0 low
4. Reading from Input Pins
To read the value of an input pin, use the PINx register.
Example: Read the value of PD2:
if (PIND & (1 << PD2)) {
// PD2 is high
} else {
// PD2 is low
}
Example Code
Here’s an example program that toggles an LED connected to PB0 when a button connected to PD2 is pressed:
#include <avr/io.h>
#include <util/delay.h>
int main(void) {
// Set PB0 as output
DDRB |= (1 << PB0);
// Set PD2 as input and enable pull-up
DDRD &= ~(1 << PD2);
PORTD |= (1 << PD2);
while (1) {
// Check if PD2 is low (button pressed)
if (!(PIND & (1 << PD2))) {
// Toggle PB0
PORTB ^= (1 << PB0);
// Debounce delay
_delay_ms(500);
}
}
}
Code Explanation
1. Include Header Files
#include <avr/io.h>
#include <util/delay.h>
• <avr/io.h>: This header file includes definitions for all the registers and pins of the AVR microcontroller. It allows you to use names like DDRB, PORTB, PIND, etc.
• <util/delay.h>: This header file provides functions for generating delays, such as _delay_ms()
2. Main Function
int main(void) {
• The main() function is the entry point of the program. The program starts executing from here.
3. Configure PB0 as Output
DDRB |= (1 << PB0);
• DDRB: Data Direction Register for Port B. It controls whether the pins of Port B are inputs or outputs.
• (1 << PB0): This creates a bitmask where the bit corresponding to PB0 is set to 1 (e.g., 0b00000001).
• |=: The OR assignment operator sets the PB0 bit in DDRB to 1 without affecting the other bits.
• Result: PB0 is configured as an output pin.
4. Configure PD2 as Input with Pull-Up Resistor
DDRD &= ~(1 << PD2);
PORTD |= (1 << PD2);
• DDRD: Data Direction Register for Port D.
• DDRD &= ~(1 << PD2): Clears the PD2 bit in DDRD (sets it to 0), making PD2 an input pin.
• PORTD: Port D Data Register. When a pin is configured as an input, writing 1 to its corresponding bit in PORTD enables the internal pull-up resistor.
• PORTD |= (1 << PD2): Sets the PD2 bit in PORTD to 1, enabling the pull-up resistor for PD2.
• Result: PD2 is configured as an input pin with the pull-up resistor enabled.
5. Infinite Loop
while (1) {
• The while (1) loop ensures that the program runs indefinitely.
6. Check if PD2 is Low (Button Pressed)
if (!(PIND & (1 << PD2))) {
• PIND: Port D Input Pins Register. It reads the current state of the pins in Port D.
• (1 << PD2): Creates a bitmask for PD2.
• PIND & (1 << PD2): Checks the state of PD2. If PD2 is high (due to the pull-up resistor), this expression evaluates to a non-zero value. If PD2 is low (button pressed), it evaluates to 0.
• !: The NOT operator inverts the result. If PD2 is low, the condition becomes true.
• Result: The code inside the if block executes when the button connected to PD2 is pressed.
7. Toggle PB0
PORTB ^= (1 << PB0);
•PORTB: Port B Data Register. It controls the output state of the pins in Port B.
• (1 << PB0): Creates a bitmask for PB0.
• ^=: The XOR assignment operator toggles the PB0 bit in PORTB. If PB0 is high, it becomes low, and vice versa.
• Result: The LED connected to PB0 toggles its state (on/off) each time the button is pressed.
8. Debounce Delay
_delay_ms(500);
• _delay_ms(500): Introduces a delay of 500 milliseconds to debounce the button. Button debouncing is necessary to avoid multiple detections of a single press due to mechanical vibrations.
9. End of Loop
}
}
• The while (1) loop repeats, continuously checking the state of PD2 and toggling PB0 when the button is pressed.
Summary of Code Functionality
1. Configure PB0 as an output (to control an LED).
2. Configure PD2 as an input with a pull-up resistor (to read a button press).
3. Continuously check the state of PD2:
• If the button is pressed (PD2 is low), toggle the LED connected to PB0.
4. Add a debounce delay to ensure reliable button detection.
Hardware Setup
1. Connect an LED to PB0 with a current-limiting resistor (e.g., 220Ω).
2. Connect a push-button to PD2:
• One side of the button to PD2.
• The other side to ground (GND).
3. The internal pull-up resistor ensures that PD2 is high when the button is not pressed.