
#include "io430.h"
#include "stdint.h"
#include "intrinsics.h"

/* 
 * LCD vezérlése 4 bites módban
 * A program kiír egy teszt szöveget, majd LED1-et villogtatja
 * 
 * Hardver követelmények:
 *  - Launchpad MSP430G22x1 mikrovezérlővel
 *  - LCD 2x16 alfanumerikus kijelző, az alábbi bekötéssel: 
 *
 * -------------------------
 *  LCD modul     Launchpad
 * -------------------------
 *  1. GND          GND
 *  2. VCC          TP3 (+5V) 
 *  3. VEE          GND
 *  4. RS           P2.6
 *  5. R/W          GND
 *  6. E            P2.7
 *  7. DB0          ---
 *  8. DB1          ---
 *  9. DB2          ---
 * 10. DB3          ---  
 * 11. DB4          P1.4
 * 12. DB5          P1.5
 * 13. DB6          P1.6
 * 14. DB7          P1.7
 *
 *   I. Cserny 
 *   2011. május 24.
 *   Fejlesztői környezet: IAR Embedded Workbench for TI MSP430 ver 5.20.4
 */


/* LCD port és vezérlő bitek megadása */
#define LCD_PORT     P1OUT
#define LCD_PORT_DIR P1DIR
#define LCD_MASK     BIT7+BIT6+BIT5+BIT4
#define LCD_RS       P2OUT_bit.P2OUT_6
#define LCD_RS_DIR   P2DIR_bit.P2DIR_6
#define LCD_E	     P2OUT_bit.P2OUT_7
#define LCD_E_DIR    P2DIR_bit.P2DIR_7


/**-----------------------------------------------
 * Inline függvény, amely egy regiszter valamelyik 
 * bitcsoportját atomi művelettel módosítja
 *-----------------------------------------------
 * \param reg a módosítandó regiszter neve
 * \param val a beírandó érték (helyiértéken helyesen!)
 * \param mask a módosítandó bitcsoportot kijelölő maszk
 */
#define ChangeBits(reg,val,mask) reg^=((reg^val)&mask)

/**----------------------------------------------
 *   Késleltető eljárás (1 - 65535 ms)
 *-----------------------------------------------
 * \param delay a késleltetés ms egységben megadva
 */
void delay_ms(uint16_t delay) {
  uint16_t i;
  for(i=0; i<delay; i++) {
    __delay_cycles(1000);
  }
}

/**----------------------------------------------
 *   LCD Enable bemenet pulzálása
 *-----------------------------------------------
 */
void lcd_toggle_E() {
  LCD_E = 1; 
    __delay_cycles(500);
  LCD_E = 0;
    __delay_cycles(500);
}  

/**----------------------------------------------
 *   Egy bájt kiküldése az LCD vezérlőjének
 *-----------------------------------------------
 * \param val a kiírandó érték
 * \param cmd regiszterválasztó bit (0: parancs, 1: adat)
 */
void lcd_write(uint8_t val, uint8_t cmd) {
  LCD_RS = cmd;
  ChangeBits(LCD_PORT,(val&0xF0),LCD_MASK);
  lcd_toggle_E();
  ChangeBits(LCD_PORT,((val&0x0F)<<4),LCD_MASK);
  lcd_toggle_E();
  __delay_cycles(1000);
}

/*-----------------------------------------------
 *   LCD kijelző inicializálása
 *-----------------------------------------------
 */
void lcd_init (void){
//-- Az LCD port inicializálása
  ChangeBits(LCD_PORT,0x00,LCD_MASK);
  ChangeBits(LCD_PORT_DIR,LCD_MASK,LCD_MASK);
  P2SEL = 0;
  LCD_RS = 0;
  LCD_RS_DIR = 1;
  LCD_E = 0; 
  LCD_E_DIR = 1; 
  delay_ms(100);                      //40ms várakozás bekapcsolás után
//-- Az LCD vezérlőjének inicializálása:
//-- 1. szoftveres LCD reset: 0x30 (8-bites mód) kiírása háromszor
//-- 2. 4-bites üzemmód beállítása
//-- 3. Képernyőtörlés, kurzor kikapcsolása
  ChangeBits(LCD_PORT,0x30,LCD_MASK); 
  lcd_toggle_E ();
  delay_ms(5);                        //várjunk legalább 4.1ms-ot
  lcd_toggle_E ();
  __delay_cycles(100);                //várjunk legalább 100us-ot
  lcd_toggle_E ();
  __delay_cycles(40);                 //várjunk legalább 37us-ot
  ChangeBits(LCD_PORT,0x20,LCD_MASK); //0x20 = 4-bites üzemmód
  lcd_toggle_E ();
  delay_ms(5);                        //várjunk legalább 4.1ms-ot
//-- Innen kezdve minden bájtot két félbájtként kell kiírni! ------
  lcd_write(0x28,0);                  // display mód beállítás
  delay_ms(5);                        //várjunk legalább 4.1ms-ot
  lcd_write(0x08,0);                  // display letiltás
  lcd_write(0x01,0);                  // képernyőtörlés
  lcd_write(0x0C,0);                  // display be, cursor, és villogás ki
}

/**----------------------------------------------
 *  Karakterfüzér kiírása az LCD-re
 *-----------------------------------------------
* \param p_str karakterfüzér mutató (nullával lezárt stringre mutat)
 */
void lcd_puts(char* p_str) {
  char c;
  while ((c=*p_str)) {
      lcd_write(c,1);
      p_str++;
  }
}

int main(void) {
  WDTCTL = WDTPW + WDTHOLD;           //watchdog letiltása
  P1DIR |= 0x01;
  lcd_init();
  lcd_puts("<== 2x16 LCD ==>");
  lcd_write(0xC0,0);                  //A második sor elejére lép
  lcd_puts("Now in 4bit mode");
  while(1) {
      P1OUT ^= 0x01;                  //LED1 villogtatása
    __delay_cycles(250000);
  }
}     