Interfacing with Peripherals
A comprehensive guide to interfacing with various peripherals in embedded systems, including sensors, displays, and communication interfaces.
Interfacing with Peripherals
Interfacing with peripherals is a crucial aspect of embedded systems development. This guide covers common peripherals, communication protocols, and best practices for hardware interfacing.
Common Peripherals
Input Devices
Buttons and Switches
- Types: Push buttons, toggle switches, rotary encoders
- Interfacing: Digital input with pull-up/pull-down resistors
- Debouncing: Software or hardware techniques to handle switch bounce
Example Button Interface:
// Button with pull-up resistor
#define BUTTON_PIN PB0
#define BUTTON_PORT PORTB
#define BUTTON_DDR DDRB
void button_init() {
// Set button pin as input with pull-up
BUTTON_DDR &= ~(1 << BUTTON_PIN);
BUTTON_PORT |= (1 << BUTTON_PIN);
}
bool button_pressed() {
// Return true if button is pressed (LOW due to pull-up)
return !(BUTTON_PORT & (1 << BUTTON_PIN));
}
Sensors
- Analog Sensors: Temperature, light, pressure
- Digital Sensors: Accelerometers, gyroscopes, GPS
- Interfacing Methods: ADC, I2C, SPI, UART
Example Temperature Sensor (LM35) Interface:
// ADC initialization for temperature sensor
void adc_init() {
// Set reference voltage to AVCC and select ADC0
ADMUX = (1 << REFS0);
// Enable ADC and set prescaler
ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1);
}
// Read temperature in Celsius
float read_temperature() {
// Start conversion
ADCSRA |= (1 << ADSC);
// Wait for conversion to complete
while (ADCSRA & (1 << ADSC));
// Convert ADC value to temperature (LM35: 10mV/°C)
return (ADC * 5.0 / 1024.0) * 100.0;
}
Output Devices
LEDs
- Types: Single-color, RGB, 7-segment displays
- Interfacing: Digital output, PWM for brightness control
- Current Limiting: Series resistors or constant current drivers
Example LED Control:
// LED with PWM control
#define LED_PIN PB1
#define LED_PORT PORTB
#define LED_DDR DDRB
void led_init() {
// Set LED pin as output
LED_DDR |= (1 << LED_PIN);
// Configure PWM
// (PWM configuration code would go here)
}
void set_brightness(uint8_t brightness) {
// Set PWM duty cycle
OCR1A = brightness;
}
Displays
- Types: LCD, OLED, e-ink
- Interfacing: Parallel, I2C, SPI
- Graphics Libraries: Often provided by manufacturer
Example OLED Display (SSD1306) Interface:
// I2C initialization for OLED display
void oled_init() {
// Initialize I2C
// (I2C initialization code would go here)
// Initialize display
// (Display initialization commands would go here)
}
void oled_clear() {
// Clear display buffer
// (Clear display commands would go here)
}
void oled_draw_text(const char* text) {
// Draw text on display
// (Text drawing commands would go here)
}
Communication Protocols
I2C (Inter-Integrated Circuit)
- Features: Two-wire interface, multiple devices
- Speed: Standard (100 kHz), Fast (400 kHz), Fast Plus (1 MHz)
- Applications: Sensors, EEPROM, RTC
Example I2C Communication:
// I2C initialization
void i2c_init() {
// Set clock frequency
TWBR = 72; // 100kHz with 8MHz clock
// Enable I2C
TWCR = (1 << TWEN);
}
// Write data to I2C device
void i2c_write(uint8_t device_addr, uint8_t reg_addr, uint8_t data) {
// Start condition
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
// Send device address
TWDR = device_addr << 1;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
// Send register address
TWDR = reg_addr;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
// Send data
TWDR = data;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
// Stop condition
TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN);
}
SPI (Serial Peripheral Interface)
- Features: Full-duplex, high-speed, simple protocol
- Signals: MOSI, MISO, SCK, CS
- Applications: Flash memory, ADCs, DACs
Example SPI Communication:
// SPI initialization
void spi_init() {
// Set MOSI, SCK as output
DDRB |= (1 << MOSI) | (1 << SCK);
// Enable SPI, Master mode, clock rate fck/16
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0);
}
// Send and receive data via SPI
uint8_t spi_transfer(uint8_t data) {
// Start transmission
SPDR = data;
// Wait for transmission complete
while (!(SPSR & (1 << SPIF)));
// Return received data
return SPDR;
}
UART (Universal Asynchronous Receiver/Transmitter)
- Features: Asynchronous, full-duplex
- Configuration: Baud rate, data bits, stop bits, parity
- Applications: Debug output, GPS modules, Bluetooth
Example UART Communication:
// UART initialization
void uart_init(uint16_t baud) {
// Set baud rate
uint16_t ubrr = F_CPU / 16 / baud - 1;
UBRR0H = (uint8_t)(ubrr >> 8);
UBRR0L = (uint8_t)ubrr;
// Enable transmitter and receiver
UCSR0B = (1 << TXEN0) | (1 << RXEN0);
// Set frame format: 8 data bits, 1 stop bit
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}
// Send character via UART
void uart_send(char data) {
// Wait for empty transmit buffer
while (!(UCSR0A & (1 << UDRE0)));
// Put data into buffer
UDR0 = data;
}
// Receive character via UART
char uart_receive() {
// Wait for data to be received
while (!(UCSR0A & (1 << RXC0)));
// Get and return received data
return UDR0;
}
Best Practices for Peripheral Interfacing
1. Hardware Design
- Use appropriate voltage levels
- Include protection circuits
- Consider power requirements
- Follow manufacturer guidelines
2. Software Design
- Use modular code structure
- Implement error handling
- Document pin assignments
- Use clear naming conventions
3. Testing and Debugging
- Use logic analyzers
- Implement debug output
- Test with different conditions
- Verify timing requirements
4. Documentation
- Document pin connections
- Specify communication parameters
- Include timing diagrams
- Note any special requirements
Example Project: Weather Station
Here's a simple example of a weather station using multiple peripherals:
#include <avr/io.h>
#include <util/delay.h>
// Function declarations
void init_peripherals();
float read_temperature();
float read_humidity();
void update_display(float temp, float humidity);
void log_data(float temp, float humidity);
int main() {
// Initialize all peripherals
init_peripherals();
while (1) {
// Read sensors
float temperature = read_temperature();
float humidity = read_humidity();
// Update display
update_display(temperature, humidity);
// Log data
log_data(temperature, humidity);
// Wait before next reading
_delay_ms(5000);
}
return 0;
}
// Initialize all peripherals
void init_peripherals() {
// Initialize I2C for sensors
i2c_init();
// Initialize SPI for display
spi_init();
// Initialize UART for logging
uart_init(9600);
// Initialize buttons
button_init();
}
// Read temperature from sensor
float read_temperature() {
// Read temperature from I2C sensor
// (Temperature reading code would go here)
return 25.0; // Placeholder
}
// Read humidity from sensor
float read_humidity() {
// Read humidity from I2C sensor
// (Humidity reading code would go here)
return 60.0; // Placeholder
}
// Update OLED display
void update_display(float temp, float humidity) {
// Clear display
oled_clear();
// Draw temperature
char temp_str[16];
sprintf(temp_str, "Temp: %.1f C", temp);
oled_draw_text(temp_str);
// Draw humidity
char hum_str[16];
sprintf(hum_str, "Hum: %.1f %%", humidity);
oled_draw_text(hum_str);
}
// Log data via UART
void log_data(float temp, float humidity) {
char log_str[32];
sprintf(log_str, "T:%.1f,H:%.1f\r\n", temp, humidity);
for (int i = 0; log_str[i] != '\0'; i++) {
uart_send(log_str[i]);
}
}
Next Steps
Now that you understand peripheral interfacing, you can explore: