// write either command or data, burst it to the expander over I2C. void LiquidTWI::send(uint8_t value, uint8_t mode) { // BURST SPEED, OH MY GOD // the (now High Speed!) I/O expander pinout // RS pin = 1 // Enable pin = 2 // Data pin 4 = 3 // Data pin 5 = 4 // Data pin 6 = 5 // Data pin 7 = 6 byte buf; // crunch the high 4 bits buf = (value & B11110000) >> 1; // isolate high 4 bits, shift over to data pins (bits 6-3: x1111xxx) if (mode) buf |= 3 << 1; // here we can just enable enable, since the value is immediately written to the pins else buf |= 2 << 1; // if RS (mode), turn RS and enable on. otherwise, just enable. (bits 2-1: xxxxx11x) buf |= _displaycontrol & LCD_BACKLIGHT; // LCD_BACKLIGHT = 0x80 backlight bit (1xxxxxxx) = convenient! burstBits(buf); // bits are now present at LCD with enable active in the same write // no need to delay since these things take WAY, WAY longer than the time required for enable to settle (1us in LCD implementation?) buf &= ~(1<<2); // toggle enable low burstBits(buf); // send out the same bits but with enable low now; LCD crunches these 4 bits. // crunch the low 4 bits buf = (value & B1111) << 3; // isolate low 4 bits, shift over to data pins (bits 6-3: x1111xxx) if (mode) buf |= 3 << 1; // here we can just enable enable, since the value is immediately written to the pins else buf |= 2 << 1; // if RS (mode), turn RS and enable on. otherwise, just enable. (bits 2-1: xxxxx11x) buf |= _displaycontrol & LCD_BACKLIGHT; // LCD_BACKLIGHT = 0x80 = LCD_DISPLAYCOMMAND = backlight bit (1xxxxxxx) = convenient! burstBits(buf); buf &= ~( 1 << 2 ); // toggle enable low (1<<2 = 00000100; NOT = 11111011; with "and", this turns off only that one bit) burstBits(buf); }
void LiquidTWI::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { Wire.begin(); // first thing we do is get the GPIO expander's head working straight, with a boatload of junk data. Wire.beginTransmission(MCP23008_ADDRESS | _i2cAddr); Wire.send(MCP23008_IODIR); Wire.send(0xFF); Wire.send(0x00); Wire.send(0x00); Wire.send(0x00); Wire.send(0x00); Wire.send(0x00); Wire.send(0x00); Wire.send(0x00); Wire.send(0x00); Wire.send(0x00); Wire.endTransmission(); // now we set the GPIO expander's I/O direction to output since it's soldered to an LCD output. Wire.beginTransmission(MCP23008_ADDRESS | _i2cAddr); Wire.send(MCP23008_IODIR); Wire.send(0x00); // all output: 00000000 for pins 1...8 Wire.endTransmission(); if (lines > 1) { _displayfunction |= LCD_2LINE; } _numlines = lines; _currline = 0; // 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 befer 4.5V so we'll wait 50 delay(50); //put the LCD into 4 bit mode // start with a non-standard command to make it realize we're speaking 4-bit here // per LCD datasheet, first command is a single 4-bit burst, 0011. burstBits(B10010100); // buffer that out to I/O pins: 1001110x with enable and backlight on burstBits(B10010000); // buffer that out to I/O pins: 1001100x with enable low, backlight on delay(5); // this shouldn't be necessary, but sometimes 16MHz is stupid-fast. command(LCD_FUNCTIONSET | _displayfunction); // then send 0010NF00 (N=lines, F=font) delay(5); // for safe keeping... command(LCD_FUNCTIONSET | _displayfunction); // ... twice. delay(5); // done! // turn on the LCD with our defaults. since these libs seem to use personal preference, I like a cursor. _displaycontrol = LCD_DISPLAYON; display(); // clear it off clear(); _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; // set the entry mode command(LCD_ENTRYMODESET | _displaymode); setBacklight(HIGH); // turn the backlight on if so equipped. }
// Allows to set the backlight, if the LCD backpack is used void LiquidTWI::setBacklight(uint8_t status) { bitWrite(_displaycontrol,7,status); // flag that the backlight is enabled, for burst commands burstBits(_displaycontrol & LCD_BACKLIGHT); }
void LiquidTWI::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { // 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 befer 4.5V so we'll wait 50 delay(50); Wire.begin(); // first thing we do is get the GPIO expander's head working straight, with a boatload of junk data. Wire.beginTransmission(MCP23008_ADDRESS | _i2cAddr); #if ARDUINO >= 100 Wire.write((byte)MCP23008_IODIR); Wire.write((byte)0xFF); Wire.write((byte)0x00); Wire.write((byte)0x00); Wire.write((byte)0x00); Wire.write((byte)0x00); Wire.write((byte)0x00); Wire.write((byte)0x00); Wire.write((byte)0x00); Wire.write((byte)0x00); Wire.write((byte)0x00); #else Wire.send(MCP23008_IODIR); Wire.send(0xFF); Wire.send(0x00); Wire.send(0x00); Wire.send(0x00); Wire.send(0x00); Wire.send(0x00); Wire.send(0x00); Wire.send(0x00); Wire.send(0x00); Wire.send(0x00); #endif Wire.endTransmission(); // now we set the GPIO expander's I/O direction to output since it's soldered to an LCD output. Wire.beginTransmission(MCP23008_ADDRESS | _i2cAddr); #if ARDUINO >= 100 Wire.write((byte)MCP23008_IODIR); Wire.write((byte)0x00); // all output: 00000000 for pins 1...8 #else Wire.send(MCP23008_IODIR); Wire.send(0x00); // all output: 00000000 for pins 1...8 #endif Wire.endTransmission(); if (lines > 1) { _displayfunction |= LCD_2LINE; } _numlines = lines; _currline = 0; // for some 1 line displays you can select a 10 pixel high font if ((dotsize != 0) && (lines == 1)) { _displayfunction |= LCD_5x10DOTS; } //put the LCD into 4 bit mode // start with a non-standard command to make it realize we're speaking 4-bit here // per LCD datasheet, first command is a single 4-bit burst, 0011. //----- // we cannot assume that the LCD panel is powered at the same time as // the arduino, so we have to perform a software reset as per page 45 // of the HD44780 datasheet - (kch) //----- // bit pattern for the burstBits function is // // 7 6 5 4 3 2 1 0 // LT D7 D6 D5 D4 EN RS n/c //----- burstBits(B10011100); // send LITE D4 D5 high with enable burstBits(B10011000); // send LITE D4 D5 high with !enable burstBits(B10011100); // burstBits(B10011000); // burstBits(B10011100); // repeat twice more burstBits(B10011000); // burstBits(B10010100); // send D4 low and LITE D5 high with enable burstBits(B10010000); // send D4 low and LITE D5 high with !enable delay(5); // this shouldn't be necessary, but sometimes 16MHz is stupid-fast. command(LCD_FUNCTIONSET | _displayfunction); // then send 0010NF00 (N=lines, F=font) delay(5); // for safe keeping... command(LCD_FUNCTIONSET | _displayfunction); // ... twice. delay(5); // done! // turn on the LCD with our defaults. since these libs seem to use personal preference, I like a cursor. _displaycontrol = LCD_DISPLAYON; display(); // clear it off clear(); _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; // set the entry mode command(LCD_ENTRYMODESET | _displaymode); setBacklight(HIGH); // turn the backlight on if so equipped. }