Microcontroller Registers & MMIO
In embedded systems, we control hardware by writing to specific memory addresses. This is called Memory-Mapped I/O (MMIO). In the C language, compiler headers map these hardware addresses to register names (such as DDRB or PORTB).
The Three GPIO Port Registers
On AVR microcontrollers (like the ATmega2560), each physical port (Port A, Port B, etc.) is controlled by three 8-bit registers. Replacing x with the port letter (e.g., PORTA, DDRA, PINA):
DDRx(Data Direction Register): Configures whether the pin is an Input or Output.- Set bit to
0: Input mode (Default). - Set bit to
1: Output mode.
- Set bit to
PORTx(Data Register): Sets the output state, or toggles internal pull-ups.- If pin is Output (
DDRx = 1): Write1to set pin High (Vcc), or0to set pin Low (GND). - If pin is Input (
DDRx = 0): Write1to enable the internal pull-up resistor, or0for floating/tri-state.
- If pin is Output (
PINx(Input Pins Address Register): Read-only register containing the physical voltage levels on the pins.- Reads
1if the pin voltage is High, and0if the pin voltage is Low.
- Reads
Active-Low Button with Pull-Up
Floating input pins are highly sensitive to electromagnetic noise, resulting in random reads. To prevent this, we connect input buttons in an active-low configuration:
- Connect the button between the input pin and Ground (GND).
- Enable the microcontroller’s internal pull-up resistor, which connects the pin internally to Vcc through a large resistor.
Button States
- Button Released: The pull-up resistor holds the pin voltage at Vcc. Reading
PINxreturns1. - Button Pressed: The switch closes, short-circuiting the pin directly to Ground (GND). The pin voltage drops to 0V. Reading
PINxreturns0.
Example Driver Implementation
To write a driver for a button connected to Port A, Pin 1 (PA1):
#include <avr/io.h>
#include <stdbool.h>
void button_init(void) {
// 1. Configure PA1 as Input (Clear DDR bit)
DDRA &= ~(1 << PA1);
// 2. Enable internal pull-up on PA1 (Set PORT bit)
PORTA |= (1 << PA1);
}
bool button_is_pressed(void) {
// Active-low: pressed state reads as 0 (LOW) in PINA
return (0 == (PINA & (1 << PA1)));
}
Active-High vs. Active-Low LEDs
- Active-High LED: Pin connects to the LED anode, LED cathode to GND. Write
1(PORTx |=) to turn ON; write0to turn OFF. - Active-Low LED: Pin connects to the LED cathode, LED anode to Vcc. Write
0(PORTx &= ~) to turn ON; write1to turn OFF.
Why must we enable the internal pull-up resistor when connecting a button between an input pin and ground?
Configure GPIO Input Pull-up
// Configure PB4 as an input pin with the internal pull-up enabled DDRB (1 << PB4); // Set to input PORTB (1 << PB4); // Enable pull-up
References & Further Reading
- Microchip Technology. ATmega2560 8-bit AVR Microcontroller Datasheet. Section 13 (I/O Ports).
- VIA University College: Embedded Software 1 (ESW1) Lecture Notes - Section 8 (AVR Register Programming).