/*---------------------------------------------------------------------* * LCD_I2C.C LCD interface over I2C * * Designed for the Hitachi H44780 LCD display controller interface. * * Copyright 2008-2020 Didier Juges * http://www.ko4bb.com * * This driver uses an NXP PCF8574A I2C/parallel interface to drive the LCD * in 4 bit mode. * * Pin-out: * PCF8574 LCD * 4 (P0) 4 (RS) * 5 (P1) 5 (R/W) * 6 (P2) 6 (E) * 7 (P3) N/C (could be used for backlight ON) * 9 (P4) 11 (DB4) * 10 (P5) 12 (DB5) * 11 (P6) 13 (DB6) * 12 (P7) 14 (DB7, busy flag functionality not used) * * Note: * * For most applications when there really is no reason to read from the LCD, * tie "R/W" to ground and just wait the maximum amount of time * for each instruction (4.1 msecs for clearing the display or moving the * cursor/display to the "home position", 160 usecs for all other commands). * As well as making application software simpler, it also frees up a * pin for other uses. Different LCDs execute instructions at * different rates and to avoid problems later on (such as if the LCD is * changed to a slower unit), simply use the maximum delays given above. * * Updated for Off-The-Shelf I2C adapters with PCF8574 at address 0x4E * * 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 * of the License, 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, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *----------------------------------------------------------------------*/ #include #include "general.h" #include "timer.h" #include "lcd_i2c.h" #include "i2c_if.h" // the following addresses are available with eBay I2C display adapters: // PCF8574: 0x40 // PCF8574: 0x4E // PCF8574A: 0x7E // these addresses must be supported #define PCF8574A_1_LCD_ADDR 0x70 #define PCF8574A_2_LCD_ADDR 0x78 #define PCF8574A_3_LCD_ADDR 0x7A #define PCF8574_1_LCD_ADDR 0x40 #define LCD_MASK 0x0F #define LCD_INIT_1 0x30 #define LCD_INIT_2 0x20 /* ----- local variables ----- */ static uchar bdata LCD_PORT; sbit LCD_RS = LCD_PORT^0; sbit LCD_RW = LCD_PORT^1; sbit LCD_EN = LCD_PORT^2; sbit LCD_BL = LCD_PORT^3; uchar xdata I2CLCDAddr; /*---------------------------------------------------------------------* * Local function prototypes (public prototypes are in lcd_i2c.h) *----------------------------------------------------------------------*/ void IRWriteLCD8( uchar); // 8 bits mode void IRWriteLCD( uchar ); // 4 bits mode void DRWriteLCD( uchar); bit WaitLCD( void ); void ClockEn( void ); bit WriteLCD( uchar reg, uchar out ); /*=====================================================================* * Public Functions *======================================================================*/ /*---------------------------------------------------------------------* * FUNCTION: InitLCD() * * initialize LCD module per specs * * NOTE: the Noritake display needs at least 260mS after Vcc rise > 4.75V *----------------------------------------------------------------------*/ bit InitLCD( void ){ // 1st part of initialization is in 8 bit mode, but only // the upper 4 bits matter. LCD_PORT = 0; LCD_BL = 1; // find which address the PCF8574 is at I2CLCDAddr = PCF8574A_1_LCD_ADDR; WriteLCD( I2CLCDAddr, LCD_PORT ); if( I2CCheckError() ){ I2CLCDAddr = PCF8574A_2_LCD_ADDR; WriteLCD( I2CLCDAddr, LCD_PORT ); if( I2CCheckError() ){ I2CLCDAddr = PCF8574A_3_LCD_ADDR; WriteLCD( I2CLCDAddr, LCD_PORT ); if( I2CCheckError() ){ I2CLCDAddr = PCF8574_1_LCD_ADDR; WriteLCD( I2CLCDAddr, LCD_PORT ); if( I2CCheckError() ) return( FALSE ); } } } // normal start Wait( 15 ); // Wait more than 15ms LCD_PORT = LCD_INIT_1; // Startup Sequence ClockEn(); // apply data to port, raise E, wait >100uS, lower E Wait( 5 ); // Wait more than 4.1ms ClockEn(); // clock again Wait( 1 ); // Wait more than 0.1ms ClockEn(); // clock again LCD_PORT = LCD_INIT_2; ClockEn(); Wait( 1 ); // Wait more than 0.1ms // LCD is now initialized, remainder of setup is in 4 bits mode IRWriteLCD( 0x28 ); // Function Set // DL=0 4bit, N=1 2Line, F=0 5x7 IRWriteLCD( 0x0C ); // Display on/off control // D=1 Disp on, C=0 Curs off, B=0 Blink off IRWriteLCD( 0x06 ); // Entry Mode Set // I/D=1 Increment, S=0 No shift IRWriteLCD( 0x01 ); // Clear Display Wait( 2 ); // Wait more than 1.64ms return( TRUE ); } // InitLCD() /*---------------------------------------------------------------------* * FUNCTION: ClearLCD() * * to clear line i: ClearLCD( i ); * to clear all lines: ClearLCD( 0 ); *---------------------------------------------------------------------*/ void ClearLCD( uchar line ){ uchar i=0, addr; // Position the cursor switch( line ){ case 0: // clear by instruction (fast) IRWriteLCD( 0x01 ); // wait at least 4.1 mS WaitTicks( 5 ); return; break; case 1: addr = 0x80; break; case 2: addr = 0xC0; break; case 3: addr = 0x94; break; case 4: addr = 0xD4; break; } IRWriteLCD( addr ); for( i=0; i