// Control 16 character LCD (2x8 chars) with 4 bit interface
// Copyright (C) 2012 Joonas Pihlajamaa. Released to public domain.
// No warranties, use at your own responsibility.

#include <avr/io.h>

#define F_CPU 12000000UL // 12 MHz
#include <util/delay.h>

#define DATA_PORT_DIR DDRB
#define DATA_PORT PORTB
#define DATA_PORT_IN PINB

#define RW_PIN (1<<PD4)
#define RS_PIN (1<<PD5)
#define EN_PIN (1<<PD6)

#define SET_CTRL_BIT(pin) (PORTD |= pin)
#define CLEAR_CTRL_BIT(pin) (PORTD &= ~pin)

// assumes EN_PIN is LOW in the beginning
void lcd_write(char rs, unsigned char data) {
    if(DATA_PORT_DIR != 0xFF)
        DATA_PORT_DIR = 0xFF;
        
    CLEAR_CTRL_BIT(RW_PIN);
    
    if(rs)
        SET_CTRL_BIT(RS_PIN);
    else
        CLEAR_CTRL_BIT(RS_PIN); 
        
    DATA_PORT = data;
    
    _delay_us(2);
    SET_CTRL_BIT(EN_PIN);
    _delay_us(2);
    CLEAR_CTRL_BIT(EN_PIN);
}

unsigned char lcd_read(char rs) {
    unsigned char data;
    
    if(DATA_PORT_DIR != 0)
        DATA_PORT_DIR = 0;
        
    SET_CTRL_BIT(RW_PIN);
    
    if(rs)
        SET_CTRL_BIT(RS_PIN);
    else
        CLEAR_CTRL_BIT(RS_PIN); 
        
    _delay_us(2);
    SET_CTRL_BIT(EN_PIN);
    _delay_us(2);
    data = DATA_PORT_IN;
    CLEAR_CTRL_BIT(EN_PIN);
    
    return data;
}

void lcd_wait() {
    while(lcd_read(0) & 0x80); // wait until display is ready
}

void lcd_init() {
    _delay_ms(50); // wait for VDD to rise
    lcd_write(0, 0x30);
    _delay_ms(5);
    lcd_write(0, 0x30);
    _delay_ms(1); // _delay_us(120);
    lcd_write(0, 0x30);
    _delay_ms(1); // _delay_us(120);

    lcd_write(0, 0x38); // 2 lines, normal font
    _delay_ms(1);
    lcd_write(0, 0xC); // display on
    _delay_ms(1);
    lcd_write(0, 1); // display clear
    _delay_ms(1);
    lcd_write(0, 0x6); // increment, don't shift
    _delay_ms(1);    
}

void lcd_puts(char * string) {
    char i;
    
    lcd_write(0, 0x80); // move to 1st line
    lcd_wait();
    
    for(i=0; i<8; i++) {
        if(string[i] == '\0')
            return;
            
        lcd_write(1, string[i]);
        lcd_wait();
    }
    
    lcd_write(0, 0x80+0x40); // move to 2nd line
    lcd_wait();
    
    for(i=8; i<16; i++) {
        if(string[i] == '\0')
            return;
            
        lcd_write(1, string[i]);
        lcd_wait();
    }
}

int main(void) {
    unsigned char i = 0;
    char message[] = "nn Mississippi..";
    
    DDRD = RS_PIN + EN_PIN + RW_PIN + LED_PIN; // Control outputs
    DDRB = 0xFF; // Port B as DB0..DB7
	    
    lcd_init();
    
    lcd_puts("Hello, World!!!");
    
    _delay_ms(2000);
        
	while(1) {
        if(++i >= 100)
            i = 1;
            
        if(i >= 10)
            message[0] = i/10+'0';
        else
            message[0] = ' ';
        message[1] = i%10+'0';
        
        lcd_puts(message);
        _delay_ms(1000);
    } 

	return 1;
}
