#define F_CPU 16000000UL // Define the CPU frequency as 16 MHz #include #include // Define LCD pins #define LCD_E PIN0_bm // Enable (PC0) #define LCD_RS PIN1_bm // Register Select (PC1) #define LCD_D4 PIN3_bm // Data Bit 4 (PC3) #define LCD_D5 PIN2_bm // Data Bit 5 (PC2) #define LCD_D6 PIN5_bm // Data Bit 6 (PC5) #define LCD_D7 PIN4_bm // Data Bit 7 (PC4) // Helper macros #define LCD_E_HIGH() (PORTC.OUTSET = LCD_E) #define LCD_E_LOW() (PORTC.OUTCLR = LCD_E) #define LCD_RS_HIGH() (PORTC.OUTSET = LCD_RS) #define LCD_RS_LOW() (PORTC.OUTCLR = LCD_RS) // Function Prototypes void lcd_send_nibble(uint8_t nibble); void lcd_send_byte(uint8_t data, uint8_t is_command); void lcd_init(); void lcd_print(const char *str); void lcd_set_cursor(uint8_t row, uint8_t col); // Send a nibble (4 bits) to the LCD void lcd_send_nibble(uint8_t nibble) { PORTC.OUTCLR = LCD_D4 | LCD_D5 | LCD_D6 | LCD_D7; // Clear D4-D7 // Set data lines based on nibble if (nibble & 0x01) PORTC.OUTSET = LCD_D4; if (nibble & 0x02) PORTC.OUTSET = LCD_D5; if (nibble & 0x04) PORTC.OUTSET = LCD_D6; if (nibble & 0x08) PORTC.OUTSET = LCD_D7; // Pulse E pin LCD_E_HIGH(); // Set E high _delay_us(1); // Pulse duration LCD_E_LOW(); // Set E low _delay_us(100); // Command latch time } // Send a byte (8 bits) to the LCD void lcd_send_byte(uint8_t data, uint8_t is_command) { if (is_command) { LCD_RS_LOW(); // Command mode } else { LCD_RS_HIGH(); // Data mode } lcd_send_nibble(data >> 4); // Send high nibble lcd_send_nibble(data & 0x0F); // Send low nibble _delay_us(50); // Command execution time } void lcd_init() { // Step 1: Initialize Data Register and Direction Register PORTC.OUTCLR = LCD_E | LCD_RS | LCD_D4 | LCD_D5 | LCD_D6 | LCD_D7; // Clear pins PORTC.DIRSET = LCD_E | LCD_RS | LCD_D4 | LCD_D5 | LCD_D6 | LCD_D7; // Set as outputs // Ensure EN and RS are LOW during initialization LCD_E_LOW(); LCD_RS_LOW(); _delay_ms(50); // Wait >40ms for power stabilization // Step 2: 8-bit Mode Initialization (3 times Function Set 0x30) lcd_send_nibble(0x03); lcd_send_nibble(0x00); // First function set _delay_ms(5); // Longer delay after the first command lcd_send_nibble(0x03); lcd_send_nibble(0x00); // Second function set _delay_us(200); // Shorter delay for subsequent commands lcd_send_nibble(0x03); lcd_send_nibble(0x00); // Third function set _delay_us(200); // Step 3: Force 4-bit Mode (0x20) lcd_send_nibble(0x02); lcd_send_nibble(0x00); // Force 4-bit mode _delay_us(200); // Step 4: Function Set (4-bit, 2-line, 5x8 dots) lcd_send_nibble(0x02); lcd_send_nibble(0x80); // Function set for 2-line mode _delay_us(200); // Step 5: Internal Oscillator Frequency (0x14) lcd_send_nibble(0x01); lcd_send_nibble(0x40); // Set oscillator frequency _delay_us(200); // Step 6: Contrast Control (Fine and Coarse settings) lcd_send_nibble(0x07); lcd_send_nibble(0x00); // Fine contrast settings _delay_us(200); lcd_send_nibble(0x05); lcd_send_nibble(0xE0); // Coarse contrast settings _delay_us(200); // Step 7: Follower Control (Gain = 50) lcd_send_nibble(0x06); lcd_send_nibble(0xC0); // Follower control _delay_ms(200); // Longer stabilization delay // Step 8: Display OFF (0x08) lcd_send_nibble(0x00); lcd_send_nibble(0x80); // Turn display off _delay_us(200); // Step 9: Clear Display (0x01) lcd_send_nibble(0x00); lcd_send_nibble(0x10); // Clear display _delay_ms(2); // Step 10: Entry Mode Set (0x06) lcd_send_nibble(0x00); lcd_send_nibble(0x60); // Entry mode set _delay_us(200); // Step 11: Display ON (0x0C) lcd_send_nibble(0x00); lcd_send_nibble(0xC0); // Turn display on _delay_us(200); } // Print a string to the LCD void lcd_print(const char *str) { while (*str) { lcd_send_byte(*str++, 0); // Send characters in data mode } } // Set cursor to a specific position void lcd_set_cursor(uint8_t row, uint8_t col) { uint8_t address = (row == 0) ? col : (0x40 + col); lcd_send_byte(0x80 | address, 1); } int main() { lcd_init(); // Initialize the LCD // Display a test message lcd_set_cursor(0, 0); // First row, first column lcd_print("Testing LCD"); lcd_set_cursor(1, 0); // Second row, first column lcd_print("1234567890"); while (1); // Infinite loop to hold the display }