not logged in | [Login]

This module allows you to use Adafruit's 16x2 Character LCD Plate for Raspberry Pi with the pyboard, the code is basically a direct translation of Adafruit's opensource Raspberry Pi CPython source into uPy, which has a different approach to I2C.

This is a trivial derivative of someone else's hard labour - please honour the fact that it's bound by the originator's copyright, which is hence included in this pyboard module source - you must carry forward the copyright if you use, modify, redistribute or publish elsewhere etc. this module (pretty please).

usage

... err, sorry ... will update this later, translated this module some time ago and now have to work out which pins on the plate are for I2C and the backlight ... shouldn't be too difficult in the meantime to work it through if you have one of these plates (check the schematic, taking the plate out to a bread-board using an Adafruit Pi Cobbler cable makes things easier.

known limitations

  • don't seem to be able to read the input buttons on the Char LCD Plate

the module

charlcdplate.py

# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# 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.
import pyb

class CharLCDPlate():

  MCP23017_IOCON_BANK0    = 0x0A
  MCP23017_IOCON_BANK1    = 0x15
  MCP23017_GPIOA          = 0x09
  MCP23017_IODIRB         = 0x10
  MCP23017_GPIOB          = 0x19
  SELECT                  = 0
  RIGHT                   = 1
  DOWN                    = 2
  UP                      = 3
  LEFT                    = 4
  OFF                     = 0x00
  RED                     = 0x01
  GREEN                   = 0x02
  BLUE                    = 0x04
  YELLOW                  = RED + GREEN
  TEAL                    = GREEN + BLUE
  VIOLET                  = RED + BLUE
  WHITE                   = RED + GREEN + BLUE
  ON                      = RED + GREEN + BLUE
  LCD_CLEARDISPLAY        = 0x01
  LCD_RETURNHOME          = 0x02
  LCD_ENTRYMODESET        = 0x04
  LCD_DISPLAYCONTROL      = 0x08
  LCD_CURSORSHIFT         = 0x10
  LCD_FUNCTIONSET         = 0x20
  LCD_SETCGRAMADDR        = 0x40
  LCD_SETDDRAMADDR        = 0x80
  LCD_DISPLAYON           = 0x04
  LCD_DISPLAYOFF          = 0x00
  LCD_CURSORON            = 0x02
  LCD_CURSOROFF           = 0x00
  LCD_BLINKON             = 0x01
  LCD_BLINKOFF            = 0x00
  LCD_ENTRYRIGHT          = 0x00
  LCD_ENTRYLEFT           = 0x02
  LCD_ENTRYSHIFTINCREMENT = 0x01
  LCD_ENTRYSHIFTDECREMENT = 0x00
  LCD_DISPLAYMOVE         = 0x08
  LCD_CURSORMOVE          = 0x00
  LCD_MOVERIGHT           = 0x04
  LCD_MOVELEFT            = 0x00

  def __init__(self, busnum, addr):
      self.i2c = pyb.I2C(busnum,pyb.I2C.MASTER)
      self.address = addr
      self.porta, self.portb, self.ddrb = 0, 0, 0b00010000
      self.i2c.mem_write(0,self.address,self.MCP23017_IOCON_BANK1)
      registers =[ 0b00111111, self.ddrb, 0b00111111, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 
                   0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00111111, 0b00000000, 
                   0b00000000, 0b00000000, 0b00000000, 0b00000000, self.porta, self.portb, self.porta, 
                   self.portb ]
      for register in registers:
        self.i2c.mem_write(0,self.address,register)
      self.i2c.mem_write(0b10100000, self.address, self.MCP23017_IOCON_BANK0)
      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)
      self.write(0x33)
      self.write(0x32)
      self.write(0x28)
      self.write(self.LCD_CLEARDISPLAY)
      self.write(self.LCD_CURSORSHIFT    | self.displayshift)
      self.write(self.LCD_ENTRYMODESET   | self.displaymode)
      self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
      self.write(self.LCD_RETURNHOME)

  flip = ( 0b00000000, 0b00010000, 0b00001000, 0b00011000,
           0b00000100, 0b00010100, 0b00001100, 0b00011100,
           0b00000010, 0b00010010, 0b00001010, 0b00011010,
           0b00000110, 0b00010110, 0b00001110, 0b00011110 )

  def out4(self, bitmask, value):
      hi = bitmask | self.flip[value >> 4]
      lo = bitmask | self.flip[value & 0x0F]
      return [hi | 0b00100000, hi, lo | 0b00100000, lo]

  pollables = ( LCD_CLEARDISPLAY, LCD_RETURNHOME )

  def write(self, value, char_mode=False):
    if self.ddrb & 0b00010000:
      lo = (self.portb & 0b00000001) | 0b01000000
      hi = lo | 0b00100000
      self.i2c.mem_write(lo,self.address,self.MCP23017_GPIOB)
      while True:
        self.i2c.send(hi,self.address)
        bits = self.i2c.recv(1,self.address)
        self.i2c.mem_write(bytearray([lo,hi,lo]),self.address,self.MCP23017_GPIOB)
        if (bits[0] & 0b00000010) == 0: break
      self.portb = lo
      self.ddrb &= 0b11101111
      self.i2c.mem_write(self.ddrb,self.address,self.MCP23017_IODIRB)
    bitmask = self.portb & 0b00000001
    if char_mode: bitmask |= 0b10000000
    if isinstance(value, str):
      last = len(value) - 1
      data = []
      for i, v in enumerate(value):
        data.extend(self.out4(bitmask, ord(v)))
        if (len(data) >= 32) or (i == last):
          self.i2c.mem_write(bytearray(data),self.address,self.MCP23017_GPIOB)
          self.portb = data[-1]
          data       = []
    elif isinstance(value, list):
      last = len(value) - 1
      data = []
      for i, v in enumerate(value):
        data.extend(self.out4(bitmask, v))
        if (len(data) >= 32) or (i == last):
          self.i2c.mem_write(data,self.address,self.MCP23017_GPIOB)
          self.portb = data[-1]
          data       = []
    else:
      data = self.out4(bitmask, value)
      self.i2c.mem_write(bytearray(data),self.address,self.MCP23017_GPIOB)
      self.portb = data[-1]
    if (not char_mode) and (value in self.pollables):
      self.ddrb |= 0b00010000
      self.i2c.mem_write(self.ddrb,self.address,self.MCP23017_IODIRB)

  def begin(self, cols, lines):
    self.currline = 0
    self.numlines = lines
    self.clear()

  def clear(self):
    self.write(self.LCD_CLEARDISPLAY)

  def home(self):
    self.write(self.LCD_RETURNHOME)

  row_offsets = ( 0x00, 0x40, 0x14, 0x54 )

  def setCursor(self, col, row):
    if row > self.numlines: row = self.numlines - 1
    elif row < 0:           row = 0
    self.write(self.LCD_SETDDRAMADDR | (col + self.row_offsets[row]))

  def display(self):
    self.displaycontrol |= self.LCD_DISPLAYON
    self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)

  def noDisplay(self):
    self.displaycontrol &= ~self.LCD_DISPLAYON
    self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)

  def cursor(self):
    self.displaycontrol |= self.LCD_CURSORON
    self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)

  def noCursor(self):
    self.displaycontrol &= ~self.LCD_CURSORON
    self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)

  def ToggleCursor(self):
    self.displaycontrol ^= self.LCD_CURSORON
    self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)

  def blink(self):
    self.displaycontrol |= self.LCD_BLINKON
    self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)

  def noBlink(self):
    self.displaycontrol &= ~self.LCD_BLINKON
    self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)

  def ToggleBlink(self):
    self.displaycontrol ^= self.LCD_BLINKON
    self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)

  def scrollDisplayLeft(self):
    self.displayshift = self.LCD_DISPLAYMOVE | self.LCD_MOVELEFT
    self.write(self.LCD_CURSORSHIFT | self.displayshift)

  def scrollDisplayRight(self):
    self.displayshift = self.LCD_DISPLAYMOVE | self.LCD_MOVERIGHT
    self.write(self.LCD_CURSORSHIFT | self.displayshift)

  def leftToRight(self):
    self.displaymode |= self.LCD_ENTRYLEFT
    self.write(self.LCD_ENTRYMODESET | self.displaymode)

  def rightToLeft(self):
    self.displaymode &= ~self.LCD_ENTRYLEFT
    self.write(self.LCD_ENTRYMODESET | self.displaymode)

  def autoscroll(self):
    self.displaymode |= self.LCD_ENTRYSHIFTINCREMENT
    self.write(self.LCD_ENTRYMODESET | self.displaymode)

  def noAutoscroll(self):
    self.displaymode &= ~self.LCD_ENTRYSHIFTINCREMENT
    self.write(self.LCD_ENTRYMODESET | self.displaymode)

  def createChar(self, location, bitmap):
    self.write(self.LCD_SETCGRAMADDR | ((location & 7) << 3))
    self.write(bitmap, True)
    self.write(self.LCD_SETDDRAMADDR)

  def message(self, text):
    lines = str(text).split('\n')
    for i, line in enumerate(lines):
      if i > 0:
        self.write(0xC0)
      self.write(line, True)

  def backlight(self, color):
    c          = ~color
    self.porta = (self.porta & 0b00111111) | ((c & 0b011) << 6)
    self.portb = (self.portb & 0b11111110) | ((c & 0b100) >> 2)
    self.i2c.mem_write(self.porta,self.address,self.MCP23017_GPIOA)
    self.i2c.mem_write(self.portb,self.address,self.MCP23017_GPIOB)

  def buttonPressed(self, b):
    return (self.i2c.mem_read(1,self.address,self.MCP23017_GPIOA,timeout=100)[0] >> b) & 1

  def buttons(self):
    return self.i2c.mem_read(1,self.address,self.MCP23017_GPIOA,timeout=100)[0] & 0b11111