Counter

Dependencies:   EthernetInterface NTPClient SDFileSystem TextLCD WebSocketClient mbed-rtos mbed Socket lwip-eth lwip-sys lwip FATFileSystem

Revision:
0:ecaf3e593122
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TextLCD/TextLCD.cpp	Mon Feb 29 18:59:15 2016 +0000
@@ -0,0 +1,1032 @@
+/* mbed TextLCD Library, for a 4-bit LCD based on HD44780
+ * Copyright (c) 2007-2010, sford, http://mbed.org
+ *               2013, v01: WH, Added LCD types, fixed LCD address issues, added Cursor and UDCs 
+ *               2013, v02: WH, Added I2C and SPI bus interfaces  
+ *               2013, v03: WH, Added support for LCD40x4 which uses 2 controllers 
+ *               2013, v04: WH, Added support for Display On/Off, improved 4bit bootprocess
+ *               2013, v05: WH, Added support for 8x2B, added some UDCs   
+ *               2013, v06: WH, Added support for devices that use internal DC/DC converters 
+ *               2013, v07: WH, Added support for backlight and include portdefinitions for LCD2004 Module from DFROBOT 
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "TextLCD.h"
+#include "mbed.h"
+
+
+/* Create a TextLCD interface for using regular mbed pins
+ *
+ * @param rs     Instruction/data control line
+ * @param e      Enable line (clock)
+ * @param d4-d7  Data lines for using as a 4-bit interface
+ * @param type   Sets the panel size/addressing mode (default = LCD16x2)
+ * @param bl     Backlight control line (optional, default = NC)  
+ * @param e2     Enable2 line (clock for second controller, LCD40x4 only) 
+ * @param ctrl   LCD controller (default = HD44780)   
+ */ 
+TextLCD::TextLCD(PinName rs, PinName e,
+                 PinName d4, PinName d5, PinName d6, PinName d7,
+                 LCDType type, PinName bl, PinName e2, LCDCtrl ctrl) : _rs(rs), _e(e), _bl(bl), _e2(e2),
+                                                                       _d(d4, d5, d6, d7),
+                                                                       _cs(NC), 
+                                                                       _type(type),
+                                                                       _ctrl(ctrl) {
+
+  _busType = _PinBus;
+
+  _init();
+
+}
+
+/* Create a TextLCD interface using an I2C PC8574 portexpander
+ *
+ * @param i2c             I2C Bus
+ * @param deviceAddress   I2C slave address (PCF8574)
+ * @param type            Sets the panel size/addressing mode (default = LCD16x2)
+ * @param ctrl            LCD controller (default = HD44780)    
+ */
+TextLCD::TextLCD(I2C *i2c, char deviceAddress, LCDType type, LCDCtrl ctrl) :
+        _rs(NC), _e(NC), _bl(NC), _e2(NC),
+        _d(NC),
+        _i2c(i2c),        
+        _cs(NC),
+        _type(type), 
+        _ctrl(ctrl) {        
+         
+  _slaveAddress = deviceAddress;
+  _busType = _I2CBus;
+
+  
+  // Init the portexpander bus
+  _lcd_bus = D_LCD_BUS_DEF;
+  
+  // write the new data to the portexpander
+  _i2c->write(_slaveAddress, &_lcd_bus, 1);    
+
+  _init();
+    
+}
+
+ /* Create a TextLCD interface using an SPI 74595 portexpander
+  *
+  * @param spi             SPI Bus
+  * @param cs              chip select pin (active low)
+  * @param type            Sets the panel size/addressing mode (default = LCD16x2)
+  * @param ctrl            LCD controller (default = HD44780)      
+  */
+TextLCD::TextLCD(SPI *spi, PinName cs, LCDType type, LCDCtrl ctrl) :
+        _rs(NC), _e(NC), _bl(NC), _e2(NC),
+        _d(NC),
+        _spi(spi),        
+        _cs(cs),
+        _type(type),
+        _ctrl(ctrl) {                
+        
+  _busType = _SPIBus;
+
+  // Setup the spi for 8 bit data, low steady state clock,
+  // rising edge capture, with a 500KHz or 1MHz clock rate  
+  _spi->format(8,0);
+  _spi->frequency(500000);    
+  //_spi.frequency(1000000);    
+
+
+  // Init the portexpander bus
+  _lcd_bus = D_LCD_BUS_DEF;
+  
+  // write the new data to the portexpander
+  _setCS(false);  
+  _spi->write(_lcd_bus);   
+  _setCS(true);  
+  
+  _init();
+    
+}
+
+
+/*  Init the LCD Controller(s)
+ *  Clear display 
+ */
+void TextLCD::_init() {
+  
+  // Select and configure second LCD controller when needed
+  if(_type==LCD40x4) {
+    _ctrl_idx=TextLCD::_LCDCtrl_1; // Select 2nd controller
+    
+    _initCtrl();                   // Init 2nd controller
+    
+    // Secondary LCD controller Clearscreen
+    _writeCommand(0x01);       // cls, and set cursor to 0    
+    wait_ms(10);     // The CLS command takes 1.64 ms.
+                     // Since we are not using the Busy flag, Lets be safe and take 10 ms
+    
+  }
+    
+  // Select and configure primary LCD controller
+  _ctrl_idx=TextLCD::_LCDCtrl_0; // Select primary controller  
+
+  _initCtrl();                   // Init primary controller
+  
+  // Primary LCD controller Clearscreen
+  _writeCommand(0x01);       // cls, and set cursor to 0
+
+  wait_ms(10);     // The CLS command takes 1.64 ms.
+                   // Since we are not using the Busy flag, Lets be safe and take 10 ms
+    
+} 
+
+/*  Init the LCD controller
+ *  4-bit mode, number of lines, fonttype, no cursor etc
+ *  
+ */
+void TextLCD::_initCtrl() {
+
+    _setRS(false);      // command mode
+    
+    wait_ms(20);        // Wait 20ms to ensure powered up
+
+    // send "Display Settings" 3 times (Only top nibble of 0x30 as we've got 4-bit bus)    
+    for (int i=0; i<3; i++) {
+        _writeNibble(0x3);
+        wait_ms(15);     // This command takes 1.64ms, so wait for it 
+    }
+    _writeNibble(0x2);   // 4-bit mode
+    wait_us(40);         // most instructions take 40us
+
+    // Display is now in 4-bit mode
+
+    
+    // Device specific initialisations for DC/DC converter to generate VLCD or VLED
+    switch (_ctrl) {
+      case ST7036:
+          // ST7036 controller: Initialise Voltage booster for VLCD. VDD=5V
+          // Note: supports 1,2 or 3 lines
+          _writeByte( 0x29 );    // 4-bit Databus, 2 Lines, Select Instruction table 1
+          wait_ms(30);           // > 26,3ms 
+          _writeByte( 0x14 );    // Bias: 1/5, 2-Lines LCD 
+          wait_ms(30);           // > 26,3ms
+          _writeByte( 0x55 );    // Icon off, Booster on, Set Contrast C5, C4
+          wait_ms(30);           // > 26,3ms
+          _writeByte( 0x6d );    // Voltagefollower On, Ampl ratio Rab2, Rab1, Rab0
+          wait_ms(200);          // > 200ms!
+          _writeByte( 0x78 );    // Set Contrast C3, C2, C1, C0
+          wait_ms(30);           // > 26,3ms
+          _writeByte( 0x28 );    // Return to Instruction table 0
+          wait_ms(50);
+        
+          break;
+
+      case WS0010:         
+          // WS0010 OLED controller: Initialise DC/DC Voltage converter for LEDs
+          // Note: supports 1 or 2 lines (and 16x100 graphics)
+          //       supports 4 fonts (English/Japanese (default), Western European-I, English/Russian, Western European-II)
+
+                           // Cursor/Disp shift set 0001 SC RL  0 0
+                           //
+                           // Mode en Power set     0001 GC PWR 1 1                           
+                           //  GC  = 0 (Graph Mode=1, Char Mode=0)             
+                           //  PWR =   (DC/DC On/Off)
+    
+          //_writeCommand(0x13);   // DC/DC off            
+    
+          _writeCommand(0x17);   // DC/DC on
+          wait_ms(10);
+          
+          break;
+        
+        default:
+          // Devices that do not use DC/DC Voltage converters but external VLCD
+          break;                  
+    }
+    
+    // Initialise Display configuration
+    switch (_type) {
+        case LCD8x1:
+        case LCD8x2B:        
+            //8x1 is a regular 1 line display
+            //8x2B is a special case of 16x1
+            _writeCommand(0x20); // Function set 001 DL N F - -
+                                 //  DL=0 (4 bits bus)             
+                                 //   N=0 (1 line)
+                                 //   F=0 (5x7 dots font)
+            break;                                
+            
+        case LCD24x4:
+            // Special mode for KS0078
+            _writeCommand(0x2A); // Function set 001 DL N RE DH REV
+                                 //   DL=0  (4 bits bus)             
+                                 //    N=1  (Dont care for KS0078)
+                                 //   RE=0  (Extended Regs, special mode for KS0078)
+                                 //   DH=1  (Disp shift, special mode for KS0078)                                
+                                 //   REV=0 (Reverse, special mode for KS0078)
+
+            _writeCommand(0x2E); // Function set 001 DL N RE DH REV
+                                 //   DL=0  (4 bits bus)             
+                                 //    N=1  (Dont care for KS0078)
+                                 //   RE=1  (Ena Extended Regs, special mode for KS0078)
+                                 //   DH=1  (Disp shift, special mode for KS0078)                                
+                                 //   REV=0 (Reverse, special mode for KS0078)
+
+            _writeCommand(0x09); // Ext Function set 0000 1 FW BW NW
+                                 //   FW=0  (5-dot font, special mode for KS0078)
+                                 //   BW=0  (Cur BW invert disable, special mode for KS0078)
+                                 //   NW=1  (4 Line, special mode for KS0078)                                
+
+            _writeCommand(0x2A); // Function set 001 DL N RE DH REV
+                                 //   DL=0  (4 bits bus)             
+                                 //    N=1  (Dont care for KS0078)
+                                 //   RE=0  (Dis. Extended Regs, special mode for KS0078)
+                                 //   DH=1  (Disp shift, special mode for KS0078)                                
+                                 //   REV=0 (Reverse, special mode for KS0078)
+            break;
+                                            
+// All other LCD types are initialised as 2 Line displays (including LCD40x4)
+        default:
+            _writeCommand(0x28); // Function set 001 DL N F - -
+                                 //  DL=0 (4 bits bus) 
+                                 //   N=1 (2 lines)
+                                 //   F=0 (5x7 dots font, only option for 2 line display)
+                                 //    -  (Don't care)                                
+            
+            break;
+    }
+
+    _writeCommand(0x06); // Entry Mode 0000 01 CD S 
+                         //   Cursor Direction and Display Shift
+                         //   CD=1 (Cur incr)
+                         //   S=0  (No display shift)                        
+
+//    _writeCommand(0x0C); // Display Ctrl 0000 1 D C B
+//                         //   Display On, Cursor Off, Blink Off   
+    setCursor(TextLCD::CurOff_BlkOff);     
+    setMode(TextLCD::DispOn);     
+}
+
+
+// Clear the screen, Cursor home. 
+void TextLCD::cls() {
+
+  // Select and configure second LCD controller when needed
+  if(_type==LCD40x4) {
+    _ctrl_idx=TextLCD::_LCDCtrl_1; // Select 2nd controller
+
+    // Second LCD controller Cursor always Off
+    _setCursorAndDisplayMode(_currentMode, TextLCD::CurOff_BlkOff);
+
+    // Second LCD controller Clearscreen
+    _writeCommand(0x01); // cls, and set cursor to 0    
+
+    wait_ms(10);     // The CLS command takes 1.64 ms.
+                     // Since we are not using the Busy flag, Lets be safe and take 10 ms
+
+  
+    _ctrl_idx=TextLCD::_LCDCtrl_0; // Select primary controller
+  }
+  
+  // Primary LCD controller Clearscreen
+  _writeCommand(0x01); // cls, and set cursor to 0
+
+  wait_ms(10);     // The CLS command takes 1.64 ms.
+                   // Since we are not using the Busy flag, Lets be safe and take 10 ms
+
+  // Restore cursormode on primary LCD controller when needed
+  if(_type==LCD40x4) {
+    _setCursorAndDisplayMode(_currentMode,_currentCursor);     
+  }
+                   
+  _row=0;          // Reset Cursor location
+  _column=0;
+}
+
+// Move cursor to selected row and column
+void TextLCD::locate(int column, int row) {
+    
+   // setAddress() does all the heavy lifting:
+   //   check column and row sanity, 
+   //   switch controllers for LCD40x4 if needed
+   //   switch cursor for LCD40x4 if needed
+   //   set the new memory address to show cursor at correct location
+   setAddress(column, row);
+       
+}
+    
+
+// Write a single character (Stream implementation)
+int TextLCD::_putc(int value) {
+  int addr;
+    
+    if (value == '\n') {
+      //No character to write
+      
+      //Update Cursor      
+      _column = 0;
+      _row++;
+      if (_row >= rows()) {
+        _row = 0;
+      }      
+    }
+    else {
+      //Character to write      
+      _writeData(value); 
+              
+      //Update Cursor
+      _column++;
+      if (_column >= columns()) {
+        _column = 0;
+        _row++;
+        if (_row >= rows()) {
+          _row = 0;
+        }
+      }          
+    } //else
+
+    //Set next memoryaddress, make sure cursor blinks at next location
+    addr = getAddress(_column, _row);
+    _writeCommand(0x80 | addr);
+            
+    return value;
+}
+
+
+// get a single character (Stream implementation)
+int TextLCD::_getc() {
+    return -1;
+}
+
+// Set E pin (or E2 pin)
+// Used for mbed pins, I2C bus expander or SPI shifregister
+void TextLCD::_setEnable(bool value) {
+
+  switch(_busType) {
+    case _PinBus : 
+                    if(_ctrl_idx==TextLCD::_LCDCtrl_0) {
+                      if (value)
+                        _e  = 1;    // Set E bit 
+                      else  
+                        _e  = 0;    // Reset E bit  
+                    }    
+                    else {   
+                      if (value)
+                        _e2 = 1;    // Set E2 bit 
+                      else  
+                        _e2 = 0;    // Reset E2 bit  
+                    }    
+
+                    break;  
+    
+    case _I2CBus : 
+    
+                   if(_ctrl_idx==TextLCD::_LCDCtrl_0) {
+                     if (value)
+                       _lcd_bus |= D_LCD_E;     // Set E bit 
+                     else                     
+                       _lcd_bus &= ~D_LCD_E;    // Reset E bit                     
+                   }
+                   else {
+                     if (value)
+                       _lcd_bus |= D_LCD_E2;    // Set E2 bit 
+                     else                     
+                       _lcd_bus &= ~D_LCD_E2;   // Reset E2bit                     
+                   }    
+
+                   // write the new data to the I2C portexpander
+                   _i2c->write(_slaveAddress, &_lcd_bus, 1);    
+
+                   break;  
+    
+    case _SPIBus :
+                   if(_ctrl_idx==TextLCD::_LCDCtrl_0) {
+                     if (value)
+                       _lcd_bus |= D_LCD_E;     // Set E bit 
+                     else                     
+                       _lcd_bus &= ~D_LCD_E;    // Reset E bit                     
+                   }
+                   else {
+                     if (value)
+                       _lcd_bus |= D_LCD_E2;    // Set E2 bit 
+                     else                     
+                       _lcd_bus &= ~D_LCD_E2;   // Reset E2 bit                     
+                   }
+                  
+                   // write the new data to the SPI portexpander
+                   _setCS(false);  
+                   _spi->write(_lcd_bus);   
+                   _setCS(true);  
+  
+                   break;
+  }
+}    
+
+// Set RS pin
+// Used for mbed pins, I2C bus expander or SPI shifregister
+void TextLCD::_setRS(bool value) {
+
+  switch(_busType) {
+    case _PinBus : 
+                    if (value)
+                      _rs  = 1;    // Set RS bit 
+                    else  
+                      _rs  = 0;    // Reset RS bit 
+
+                    break;  
+    
+    case _I2CBus : 
+                   if (value)
+                     _lcd_bus |= D_LCD_RS;    // Set RS bit 
+                   else                     
+                     _lcd_bus &= ~D_LCD_RS;   // Reset RS bit                     
+
+                   // write the new data to the I2C portexpander
+                   _i2c->write(_slaveAddress, &_lcd_bus, 1);    
+                   
+                   break;
+                       
+    case _SPIBus :
+                   if (value)
+                     _lcd_bus |= D_LCD_RS;    // Set RS bit 
+                   else                     
+                     _lcd_bus &= ~D_LCD_RS;   // Reset RS bit                     
+      
+                   // write the new data to the SPI portexpander
+                   _setCS(false);  
+                   _spi->write(_lcd_bus);   
+                   _setCS(true);  
+     
+                   break;
+  }
+
+}    
+
+// Set BL pin
+// Used for mbed pins, I2C bus expander or SPI shifregister
+void TextLCD::_setBL(bool value) {
+
+  switch(_busType) {
+    case _PinBus : 
+                    if (value)
+                      _bl  = 1;    // Set BL bit 
+                    else  
+                      _bl  = 0;    // Reset BL bit 
+
+                    break;  
+    
+    case _I2CBus : 
+                   if (value)
+                     _lcd_bus |= D_LCD_BL;    // Set BL bit 
+                   else                     
+                     _lcd_bus &= ~D_LCD_BL;   // Reset BL bit                     
+
+                   // write the new data to the I2C portexpander
+                   _i2c->write(_slaveAddress, &_lcd_bus, 1);    
+                   
+                   break;
+                       
+    case _SPIBus :
+                   if (value)
+                     _lcd_bus |= D_LCD_BL;    // Set BL bit 
+                   else                     
+                     _lcd_bus &= ~D_LCD_BL;   // Reset BL bit                     
+      
+                   // write the new data to the SPI portexpander
+                   _setCS(false);  
+                   _spi->write(_lcd_bus);   
+                   _setCS(true);  
+     
+                   break;
+  }
+
+}    
+
+
+
+// Place the 4bit data on the databus
+// Used for mbed pins, I2C bus expander or SPI shifregister
+void TextLCD::_setData(int value) {
+  int data;
+  
+  switch(_busType) {
+    case _PinBus : 
+                    _d = value & 0x0F;   // Write Databits 
+
+                    break;  
+    
+    case _I2CBus : 
+                    data = value & 0x0F;
+                    if (data & 0x01)
+                      _lcd_bus |= D_LCD_D4;   // Set Databit 
+                    else                     
+                      _lcd_bus &= ~D_LCD_D4;  // Reset Databit                     
+
+                    if (data & 0x02)
+                      _lcd_bus |= D_LCD_D5;   // Set Databit 
+                    else                     
+                      _lcd_bus &= ~D_LCD_D5;  // Reset Databit                     
+
+                    if (data & 0x04)
+                      _lcd_bus |= D_LCD_D6;   // Set Databit 
+                    else                     
+                      _lcd_bus &= ~D_LCD_D6;  // Reset Databit                     
+
+                    if (data & 0x08)
+                      _lcd_bus |= D_LCD_D7;   // Set Databit 
+                    else                     
+                      _lcd_bus &= ~D_LCD_D7;  // Reset Databit                     
+                    
+                    // write the new data to the I2C portexpander
+                    _i2c->write(_slaveAddress, &_lcd_bus, 1);  
+                   
+                    break;                    
+                    
+    case _SPIBus :
+    
+                    data = value & 0x0F;
+                    if (data & 0x01)
+                      _lcd_bus |= D_LCD_D4;   // Set Databit 
+                    else                     
+                      _lcd_bus &= ~D_LCD_D4;  // Reset Databit                     
+
+                    if (data & 0x02)
+                      _lcd_bus |= D_LCD_D5;   // Set Databit 
+                    else                     
+                      _lcd_bus &= ~D_LCD_D5;  // Reset Databit                     
+
+                    if (data & 0x04)
+                      _lcd_bus |= D_LCD_D6;   // Set Databit 
+                    else                     
+                      _lcd_bus &= ~D_LCD_D6;  // Reset Databit                     
+
+                    if (data & 0x08)
+                      _lcd_bus |= D_LCD_D7;   // Set Databit 
+                    else                     
+                      _lcd_bus &= ~D_LCD_D7;  // Reset Databit                     
+                    
+                   // write the new data to the SPI portexpander
+                   _setCS(false);  
+                   _spi->write(_lcd_bus);   
+                   _setCS(true);  
+        
+                   break;
+  }
+
+}    
+
+
+// Set CS line.
+// Only used for SPI bus
+void TextLCD::_setCS(bool value) {
+
+  if (value) {   
+    _cs  = 1;    // Set CS pin 
+  }  
+  else  
+    _cs  = 0;    // Reset CS pin 
+  
+}
+
+
+// Write a nibble using the 4-bit interface
+// Used for mbed pins, I2C bus expander or SPI shifregister
+void TextLCD::_writeNibble(int value) {
+
+// Enable is Low
+    _setEnable(true);        
+    _setData(value & 0x0F);   // Low nibble
+    wait_us(1); // Data setup time        
+    _setEnable(false);    
+    wait_us(1); // Datahold time
+
+// Enable is Low
+
+}
+
+
+// Write a byte using the 4-bit interface
+// Used for mbed pins, I2C bus expander or SPI shifregister
+void TextLCD::_writeByte(int value) {
+
+// Enable is Low
+    _setEnable(true);          
+    _setData(value >> 4);   // High nibble
+    wait_us(1); // Data setup time    
+    _setEnable(false);   
+    wait_us(1); // Data hold time
+    
+    _setEnable(true);        
+    _setData(value >> 0);   // Low nibble
+    wait_us(1); // Data setup time        
+    _setEnable(false);    
+    wait_us(1); // Datahold time
+
+// Enable is Low
+
+}
+
+void TextLCD::_writeCommand(int command) {
+
+    _setRS(false);        
+    wait_us(1);  // Data setup time for RS       
+    
+    _writeByte(command);   
+    wait_us(40); // most instructions take 40us            
+}
+
+void TextLCD::_writeData(int data) {
+
+    _setRS(true);            
+    wait_us(1);  // Data setup time for RS 
+        
+    _writeByte(data);
+    wait_us(40); // data writes take 40us                
+}
+
+
+#if (0)
+// This is the original _address() method.
+// It is confusing since it returns the memoryaddress or-ed with the set memorycommand 0x80.
+// Left it in here for compatibility with older code. New applications should use getAddress() instead.
+// 
+int TextLCD::_address(int column, int row) {
+    switch (_type) {
+        case LCD20x4:
+            switch (row) {
+                case 0:
+                    return 0x80 + column;
+                case 1:
+                    return 0xc0 + column;
+                case 2:
+                    return 0x94 + column;
+                case 3:
+                    return 0xd4 + column;
+            }
+        case LCD16x2B:
+            return 0x80 + (row * 40) + column;
+        case LCD16x2:
+        case LCD20x2:
+        default:
+            return 0x80 + (row * 0x40) + column;
+    }
+}
+#endif
+
+
+// This replaces the original _address() method.
+// Left it in here for compatibility with older code. New applications should use getAddress() instead.
+int TextLCD::_address(int column, int row) {
+  return 0x80 | getAddress(column, row);
+}
+
+// This is new method to return the memory address based on row, column and displaytype.
+//
+int TextLCD::getAddress(int column, int row) {
+
+    switch (_type) {
+        case LCD8x1:
+            return 0x00 + column;                        
+
+        case LCD8x2B:
+            // LCD8x2B is a special layout of LCD16x1
+            if (row==0) 
+              return 0x00 + column;                        
+            else   
+              return 0x08 + column;                        
+
+
+        case LCD16x1:
+            // LCD16x1 is a special layout of LCD8x2
+            if (column<8) 
+              return 0x00 + column;                        
+            else   
+              return 0x40 + (column - 8);                        
+
+        case LCD12x4:
+            switch (row) {
+                case 0:
+                    return 0x00 + column;
+                case 1:
+                    return 0x40 + column;
+                case 2:
+                    return 0x0C + column;
+                case 3:
+                    return 0x4C + column;
+            }
+
+        case LCD16x4:
+            switch (row) {
+                case 0:
+                    return 0x00 + column;
+                case 1:
+                    return 0x40 + column;
+                case 2:
+                    return 0x10 + column;
+                case 3:
+                    return 0x50 + column;
+            }
+
+        case LCD20x4:
+            switch (row) {
+                case 0:
+                    return 0x00 + column;
+                case 1:
+                    return 0x40 + column;
+                case 2:
+                    return 0x14 + column;
+                case 3:
+                    return 0x54 + column;
+            }
+
+// Special mode for KS0078
+        case LCD24x4:
+            switch (row) {
+                case 0:
+                    return 0x00 + column;
+                case 1:
+                    return 0x20 + column;
+                case 2:
+                    return 0x40 + column;
+                case 3:
+                    return 0x60 + column;
+            }
+
+// Not sure about this one, seems wrong.
+        case LCD16x2B:      
+            return 0x00 + (row * 40) + column;
+      
+        case LCD8x2:               
+        case LCD12x2:                
+        case LCD16x2:
+        case LCD20x2:
+        case LCD24x2:        
+        case LCD40x2:                
+            return 0x00 + (row * 0x40) + column;
+
+        case LCD40x4:                
+          // LCD40x4 is a special case since it has 2 controllers
+          // Each controller is configured as 40x2
+          if (row<2) { 
+            // Test to see if we need to switch between controllers  
+            if (_ctrl_idx != _LCDCtrl_0) {
+
+              // Second LCD controller Cursor Off
+              _setCursorAndDisplayMode(_currentMode, TextLCD::CurOff_BlkOff);    
+
+              // Select primary controller
+              _ctrl_idx = _LCDCtrl_0;
+
+              // Restore cursormode on primary LCD controller
+              _setCursorAndDisplayMode(_currentMode, _currentCursor);    
+            }           
+            
+            return 0x00 + (row * 0x40) + column;          
+          }
+          else {
+
+            // Test to see if we need to switch between controllers  
+            if (_ctrl_idx != _LCDCtrl_1) {
+              // Primary LCD controller Cursor Off
+              _setCursorAndDisplayMode(_currentMode, TextLCD::CurOff_BlkOff);    
+
+              // Select secondary controller
+              _ctrl_idx = _LCDCtrl_1;
+
+              // Restore cursormode on secondary LCD controller
+              _setCursorAndDisplayMode(_currentMode, _currentCursor);    
+            }           
+                                   
+            return 0x00 + ((row-2) * 0x40) + column;          
+          } 
+            
+// Should never get here.
+        default:            
+            return 0x00;        
+    }
+}
+
+
+// Set row, column and update memoryaddress.
+//
+void TextLCD::setAddress(int column, int row) {
+   
+// Sanity Check column
+    if (column < 0) {
+      _column = 0;
+    }
+    else if (column >= columns()) {
+      _column = columns() - 1;
+    } else _column = column;
+    
+// Sanity Check row
+    if (row < 0) {
+      _row = 0;
+    }
+    else if (row >= rows()) {
+      _row = rows() - 1;
+    } else _row = row;
+    
+    
+// Compute the memory address
+// For LCD40x4:  switch controllers if needed
+//               switch cursor if needed
+    int addr = getAddress(_column, _row);
+    
+    _writeCommand(0x80 | addr);
+}
+
+int TextLCD::columns() {
+    switch (_type) {
+        case LCD8x1:
+        case LCD8x2:
+        case LCD8x2B:                
+            return 8;
+        
+        case LCD12x2:        
+        case LCD12x4:        
+            return 12;        
+
+        case LCD16x1:        
+        case LCD16x2:
+        case LCD16x2B:
+        case LCD16x4:        
+            return 16;
+            
+        case LCD20x2:
+        case LCD20x4:
+            return 20;
+
+        case LCD24x2:
+        case LCD24x4:        
+            return 24;        
+
+        case LCD40x2:
+        case LCD40x4:
+            return 40;        
+        
+// Should never get here.
+        default:
+            return 0;
+    }
+}
+
+int TextLCD::rows() {
+    switch (_type) {
+        case LCD8x1: 
+        case LCD16x1:         
+            return 1;           
+
+        case LCD8x2:  
+        case LCD8x2B:                        
+        case LCD12x2:                      
+        case LCD16x2:
+        case LCD16x2B:
+        case LCD20x2:
+        case LCD24x2:        
+        case LCD40x2:                
+            return 2;
+                    
+        case LCD12x4:        
+        case LCD16x4:
+        case LCD20x4:
+        case LCD24x4:        
+        case LCD40x4:
+            return 4;
+
+// Should never get here.      
+        default:
+            return 0;        
+    }
+}
+
+
+// Set the Cursor Mode (Cursor Off & Blink Off, Cursor On & Blink Off, Cursor Off & Blink On, Cursor On & Blink On
+void TextLCD::setCursor(TextLCD::LCDCursor cursorMode) { 
+
+  // Save new cursor mode, needed when 2 controllers are in use or when display is switched off/on
+  _currentCursor = cursorMode;
+    
+  // Configure only current LCD controller
+  _setCursorAndDisplayMode(_currentMode, _currentCursor);
+    
+}
+
+// Set the Displaymode (On/Off)
+void TextLCD::setMode(TextLCD::LCDMode displayMode) { 
+
+  // Save new displayMode, needed when 2 controllers are in use or when cursor is changed
+  _currentMode = displayMode;
+    
+  // Select and configure second LCD controller when needed
+  if(_type==LCD40x4) {
+    if (_ctrl_idx==TextLCD::_LCDCtrl_0) {
+      // Configure primary LCD controller
+      _setCursorAndDisplayMode(_currentMode, _currentCursor);
+
+      // Select 2nd controller
+      _ctrl_idx=TextLCD::_LCDCtrl_1;
+  
+      // Configure secondary LCD controller    
+      _setCursorAndDisplayMode(_currentMode, TextLCD::CurOff_BlkOff);
+
+      // Restore current controller
+      _ctrl_idx=TextLCD::_LCDCtrl_0;       
+    }
+    else {
+      // Select primary controller
+      _ctrl_idx=TextLCD::_LCDCtrl_0;
+    
+      // Configure primary LCD controller
+      _setCursorAndDisplayMode(_currentMode, TextLCD::CurOff_BlkOff);
+       
+      // Restore current controller
+      _ctrl_idx=TextLCD::_LCDCtrl_1;
+
+      // Configure secondary LCD controller    
+      _setCursorAndDisplayMode(_currentMode, _currentCursor);
+
+    }
+  }
+  else {
+    // Configure primary LCD controller
+    _setCursorAndDisplayMode(_currentMode, _currentCursor);
+  }   
+    
+}
+
+
+// Set the Displaymode (On/Off) and Cursortype for current controller
+void TextLCD::_setCursorAndDisplayMode(TextLCD::LCDMode displayMode, TextLCD::LCDCursor cursorType) { 
+    
+    // Configure current LCD controller       
+    _writeCommand(0x08 | displayMode | cursorType);
+}
+
+// Set the Backlight mode (Off/On)
+void TextLCD::setBacklight(TextLCD::LCDBacklight backlightMode) {
+
+    if (backlightMode == LightOn) {
+      _setBL(true);
+    }
+    else {
+      _setBL(false);    
+    }
+} 
+
+
+void TextLCD::setUDC(unsigned char c, char *udc_data) {
+  
+  // Select and configure second LCD controller when needed
+  if(_type==LCD40x4) {
+    _LCDCtrl_Idx current_ctrl_idx = _ctrl_idx; // Temp save current controller
+   
+    // Select primary controller     
+    _ctrl_idx=TextLCD::_LCDCtrl_0;
+    
+    // Configure primary LCD controller
+    _setUDC(c, udc_data);
+
+    // Select 2nd controller
+    _ctrl_idx=TextLCD::_LCDCtrl_1;
+  
+    // Configure secondary LCD controller    
+    _setUDC(c, udc_data);
+
+    // Restore current controller
+    _ctrl_idx=current_ctrl_idx;       
+  }
+  else {
+    // Configure primary LCD controller
+    _setUDC(c, udc_data); 
+  }
+    
+}
+
+void TextLCD::_setUDC(unsigned char c, char *udc_data) {
+  
+  // Select CG RAM for current LCD controller
+  _writeCommand(0x40 + ((c & 0x07) << 3)); //Set CG-RAM address,
+                                           //8 sequential locations needed per UDC
+  // Store UDC pattern 
+  for (int i=0; i<8; i++) {
+    _writeData(*udc_data++);
+  }
+   
+  //Select DD RAM again for current LCD controller
+  int addr = getAddress(_column, _row);
+  _writeCommand(0x80 | addr);
+  
+}