#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 } // Initialize the LCD void lcd_init() { // Set LCD pins as outputs PORTC.DIRSET = LCD_E | LCD_RS | LCD_D4 | LCD_D5 | LCD_D6 | LCD_D7; // Ensure EN and RS are LOW during initialization LCD_E_LOW(); LCD_RS_LOW(); _delay_ms(50); // Wait >40ms for power stabilization // Force 4-bit mode initialization lcd_send_nibble(0x03); _delay_ms(5); lcd_send_nibble(0x03); _delay_us(200); lcd_send_nibble(0x03); _delay_us(200); lcd_send_nibble(0x02); // Set to 4-bit mode // Function Set: 4-bit, 2-line, 5x8 dots lcd_send_byte(0x28, 1); _delay_us(500); // Increase delay // Contrast Control Commands lcd_send_byte(0x70, 1); // Fine contrast settings (adjust C3-C0) _delay_us(500); lcd_send_byte(0x5E, 1); // Power/ICON/Contrast settings (adjust C5-C4) _delay_us(500); lcd_send_byte(0x6C, 1); // Follower control (gain = 50) _delay_ms(10); // Longer stabilization delay // Display OFF lcd_send_byte(0x08, 1); _delay_us(500); // Clear Display lcd_send_byte(0x01, 1); _delay_ms(2); // Entry Mode Set: Increment cursor, no display shift lcd_send_byte(0x06, 1); _delay_us(500); // Display ON: Display ON, Cursor OFF, Blink OFF lcd_send_byte(0x0C, 1); _delay_us(500); } // 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() { // Configure all LCD-related pins (RS, E, D4-D7) as outputs PORTC.DIRSET = LCD_D4 | LCD_D5 | LCD_D6 | LCD_D7 | LCD_RS | LCD_E; // Explicitly set EN and RS to LOW at startup LCD_E_LOW(); LCD_RS_LOW(); // Initialize the LCD lcd_init(); // Display static text for debugging lcd_set_cursor(0, 0); // First row, first column lcd_print("Hello, World!"); lcd_set_cursor(1, 0); // Second row, first column lcd_print("Debug Test!"); // Halt further execution while (1); }