/* www.microDigitalEd.com
 * p3_2.c: Initialize and display "Hello" on the LCD using 8-bit data mode.
 *
 * Data pins use Port 4; control pins use Port 3.
 * Polling of the busy bit of the LCD status register is used for timing.
 *
 * Tested with Keil 5.20 and MSP432 Device Family Pack V2.2.0
 * on XMS432P401R Rev C.
 */

#include "msp.h"

#define RS 0x20     /* P3.5 mask */ 
#define RW 0x40     /* P3.6 mask */ 
#define EN 0x80     /* P3.7 mask */

void delayMs(int n);
void LCD_command(unsigned char command);
void LCD_command_noPoll(unsigned char command);
void LCD_data(unsigned char data);
void LCD_init(void);
void LCD_ready(void);

int main(void) {
    LCD_init();

    while(1) {
        LCD_command(1);         /* clear display */
        delayMs(500);
        LCD_command(0x80);      /* set cursor at beginning of first line */
        LCD_data('H');          /* write the word "Hello" */
        LCD_data('e');
        LCD_data('l');
        LCD_data('l');
        LCD_data('o');
        delayMs(500);
    }
}

void LCD_init(void) {
    P3->DIR |= RS | RW | EN;  /* make P3 pins output for control */
    P4->DIR = 0xFF;           /* make P4 pins output for data */
    
    delayMs(30);            /* initialization sequence */
    LCD_command_noPoll(0x30);	/* LCD does not respond to status poll yet */
    delayMs(5);
    LCD_command_noPoll(0x30);
    delayMs(1);
    LCD_command_noPoll(0x30);   /* busy flag cannot be polled before this command */
    
    LCD_command(0x38);      /* set 8-bit data, 2-line, 5x7 font */
    LCD_command(0x06);      /* move cursor right after each char */
    LCD_command(0x01);      /* clear screen, move cursor to home */
    LCD_command(0x0F);      /* turn on display, cursor blinking */
}

/* This function waits until LCD controller is ready to
 * accept a new command/data before returns.
 * It polls the busy bit of the status register of LCD controller.
 * In order to read the status register, the data port of the 
 * microcontroller has to change to an input port before reading
 * the LCD. The data port of the microcontroller is return to
 * output port before the end of this function.
 */
void LCD_ready(void) {
    char status;
    
    /* change to read configuration to poll the status register */
    P4->DIR = 0;            /* Port 4 as input port */
    P3->OUT &= ~RS;         /* RS = 0 for status */
    P3->OUT |= RW;          /* R/W = 1, LCD output */
    
    do {    /* stay in the loop until it is not busy */
        P3->OUT |= EN;      /* pulse E high */
        delayMs(0);
        status = P4->IN;    /* read status register */
        P3->OUT &= ~EN;     /* clear E */
        delayMs(0);			
    } while (status & 0x80);    /* check busy bit */
    
    /* return to default write configuration */
    P3->OUT &= ~RW;         /* R/W = 0, LCD input */
    P4->DIR = 0xFF;         /* Port 4 as output */
}

/* This function waits for LCD controller ready before issuing
 * the next command.
 */
void LCD_command(unsigned char command) {
    LCD_ready();            /* wait for LCD controller ready */
    P3->OUT &= ~(RS | RW);  /* RS = 0, R/W = 0 */
    P4->OUT = command;      /* put command on data bus */
    P3->OUT |= EN;          /* pulse E high */
    delayMs(0);
    P3->OUT &= ~EN;         /* clear E */
}

/* This function is used at the beginning of the initialization
 * when the busy bit of the status register is not readable.
 */
void LCD_command_noPoll(unsigned char command) {
    P3->OUT &= ~(RS | RW);  /* RS = 0, R/W = 0 */
    P4->OUT = command;      /* put command on data bus */
    P3->OUT |= EN;          /* pulse E high */
    delayMs(0);
    P3->OUT &= ~EN;         /* clear E */
}

/* This function waits for LCD controller ready before issuing
 * the next data.
 */
void LCD_data(unsigned char data) {
    LCD_ready();            /* wait for LCD controller ready */
    P3->OUT |= RS;          /* RS = 1 */
    P3->OUT &= ~RW;         /* R/W = 0 */
    P4->OUT = data;         /* put data on bus */
    P3->OUT |= EN;          /* pulse E */
    delayMs(0);
    P3->OUT &= ~EN;
}

/* delay milliseconds when system clock is at 3 MHz */
void delayMs(int n) {
    int i, j;
    
    for (j = 0; j < n; j++)
        for (i = 750; i > 0; i--);      /* Delay */
}