#define I2C_ADDR 0x27 // I2C address of the LCD
// I2C setup
void I2C_Init() {
// Set SCL frequency to 100kHz with prescaler of 1 and assuming 16MHz clock
TWSR = 0x00; // Set prescaler to 1
TWBR = 72; // Set bit rate register (100kHz)
TWCR = (1 << TWEN); // Enable TWI (I2C)
}
// I2C Start Condition
void I2C_Start() {
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // Send START condition
while (!(TWCR & (1 << TWINT))); // Wait for TWINT flag set
}
// I2C Stop Condition
void I2C_Stop() {
TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN); // Send STOP condition
_delay_ms(1);
}
// I2C Write Byte
void I2C_Write(uint8_t data) {
TWDR = data; // Load data into TWDR register
TWCR = (1 << TWINT) | (1 << TWEN); // Start transmission
while (!(TWCR & (1 << TWINT))); // Wait for TWINT flag set
}
// Write data to LCD
void LCD_I2C_Write(uint8_t data, uint8_t control) {
uint8_t highNibble = data & 0xF0;
uint8_t lowNibble = (data << 4) & 0xF0;
// Write high nibble
I2C_Start();
I2C_Write(I2C_ADDR << 1); // LCD address + write bit (0)
I2C_Write(highNibble | control | 0x04); // Enable = 1
I2C_Write(highNibble | control); // Enable = 0
I2C_Stop();
// Write low nibble
I2C_Start();
I2C_Write(I2C_ADDR << 1); // LCD address + write bit (0)
I2C_Write(lowNibble | control | 0x04); // Enable = 1
I2C_Write(lowNibble | control); // Enable = 0
I2C_Stop();
_delay_us(50); // Wait for LCD to process the command
}
// Send command to LCD
void LCD_Send_Command(uint8_t command) {
LCD_I2C_Write(command, 0x00); // Command mode
}
// Send data to LCD
void LCD_Send_Data(uint8_t data) {
LCD_I2C_Write(data, 0x01); // Data mode
}
// Initialize the LCD
void LCD_Init() {
_delay_ms(50); // Wait for LCD to power up
LCD_Send_Command(0x03); // Initialize LCD in 4-bit mode (repeat 3 times)
_delay_ms(5);
LCD_Send_Command(0x03);
_delay_us(150);
LCD_Send_Command(0x03);
LCD_Send_Command(0x02); // Finally, set to 4-bit mode
// Configure the LCD in 2-line, 5x7 matrix, and turn on display
LCD_Send_Command(0x28); // 4-bit, 2-line, 5x7 matrix
LCD_Send_Command(0x0C); // Display ON, Cursor OFF, Blink OFF
LCD_Send_Command(0x01); // Clear display
_delay_ms(2);
LCD_Send_Command(0x06); // Increment cursor, no display shift
}
// Clear the LCD display
void LCD_Clear() {
LCD_Send_Command(0x01); // Clear display command
_delay_ms(2); // Wait for the command to complete
}
// Set cursor position (row 0 or 1, col 0-15)
void LCD_Set_Cursor(uint8_t row, uint8_t col) {
uint8_t address = (row == 0) ? (0x80 + col) : (0xC0 + col);
LCD_Send_Command(address);
}
// Write string to LCD
void LCD_Write_String(const char* str) {
while (*str) {
LCD_Send_Data(*str++);
}
}
int main(void) {
// Initialize I2C and LCD
I2C_Init();
LCD_Init();
// Write "Hello, World!" to the LCD
LCD_Clear();
LCD_Set_Cursor(0, 0);
LCD_Write_String(" I LOVE YOU");
while (1) {
// Main loop (LCD display remains active)
}
}