#include <avr/io.h>
#include <util/delay.h>
#define I2C_ADDR 0x27 // I2C address for the LCD
#define LCD_COLUMNS 20 // Number of columns on the LCD
#define LCD_LINES 4 // Number of lines on the LCD
#define LCD_BACKLIGHT 0x08 // Backlight bit for the LCD
#define LCD_ENABLE_BIT 0x04 // Enable bit for the LCD
// I2C functions to replace Wire.h functionality
void i2c_init() {
TWSR = 0x00; // Set prescaler to 1
TWBR = 0x12; // Set SCL frequency (100 kHz if F_CPU is 16MHz)
//TWCR = (1 << TWEN); // Enable TWI
}
void i2c_start() {
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // Send START condition
while (!(TWCR & (1 << TWINT))); // Wait for TWINT flag set
}
void i2c_stop() {
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); // Send STOP condition
while (TWCR & (1 << TWSTO)); // Wait for STOP to finish
}
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
}
// LCD functions
void lcd_expanderWrite(uint8_t data) {
i2c_start();
i2c_write((I2C_ADDR << 1) | 0); // Send address with write bit
i2c_write(data | LCD_BACKLIGHT); // Write data with backlight
i2c_stop();
}
void lcd_pulseEnable(uint8_t data) {
lcd_expanderWrite(data | LCD_ENABLE_BIT); // Pulse enable bit
_delay_us(1);
lcd_expanderWrite(data & ~LCD_ENABLE_BIT);
_delay_us(50);
}
void lcd_write4Bits(uint8_t value) {
lcd_expanderWrite(value);
lcd_pulseEnable(value);
}
void lcd_send(uint8_t value, uint8_t mode) {
uint8_t highNibble = value & 0xF0;
uint8_t lowNibble = (value << 4) & 0xF0;
lcd_write4Bits(highNibble | mode);
lcd_write4Bits(lowNibble | mode);
}
void lcd_sendCommand(uint8_t command) {
lcd_send(command, 0);
}
void lcd_sendData(uint8_t data) {
lcd_send(data, 0x01);
}
void lcd_init() {
i2c_init();
_delay_ms(50);
lcd_sendCommand(0x33); // Initialize in 8-bit mode
lcd_sendCommand(0x32); // Switch to 4-bit mode
lcd_sendCommand(0x28); // Set to 2-line, 5x8 dots per character
lcd_sendCommand(0x0C); // Display on, cursor off
lcd_sendCommand(0x06); // Set entry mode
lcd_sendCommand(0x01); // Clear display
_delay_ms(2);
}
void lcd_setCursor(uint8_t col, uint8_t row) {
uint8_t row_offsets[] = {0x00, 0x40, 0x14, 0x54};
if (row >= LCD_LINES) row = LCD_LINES - 1;
lcd_sendCommand(0x80 | (col + row_offsets[row]));
}
void lcd_print(const char* str) {
while (*str) {
lcd_sendData(*str++);
}
}
void lcd_backlight() {
lcd_expanderWrite(0x08); // Turn on backlight
}
void setup1() {
lcd_init();
lcd_backlight();
lcd_setCursor(3, 0);
lcd_print("Hello, world!");
lcd_setCursor(2, 1);
lcd_print("Wokwi Online IoT");
lcd_setCursor(5, 2);
lcd_print("Simulator");
lcd_setCursor(7, 3);
lcd_print("Enjoy!");
}
int main(void) {
setup1();
while (1) {
// Main loop does nothing in this example
}
}