|
| 1 | +/// |
| 2 | +/// \file LiquidCrystal_PCF8574.cpp |
| 3 | +/// \brief LiquidCrystal (LCD) library with PCF8574 I2C adapter. |
| 4 | +/// |
| 5 | +/// \author Matthias Hertel, http://www.mathertel.de |
| 6 | +/// \copyright Copyright (c) 2014 by Matthias Hertel.\n |
| 7 | +/// This work is licensed under a BSD style license.\n |
| 8 | +/// See http://www.mathertel.de/License.aspx |
| 9 | +/// |
| 10 | +/// \details |
| 11 | +/// This is a library for driving LiquidCrystal displays (LCD) by using the I2C bus and an PCF8574 I2C adapter. |
| 12 | +/// This library is derived from the original Arduino LiquidCrystal library and uses the original Wire library for communication. |
| 13 | +/// |
| 14 | +/// More documentation and source code is available at http://www.mathertel.de/Arduino |
| 15 | +/// |
| 16 | +/// ChangeLog see: LiquidCrystal_PCF8574.h |
| 17 | + |
| 18 | +#include "LiquidCrystal_PCF8574.h" |
| 19 | + |
| 20 | +#include <stdio.h> |
| 21 | +#include <string.h> |
| 22 | +#include <inttypes.h> |
| 23 | +#include "Arduino.h" |
| 24 | +#include <Wire.h> |
| 25 | + |
| 26 | +/// Definitions on how the PCF8574 is connected to the LCD |
| 27 | + |
| 28 | +/// These are Bit-Masks for the special signals and background light |
| 29 | +#define PCF_RS 0x01 |
| 30 | +#define PCF_RW 0x02 |
| 31 | +#define PCF_EN 0x04 |
| 32 | +#define PCF_BACKLIGHT 0x08 |
| 33 | + |
| 34 | +// Definitions on how the PCF8574 is connected to the LCD |
| 35 | +// These are Bit-Masks for the special signals and Background |
| 36 | +#define RSMODE_CMD 0 |
| 37 | +#define RSMODE_DATA 1 |
| 38 | + |
| 39 | + |
| 40 | +// When the display powers up, it is configured as follows: |
| 41 | +// |
| 42 | +// 1. Display clear |
| 43 | +// 2. Function set: |
| 44 | +// DL = 1; 8-bit interface data |
| 45 | +// N = 0; 1-line display |
| 46 | +// F = 0; 5x8 dot character font |
| 47 | +// 3. Display on/off control: |
| 48 | +// D = 0; Display off |
| 49 | +// C = 0; Cursor off |
| 50 | +// B = 0; Blinking off |
| 51 | +// 4. Entry mode set: |
| 52 | +// I/D = 1; Increment by 1 |
| 53 | +// S = 0; No shift |
| 54 | +// |
| 55 | +// Note, however, that resetting the Arduino doesn't reset the LCD, so we |
| 56 | +// can't assume that its in that state when a sketch starts (and the |
| 57 | +// LiquidCrystal constructor is called). |
| 58 | + |
| 59 | +// modification: |
| 60 | +// don't use ports from Arduino, but use ports from Wire |
| 61 | + |
| 62 | +// a nibble is a half Byte |
| 63 | + |
| 64 | +// NEW: http://playground.arduino.cc//Code/LCDAPI |
| 65 | +// NEW: setBacklight |
| 66 | + |
| 67 | + |
| 68 | +LiquidCrystal_PCF8574::LiquidCrystal_PCF8574(uint8_t addr) |
| 69 | +{ |
| 70 | + _Addr = addr; |
| 71 | + _backlight = 0; |
| 72 | +} // LiquidCrystal_PCF8574 |
| 73 | + |
| 74 | + |
| 75 | +void LiquidCrystal_PCF8574::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { |
| 76 | + // cols ignored ! |
| 77 | + _numlines = lines; |
| 78 | + |
| 79 | + _displayfunction = 0; |
| 80 | + |
| 81 | + if (lines > 1) { |
| 82 | + _displayfunction |= LCD_2LINE; |
| 83 | + } |
| 84 | + |
| 85 | + // for some 1 line displays you can select a 10 pixel high font |
| 86 | + if ((dotsize != 0) && (lines == 1)) { |
| 87 | + _displayfunction |= LCD_5x10DOTS; |
| 88 | + } |
| 89 | + |
| 90 | + // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! |
| 91 | + // according to datasheet, we need at least 40ms after power rises above 2.7V |
| 92 | + // before sending commands. Arduino can turn on way befor 4.5V so we'll wait 50 |
| 93 | + Wire.begin(); |
| 94 | + |
| 95 | + // initializing th display |
| 96 | + _write2Wire(0x00, LOW, false); |
| 97 | + delayMicroseconds(50000); |
| 98 | + |
| 99 | + // put the LCD into 4 bit mode according to the hitachi HD44780 datasheet figure 26, pg 47 |
| 100 | + _sendNibble(0x03, RSMODE_CMD); |
| 101 | + delayMicroseconds(4500); |
| 102 | + _sendNibble(0x03, RSMODE_CMD); |
| 103 | + delayMicroseconds(4500); |
| 104 | + _sendNibble(0x03, RSMODE_CMD); |
| 105 | + delayMicroseconds(150); |
| 106 | + // finally, set to 4-bit interface |
| 107 | + _sendNibble(0x02, RSMODE_CMD); |
| 108 | + |
| 109 | + // finally, set # lines, font size, etc. |
| 110 | + _command(LCD_FUNCTIONSET | _displayfunction); |
| 111 | + |
| 112 | + // turn the display on with no cursor or blinking default |
| 113 | + _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; |
| 114 | + display(); |
| 115 | + |
| 116 | + // clear it off |
| 117 | + clear(); |
| 118 | + |
| 119 | + // Initialize to default text direction (for romance languages) |
| 120 | + _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; |
| 121 | + // set the entry mode |
| 122 | + _command(LCD_ENTRYMODESET | _displaymode); |
| 123 | +} |
| 124 | + |
| 125 | +/********** high level commands, for the user! */ |
| 126 | +void LiquidCrystal_PCF8574::clear() |
| 127 | +{ |
| 128 | + _command(LCD_CLEARDISPLAY); // clear display, set cursor position to zero |
| 129 | + delayMicroseconds(2000); // this command takes a long time! |
| 130 | +} |
| 131 | + |
| 132 | +void LiquidCrystal_PCF8574::home() |
| 133 | +{ |
| 134 | + _command(LCD_RETURNHOME); // set cursor position to zero |
| 135 | + delayMicroseconds(2000); // this command takes a long time! |
| 136 | +} |
| 137 | + |
| 138 | + |
| 139 | +/// Set the cursor to a new position. |
| 140 | +void LiquidCrystal_PCF8574::setCursor(uint8_t col, uint8_t row) |
| 141 | +{ |
| 142 | + int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; |
| 143 | + if ( row >= _numlines ) { |
| 144 | + row = _numlines-1; // we count rows starting w/0 |
| 145 | + } |
| 146 | + |
| 147 | + _command(LCD_SETDDRAMADDR | (col + row_offsets[row])); |
| 148 | +} |
| 149 | + |
| 150 | +// Turn the display on/off (quickly) |
| 151 | +void LiquidCrystal_PCF8574::noDisplay() { |
| 152 | + _displaycontrol &= ~LCD_DISPLAYON; |
| 153 | + _command(LCD_DISPLAYCONTROL | _displaycontrol); |
| 154 | +} |
| 155 | +void LiquidCrystal_PCF8574::display() { |
| 156 | + _displaycontrol |= LCD_DISPLAYON; |
| 157 | + _command(LCD_DISPLAYCONTROL | _displaycontrol); |
| 158 | +} |
| 159 | + |
| 160 | +// Turns the underline cursor on/off |
| 161 | +void LiquidCrystal_PCF8574::noCursor() { |
| 162 | + _displaycontrol &= ~LCD_CURSORON; |
| 163 | + _command(LCD_DISPLAYCONTROL | _displaycontrol); |
| 164 | +} |
| 165 | +void LiquidCrystal_PCF8574::cursor() { |
| 166 | + _displaycontrol |= LCD_CURSORON; |
| 167 | + _command(LCD_DISPLAYCONTROL | _displaycontrol); |
| 168 | +} |
| 169 | + |
| 170 | +// Turn on and off the blinking cursor |
| 171 | +void LiquidCrystal_PCF8574::noBlink() { |
| 172 | + _displaycontrol &= ~LCD_BLINKON; |
| 173 | + _command(LCD_DISPLAYCONTROL | _displaycontrol); |
| 174 | +} |
| 175 | +void LiquidCrystal_PCF8574::blink() { |
| 176 | + _displaycontrol |= LCD_BLINKON; |
| 177 | + _command(LCD_DISPLAYCONTROL | _displaycontrol); |
| 178 | +} |
| 179 | + |
| 180 | +// These commands scroll the display without changing the RAM |
| 181 | +void LiquidCrystal_PCF8574::scrollDisplayLeft(void) { |
| 182 | + _command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); |
| 183 | +} |
| 184 | +void LiquidCrystal_PCF8574::scrollDisplayRight(void) { |
| 185 | + _command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT); |
| 186 | +} |
| 187 | + |
| 188 | +// This is for text that flows Left to Right |
| 189 | +void LiquidCrystal_PCF8574::leftToRight(void) { |
| 190 | + _displaymode |= LCD_ENTRYLEFT; |
| 191 | + _command(LCD_ENTRYMODESET | _displaymode); |
| 192 | +} |
| 193 | + |
| 194 | +// This is for text that flows Right to Left |
| 195 | +void LiquidCrystal_PCF8574::rightToLeft(void) { |
| 196 | + _displaymode &= ~LCD_ENTRYLEFT; |
| 197 | + _command(LCD_ENTRYMODESET | _displaymode); |
| 198 | +} |
| 199 | + |
| 200 | +// This will 'right justify' text from the cursor |
| 201 | +void LiquidCrystal_PCF8574::autoscroll(void) { |
| 202 | + _displaymode |= LCD_ENTRYSHIFTINCREMENT; |
| 203 | + _command(LCD_ENTRYMODESET | _displaymode); |
| 204 | +} |
| 205 | + |
| 206 | +// This will 'left justify' text from the cursor |
| 207 | +void LiquidCrystal_PCF8574::noAutoscroll(void) { |
| 208 | + _displaymode &= ~LCD_ENTRYSHIFTINCREMENT; |
| 209 | + _command(LCD_ENTRYMODESET | _displaymode); |
| 210 | +} |
| 211 | + |
| 212 | + |
| 213 | +/// Setting the brightness of the background display light. |
| 214 | +/// The backlight can be switched on and off. |
| 215 | +/// The current brightness is stored in the private _backlight variable to have it available for further data transfers. |
| 216 | +void LiquidCrystal_PCF8574::setBacklight(uint8_t brightness) { |
| 217 | + _backlight = brightness; |
| 218 | + // send no data but set the background-pin right; |
| 219 | + _write2Wire(0x00, RSMODE_DATA, false); |
| 220 | +} // setBacklight |
| 221 | + |
| 222 | + |
| 223 | +// Allows us to fill the first 8 CGRAM locations |
| 224 | +// with custom characters |
| 225 | +void LiquidCrystal_PCF8574::createChar(uint8_t location, uint8_t charmap[]) { |
| 226 | + location &= 0x7; // we only have 8 locations 0-7 |
| 227 | + _command(LCD_SETCGRAMADDR | (location << 3)); |
| 228 | + for (int i=0; i<8; i++) { |
| 229 | + write(charmap[i]); |
| 230 | + } |
| 231 | +} |
| 232 | + |
| 233 | +/* The write function is needed for derivation from the Print class. */ |
| 234 | +inline size_t LiquidCrystal_PCF8574::write(uint8_t value) { |
| 235 | + _send(value, RSMODE_DATA); |
| 236 | + return 1; // assume sucess |
| 237 | +} |
| 238 | + |
| 239 | +/* ----- low level functions ----- */ |
| 240 | + |
| 241 | +inline void LiquidCrystal_PCF8574::_command(uint8_t value) { |
| 242 | + _send(value, RSMODE_CMD); |
| 243 | +} // _command() |
| 244 | + |
| 245 | + |
| 246 | +// write either command or data |
| 247 | +void LiquidCrystal_PCF8574::_send(uint8_t value, uint8_t mode) { |
| 248 | + // separate the 4 value-nibbles |
| 249 | + uint8_t valueLo = value & 0x0F; |
| 250 | + uint8_t valueHi = value>>4 & 0x0F; |
| 251 | + |
| 252 | + _sendNibble(valueHi, mode); |
| 253 | + _sendNibble(valueLo, mode); |
| 254 | +} // _send() |
| 255 | + |
| 256 | + |
| 257 | +// write a nibble / halfByte with handshake |
| 258 | +void LiquidCrystal_PCF8574::_sendNibble(uint8_t halfByte, uint8_t mode) { |
| 259 | + _write2Wire(halfByte, mode, true); |
| 260 | + delayMicroseconds(1); // enable pulse must be >450ns |
| 261 | + _write2Wire(halfByte, mode, false); |
| 262 | + delayMicroseconds(37); // commands need > 37us to settle |
| 263 | +} // _sendNibble |
| 264 | + |
| 265 | + |
| 266 | +// private function to change the PCF8674 pins to the given value |
| 267 | +void LiquidCrystal_PCF8574::_write2Wire(uint8_t halfByte, uint8_t mode, uint8_t enable) { |
| 268 | + // map the given values to the hardware of the I2C schema |
| 269 | + uint8_t i2cData = halfByte << 4; |
| 270 | + if (mode > 0) i2cData |= PCF_RS; |
| 271 | + // PCF_RW is never used. |
| 272 | + if (enable > 0) i2cData |= PCF_EN; |
| 273 | + if (_backlight > 0) i2cData |= PCF_BACKLIGHT; |
| 274 | + |
| 275 | + Wire.beginTransmission(_Addr); |
| 276 | + Wire.write(i2cData); |
| 277 | + Wire.endTransmission(); |
| 278 | +} // write2Wire |
| 279 | + |
| 280 | + |
| 281 | +// The End. |
0 commit comments