Menu
Last update: July 1, 2016 11:22
src
main
com
raspoid
AnalogComponent.java
Component.java
Config.java
GPIOComponent.java
GPIOPin.java
I2CComponent.java
I2CPin.java
PWMComponent.java
PWMPin.java
Pin.java
RaspberryPiModel.java
Tools.java
UARTPin.java
additionalcomponents
behavioral
brickpi
examples
exceptions
network
test

/*******************************************************************************
 * Copyright (c) 2016 Julien Louette & Gaƫl Wittorski
 * 
 * This file is part of Raspoid.
 * 
 * Raspoid 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 3 of the License, or
 * (at your option) any later version.
 * 
 * Raspoid 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 Raspoid.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package com.raspoid.additionalcomponents;

import java.io.IOException;

import com.raspoid.I2CComponent;
import com.raspoid.Tools;
import com.raspoid.examples.additionalcomponents.LCM1602Example;
import com.raspoid.exceptions.RaspoidException;

/**
 * An LCM is mainly an LCD1602 display that can be used with the i2c protocol.
 * 
 * <p>Datasheet: <a href="http://raspoid.com/download/datasheet/LCM_MODULE">LCM_MODULE (part 1)</a>,
 *  <a href="http://raspoid.com/download/datasheet/LCM_MODULE_2">LCM_MODULE (part 2)</a></p>
 * 
 * <p>To use this display, we use the 4-Bit interface (cfr. <b>[datasheet - p.13]</b>).</p>
 * 
 * <p>Example of use: {@link LCM1602Example}</p>
 * 
 * @author Julien Louette &amp; Ga&euml;l Wittorski
 * @version 1.0
 */
public class LCM1602 extends I2CComponent {
    
    /**
     * Default i2c LCM1602 address.
     */
    public static final int DEFAULT_LCM1602_ADDRESS = 0x27;
    
    /**
     * Number of lines on the LCD.
     */
    public static final int NB_LINES = 2;
    
    /**
     * Number of columns on the LCD.
     */
    public static final int NB_COL = 16;
    
    /**
     * Maximum index used for the lines on the LCD.
     */
    public static final int MAX_LINE_NB = NB_LINES - 1;
    
    /**
     * Minimum index used for the lines on the LCD.
     */
    public static final int MIN_LINE_NB = 0;
    
    /**
     * Maximum index used for columns on the LCD.
     */
    public static final int MAX_COL_NB = NB_COL - 1;
    
    /**
     * Minimum index used for columns on the LCD.
     */
    public static final int MIN_COL_NB = 0;
    
    /**
     * Instruction codes headers, used to construct packets 
     * in correct format for the LCM.
     * @see #RS1_RW0_EN1
     * @see #MAKE_EN0
     */
    private static final int RS0_RW0_EN1 = 0x04; // RS = 0, RW = 0, EN = 1
    
    /**
     * Instruction codes headers, used to construct packets 
     * in correct format for the LCM.
     * @see #RS0_RW0_EN1
     * @see #MAKE_EN0
     */
    private static final int RS1_RW0_EN1 = 0x05; // RS = 1, RW = 0, EN = 1
    
    /**
     * Instruction codes headers, used to construct packets 
     * in correct format for the LCM.
     * @see #RS0_RW0_EN1
     * @see #RS1_RW0_EN1
     */
    private static final int MAKE_EN0 = 0xFB;
    
    /**
     * [Command] Clear display <b>[datasheet - p.11]</b>.
     */
    private static final int CLEAR_DISPLAY_COMMAND = 0x01; // 0000 0001

    /**
     * [Command] Return home <b>[datasheet - p.11]</b>.
     */
    private static final int RETURN_HOME_COMMAND = 0x02; // 0000 0010
    
    /**
     * [Command] Set entry mode <b>[datasheet - p.11]</b>.
     */
    private static final int SET_ENTRY_MODE_COMMAND = 0x04;
    
    /**
     * [Command {@link #SET_ENTRY_MODE_COMMAND}]
     * <p>Cursor moving from left to right.</p>
     */
    private static final int CURSOR_MOVING_FROM_LEFT_TO_RIGHT = 0x02;
    
    /**
     * [Command {@link #SET_ENTRY_MODE_COMMAND}]
     * <p>Enable entire display shift.</p>
     */
    private static final int ENABLE_ENTIRE_DISPLAY_SHIFT = 0x01;
        
    /**
     * [Command] Set display <b>[datasheet - p.11]</b>.
     */
    private static final int SET_DISPLAY_COMMAND = 0x08; // 0000 1000
    
    /**
     * [Command {@link #SET_DISPLAY_COMMAND}]
     * <p>Enable display.</p>
     */
    private static final int ENABLE_DISPLAY = 0x04; // 0000 0100
    
    /**
     * [Command {@link #SET_DISPLAY_COMMAND}]
     * <p>Enable cursor.</p>
     */
    private static final int ENABLE_CURSOR = 0x02; // 0000 0010
    
    /**
     * [Command {@link #SET_DISPLAY_COMMAND}]
     * <p>Enable blinking cursor.</p>
     */
    private static final int ENABLE_BLINKING_CURSOR = 0x01; // 0000 0001
    
    /**
     * [Command] Set function (datasheet p.11).
     */
    private static final int SET_FUNCTION_COMMAND = 0x20; // 0010 0000
    
    /**
     * [Command {@link #SET_FUNCTION_COMMAND}]
     * <p>Enable 8 bits interface.</p>
     */
    private static final int ENABLE_8BITS_INTERFACE = 0x10; // 0001 0000
    
    /**
     * [Command {@link #SET_FUNCTION_COMMAND}]
     * <p>Enable 2 lines mode.</p>
     */
    private static final int ENABLE_2LINES_MODE = 0x08; // 0000 1000
    
    /**
     * [Command {@link #SET_FUNCTION_COMMAND}]
     * <p>Enable 5x10 dots font.</p>
     */
    private static final int ENABLE_5X10_DOTS_FONT = 0x04; // 0000 0100
    
    /**
     * Default constructor for an LCM1602 using the default LCM i2c address.
     */
    public LCM1602() {
        this(DEFAULT_LCM1602_ADDRESS);
    }
    
    /**
     * Constructor for an LCM1602 using a specific i2cAddress.
     * @param i2cAddress the i2c address of the LCM1602.
     */
    public LCM1602(int i2cAddress) {
        super(i2cAddress);
        resetFunction(); // init phase [datasheet - p.13]
    }
    
    /* ===============================================
     *                    Commands
     * =============================================*/
    
    /**
     * Clears the content of the display and move the cursor to the (0,0) position.
     */
    public void clearDisplay() {
        sendCommand(CLEAR_DISPLAY_COMMAND);
        Tools.sleepMilliseconds(3);
    }
    
    /**
     * The cursor is shifted to its original position (0, 0) if shifted,
     * and the content of the display is not changed.
     */
    public void returnHome() {
        sendCommand(RETURN_HOME_COMMAND);
        Tools.sleepMilliseconds(3);
    }
    
    /**
     * Assigns cursor moving direction and enable the shift of entire display.
     * @param cursorMovingFromLeftToRight true for a cursor moving from left to right.
     *        False for a cursor moving from right to left.
     * @param enableEntireDisplayShift true to enable the entire display shift. False to disable.
     */
    public void setEntryMode(boolean cursorMovingFromLeftToRight, boolean enableEntireDisplayShift) {
        int command = SET_ENTRY_MODE_COMMAND;
        if(cursorMovingFromLeftToRight)
            command += CURSOR_MOVING_FROM_LEFT_TO_RIGHT;
        if(enableEntireDisplayShift)
            command += ENABLE_ENTIRE_DISPLAY_SHIFT;
        sendCommand(command);
    }
        
    /**
     * Sets the display according to the specified parameters.
     * <p>Note: the cursor position can't be changed.</p>
     * @param enableDisplay the display is enabled/disabled (on/off).
     * @param enableCursor a cursor is displayed at the current cursor position "_".
     * @param enableBlinkingCursor a plein cursor is displayed and blinking at the current cursor position.
     */
    public void setDisplay(boolean enableDisplay, boolean enableCursor, boolean enableBlinkingCursor) {
        int command = SET_DISPLAY_COMMAND;
        if(enableDisplay)
            command += ENABLE_DISPLAY;
        if(enableCursor)
            command += ENABLE_CURSOR;
        if(enableBlinkingCursor)
            command += ENABLE_BLINKING_CURSOR;
        sendCommand(command);
    }
    
    /**
     * Disable the display.
     * <p>The content of the display is deleted and the cursor is moved to (0,0).</p>
     */
    public void disableDisplay() {
        clearDisplay();
        setDisplay(false, false, false);
    }
    
    /**
     * Sets the interface data length (8bit/4bit),
     * the number of display lines (2-line/1-line) and 
     * the display font type (5x10 dots/5x8 dots).
     * @param heightBitsInterface 8bit if true, 4bit if false.
     * @param twoLines 2-line if true, 1-line if false.
     * @param fiveTenDots 5x10 if true, 5x8 if false.
     */
    public void setFunction(boolean heightBitsInterface, boolean twoLines, boolean fiveTenDots) {
        int command = SET_FUNCTION_COMMAND;
        if(heightBitsInterface)
            command += ENABLE_8BITS_INTERFACE;
        if(twoLines)
            command += ENABLE_2LINES_MODE;
        if(fiveTenDots)
            command += ENABLE_5X10_DOTS_FONT;
        sendCommand(command);
    }
    
    /**
     * Moves the cursor to the given position.
     * @param line the new line of the cursor, in the {@link #MIN_LINE_NB}..{@link #MAX_LINE_NB} interval.
     * @param col the new column of the cursor, in the {@link #MIN_COL_NB}..{@link #MAX_COL_NB} interval.
     */
    public void moveCursor(int line, int col) {
        if(col < MIN_COL_NB)
            col = MIN_COL_NB;
        if(col > MAX_COL_NB)
            col = MAX_COL_NB;
        if(line < MIN_LINE_NB)
            line = MIN_LINE_NB;
        if(line > MAX_LINE_NB)
            line = MAX_LINE_NB;
        
        int command = 0x80 + 0x40 * line + col;
        sendCommand(command);
    }
    
    /**
     * Writes a text on the display, starting at the current cursor position.
     * @param text the text to print on the display.
     */
    public void writeText(String text) {
        this.writeCharArray(text.toCharArray());
    }
    
    /**
     * Sets the current cursor position to (line, col),
     * and writes a text starting at this position.
     * @param line the new line for the cursor.
     * @param col the new column for the cursor.
     * @param text the text to print on the display.
     */
    public void writeText(int line, int col, String text) {
        moveCursor(line, col);
        writeText(text);
    }
    
    /**
     * Writes a text from left to right. The text is right align if text.length < {@link #NB_COL}.
     * <p>Pay attention to the length of the text. The screen is only 2x16 chars.
     * If the text is too long, some chars may not be displayed.
     * It is for you to manage.</p>
     * @param line the line to print the text.
     * @param text the text to print on the display.
     */
    public void writeTextRightAlign(int line, String text) {
        int textLength = text.length();
        if(textLength > NB_COL)
            moveCursor(line, 0);
        else
            moveCursor(line, NB_COL - textLength);
        writeText(text);
    }
    
    /**
     * Writes an array of char at the current cursor position.
     * The cursor is then switched to initial_position + data.length().
     * @param data the data to write on the display.
     */
    private void writeCharArray(char[] data) {
        int tmp = data.length;
        for (int i = 0; i < tmp; i++)
            sendData(data[i]);
    }
    
    /* ===============================================
     *                      Utils
     * =============================================*/
    
    /**
     * Writes data on the I2C bus, respecting the requested format (cfr <b>[datasheet - p.11]</b>).
     * @param data
     */
    private void writeDataToI2C(int data) {
        try {
            data |= 0x08;
            byte sent = (byte)(data);
            device.write(sent);
        } catch (IOException e) {
            throw new RaspoidException(e);
        }
    }
    
    /**
     * Send a command to the LCM using I2C protocol. This command is sent using
     * the 4-bit data-length interface.
     * @param command
     */
    private void sendCommand(int command) {
        int buf;
        // Send bit7-4 firstly
        buf = command & 0xF0;
        buf |= RS0_RW0_EN1;
        writeDataToI2C(buf);
        Tools.sleepMilliseconds(2);
        buf &= MAKE_EN0;
        writeDataToI2C(buf);

        // Send bit3-0 secondly
        buf = (command & 0x0F) << 4;
        buf |= RS0_RW0_EN1;
        writeDataToI2C(buf);
        Tools.sleepMilliseconds(2);
        buf &= MAKE_EN0;
        writeDataToI2C(buf);
    }
    
    /**
     * Print a char on lcd at current cursor position.
     * @param data
     */
    private void sendData(int data) {
        int buf;
        // Send bit7-4 firstly
        buf = data & 0xF0;
        buf |= RS1_RW0_EN1;
        writeDataToI2C(buf);
        Tools.sleepMilliseconds(2);
        buf &= MAKE_EN0;
        writeDataToI2C(buf);

        // Send bit3-0 secondly
        buf = (data & 0x0F) << 4;
        buf |= RS1_RW0_EN1;
        writeDataToI2C(buf);
        Tools.sleepMilliseconds(2);
        buf &= MAKE_EN0;
        writeDataToI2C(buf);
    }
    
    /**
     * <b>[datasheet - p.13]</b>
     * Implementation of the initial procedure, for the 4-bit interface.
     * Only used once, when the cursor is powered on.
     */
    private void resetFunction() {
        Tools.sleepMilliseconds(40);
        sendCommand(0x33); // 0011 and then 0011
        sendCommand(0x32); // 0011 and then 0010
        setFunction(false, true, false);
        setDisplay(false, false, false);
        setEntryMode(true, false);
    }
}