EMBEDDED SYSTEMS REPORT (LAB 9)
Name: Fasih ur Rehman
Roll No: EE-22155
Course Code: EE-354 (Embedded Systems)
Instructor: Miss Aiman Najeeb
Objective:
To interface a 16x2 LCD display with ATmega328P microcontroller in 4-bit
mode using the I2C communication protocol. The tasks include:
1. Displaying a message with a blinking effect using the I2C expander.
2. Displaying an analog voltage value measured via ADC on the LCD.
3. Comparing UART, SPI, and I2C protocols.
TASK 1: LCD Interface in 4-bit Mode via I2C
Steps:
1. Program ATmega328P to control a 16x2 LCD using I2C communication.
2. Connect the PCF8574 I2C LCD module to the microcontroller.
3. Use TWI (Two-Wire Interface) pins for communication.
4. Modify the code to create a blinking effect on the LCD using display
ON/OFF commands and delays.
Code:
#include <avr/io.h> #include <util/delay.h> #include <stdlib.h>
// Including I2C functions void i2c_init() { TWBR = 0x62; //
Setting bit rate TWCR = (1<<TWEN); // Enable I2C TWSR = 0x00; //
Prescaler set to 1 }
// Start condition void i2c_start() { TWCR = (1<<TWINT) |
(1<<TWEN) | (1<<TWSTA); while (!(TWCR & (1<<TWINT))); // Wait
for start condition }
// I2C write (for sending address and data) void i2c_write(char
x) { TWDR = x; TWCR = (1<<TWINT) | (1<<TWEN); while (!(TWCR &
(1<<TWINT))); }
// Toggle function for LCD enable pulse void toggle()
{ i2c_write((TWDR |= 0x04)); _delay_us(1); i2c_write((TWDR &=
~0x04)); _delay_us(100); }
// Send command to LCD void lcd_cmd(char v2) { i2c_write((TWDR
&= ~0x03)); i2c_write((TWDR &= 0x0F)); i2c_write((TWDR |= (v2 &
0xF0))); toggle(); i2c_write((TWDR &= 0x0F)); i2c_write((TWDR |=
((v2 & 0x0F)<<4))); toggle(); }
// Write data to LCD void lcd_dwr(char v3) { i2c_write((TWDR |=
0x01)); i2c_write((TWDR &= 0x0F)); i2c_write((TWDR |= (v3 &
0xF0))); toggle(); i2c_write((TWDR &= 0x0F)); i2c_write((TWDR |=
((v3 & 0x0F)<<4))); toggle(); }
// Send string message to LCD void lcd_msg(char *c) { while(*c !
= 0) lcd_dwr(*c++); }
// LCD Initialization void lcd_init() { _delay_ms(100);
lcd_cmd(0x30); _delay_ms(10); lcd_cmd(0x30); _delay_ms(5);
lcd_cmd(0x30); _delay_ms(5); lcd_cmd(0x02); _delay_ms(1);
lcd_cmd(0x28); _delay_ms(1); lcd_cmd(0x0C); lcd_cmd(0x06);
lcd_cmd(0x01); _delay_ms(4); }
int main(void) { i2c_init(); i2c_start(); i2c_write(0x4E); //
Device Address
lcd_init();
float Percentage = 75.5; char buffer_str[10];
dtostrf(Percentage, 5, 1, buffer_str);
lcd_cmd(0x80); lcd_msg("Expected ES Lab");
lcd_cmd(0xC0); lcd_msg("Result "); lcd_msg(buffer_str);
lcd_msg("% :)");
while(1) { _delay_ms(1000); lcd_cmd(0x08); _delay_ms(1000);
lcd_cmd(0x0C); } }
Observation:
The message "Expected ES Lab" was displayed on the first line, and the
percentage result (75.5%) appeared on the second line. The LCD display
blinked every 1000 ms using lcd_cmd(0x08) and lcd_cmd(0x0C).
Conclusion:
The LCD was successfully interfaced using I2C. The use of display ON/OFF
commands allowed the blinking effect to be achieved.
TASK 2: Comparison of UART, SPI, and I2C
Feature UART SPI I2C
Asynchronous (No Synchronous (Uses
Type Synchronous (Uses clock)
clock) clock)
4 (MOSI, MISO,
Wires 2 (TX, RX) 2 (SDA, SCL)
SCK, SS)
Speed Up to 1 Mbps >10 Mbps 100 kbps – 3.4 Mbps
Devices Multiple (via SS
2 (Point-to-point) Multiple (via addresses)
Supported lines)
Clock
Not required Required Required
Requirement
Complexity Simple Moderate High
Serial with PC, Memory, sensors, EEPROMs, sensors in
Use Cases
Bluetooth displays embedded systems
Conclusion:
Each communication protocol has its pros and cons. UART is simplest, SPI is
fastest, and I2C is efficient for multiple low-speed peripherals.
TASK 3: Displaying Analog Voltage via ADC
on I2C LCD
Steps:
1. Use the ADC module of ATmega328P to read analog voltage.
2. Convert ADC value to a voltage.
3. Display the measured voltage on the LCD screen using I2C.
Code:
#include <avr/io.h> #include <util/delay.h> #include <stdlib.h>
// Including I2C functions void i2c_init() { TWBR = 0x62; TWCR =
(1 << TWEN); TWSR = 0x00; }
void i2c_start() { TWCR = (1 << TWINT) | (1 << TWEN) | (1 <<
TWSTA); while (!(TWCR & (1 << TWINT))); }
void i2c_write(char x) { TWDR = x; TWCR = (1 << TWINT) | (1 <<
TWEN); while (!(TWCR & (1 << TWINT))); }
void toggle() { i2c_write((TWDR |= 0x04)); _delay_us(1);
i2c_write((TWDR &= ~0x04)); _delay_us(100); }
void lcd_cmd(char v2) { i2c_write((TWDR &= ~0x03));
i2c_write((TWDR &= 0x0F)); i2c_write((TWDR |= (v2 & 0xF0)));
toggle(); i2c_write((TWDR &= 0x0F)); i2c_write((TWDR |= ((v2 &
0x0F) << 4))); toggle(); }
void lcd_dwr(char v3) { i2c_write((TWDR |= 0x01));
i2c_write((TWDR &= 0x0F)); i2c_write((TWDR |= (v3 & 0xF0)));
toggle(); i2c_write((TWDR &= 0x0F)); i2c_write((TWDR |= ((v3 &
0x0F) << 4))); toggle(); }
void lcd_init() { _delay_ms(100); lcd_cmd(0x30); _delay_ms(10);
lcd_cmd(0x30); _delay_ms(5); lcd_cmd(0x30); _delay_ms(5);
lcd_cmd(0x02); _delay_ms(1); lcd_cmd(0x28); _delay_ms(1);
lcd_cmd(0x0C); lcd_cmd(0x06); lcd_cmd(0x01); _delay_ms(4); }
void lcd_msg(char *c) { while (*c != 0) lcd_dwr(*c++); }
int main(void) { DDRC = 0x00; ADCSRA = 0x87; ADMUX = 0x60;
i2c_init(); i2c_start(); i2c_write(0x20 << 1); // Address for
PCF8574
lcd_init();
while (1) { ADCSRA |= (1 << ADSC); while ((ADCSRA & (1 << ADIF))
== 0);
char buffer_str[10];
float Percentage = ADCH * 5.0 / 255;
dtostrf(Percentage, 5, 1, buffer_str);
lcd_cmd(0x80);
lcd_msg("Measured Voltage");
lcd_cmd(0xC0);
lcd_msg("Result ");
lcd_msg(buffer_str);
lcd_msg("V");
} }
Observation:
The analog voltage, calculated from the ADC, was correctly displayed on the
LCD. The output format was as follows:
Measured Voltage
Result 2.5V
Conclusion:
Successful integration of ADC and I2C LCD allowed for real-time monitoring
of analog signals, demonstrating the ability to combine peripherals in
embedded systems.