/* * LiquidCrystalFast.cpp, version 1.1 * Copyright (c) 2010 John Raines Minor changes by Paul Stoffregen , Copyright 2011 This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "LiquidCrystalFast.h" //#include //#include /******************************************/ /** hardware initialization **/ /******************************************/ // When the display powers up, it is configured as follows: // // 1. Display clear // 2. Function set: // DL = 1; 8-bit interface data // N = 0; 1-line display // F = 0; 5x8 dot character font // 3. Display on/off control: // D = 0; Display off // C = 0; Cursor off // B = 0; Blinking off // 4. Entry mode set: // I/D = 1; Increment by 1 // S = 0; No shift // // Note, however, that resetting the Arduino doesn't reset the LCD, so we // can't assume that its in that state when a sketch starts (and the // LiquidCrystalFast constructor is called). void LiquidCrystalFast::init(uint8_t rs, uint8_t rw, uint8_t enable, uint8_t en2, uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3) { _rs_pin = rs; _rw_pin = 255; _enable_pin = enable; _en2 = en2; _chip = 0; _scroll_count = 0; //to fix the bug if we scroll and then setCursor w/o home() or clear() _x = 0; _y = 0; _setCursFlag = 0; _direction = LCD_Right; _data_pins[0] = d0; _data_pins[1] = d1; _data_pins[2] = d2; _data_pins[3] = d3; pinMode(d0, OUTPUT); //set data pin modes pinMode(d1, OUTPUT); pinMode(d2, OUTPUT); pinMode(d3, OUTPUT); row_offsets[0] = 00; // DDRAM addresses inside the HD44780 are strange: 0-nColumns-1 on line 0 row_offsets[1] = 0x40; // 64-(63+nColumns) for line 1 row_offsets[2] = 0x14; // 20- (19+nColumns) for line 2 --- NOTHING FROM 40-63 ! row_offsets[3] = 0x54; // 84 - (83+nColumns) for line 3 -- so 80 characters tops out at #103 ! pinMode(_rs_pin, OUTPUT); // we can save 1 pin by not using RW. Indicate by passing 255 instead of pin# if (rw != 255) { pinMode(rw, OUTPUT); //once in init does it digitalWrite(rw,LOW); //write data to LCD mode } pinMode(_enable_pin, OUTPUT); if( en2 != 255) pinMode(en2,OUTPUT); //4X40 LCD begin(20, 1); _rw_pin = rw; //the game to initialize the 40x4 is over } void LiquidCrystalFast::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { numcols=_numcols=cols; //there is an implied lack of trust; the private version can't be munged up by the user. numlines=_numlines=lines; row_offsets[2] = cols + row_offsets[0]; //should autoadjust for 16/20 or whatever columns now row_offsets[3] = cols + row_offsets[1]; begin2( cols, lines, dotsize, _enable_pin); if (_en2 != 255) { //if we were called with a 2nd enable line i.e. 4x40 LCD row_offsets[2] = 0; row_offsets[3] = 0x40; //each line gets its own little 40 char section of DDRAM--would be fine if there were a 4x32, I suppose _chip = 2; begin2( cols, lines, dotsize,_en2);//initialize the second HD44780 chip } } void LiquidCrystalFast::begin2(uint8_t cols, uint8_t lines, uint8_t dotsize, uint8_t enable) { uint8_t displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; if (lines > 1) { displayfunction |= LCD_2LINE; } // for some 1 line displays you can select a 10 pixel high font if ((dotsize != 0) && (lines == 1)) { displayfunction |= LCD_5x10DOTS; } // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! // according to datasheet, we need at least 40ms after power rises above 2.7V // before sending commands. Arduino can turn on way before 4.5V. // is this delay long enough for all cases?? for (uint8_t i=0;i<18;i++) { delayMicroseconds(7500); } // Now we pull both RS and R/W low to begin commands digitalWrite(_rs_pin, LOW); digitalWrite(enable, LOW); //put the LCD into 4 bit mode // this is according to the hitachi HD44780 datasheet // figure 24, pg 46 // we start in 8bit mode, try to set 4 bit mode // at this point we are in 8 bit mode but of course in this // interface 4 pins are dangling unconnected and the values // on them don't matter for these instructions. digitalWrite(_rs_pin, LOW); write4bits(0x03); delayMicroseconds(5000); // I have one LCD for which 4500 here was not long enough. // second try write4bits(0x03); delayMicroseconds(150); // wait // third go! write4bits(0x03); delayMicroseconds(150); // finally, set to 4-bit interface write4bits(0x02); delayMicroseconds(150); // finally, set # lines, font size, etc. command(LCD_FUNCTIONSET | displayfunction); // turn the display on with no cursor or blinking default _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; display(); // clear it off clear(); // Initialize to default text direction (for romance languages) _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; // set the entry mode command(LCD_ENTRYMODESET | _displaymode); } /******************************************/ /** high level commands, for the user! */ /******************************************/ void LiquidCrystalFast::clear() { if (_en2 != 255) { _chip=2; command(LCD_CLEARDISPLAY); _chip=0; command(LCD_CLEARDISPLAY); delayPerHome(); setCursor(0,0); } else { command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero delayPerHome(); } _scroll_count = 0; } void LiquidCrystalFast::home() { commandBoth(LCD_RETURNHOME); // set cursor position to zero both chips. delayPerHome(); _scroll_count = 0; if (_en2 != 255) setCursor(0,0); } // Turn the display on/off (quickly) void LiquidCrystalFast::noDisplay() { _displaycontrol &= ~LCD_DISPLAYON; commandBoth(LCD_DISPLAYCONTROL | _displaycontrol); //both chips } void LiquidCrystalFast::display() { _displaycontrol |= LCD_DISPLAYON; commandBoth(LCD_DISPLAYCONTROL | _displaycontrol); //both chips } // Turns the underline cursor on/off void LiquidCrystalFast::noCursor() { _displaycontrol &= ~LCD_CURSORON; command(LCD_DISPLAYCONTROL | _displaycontrol); } void LiquidCrystalFast::cursor() { _displaycontrol |= LCD_CURSORON; command(LCD_DISPLAYCONTROL | _displaycontrol); } // Turn on and off the blinking cursor void LiquidCrystalFast::noBlink() { _displaycontrol &= ~LCD_BLINKON; command(LCD_DISPLAYCONTROL | _displaycontrol); } void LiquidCrystalFast::blink() { _displaycontrol |= LCD_BLINKON; command(LCD_DISPLAYCONTROL | _displaycontrol); } // These commands scroll the display without changing the RAM void LiquidCrystalFast::scrollDisplayLeft(void) { commandBoth(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); //both chips _scroll_count++; if (_scroll_count >= 40) _scroll_count -= 40; // -39< scroll_count<39 } void LiquidCrystalFast::scrollDisplayRight(void) { commandBoth(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT); //both chips _scroll_count--; if (_scroll_count <= -40) _scroll_count += 40; } // This is for text that flows Left to Right void LiquidCrystalFast::leftToRight(void) { _displaymode |= LCD_ENTRYLEFT; _direction = LCD_Right; commandBoth(LCD_ENTRYMODESET | _displaymode); //both chips } // This is for text that flows Right to Left void LiquidCrystalFast::rightToLeft(void) { _displaymode &= ~LCD_ENTRYLEFT; _direction = LCD_Left; commandBoth(LCD_ENTRYMODESET | _displaymode); //both chips } // Allows us to fill the first 8 CGRAM locations // with custom characters void LiquidCrystalFast::createChar(uint8_t location, uint8_t charmap[]) { location &= 0x7; // we only have 8 locations 0-7 if (_en2 == 255) { command(LCD_SETCGRAMADDR | (location << 3)); for (int i=0; i<8; i++) { send(charmap[i],HIGH); } } else { uint8_t chipSave = _chip; _chip = 0; command(LCD_SETCGRAMADDR | (location << 3)); for (uint8_t i=0; i<8; i++) { send(charmap[i],HIGH); } _chip = 2; command(LCD_SETCGRAMADDR | (location << 3)); for (uint8_t i=0; i<8; i++) { send(charmap[i],HIGH); } _chip = chipSave; } } void LiquidCrystalFast::setCursor(uint8_t col, uint8_t row) // this can be called by the user but is also called before writing some characters. { if ( row > _numlines ) { row = _numlines-1; // we count rows starting w/0 } _y = row; _x = col; _setCursFlag = 0; //user did a setCursor--clear the flag that may have been set in write() int8_t high_bit = row_offsets[row] & 0x40; // this keeps coordinates pegged to a spot on the LCD screen even if the user scrolls right or int8_t offset = col + (row_offsets[row] &0x3f) + _scroll_count; //left under program control. Previously setCursor was pegged to a location in DDRAM //the 3 quantities we add are each <40 if (offset > 39) offset -= 40; // if the display is autoscrolled this method does not work, however. if (offset < 0) offset += 40; offset |= high_bit; if (_chip != (row & 0b10)) noCursor(); //turn off cursor on chip we are leaving _chip = row & 0b10; //if it is row 0 or 1 this is 0; if it is row 2 or 3 this is 2 command(LCD_SETDDRAMADDR | (byte) offset ); } // This will 'right justify' text from the cursor void LiquidCrystalFast::autoscroll(void) { //to count the number of times we scrolled; here we'd need to keep track of microseconds and divide. I'm not going there. _displaymode |= LCD_ENTRYSHIFTINCREMENT; commandBoth(LCD_ENTRYMODESET | _displaymode); //both chips } // This will 'left justify' text from the cursor void LiquidCrystalFast::noAutoscroll(void) { _displaymode &= ~LCD_ENTRYSHIFTINCREMENT; //both chips commandBoth(LCD_ENTRYMODESET | _displaymode); } /*************************************************/ /** mid level commands, for sending data/cmds */ /*************************************************/ inline void LiquidCrystalFast::command(uint8_t value) { send(value, LOW); } //for many of the commands that need to be sent twice if 2 controller chips void LiquidCrystalFast::commandBoth(uint8_t value) { if (_en2 == 255) { send(value, LOW); // normal LCD, single controller chip } else { uint8_t chipSave = _chip; _chip = 0; send(value,LOW); //send command to first HD44780 _chip = 2; send(value,LOW); //send to 2nd HD44780 _chip=chipSave; } } //print calls this to send characters to the LCD #if defined(ARDUINO) && ARDUINO >= 100 size_t LiquidCrystalFast::write(uint8_t value) { #else void LiquidCrystalFast::write(uint8_t value) { #endif if ((_scroll_count != 0) || (_setCursFlag != 0) ) setCursor(_x,_y); //first we call setCursor and send the character if ((value != '\r') && (value != '\n') ) send(value, HIGH); _setCursFlag = 0; if (_direction == LCD_Right) { // then we update the x & y location for the NEXT character _x++; if ((value == '\r') ||(_x >= _numcols) ) { //romance languages go left to right _x = 0; _y++; _setCursFlag = 1; //we'll need a setCursor() before the next char to move to begin of next line } } else { _x--; if ( (value == '\n') || (_x < 0)) { //emulate right to left text mode which is built in but would be defeated by my code above _x = _numcols-1; _y++; _setCursFlag = 1; } } if (_y >= _numlines) _y = 0; //wrap last line up to line 0 #if defined(ARDUINO) && ARDUINO >= 100 return 1; #endif } /****************************************/ /** low level data pushing commands **/ /****************************************/ // write either command or data, with automatic 4/8-bit selection void LiquidCrystalFast::send(uint8_t value, uint8_t mode) { uint8_t en = _enable_pin; if ((_en2 != 255) && (_chip)) en = _en2; if (_rw_pin == 255) { delayMicroseconds(DELAYPERCHAR); } else { pinMode(_data_pins[0], INPUT); pinMode(_data_pins[1], INPUT); pinMode(_data_pins[2], INPUT); pinMode(_data_pins[3], INPUT); digitalWrite(_rw_pin, HIGH); digitalWrite(_rs_pin, LOW); uint8_t busy; do { digitalWrite(en, HIGH); busy = digitalRead(_data_pins[3]); digitalWrite(en, LOW); digitalWrite(en, HIGH); digitalWrite(en, LOW); } while (busy == HIGH); pinMode(_data_pins[0], OUTPUT); pinMode(_data_pins[1], OUTPUT); pinMode(_data_pins[2], OUTPUT); pinMode(_data_pins[3], OUTPUT); digitalWrite(_rw_pin, LOW); } digitalWrite(_rs_pin, mode); digitalWrite(_data_pins[0], value & 0x10); digitalWrite(_data_pins[1], value & 0x20); digitalWrite(_data_pins[2], value & 0x40); digitalWrite(_data_pins[3], value & 0x80); digitalWrite(en, HIGH); // enable pulse must be >450ns digitalWrite(en, LOW); digitalWrite(_data_pins[0], value & 0x01); digitalWrite(_data_pins[1], value & 0x02); digitalWrite(_data_pins[2], value & 0x04); digitalWrite(_data_pins[3], value & 0x08); digitalWrite(en, HIGH); // enable pulse must be >450ns digitalWrite(en, LOW); } // used during init void LiquidCrystalFast::write4bits(uint8_t value) { uint8_t v=value; uint8_t *pinptr = _data_pins; digitalWrite(*pinptr++, v & 1 ); digitalWrite(*pinptr++,( v >>= 1) & 1 ); digitalWrite(*pinptr++,( v >>= 1) & 1 ); digitalWrite(*pinptr++,( v >>= 1) & 1 ); byte en = _enable_pin; if ((_en2 != 255) && (_chip)) en = _en2; // 4x40 LCD with 2 controller chips with separate enable lines if we called w 2 enable pins and are on lines 2 or 3 enable chip 2 digitalWrite(en, HIGH); // enable pulse must be >450ns digitalWrite(en, LOW); }