Since I see your lower nibble from bits 0 to 3 are attached to LCD pins RS, RW, EN, BACKLIGHT, I take it that you're going to use 4 bit LCD mode. But you haven't switched the LCD to that mode in your LCD_init() function! There is a three byte incantation that needs to be performed first. Oh, that's the "LCD_CURSORSHIFT | LCD_FUNCTIONSET" three times call. However, there is a difference with my init sequence (see below) since I send two separate LCD_FUNCTIONSET commands (after the first three) before the "LCD_2LINE | LCD_5x8DOTS" command. Oh, nevermind. After the fourth call (sending just LCD_FUNCTIONSET), then you switch to using LCD_send_cmd() which send two bytes for every command.
See below for my minimalist LCD functions (I use SPI to a '595 then LCD).
I wait 75ms after reset/powerup. You have just 20ms. Confirm that your delays are consistant with what I've programmed below.
In LCD_I2C_send(), there should be a delay (50us) after the second byte sent. 2ms delay after the first byte is a bit overkill. In LCD_send_cmd() and LCD_send_data(), why aren't you reusing LCD_I2C_send() vs. calling I2C routines directly? Only the RS bit is different between the two, right? This is unnecessary duplication.
Why the Nop() calls? Alignment? Surely not for delay.
void lcdSend(uint8_t data) {
// HD44780 display is attached to a 74HC595
// bit0=backlight(1=on; 0=off), bit1=RS, bit2=RW, bit3=EN, bits4-7=DB4-7
// below, we override the lower nibble except for the RS bit
data |= _BV(0); // backlight always on
data &= ~_BV(2); // RW in write mode
data &= ~_BV(3); // keep EN low while DB4-7, and control bits settle
spi_transfer(data);
// toggle EN pin; high, then low
data |= _BV(3); spi_transfer(data); _delay_us(1);
data &= ~_BV(3); spi_transfer(data); _delay_us(50);
}
void lcdClear() {
lcdSend(0b00000000); //RS=0;
lcdSend(0b00010000); //RS=0; clear display
_delay_ms(5);
}
void lcdHome() {
lcdSend(0b00000000); //RS=0;
lcdSend(0b00100000); //RS=0; return home
_delay_ms(5);
}
void lcdInit() {
spi_setup(); _delay_ms(75); //min. after start before sending commands
lcdSend(0b00110000); _delay_ms(5); //RS=0; initialize
lcdSend(0b00110000); _delay_ms(5); //RS=0; initialize
lcdSend(0b00110000); _delay_ms(5); //RS=0; initialize; "Thrice the brinded cat hath mewed."
lcdSend(0b00100000); _delay_ms(5); //RS=0; switch to 4-bit mode
lcdSend(0b00100000); //RS=0;
lcdSend(0b10000000); _delay_ms(5); //RS=0; 5x8 chars, 2 lines/display
lcdSend(0b00000000); //RS=0;
lcdSend(0b11000000); _delay_ms(5); //RS=0; display on, no cursor
lcdClear();
lcdHome();
}
void setCursor(uint8_t col, uint8_t row) {
uint8_t row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
uint8_t data = 0b10000000|((col&0x0f)+row_offsets[row&0x03]);
lcdSend((data&0xf0)|0b0000); //RS=0;
data <<= 4;
lcdSend((data&0xf0)|0b0000); //RS=0; setddramaddr
}
void lcdChar(uint8_t data) {
lcdSend((data&0xf0)|0b0010); //RS=1; high nibble
data <<= 4;
lcdSend((data&0xf0)|0b0010); //RS=1; low nibble
}
void lcdString(char *datap) {
while (*datap != '\0')
lcdChar(*datap++);
}
// somewhere in your main():
lcdInit();
lcdString("FOOBAR v1.0");
_delay_ms(1000);