Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add LCD 16x02 support #3

Open
11 of 24 tasks
SrMouraSilva opened this issue Sep 29, 2016 · 1 comment
Open
11 of 24 tasks

Add LCD 16x02 support #3

SrMouraSilva opened this issue Sep 29, 2016 · 1 comment

Comments

@SrMouraSilva
Copy link
Member

SrMouraSilva commented Sep 29, 2016

Description

Add LCD 16x02 display support

A LCD with 16 columns, 2 rows and green backlight

Steps

  • Clear Display
  • Cursor home
  • Entry Mode set
    • Increment / Decrement
    • ACCOPANIES_DISPLAY_SHIFT / NOT_ACCOPANIES_DISPLAY_SHIFT
  • Display status controller https://github.com/arduino/Arduino/blob/master/libraries/LiquidCrystal/src/LiquidCrystal.cpp#L253
    • Display on/off
    • Cursor on/off
    • Blinking on/off
  • Cursor / Display direction
    • Cursor left/right Uses liquid_crystal.position
    • Display left/right
  • System Set
    • Bits mode (8 pins or 4 pins)
    • 8 pins
    • 4 pins
    • Line displays (1 or 2)
    • 1 line
    • 2 lines
    • Character size
    • 5x7
    • 5x10
  • Set CG Ram Address
  • Set DD Ram Address
  • Read Busy Flag/Address counter
  • Write Data
  • Simulated Read Data
  • Custom chars
  • Multiple displays in same data pins

Material

@SrMouraSilva
Copy link
Member Author

SrMouraSilva commented Sep 30, 2016

import time
from gpiozero import DigitalOutputDevice

def msleep(milliseconds):
    """Sleep the specified amount of milliseconds."""
    time.sleep(milliseconds / 1000.0)


def usleep(microseconds):
    """Sleep the specified amount of microseconds."""
    time.sleep(microseconds / 1000000.0)

class Commands(object):
    """
    For details:
     - http://www.microcontrollerboard.com/lcd.html
     - https://www.sparkfun.com/datasheets/LCD/HD44780.pdf

    int CLEAR: Clears entire display and sets DDRAM address 0 in address counter.
    int RETURN_HOME: Sets DDRAM address 0 in address counter.
                     Also returns display from being shifted to original position.
                     DDRAM contents remain unchanged.
    int ENTRY_MODE_SET: Sets
                         - Cursor move direction - MODE_INCREMENT (left to right) or MODE_DECREMENT (right to left)
                         - Specifies display shift - ACCOPANIES_DISPLAY_SHIFT or NOT_ACCOPANIES_DISPLAY_SHIFT
                        These operations are performed during data write and read.

    int DISPLAY_ON_OFF_CONTROL: Sets
                                 - entire display on/off - DISPLAY_ON or DISPLAY_OFF
                                 - cursor on/off - CURSOR_ON or CURSOR_OFF
                                 - blinking of cursor position character (B).
    int CURSOR_OR_DISPLAY_SHIFT: Moves cursor and shifts display without changing
                                 DDRAM contents.
    int FUNCTION_SET: Sets:
                       - interface data length (DL),
                       - number of display lines - ONE_ROW or TWO_ROWS
                       - and character font - CHARACTER_5x7 or CHARACTER_5x10
    """
    CLEAR = 0x01

    RETURN_HOME = 0x02

    ENTRY_MODE_SET = 0x04
    MODE_INCREMENT = 0b0010
    MODE_DECREMENT = 0b0000
    ACCOPANIES_DISPLAY_SHIFT = 0b0001
    NOT_ACCOPANIES_DISPLAY_SHIFT = 0b0000

    DISPLAY_ON_OFF_CONTROL = 0x08
    DISPLAY_ON = 0b0100
    DISPLAY_OFF = 0b0000
    CURSOR_ON = 0b0010
    CURSOR_OFF = 0b0000
    BLINKING_ON = 0b0001
    BLINKING_OFF = 0b0000

    CURSOR_OR_DISPLAY_SHIFT = 0x10
    DISPLAY_SHIFT = 0b1000
    CURSOR_MOVE = 0b0000
    MOVE_CURSOR_RIGHT = 0b0100
    MOVE_CURSOR_LEFT = 0b0000

    FUNCTION_SET = 0x20
    ONE_ROW = 0b0000
    TWO_ROWS = 0b1000
    CHARACTER_5x7 = 0b0000
    CHARACTER_5x10 = 0b0100

    ROWS = [0x80, 0xC0, 0x94, 0xD4]

class LCD8Pins(object):
    def __init__(self, LCD):
        self.LCD = LCD

    def initialize_display(self):
        msleep(50)

        self.LCD.rs.off()
        self.LCD.rw.off()

        self._write_4bits(0x03)
        msleep(4.5)
        self._write_4bits(0x03)
        usleep(100)
        self._write_4bits(0x03)

        usleep(100)

    def write_byte(self, byte):
        self.LCD.enable.off()

        self._write_pins(data)

        usleep(2)
        self.LCD.enable.on()
        usleep(5)
        self.LCD.enable.off()

    def _write_pins(self, data):
        self.db7.value = (data & 0b10000000) >> 7 == 1
        self.db6.value = (data & 0b01000000) >> 6 == 1
        self.db5.value = (data & 0b00100000) >> 5 == 1
        self.db4.value = (data & 0b00010000) >> 4 == 1
        self.db7.value = (data & 0b00001000) >> 3 == 1
        self.db6.value = (data & 0b00000100) >> 2 == 1
        self.db5.value = (data & 0b00000010) >> 1 == 1
        self.db4.value = (data & 0b00000001) == 1

class LCD4Pins(object):
    def __init__(self, LCD):
        self.LCD = LCD

    def initialize_display(self):
        msleep(50)

        self.LCD.rs.off()
        self.LCD.rw.off()

        self._write_4bits(0x03)
        msleep(4.5)
        self._write_4bits(0x03)
        usleep(100)
        self._write_4bits(0x03)

        self._write_4bits(0x02)
        usleep(100)

    def write_byte(self, byte):
        self._write_4bits(byte >> 4)
        self._write_4bits(byte)

    def _write_4bits(self, data):
        self.LCD.enable.off()

        self._write_pins(data)

        usleep(2)
        self.LCD.enable.on()
        usleep(5)
        self.LCD.enable.off()

    def _write_pins(self, data):
        self.LCD.db7.value = (data & 0b00001000) >> 3 == 1
        self.LCD.db6.value = (data & 0b00000100) >> 2 == 1
        self.LCD.db5.value = (data & 0b00000010) >> 1 == 1
        self.LCD.db4.value = (data & 0b00000001) == 1

class LiquidCrystal(object):
    """
    Based in https://www.sparkfun.com/datasheets/LCD/HD44780.pdf
    """

    def __init__(self):
        self.strategy = LCD4Pins(self)
        self._init_pins()
        self._init_status()
        self._init_display()

    def _init_pins(self):
        self.db7 = DigitalOutputDevice(18)
        self.db6 = DigitalOutputDevice(15)
        self.db5 = DigitalOutputDevice(14)
        self.db4 = DigitalOutputDevice(4)
        #self.db3 = DigitalOutputDevice()
        #self.db2 = DigitalOutputDevice()
        #self.db1 = DigitalOutputDevice()
        #self.db0 = DigitalOutputDevice()

        self.rs = DigitalOutputDevice(2)
        self.rw = DigitalOutputDevice(6)
        self.enable = DigitalOutputDevice(3)

    def _init_status(self):
        self.rows = 2
        self.columns = 16

        self.content = [[' '] * self.columns for _ in range(self.rows)]
        self.row = 0
        self.column = 0

        self._entry_mode_set = Commands.DISPLAY_ON_OFF_CONTROL | Commands.DISPLAY_ON | Commands.CURSOR_ON | Commands.BLINKING_ON

    def _init_display(self):
        self.strategy.initialize_display()

        self.command(Commands.FUNCTION_SET | Commands.TWO_ROWS | Commands.CHARACTER_5x7)
        self.command(Commands.CURSOR_OR_DISPLAY_SHIFT | Commands.CURSOR_MOVE | Commands.MOVE_CURSOR_LEFT)

        self.clear()
        self.command(self._entry_mode_set)

    def command(self, command):
        """
        Send a command to display
        Please, use Commands static attributes to splicit code
        """
        self._write_data(True, command)

    def data(self, char):
        """
        Send data to display
        If you request send text, please, use self.write() method
        """
        self._write_data(False, char)

    def _write_data(self, is_command, byte):
        self.rs.value = not is_command

        self.strategy.write_byte(byte)

        usleep(50) # HD44780: > 37us

    def _write_byte(self, data):
        self.strategy.write_byte(data)

    def write(self, string, row=None, column=None):
        if row is not None and column is not None:
            self.position = (row, column)

        for char in string:
            byte_char = ord(char)
            self.data(byte_char)
            self.content[self.row][self.column] = byte_char

    @property
    def position(self):
        return (self.row, self.column)

    @position.setter
    def position(self, position):
        self.row, self.column = position[0], position[1]

        data = Commands.ROWS[row] | column
        self.command(data)

    def clear(self):
        self.row = 0
        self.column = 0

        self.content = [[None] * self.columns for _ in range(self.rows)]

        self.command(Commands.CLEAR)
        msleep(2) #  Clear requests more time than anothers commands

    def home(self):
        """Return to initial position (row=0, column=0)"""
        self.row = 0
        self.column = 0

        command(Commands.RETURN_HOME);
        msleep(2) #  Got to home requests more time than anothers commands

    @property
    def visible(self):
        """return bool: Is display visible?"""
        return ByteUtil.is_flag_active(self._entry_mode_set, Commands.DISPLAY_ON)

    @visible.setter
    def visible(self, status):
        """Turn the display on/off (quickly)"""
        self._entry_mode_set = ByteUtil.apply_flag(self._entry_mode_set, Commands.DISPLAY_ON, status)
        self.command(self._entry_mode_set)

    @property
    def cursor(self):
        """return bool: Is underline cursor visible?"""
        return ByteUtil.is_flag_active(self._entry_mode_set, Commands.CURSOR_ON)

    @cursor.setter
    def cursor(self, status):
        """Turn underline cursor visibility on/off"""
        self._entry_mode_set = ByteUtil.apply_flag(self._entry_mode_set, Commands.CURSOR_ON, status)
        self.command(self._entry_mode_set)

    @property
    def blink(self):
        """return bool: Is cursor blinking enabled?"""
        return ByteUtil.is_flag_active(self._entry_mode_set, Commands.BLINKING_ON)

    @blink.setter
    def blink(self, status):
        """Turn blink cursor visibility on/off"""
        self._entry_mode_set = ByteUtil.apply_flag(self._entry_mode_set, Commands.BLINKING_ON, status)
        self.command(self._entry_mode_set)

    @property
    def string_content(self):
        content = ''
        for row in self.content:
            for byte_char in row:
                row += chr(byte_char)
            content += '\n'

        return content

    """
    void LiquidCrystal::scrollDisplayLeft(void) {
        command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
    }
    void LiquidCrystal::scrollDisplayRight(void) {
        command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
    }
    """

class ByteUtil(object):

    @staticmethod
    def apply_flag(byte, flag, status):
        if status:
            byte |= flag
        else:
            byte &= ~flag

        return byte

    def is_flag_active(byte, flag):
        return (byte & flag) != 0

display = LiquidCrystal()
while True:
    display.write("Teste           ", 0, 0)
    '''
    display.write("                ", 1, 0)
    time.sleep(4)
    display.write("Pedal Pi", 0, 0)
    time.sleep(4)
    display.write(" O Leo ligou os ", 0, 0)
    display.write("     direito?   ", 1, 0)
    time.sleep(4)
    '''
    display.cursor = False
    display.blink = True
    for row in range(2):
        for column in range(16):
            display.position = (row, column)
            time.sleep(0.2)
    display.cursor = True
    display.blink = False
    for row in range(2):
        for column in range(16):
            display.position = (row, column)
            time.sleep(0.2)

    display.visible = False
    time.sleep(2)
    display.visible = True

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant