HITACHI HD 44780 4Bit Mode LCD Driver

/*==============================================================================
   lcd.h
 
   HITACHI HD44780 4Bit Mode LCD Driver for Small Device C Compiler
   Written by Ervin Jung (2010)
 
--------------------------------------------------------------------------------
 
   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
   In other words, you are welcome to use, share and improve this program.
   You are forbidden to forbid anyone else to use, share and improve
   what you give them.   Help stamp out software-hoarding!
 
   As a special exception, if you link this library with other files,
   some of which are compiled with SDCC, to produce an executable,
   this library does not by itself cause the resulting executable
   to be covered by the GNU General Public License.
   This exception does not however invalidate any other reasons why
   the executable file might be covered by the GNU General Public License.
 
--------------------------------------------------------------------------------
 
   defaults:
 
   -- INTERNAL NAME --+-- PIC PIN  --+-- LCD PIN --
      LCD_RS_PIN      |   RB0        |   RS 
      LCD_RW_PIN      |   RB1        |   RW
      LCD_EN_PIN      |   RB2        |   EN
      -               |   -          |   D0
      -               |   -          |   D1
      -               |   -          |   D2
      -               |   -          |   D3
      LCD_DATA1       |   RB4        |   D4
      LCD_DATA2       |   RB5        |   D5
      LCD_DATA3       |   RB6        |   D6
      LCD_DATA4       |   RB7        |   D7  Busy Pin
 
   LCD_BUSY_PIN USE LCD_DATA4_TRIS AND YOU NEED DEFINE PORT TO READ BUSY FLAG !                
 
   printf("\c"); // Clear display
   printf("\n"); // New line
 
   example (18F4550 20Mhz PPL5):
 
      #include <pic18fregs.h>
 
      #define  FOSC 48000000    // because PPL5
      #include "simple_delay.h"
 
      #define  LCD_USE_DEFAULTS
      #define  LCD_ROW_COUNT 2
      #include "lcd.h"
 
      void main(void) {
        unsigned long int n = 0;
        lcd_init();
        printf("\cIDE for PIC18F");
        while(1) {
          lcd_gotoxy(1,2);
          printf("N = %ld ", n++);
        }
      }    
--------------------------------------------------------------------------------
  you can define custom characters from 0 to 7 (mode 5x7)
 
  custom character at code 0:
 
    0.   000 00000
    1.   000 00000
    2.   000 00000
    3.   000 00000
    4.   000 00000
    5.   000 00000
    6.   000 00000
    7.   000 11111 = 0x1f = 31
 
    1. char lcd_char0[8] = {0,0,0,0,0,0,0,31};
    2. lcd_init();
    3. lcd_init_char(0, lcd_char0);
    4. lcd_send_byte(1, 0);
 
--------------------------------------------------------------------------------
 
    #define  LCD_USE_STRING_SHIFT
 
    const char buffer[] = " *** HD44780 4BIT LCD DRIVER  HTTP://JUNIF.HU\0";
 
    main() {
      char position = 0;
      while(1) {
        lcd_gotoxy(0,1);
        lcd_string_shift(&position, buffer);
        delay_ms(250);
      }
    }
 
==============================================================================*/
 
#ifndef __LCD_H__
#define __LCD_H__
 
#ifndef LCD_USE_CUSTOM
  #ifndef LCD_USE_DEFAULTS
    #ERROR YOU MUST DEFINE LCD PINS !
  #endif
#endif
 
#include <stdio.h>
#include "simple_delay.h"
 
#define LCD_DELAY_1CYCLE   delay_cycles(1)
#define LCD_DELAY_10CYCLES delay_cycles(10)
 
#define LCD_CMD  0
#define LCD_DATA 1
 
#ifdef LCD_USE_DEFAULTS
 
  #ifndef LCD_RS_PIN
    #define LCD_RS_PIN     LATBbits.LATB0
  #endif
 
  #ifndef LCD_RW_PIN 
    #define LCD_RW_PIN     LATBbits.LATB1
  #endif
 
  #ifndef LCD_EN_PIN
    #define LCD_EN_PIN     LATBbits.LATB2
  #endif
 
  #ifndef LCD_RS_TRIS
    #define LCD_RS_TRIS    TRISBbits.TRISB0
  #endif
 
  #ifndef LCD_RW_TRIS
    #define LCD_RW_TRIS    TRISBbits.TRISB1
  #endif
 
  #ifndef LCD_EN_TRIS
    #define LCD_EN_TRIS    TRISBbits.TRISB2
  #endif
 
  #ifndef LCD_BUSY_PIN
    #define LCD_BUSY_PIN   PORTBbits.RB7     // BUSY PIN
  #endif
 
  #ifndef LCD_DATA4
    #define LCD_DATA4      LATBbits.LATB7    // BUSY PIN   
  #endif
 
  #ifndef LCD_DATA3
    #define LCD_DATA3      LATBbits.LATB6
  #endif
 
  #ifndef LCD_DATA2
    #define LCD_DATA2      LATBbits.LATB5
  #endif
 
  #ifndef LCD_DATA1
    #define LCD_DATA1      LATBbits.LATB4
  #endif
 
  #ifndef LCD_DATA4_TRIS
    #define LCD_DATA4_TRIS TRISBbits.TRISB7  // BUSY PIN
  #endif
 
  #ifndef LCD_DATA3_TRIS
    #define LCD_DATA3_TRIS TRISBbits.TRISB6
  #endif
 
  #ifndef LCD_DATA2_TRIS
    #define LCD_DATA2_TRIS TRISBbits.TRISB5
  #endif
 
  #ifndef LCD_DATA1_TRIS
    #define LCD_DATA1_TRIS TRISBbits.TRISB4
  #endif
#endif //LCD_USE_DEFAULTS
 
 
#ifndef LCD_ROW_COUNT
  #error YOU MUST DEFINE LCD ROW COUNT (2 OR 4)
#endif
 
#ifndef LCD_COL_COUNT
  #define LCD_COL_COUNT 16
#endif
 
#if LCD_ROW_COUNT == 2
  #define LCD_LINE0_ADDRESS 0x80    
  #define LCD_LINE1_ADDRESS 0xC0    
#endif
 
#if LCD_ROW_COUNT == 4
  #define LCD_LINE0_ADDRESS 0x80    
  #define LCD_LINE1_ADDRESS 0xC0    
  #define LCD_LINE2_ADDRESS 0x90    
  #define LCD_LINE3_ADDRESS 0xD0    
#endif
 
/* Clear Display */
#define LCD_CLEAR_DISPLAY     0b00000001
 
/* Home position */
#define LCD_CURSOR_HOME       0b00000010
 
/* Entry Mode Control */
#define LCD_ENTRY_CONTROL     0b00000100
#define LCD_CURSOR_INCREMENT  0b00000110
#define LCD_DISPLAY_SHIFT     0b00000101
 
/* Display Control */
#define LCD_DISPLAY_CONTROL   0b00001000
#define LCD_DISPLAY_ON        0b00001100
#define LCD_CURSOR_ON         0b00001010
#define LCD_CURSOR_BLINK_ON   0b00001001
 
/* Cursor or Display Shift Control */
#define LCD_SHIFT_CONTROL     0b00010000
#define LCD_CURSOR_SHIFT      0b00011000
#define LCD_CURSOR_SHIFT_LEFT 0b00010100
 
/* Function Set Control */
#define LCD_FUNCTION_SET      0b00100000
#define LCD_FUNCTION_8BIT     0b00110000
#define LCD_FUNCTION_2LINE    0b00101000
#define LCD_FUNCTION_5x10     0b00100100
 
//data 
static char const LCD_INITS[4] = { 
  LCD_FUNCTION_2LINE,
  LCD_CURSOR_INCREMENT,
  LCD_CURSOR_SHIFT,
  LCD_CLEAR_DISPLAY}; 
 
#if LCD_ROW_COUNT == 2
static char const LCD_LINES[2] = {
  LCD_LINE0_ADDRESS, 
  LCD_LINE1_ADDRESS};
#endif
 
#if LCD_ROW_COUNT == 4
static char const LCD_LINES[4] = {
  LCD_LINE0_ADDRESS, 
  LCD_LINE1_ADDRESS, 
  LCD_LINE2_ADDRESS, 
  LCD_LINE3_ADDRESS};
#endif
 
static void lcd_send_half_byte(char b) __wparam {
   LCD_DATA4 = b >> 7;  
   LCD_DATA3 = b >> 6; 
   LCD_DATA2 = b >> 5;  
   LCD_DATA1 = b >> 4; 
   LCD_DELAY_1CYCLE;
   LCD_EN_PIN = 1;
   delay_us(2);
   LCD_EN_PIN = 0;
}
 
void lcd_send_byte(char mode, char b) {
  LCD_DATA4_TRIS = 1;
  do {
    LCD_RW_PIN = 1;
  } while (!LCD_BUSY_PIN);
  LCD_DATA4_TRIS = 0;
  LCD_DELAY_10CYCLES; 
  LCD_RS_PIN = mode;
  LCD_RW_PIN = 0; 
  LCD_EN_PIN = 0;
  LCD_DELAY_10CYCLES;
  lcd_send_half_byte(b & 0xf0);
  lcd_send_half_byte(b << 4);  
}
 
void lcd_init_char(char charcode, char pattern[]) {
  char i;
  charcode = (charcode << 3) | 0x40;
  putchar('\c');
  for(i = 0; i < 8; i++) {
    lcd_send_byte(LCD_CMD,  charcode | i);
    lcd_send_byte(LCD_DATA, pattern[i]);
  }
}
 
void lcd_init(void)  {
  char i;
  LCD_RS_TRIS = 0;
  LCD_RW_TRIS = 0;
  LCD_EN_TRIS = 0;
  LCD_DATA4_TRIS = 0;
  LCD_DATA3_TRIS = 0;
  LCD_DATA2_TRIS = 0;
  LCD_DATA1_TRIS = 0;
  delay_ms(25);
  lcd_send_half_byte(3);
  delay_ms(10);
  lcd_send_half_byte(3);
  delay_us(200);
  lcd_send_half_byte(3);
  lcd_send_half_byte(2);
  for(i = 0; i < 4; i++)
    lcd_send_byte(LCD_CMD, LCD_INITS[i]);
  stdout = STREAM_USER; 
}
 
void lcd_gotoxy(unsigned char x, unsigned char y) {
  unsigned char addr;
  if(y >= LCD_ROW_COUNT) y = 0;
  addr = LCD_LINES[y] + x;
  lcd_send_byte(LCD_CMD, 0x80 | addr);
}
 
void putchar(char c) __wparam {
  switch (c) {
    case '\c' : lcd_send_byte(LCD_CMD, LCD_CLEAR_DISPLAY);
                delay_ms(2);
                break;
 
    case '\n' : lcd_gotoxy(1, 2);
                break;
 
    default   : lcd_send_byte(LCD_DATA, c);
                break;
  }
}
 
 
#ifdef LCD_USE_STRING_SHIFT
#include <string.h>
void lcd_string_shift(char * index, char buffer[]) {
  char i;
  char j;
 
  if(* index > strlen(buffer))
    * index = 0;
  j = * index;
 
  for(i = 0; i < LCD_COL_COUNT; i++) {
    if (j >= strlen(buffer))
      j = 0;
    lcd_send_byte(LCD_DATA, buffer[j++]);
  }
 
  * index += 1;
}
#endif
 
#endif

LCD_Custom_Char