All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.diozero.devices.HD44780Lcd Maven / Gradle / Ivy

There is a newer version: 1.4.1
Show newest version
package com.diozero.devices;

/*-
 * #%L
 * Organisation: diozero
 * Project:      diozero - Core
 * Filename:     HD44780Lcd.java
 * 
 * This file is part of the diozero project. More information about this project
 * can be found at https://www.diozero.com/.
 * %%
 * Copyright (C) 2016 - 2024 diozero
 * %%
 * 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.
 * #L%
 */

import com.diozero.api.RuntimeIOException;
import com.diozero.util.SleepUtil;

/**
 * 

* LCD with HD44780 controller.
* Code based on * this Raspberry-Pi Spy article, Python * code. *

*

* Another source of information: https://gist.github.com/DenisFromHR/cc863375a6e19dce359d. *

*

* HD44780 * Datasheet. *

*/ public class HD44780Lcd implements LcdInterface { private static final boolean DEFAULT_BACKLIGHT_STATE = true; private static final byte DB0 = (byte) (1 << 0); private static final byte DB1 = (byte) (1 << 1); private static final byte DB2 = (byte) (1 << 2); private static final byte DB3 = (byte) (1 << 3); private static final byte DB4 = (byte) (1 << 4); private static final byte DB5 = (byte) (1 << 5); private static final byte DB6 = (byte) (1 << 6); private static final byte DB7 = (byte) (1 << 7); /*- * Instructions: * Instruction | RS | RW | DB7 | DB6 | DB5 | DB4 | DB3 | DB2 | DB1 | DB0 | Description | Exec Time * ------------+----+----+-----+-----+-----+-----+-----+-----+-----+-----+------------------------------+---------- * Clear | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | Clears entire display and | * display | | | | | | | | | | | sets DDRAM address 0 in | * | | | | | | | | | | | address counter. | * ------------+----+----+-----+-----+-----+-----+-----+-----+-----+-----+------------------------------+---------- * Return | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | - | Sets DDRAM address 0 in | 1.52ms * home | | | | | | | | | | | address counter. Also | * | | | | | | | | | | | returns display from being | * | | | | | | | | | | | shifted to original position.| * | | | | | | | | | | | DDRAM contents remain | * | | | | | | | | | | | unchanged. | * ------------+----+----+-----+-----+-----+-----+-----+-----+-----+-----+------------------------------+---------- * Entry mode | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | I/D | S | Sets cursor move direction | 37us * set | | | | | | | | | | | and specifies display shift. | * | | | | | | | | | | | These operations are | * | | | | | | | | | | | performed during data write | * | | | | | | | | | | | and read. | * ------------+----+----+-----+-----+-----+-----+-----+-----+-----+-----+------------------------------+---------- * Display on/ | 0 | 0 | 0 | 0 | 0 | 0 | 1 | D | C | B | Sets entire display (D) on/ | 37us * off control | | | | | | | | | | | off, cursor on/off (C), and | * | | | | | | | | | | | blinking of cursor position | * | | | | | | | | | | | character (B). | * ------------+----+----+-----+-----+-----+-----+-----+-----+-----+-----+------------------------------+---------- * Cursor or | 0 | 0 | 0 | 0 | 0 | 1 | S/C | R/L | - | - | Moves cursor and shifts | 37us * display | | | | | | | | | | | display without changing | * shift | | | | | | | | | | | DDRAM contents. | * ------------+----+----+-----+-----+-----+-----+-----+-----+-----+-----+------------------------------+---------- * Function | 0 | 0 | 0 | 0 | 1 | DL | N | F | - | - | Sets interface data length | 37us * set | | | | | | | | | | | (DL), number of display lines| * | | | | | | | | | | | (N), and character font (F). | * ------------+----+----+-----+-----+-----+-----+-----+-----+-----+-----+------------------------------+---------- * Set CGRAM | 0 | 0 | 0 | 1 | ACG | ACG | ACG | ACG | ACG | ACG | Sets CGRAM address. | 37us * address | | | | | | | | | | | CGRAM data is sent and | * | | | | | | | | | | | received after this setting. | * ------------+----+----+-----+-----+-----+-----+-----+-----+-----+-----+------------------------------+---------- * Set DDRAM | 0 | 0 | 1 | ADD | ADD | ADD | ADD | ADD | ADD | ADD | Sets DDRAM address. | 37us * address | | | | | | | | | | | DDRAM data is sent and | * | | | | | | | | | | | received after this setting. | * ------------+----+----+-----+-----+-----+-----+-----+-----+-----+-----+------------------------------+---------- * Read Busy | 0 | 1 | BF | a | a | a | a | a | a | a | Reads busy flag (BF) | 0us * Flag and | | | | | | | | | | | indicating internal operation| * Address | | | | | | | | | | | is being performed and reads | * | | | | | | | | | | | address counter contents. | * ------------+----+----+-----+-----+-----+-----+-----+-----+-----+-----+------------------------------+---------- * Write data | 1 | 0 | d | d | d | d | d | d | d | d | Writes data into DDRAM or | 37us * to CG or | | | | | | | | | | | CGRAM. | tADD=4us * DDRAM | | | | | | | | | | | | * ------------+----+----+-----+-----+-----+-----+-----+-----+-----+-----+------------------------------+---------- * Read data | 1 | 1 | d | d | d | d | d | d | d | d | Reads data from DDRAM or | 37us * from CG or | | | | | | | | | | | CGRAM. | tADD=4us * DDRAM | | | | | | | | | | | | * ------------+----+----+-----+-----+-----+-----+-----+-----+-----+-----+------------------------------+---------- * DDRAM = Display Data RAM. * CGRAM = Character Generator RAM. */ /** Clears entire display and sets DDRAM address 0 in address counter. */ private static final byte INST_CLEAR_DISPLAY = 0x01; /** * Sets DDRAM address 0 in address counter. Also returns display from being * shifted to original position. DDRAM contents remain unchanged. */ private static final byte INST_RETURN_HOME = 0x02; /** * Sets cursor move direction and specifies display shift. These operations are * performed during data write and read. */ private static final byte INST_ENTRY_MODE_SET = 0x04; /** * Sets entire display (D) on/off, cursor on/off (C), and blinking of cursor * position character (B). */ private static final byte INST_DISPLAY_CONTROL = 0x08; /** Moves cursor and shifts display without changing DDRAM contents. */ private static final byte INST_CURSOR_DISPLAY_SHIFT = 0x10; /** * Perform the function at the head of the program before executing any * instructions (except for the read busy flag and address instruction). From * this point, the function set instruction cannot be executed unless the * interface data length is changed. */ private static final byte INST_FUNCTION_SET = 0x20; /** Sets CGRAM address. CGRAM data is sent and received after this setting. */ private static final byte INST_SET_CGRAM_ADDR = 0x40; /** Sets DDRAM address. DDRAM data is sent and received after this setting. */ private static final byte INST_SET_DDRAM_ADDR = (byte) 0x80; // Flags for INST_ENTRY_MODE_SET /** * Cursor increment/decrement control, 1=increment, 0=decrement. The cursor or * blinking moves to the right when incremented by 1 and to the left when * decremented by 1. */ private static final byte EMS_CURSOR_INCREMENT = DB1; private static final byte EMS_CURSOR_DECREMENT = 0x00; /** * Display shift control, 1=on, 0=off. Shifts the entire display either to the * right (I/D = 0) or to the left (I/D = 1) when S is 1. If S is 1, it will seem * as if the cursor does not move but the display does. The display does not * shift if S is 0. */ private static final byte EMS_DISPLAY_SHIFT_ON = DB0; private static final byte EMS_DISPLAY_SHIFT_OFF = 0x00; // Flags for INST_DISPLAY_CONTROL /** Display on/off, 1=on, 0=off. */ private static final byte DC_DISPLAY_ON = DB2; private static final byte DC_DISPLAY_OFF = 0x00; /** Cursor on/off, 1=on, 0=off. */ private static final byte DC_CURSOR_ON = DB1; private static final byte DC_CURSOR_OFF = 0x00; /** Cursor blink control, 1=blink, 0=no blink. */ private static final byte DC_BLINK_ON = DB0; private static final byte DC_BLINK_OFF = 0x00; // Flags for INST_CURSOR_DISPLAY_SHIFT /** Shift either the display or the cursor, 1=display, 0=cursor. */ private static final byte CDS_DISPLAY_SHIFT = DB3; private static final byte CDS_CURSOR_MOVE = 0x00; /** Shift the cursor, 1=right, 0=left. */ private static final byte CDS_SHIFT_RIGHT = DB2; private static final byte CDS_SHIFT_LEFT = 0x00; // Flags for INST_FUNCTION_SET /** * Data is sent or received in 8-bit lengths (DB7 to DB0) when DL is 1, and in * 4-bit lengths (DB7 to DB4) when DL is 0. */ private static final byte FS_DATA_LENGTH_8BIT = DB4; private static final byte FS_DATA_LENGTH_4BIT = 0x00; /** Sets the number of display lines. 1=2 lines, 0=1 line. */ private static final byte FS_DISPLAY_2LINES = DB3; private static final byte FS_DISPLAY_1LINE = 0x00; /** * Sets the character font. 1=5x10 dots (32 character fonts), 0=5x8 dots (208 * character fonts). For some 1 line displays you can select a 10 pixel high * font. Cannot select 2 lines with a 5x10 font. */ private static final byte FS_CHAR_FONT_5X10DOTS = DB2; private static final byte FS_CHAR_FONT_5X8DOTS = 0x00; private static final byte BUSY_FLAG = DB7; // For 2-row LCDs private static final byte[] ROW_OFFSETS_2ROWS = { 0x00, 0x40 }; // For 20x4 LCDs private static final byte[] ROW_OFFSETS_20x4 = { 0x00, 0x40, 20, 0x40 + 20 }; // For 16x4 LCDs - special memory map layout private static final byte[] ROW_OFFSETS_16x4 = { 0x00, 0x40, 16, 0x40 + 16 }; private final LcdConnection lcdConnection; private final boolean dataInHighNibble; private final int registerSelectDataMask; private final int dataReadMask; private final int enableMask; private final int backlightOnMask; private boolean backlightEnabled; private final int columns; private final int rows; private final boolean characterFont5x8; private boolean displayOn; private boolean cursorEnabled; private boolean blinkEnabled; private boolean increment; private boolean shiftDisplay; private byte[] rowOffsets; public HD44780Lcd(LcdConnection lcdConnection, int columns, int rows) { if (rows == 2) { rowOffsets = ROW_OFFSETS_2ROWS; } else if (rows == 4) { if (columns == 16) { rowOffsets = ROW_OFFSETS_16x4; } else if (columns == 20) { rowOffsets = ROW_OFFSETS_20x4; } } if (rowOffsets == null) { throw new IllegalArgumentException(columns + "x" + rows + " LCDs not supported"); } this.columns = columns; this.rows = rows; backlightEnabled = DEFAULT_BACKLIGHT_STATE; characterFont5x8 = true; this.lcdConnection = lcdConnection; dataInHighNibble = lcdConnection.isDataInHighNibble(); registerSelectDataMask = 1 << lcdConnection.getRegisterSelectBit(); dataReadMask = 1 << lcdConnection.getDataReadWriteBit(); enableMask = 1 << lcdConnection.getEnableBit(); backlightOnMask = 1 << lcdConnection.getBacklightBit(); // Initialise the display. From p45/46 of the datasheet: // https://www.sparkfun.com/datasheets/LCD/HD44780.pdf // If the power supply conditions for correctly operating the internal reset // circuit are not met, initialisation by instructions becomes necessary. // Need to do this 3 times for the 4-bit interface (p46) /* * From p39 (4-bit operation, 8-digit × 1-line display with internal reset): * * The program must set all functions prior to the 4-bit operation (Table 12). * When the power is turned on, 8-bit operation is automatically selected and * the first write is performed as an 8-bit operation. Since DB0 to DB3 are not * connected, a rewrite is then required. However, since one operation is * completed in two accesses for 4-bit operation, a rewrite is needed to set the * functions (see Table 12). Thus, DB4 to DB7 of the function set instruction is * written twice. */ // Function set (Interface is 8 bits long). write4Bits(true, (byte) (INST_FUNCTION_SET | FS_DATA_LENGTH_8BIT)); // Wait for more than 4.1 ms SleepUtil.sleepMillis(5); // Function set (Interface is 8 bits long). write4Bits(true, (byte) (INST_FUNCTION_SET | FS_DATA_LENGTH_8BIT)); // Wait for more than 100us // SleepUtil.sleepMicros(100); SleepUtil.busySleep(110_000); // Function set (Interface is 8 bits long). write4Bits(true, (byte) (INST_FUNCTION_SET | FS_DATA_LENGTH_8BIT)); // Now set it to 4-bit mode write4Bits(true, (byte) (INST_FUNCTION_SET | FS_DATA_LENGTH_4BIT)); // Function set: 4-bit data length, lines & character font as requested writeInstruction( (byte) (INST_FUNCTION_SET | FS_DATA_LENGTH_4BIT | (rows == 1 ? FS_DISPLAY_1LINE : FS_DISPLAY_2LINES) | (characterFont5x8 ? FS_CHAR_FONT_5X8DOTS : FS_CHAR_FONT_5X10DOTS))); // Display On, Cursor on, Blink on displayControl(true, true, true); // Cursor increment, display shift off entryModeControl(true, false); // Clear display clear(); } private void writeInstruction(byte data) { writeByte(true, data); } private void writeData(byte data) { writeByte(false, data); } private void writeByte(boolean instruction, byte data) { // High nibble first write4Bits(instruction, (byte) (data & 0xF0)); // Then the low nibble write4Bits(instruction, (byte) (data << 4)); } private void write4Bits(boolean instruction, byte value) { byte data; if (dataInHighNibble) { data = (byte) (value & 0xF0); } else { data = (byte) ((value >> 4) & 0x0F); } data |= (byte) (instruction ? 0 : registerSelectDataMask) | (backlightEnabled ? backlightOnMask : 0); lcdConnection.write((byte) (data | enableMask)); // 50us delay enough? // SleepUtil.sleepMicros(50); SleepUtil.busySleep(50_000); lcdConnection.write(data); // 50us delay enough? // SleepUtil.sleepMicros(50); SleepUtil.busySleep(50_000); } public int getColumnCount() { return columns; } public int getRowCount() { return rows; } public boolean isBacklightEnabled() { return backlightEnabled; } public HD44780Lcd setBacklightEnabled(boolean backlightEnabled) { this.backlightEnabled = backlightEnabled; writeByte(true, (byte) 0); return this; } public HD44780Lcd setCursorPosition(int column, int row) { columnCheck(column); rowCheck(row); writeInstruction((byte) (INST_SET_DDRAM_ADDR | (column + rowOffsets[row]))); return this; } public HD44780Lcd addText(char character) { writeData((byte) character); return this; } public HD44780Lcd addText(int code) { writeData((byte) code); return this; } public HD44780Lcd clear() { writeInstruction(INST_CLEAR_DISPLAY); // Seem to have to wait after clearing the display, encounter strange errors // otherwise SleepUtil.sleepMillis(1); return this; } public HD44780Lcd returnHome() { writeInstruction(INST_RETURN_HOME); return this; } public HD44780Lcd entryModeControl(boolean increment, boolean shiftDisplay) { this.increment = increment; this.shiftDisplay = shiftDisplay; writeInstruction((byte) (INST_ENTRY_MODE_SET | (increment ? EMS_CURSOR_INCREMENT : EMS_CURSOR_DECREMENT) | (shiftDisplay ? EMS_DISPLAY_SHIFT_ON : EMS_DISPLAY_SHIFT_OFF))); return this; } public HD44780Lcd autoscrollOn() { entryModeControl(displayOn, true); return this; } public HD44780Lcd autoscrollOff() { entryModeControl(displayOn, false); return this; } public boolean isIncrementOn() { return increment; } public boolean isShiftDisplayOn() { return shiftDisplay; } public HD44780Lcd displayControl(boolean displayOn, boolean cursorEnabled, boolean blinkEnabled) { this.displayOn = displayOn; this.cursorEnabled = cursorEnabled; this.blinkEnabled = blinkEnabled; writeInstruction((byte) (INST_DISPLAY_CONTROL | (displayOn ? DC_DISPLAY_ON : DC_DISPLAY_OFF) | (cursorEnabled ? DC_CURSOR_ON : DC_CURSOR_OFF) | (blinkEnabled ? DC_BLINK_ON : DC_BLINK_OFF))); return this; } public HD44780Lcd displayOn() { return displayControl(true, cursorEnabled, blinkEnabled); } public HD44780Lcd displayOff() { return displayControl(false, cursorEnabled, blinkEnabled); } public HD44780Lcd cursorOn() { return displayControl(displayOn, true, blinkEnabled); } public HD44780Lcd cursorOff() { // also turns off "blink" as that makes the cursor position still visible if enabled return displayControl(displayOn, false, false); } public HD44780Lcd blinkOn() { return displayControl(displayOn, true, true); } public HD44780Lcd blinkOff() { return displayControl(displayOn, cursorEnabled, false); } public boolean isCursorEnabled() { return cursorEnabled; } public boolean isBlinkEnabled() { return blinkEnabled; } public HD44780Lcd cursorOrDisplayShift(boolean displayShift, boolean shiftRight) { writeInstruction((byte) (INST_CURSOR_DISPLAY_SHIFT | (displayShift ? CDS_DISPLAY_SHIFT : CDS_CURSOR_MOVE) | (shiftRight ? CDS_SHIFT_RIGHT : CDS_SHIFT_LEFT))); return this; } public HD44780Lcd shiftDisplayRight() { cursorOrDisplayShift(true, true); return this; } public HD44780Lcd shiftDisplayLeft() { cursorOrDisplayShift(true, false); return this; } public HD44780Lcd moveCursorRight() { cursorOrDisplayShift(false, true); return this; } public HD44780Lcd moveCursorLeft() { cursorOrDisplayShift(false, false); return this; } public HD44780Lcd createChar(int location, byte[] charMap) { /* * In the character generator RAM, the user can rewrite character patterns by * program. For 5x8 dots, eight character patterns can be written, and for 5x10 * dots, four character patterns can be written. */ if (characterFont5x8) { if (location < 0 || location >= 8) { throw new IllegalArgumentException("Invalid location (" + location + ") , must be 0..7"); } if (charMap.length != 8) { throw new IllegalArgumentException("Invalid charMap length (" + charMap.length + ") , must be 8"); } } else { if (location < 0 || location >= 4) { throw new IllegalArgumentException("Invalid location (" + location + ") , must be 0..3"); } if (charMap.length != 10) { throw new IllegalArgumentException("Invalid charMap length (" + charMap.length + ") , must be 10"); } } writeInstruction((byte) (INST_SET_CGRAM_ADDR | (location << 3))); // SleepUtil.sleepMicros(100); SleepUtil.busySleep(100_000); for (int i = 0; i < charMap.length; i++) { writeData((byte) (charMap[i] & 0b11111)); // SleepUtil.sleepMicros(100); SleepUtil.busySleep(100_000); } return this; } @Override public void close() throws RuntimeIOException { backlightEnabled = false; clear(); displayControl(false, false, false); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy