Counter
Dependencies: EthernetInterface NTPClient SDFileSystem TextLCD WebSocketClient mbed-rtos mbed Socket lwip-eth lwip-sys lwip FATFileSystem
Diff: TextLCD/TextLCD.cpp
- 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); + +}