Skip to content

Instantly share code, notes, and snippets.

@tdack
Forked from ufux/lcdui.py
Last active August 29, 2015 14:06
Show Gist options
  • Select an option

  • Save tdack/99969967f81942e39847 to your computer and use it in GitHub Desktop.

Select an option

Save tdack/99969967f81942e39847 to your computer and use it in GitHub Desktop.

Revisions

  1. @ufux ufux created this gist Jul 27, 2013.
    256 changes: 256 additions & 0 deletions lcdui.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,256 @@
    import smbus
    import getopt
    import sys
    from time import *
    from time import gmtime, strftime

    # TODO: Factor out all device_write calls to some PCF8574 specific module ...
    # will be different with another io expander

    # communication from expander to display: high nibble first, then low nibble
    # communication via i2c to the PCF 8547: bits are processed from highest to lowest (send P7 bit first)


    # General i2c device class so that other devices can be added easily
    class i2c_device:
    def __init__(self, addr, port):
    self.addr = addr
    self.bus = smbus.SMBus(port)

    def write(self, byte):
    self.bus.write_byte(self.addr, byte)

    def read(self):
    return self.bus.read_byte(self.addr)

    def read_nbytes_data(self, data, n): # For sequential reads > 1 byte
    return self.bus.read_i2c_block_data(self.addr, data, n)


    class ioexpander:
    def __init__(self):
    pass

    class lcd:
    #initializes objects and lcd

    # LCD Commands
    LCD_CLEARDISPLAY = 0x01
    LCD_RETURNHOME = 0x02
    LCD_ENTRYMODESET = 0x04
    LCD_DISPLAYCONTROL = 0x08
    LCD_CURSORSHIFT = 0x10
    LCD_FUNCTIONSET = 0x20
    LCD_SETCGRAMADDR = 0x40
    LCD_SETDDRAMADDR = 0x80

    # Flags for display on/off control
    LCD_DISPLAYON = 0x04
    LCD_DISPLAYOFF = 0x00
    LCD_CURSORON = 0x02
    LCD_CURSOROFF = 0x00
    LCD_BLINKON = 0x01
    LCD_BLINKOFF = 0x00

    # Flags for display entry mode
    LCD_ENTRYRIGHT = 0x00
    LCD_ENTRYLEFT = 0x02
    LCD_ENTRYSHIFTINCREMENT = 0x01
    LCD_ENTRYSHIFTDECREMENT = 0x00

    # Flags for display/cursor shift
    LCD_DISPLAYMOVE = 0x08
    LCD_CURSORMOVE = 0x00
    LCD_MOVERIGHT = 0x04
    LCD_MOVELEFT = 0x00

    # flags for function set
    LCD_8BITMODE = 0x10
    LCD_4BITMODE = 0x00
    LCD_2LINE = 0x08
    LCD_1LINE = 0x00
    LCD_5x10DOTS = 0x04
    LCD_5x8DOTS = 0x00

    # flags for backlight control
    LCD_BACKLIGHT = 0x08
    LCD_NOBACKLIGHT = 0x00

    EN = 0b00000100 # Enable bit
    RW = 0b00000010 # Read/Write bit
    RS = 0b00000001 # Register select bit



    '''
    new pinout:
    ----------
    0x80 P7 - - D7
    0x40 P6 - - D6
    0x20 P5 - - D5
    0x10 P4 - - D4
    -----------
    0x08 P3 - - BL Backlight ???
    0x04 P2 - - EN Starts Data read/write
    0x02 P1 - - RW low: write, high: read
    0x01 P0 - - RS Register Select: 0: Instruction Register (IR) (AC when read), 1: data register (DR)
    '''

    def __init__(self, addr, port, withBacklight=True, withOneTimeInit=False):
    '''
    device writes!
    crosscheck also http://www.monkeyboard.org/tutorials/81-display/70-usb-serial-to-hd44780-lcd
    here a sequence is listed
    '''
    self.displayshift = (self.LCD_CURSORMOVE |
    self.LCD_MOVERIGHT)
    self.displaymode = (self.LCD_ENTRYLEFT |
    self.LCD_ENTRYSHIFTDECREMENT)
    self.displaycontrol = (self.LCD_DISPLAYON |
    self.LCD_CURSOROFF |
    self.LCD_BLINKOFF)


    if withBacklight:
    self.blFlag=self.LCD_BACKLIGHT
    else:
    self.blFlag=self.LCD_NOBACKLIGHT


    self.lcd_device = i2c_device(addr, port)

    # we can initialize the display only once after it had been powered on
    if(withOneTimeInit):
    self.lcd_device.write(0x20)
    self.lcd_strobe()
    sleep(0.0100) # TODO: Not clear if we have to wait that long
    self.lcd_write(self.LCD_FUNCTIONSET | self.LCD_4BITMODE | self.LCD_2LINE | self.LCD_5x8DOTS) # 0x28

    self.lcd_write(self.LCD_DISPLAYCONTROL | self.displaycontrol) # 0x08 + 0x4 = 0x0C
    self.lcd_write(self.LCD_ENTRYMODESET | self.displaymode) # 0x06
    self.lcd_write(self.LCD_CLEARDISPLAY) # 0x01
    self.lcd_write(self.LCD_CURSORSHIFT | self.displayshift) # 0x14
    self.lcd_write(self.LCD_RETURNHOME)


    # clocks EN to latch command
    def lcd_strobe(self):
    self.lcd_device.write((self.lcd_device.read() | self.EN | self.blFlag)) # | 0b0000 0100 # set "EN" high
    self.lcd_device.write(( (self.lcd_device.read() | self.blFlag) & 0xFB)) # & 0b1111 1011 # set "EN" low

    # write data to lcd in 4 bit mode, 2 nibbles
    # high nibble is sent first
    def lcd_write(self, cmd):

    #write high nibble first
    self.lcd_device.write( (cmd & 0xF0) | self.blFlag )
    hi= self.lcd_device.read()
    self.lcd_strobe()

    # write low nibble second ...
    self.lcd_device.write( (cmd << 4) | self.blFlag )
    lo= self.lcd_device.read()
    self.lcd_strobe()
    self.lcd_device.write(self.blFlag)


    # write a character to lcd (or character rom) 0x09: backlight | RS=DR
    # works as expected
    def lcd_write_char(self, charvalue):
    controlFlag = self.blFlag | self.RS

    # write high nibble
    self.lcd_device.write((controlFlag | (charvalue & 0xF0)))
    self.lcd_strobe()

    # write low nibble
    self.lcd_device.write((controlFlag | (charvalue << 4)))
    self.lcd_strobe()
    self.lcd_device.write(self.blFlag)


    # put char function
    def lcd_putc(self, char):
    self.lcd_write_char(ord(char))

    def _setDDRAMAdress(self, line, col):
    # we write to the Data Display RAM (DDRAM)
    # TODO: Factor line offsets for other display organizations; this is for 20x4 only
    if line == 1:
    self.lcd_write(self.LCD_SETDDRAMADDR | (0x00 + col) )
    if line == 2:
    self.lcd_write(self.LCD_SETDDRAMADDR | (0x40 + col) )
    if line == 3:
    self.lcd_write(self.LCD_SETDDRAMADDR | (0x14 + col) )
    if line == 4:
    self.lcd_write(self.LCD_SETDDRAMADDR | (0x54 + col) )


    # put string function
    def lcd_puts(self, string, line):
    self._setDDRAMAdress(line, 0)
    for char in string:
    self.lcd_putc(char)

    # clear lcd and set to home
    def lcd_clear(self):
    # self.lcd_write(0x10)
    self.lcd_write(self.LCD_CLEARDISPLAY)
    # self.lcd_write(0x20)
    self.lcd_write(self.LCD_RETURNHOME)

    # add custom characters (0 - 7)
    def lcd_load_custon_chars(self, fontdata):
    self.lcd_device.bus.write(0x40);
    for char in fontdata:
    for line in char:
    self.lcd_write_char(line)

    # Let them know how it works
    def usage():
    print 'Usage: lcdui.py --init --debug --backlightoff'

    # Handle the command line arguments
    def main():
    initFlag=False
    debug=False
    backlight=True

    try:
    opts, args = getopt.getopt(sys.argv[1:],"idb",["init","debug","backlightoff"])

    except getopt.GetoptError:
    usage()
    sys.exit(2)


    for opt, arg in opts:
    if opt == '-h':
    usage()
    sys.exit()
    elif opt in ("-i", "--init"):
    initFlag = True
    elif opt in ("-d", "--debug"):
    debug = True
    elif opt in ("-b", "--backlightoff"):
    backlight = False

    if initFlag:
    print "Doing initial init ..."
    else:
    print "Skipping init ..."

    device = lcd(0x27,1,backlight, initFlag)
    device.lcd_puts("01234567890123456789",1)
    device.lcd_puts("012345 Zeile 2 56789",2)
    device.lcd_puts("012345 Zeile 3 56789",3)
    device.lcd_puts(strftime("%Y-%m-%d %H:%M:%S", gmtime()),4)
    sleep(3)
    device.lcd_clear()
    device.lcd_puts(" Simple Clock ",1)
    while True:
    device.lcd_puts(strftime("%Y-%m-%d %H:%M:%S ", gmtime()),3)
    sleep(1)

    if __name__ == '__main__':
    main()