Peripheral Interfacing
Peripherals are the bridge between your microcontroller and the
physical world. Understanding how to configure and control GPIOs,
timers, interrupts, and external sensors is fundamental to embedded
systems development.
[Diagram: MCU connected to LEDs, buttons, sensors, and displays]
General Purpose I/O (GPIO)
GPIOs are the most basic form of peripheral—digital pins that can be
configured as inputs or outputs to interact with external components.
GPIO Configuration
- Direction: Input or Output mode
-
Pull-up/Pull-down: Internal resistors to define
default state
-
Push-Pull vs Open-Drain: Output driver
configuration
-
Speed/Slew Rate: How fast the pin can toggle
(affects EMI)
-
Alternate Function: Map pin to specific peripheral
(UART, SPI, etc.)
// Configure GPIO pin as output (pseudo-code) GPIO_SetMode(GPIOA,
PIN_5, GPIO_MODE_OUTPUT); GPIO_SetOutputType(GPIOA, PIN_5,
GPIO_PUSH_PULL); GPIO_SetSpeed(GPIOA, PIN_5, GPIO_SPEED_HIGH); //
Toggle LED GPIO_Toggle(GPIOA, PIN_5);
Reading Digital Inputs
// Configure as input with pull-up resistor GPIO_SetMode(GPIOB, PIN_0,
GPIO_MODE_INPUT); GPIO_SetPull(GPIOB, PIN_0, GPIO_PULL_UP); // Read
button state (active low) if (GPIO_Read(GPIOB, PIN_0) == 0) { //
Button is pressed handle_button_press(); }
⚠️ Debouncing: Mechanical switches bounce when
pressed, causing multiple false triggers. Always implement software
or hardware debouncing!
Timers and Counters
Timers are essential for creating precise delays, generating PWM
signals, measuring pulse widths, and scheduling periodic tasks.
Timer Modes
-
Basic Timer: Simple counting for delays and time
measurement
-
PWM Generation: Create pulse-width modulated
signals
-
Input Capture: Measure external signal timing
-
Output Compare: Trigger actions at specific count
values
- Encoder Mode: Interface with rotary encoders
// Configure timer for 1ms interrupt void Timer_Init(void) { //
Prescaler: divide clock to get 1MHz (1µs ticks) TIM2->PSC =
(SystemCoreClock / 1000000) - 1; // Auto-reload: count to 1000 for 1ms
period TIM2->ARR = 1000 - 1; // Enable update interrupt TIM2->DIER |=
TIM_DIER_UIE; // Start timer TIM2->CR1 |= TIM_CR1_CEN; }
Interrupts
Interrupts allow the MCU to respond immediately to events without
constantly polling, making your code more efficient and responsive.
[Diagram: Interrupt flow - Event → ISR → Resume main code]
Interrupt Types
-
External Interrupts: Triggered by GPIO pin changes
(rising/falling edge)
-
Timer Interrupts: Triggered when timer overflows or
matches a value
-
Communication Interrupts: UART RX complete, SPI
transfer done, etc.
-
DMA Interrupts: Data transfer complete
notifications
-
System Interrupts: SysTick, fault handlers, reset
Writing an ISR (Interrupt Service Routine)
// External interrupt handler for button press void
EXTI0_IRQHandler(void) { if (EXTI->PR & EXTI_PR_PR0) { // Check
pending flag EXTI->PR = EXTI_PR_PR0; // Clear flag (write 1 to clear)
// Handle the interrupt - keep it SHORT! button_pressed_flag = 1; } }
// In main loop, process the flag while (1) { if (button_pressed_flag)
{ button_pressed_flag = 0; process_button_action(); // Heavy
processing here } }
💡 Best Practice: Keep ISRs short! Set a flag and
do the heavy work in the main loop or a task. Avoid blocking calls
and printf in ISRs.
Sensor Integration
Sensors convert physical quantities into electrical signals. Common
interfaces include analog (ADC), digital (GPIO), and protocol-based
(I2C, SPI).
Common Sensor Types
-
Temperature: LM35 (Analog), DS18B20 (1-Wire),
BMP280 (I2C/SPI)
- Motion/IMU: MPU6050 (I2C), LSM6DS3 (SPI)
-
Proximity/Distance: HC-SR04 (Ultrasonic), VL53L0X
(ToF/I2C)
- Light: LDR (Analog), BH1750 (I2C)
- Humidity: DHT22 (1-Wire), SHT31 (I2C)
Example: Reading I2C Temperature Sensor
// Read temperature from BMP280 (simplified) float
read_temperature(void) { uint8_t data[3]; // Read raw temperature
registers I2C_ReadRegisters(BMP280_ADDR, 0xFA, data, 3); // Combine
bytes into 20-bit raw value int32_t raw = (data[0] << 12) | (data[1]
<< 4) | (data[2] >> 4); // Apply compensation formula (from datasheet)
float temp = compensate_temperature(raw); return temp; }
Display Interfacing
Displays provide visual feedback. Common types include character LCDs,
graphic LCDs, OLEDs, and seven-segment displays.
Popular Display Interfaces
-
HD44780 Character LCD: 4-bit or 8-bit parallel
interface
- SSD1306 OLED: I2C or SPI, 128x64 pixels
- ST7735 TFT: SPI, color graphics
-
MAX7219: SPI, for LED matrices and 7-segment
displays
// Initialize SSD1306 OLED via I2C void OLED_Init(void) {
OLED_Command(0xAE); // Display OFF OLED_Command(0xD5); // Set clock
divide OLED_Command(0x80); OLED_Command(0xA8); // Set multiplex ratio
OLED_Command(0x3F); // 64 rows OLED_Command(0xAF); // Display ON }
void OLED_Print(const char* str) { while (*str) {
OLED_WriteChar(*str++); } }
🎯 Key Takeaway: Mastering peripheral interfacing
unlocks the ability to connect your embedded system to virtually any
sensor, actuator, or display—bringing your projects to life!