/*
 * Author: Jake Goodwin
 * Date: 2022
 * Description: Interfacing with a 1602LCD only with the uc no perifs  
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <util/delay.h>
#include "1602LCD_char_display.h"



//#############################
//FUNCTION PROTOTYPES
//#############################

int main() {
    PORTD = 0x00;
    DDRD = 0xff;

    //SETUP HERE.
    led_blink();
    led_blink();
    init_lcd(); 
    _delay_ms(500); 
    
    return_home_lcd();

    while(1) {
        led_blink();
        _delay_ms(1000);
       
        set_entry_mode_increment_lcd();
        for(uint8_t i = 0; i < 15; i++) {
            //shift_right_lcd();
            write_lcd_address(0x7e);
            _delay_ms(100);        
        }
        
        set_entry_mode_decrement_lcd();
        for(uint8_t i = 0; i < 15; i++) {
            //shift_left_lcd();
            write_lcd_address(0x7f);
            _delay_ms(100);        
        }
        //set_cgram_lcd_address(0x00);
        //set_entry_mode_increment_lcd();
        //bbs();
        _delay_ms(1000);
        clear_lcd();
        PORTD = 0x00;
        led_blink();
        led_blink();
        led_blink();
        _delay_ms(1000);
        _delay_ms(1000);
        _delay_ms(1000);
        _delay_ms(1000);
        _delay_ms(1000);
    }
    
    return 0;
}



//#############################
//FUNCTIONS
//#############################

/*
 * Input: none
 * Output: none
 * Description: setsup the lcd for two line display and sets start posistion.
 */
void init_lcd(void) {
    _delay_ms(500);
    clear_buf_lcd();
    clear_buf_lcd();
    _delay_ms(10);
    turn_on_lcd();
    _delay_ms(10);
    
    set_lcd_two_line();
    _delay_ms(10);
    
    //clear_lcd();
    _delay_ms(5);
    
    //return_home_lcd();
    _delay_ms(5);
}


/*
 * Input: None
 * Outupt: None
 * Description: Sets the LCD into 4bit two line display mode.
 */
void set_lcd_two_line(void) {
    cmd_lcd(0x28);
}

/*
 * Input: None
 * Outupt: None
 * Description: Sets the LCD into 4bit one line display mode.
 */
void set_lcd_one_line(void) {
    cmd_lcd(0x20);
}


/*
 * Input: None
 * Outupt: None
 * Description: Sends the clear screen command to the LCD
 */
void clear_lcd(void) {
    cmd_lcd(0x01);
}

/*
 * Input: None
 * Outupt: None
 * Description: Returns the LCD cursor to home position. 
 */
void return_home_lcd(void) {
    cmd_lcd(0x02);
}

/*
 * Input: None
 * Output: None
 * Description: Shifts cusor to the right one char.
 */
void shift_right_lcd(void) {
    cmd_lcd(0x14);
}

/*
 * Input: None
 * Output: None
 * Description: Shifts cusor to the left one char.
 */
void shift_left_lcd(void) {
    cmd_lcd(0x10);
}




/*
 * Input: None
 * Outupt: None
 * Description: Toggles the LCD power state. 
 */
void turn_off_lcd(void) {
    cmd_lcd(0x08);
}

/*
 * Input: None
 * Outupt: None
 * Description: Toggles the LCD power state.
 */
void turn_on_lcd(void) {
    cmd_lcd(0x0f);
}

/*
 * Input: None
 * Outupt: None
 * Description:  
 */
void set_cgram_lcd_address(uint8_t addr) {
    uint8_t cmd = 0x40;
    cmd |= addr;
    cmd_lcd(cmd);
}

/*
 * Input: None
 * Outupt: None
 * Description: 
 */
void set_ddram_lcd_address(uint8_t addr) {
    uint8_t cmd = 0x80;
    cmd |= addr;
    cmd_lcd(cmd);
}

/*
 * Input: None
 * Outupt: None
 * Description: 
 */
void write_lcd_address(uint8_t addr) {
    setbit(PORTD, RS_PIN);
    cmd_lcd(addr);
    clearbit(PORTD, RS_PIN);
    _delay_ms(10); 
}

void set_entry_mode_increment_lcd(void) {
    cmd_lcd(0x06);
}

void set_entry_mode_decrement_lcd(void) {
    cmd_lcd(0x04);
}

void clear_buf_lcd() {
    DDRD = 0xff;
    PORTD &=0x0f;
    setbit(PORTD, ENA_PIN);
    _delay_ms(5);
    clearbit(PORTD, ENA_PIN);

    _delay_ms(2); 

    setbit(PORTD, ENA_PIN);
    _delay_ms(5);
    clearbit(PORTD, ENA_PIN);
}


/*
 * Input: uint8_t, a 8bit command code.
 * Output: none
 * Description: Takes a 8bit command code and sends it as two 4bit nibbles
 * to the 1602LCD. The timing of delays is very important to match the 
 * datasheets timing diagrams.
 */
void cmd_lcd(uint8_t cmd) {
    DDRD = 0xff;
    PORTD &=0x0f;

    //Send first nibble
    setbit(PORTD, ENA_PIN);
    PORTD |= (cmd & 0xf0);
    _delay_ms(1);
    clearbit(PORTD, ENA_PIN);
    _delay_ms(3);

    //Send the second nibble and shift cmd by four.
    PORTD &= 0x0f;
    setbit(PORTD, ENA_PIN);
    PORTD |= (cmd<<4);
    _delay_ms(1);
    clearbit(PORTD, ENA_PIN);
    _delay_ms(3);
}


/*
 * Input: None
 * Output: None
 * Description: Toggles the pin for the LED indicator.
 */
static void led_blink(void) {
    //Set the DDR for output.
    DDRC |= (1<<LED_PIN);

    PORTC ^= (1<<LED_PIN);
    _delay_ms(250);
    PORTC ^= (1<<LED_PIN);
    _delay_ms(250);
    
}


