Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
I2C working, SPI needs testing
  • Loading branch information
kattni committed Nov 13, 2018
commit 3e6a1a64d1ff386cc176598ea6b82d9824c82afd
111 changes: 19 additions & 92 deletions adafruit_character_lcd/character_lcd.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -350,65 +350,21 @@ def create_char(self, location, pattern):

#pylint: enable-msg=too-many-instance-attributes

class Character_LCD_I2C(Character_LCD):
"""Character LCD connected to I2C/SPI backpack using its I2C connection.
This is a subclass of Character_LCD and implements all of the same
functions and functionality.
"""

class Character_LCD_I2C(Character_LCD):
def __init__(self, i2c, cols, lines):
"""Initialize character LCD connectedto backpack using I2C connection
on the specified I2C bus and of the specified number of columns and
lines on the display.
"""
# Import the MCP23008 module here when the class is used
# to keep memory usage low. If you attempt to import globally at the
# top of this file you WILL run out of memory on the M0, even with
# MPY files. The amount of code and classes implicitly imported
# by all the SPI and I2C code is too high. Thus import on demand.
import adafruit_character_lcd.mcp23008 as mcp23008
self._mcp = mcp23008.MCP23008(i2c)
# Setup pins for I2C backpack, see diagram:
# https://learn.adafruit.com/assets/35681
reset = self._mcp.DigitalInOut(_MCP23008_LCD_RS, self._mcp)
enable = self._mcp.DigitalInOut(_MCP23008_LCD_EN, self._mcp)
dl4 = self._mcp.DigitalInOut(_MCP23008_LCD_D4, self._mcp)
dl5 = self._mcp.DigitalInOut(_MCP23008_LCD_D5, self._mcp)
dl6 = self._mcp.DigitalInOut(_MCP23008_LCD_D6, self._mcp)
dl7 = self._mcp.DigitalInOut(_MCP23008_LCD_D7, self._mcp)
backlight = self._mcp.DigitalInOut(_MCP23008_LCD_BACKLIGHT, self._mcp)
# Call superclass initializer with MCP23008 pins.
super().__init__(reset, enable, dl4, dl5, dl6, dl7, cols, lines,
import adafruit_mcp230xx
self._mcp = adafruit_mcp230xx.MCP23008(i2c)
reset = self._mcp.get_pin(1)
enable = self._mcp.get_pin(2)
d4 = self._mcp.get_pin(3)
d5 = self._mcp.get_pin(4)
d6 = self._mcp.get_pin(5)
d7 = self._mcp.get_pin(6)
backlight = self._mcp.get_pin(7)
super().__init__(reset, enable, d4, d5, d6, d7, cols, lines,
backlight=backlight)

def _write8(self, value, char_mode=False):
# Optimize a command write by changing all GPIO pins at once instead
# of letting the super class try to set each one invidually (far too
# slow with overhead of I2C communication).
gpio = self._mcp.gpio
# Make sure enable is low.
gpio = _set_bit(gpio, _MCP23008_LCD_EN, False)
# Set character/data bit. (charmode = False).
gpio = _set_bit(gpio, _MCP23008_LCD_RS, char_mode)
# Set upper 4 bits.
gpio = _set_bit(gpio, _MCP23008_LCD_D4, ((value >> 4) & 1) > 0)
gpio = _set_bit(gpio, _MCP23008_LCD_D5, ((value >> 5) & 1) > 0)
gpio = _set_bit(gpio, _MCP23008_LCD_D6, ((value >> 6) & 1) > 0)
gpio = _set_bit(gpio, _MCP23008_LCD_D7, ((value >> 7) & 1) > 0)
self._mcp.gpio = gpio
# Send command.
self._pulse_enable()
# Now repeat for lower 4 bits.
gpio = self._mcp.gpio
gpio = _set_bit(gpio, _MCP23008_LCD_EN, False)
gpio = _set_bit(gpio, _MCP23008_LCD_RS, char_mode)
gpio = _set_bit(gpio, _MCP23008_LCD_D4, (value & 1) > 0)
gpio = _set_bit(gpio, _MCP23008_LCD_D5, ((value >> 1) & 1) > 0)
gpio = _set_bit(gpio, _MCP23008_LCD_D6, ((value >> 2) & 1) > 0)
gpio = _set_bit(gpio, _MCP23008_LCD_D7, ((value >> 3) & 1) > 0)
self._mcp.gpio = gpio
self._pulse_enable()


class Character_LCD_SPI(Character_LCD):
"""Character LCD connected to I2C/SPI backpack using its SPI connection.
Expand All @@ -426,41 +382,12 @@ def __init__(self, spi, latch, cols, lines):
self._sr = shift_reg_74hc595.ShiftReg74HC595(spi, latch)
# Setup pins for SPI backpack, see diagram:
# https://learn.adafruit.com/assets/35681
reset = self._sr.DigitalInOut(_74HC595_LCD_RS, self._sr)
enable = self._sr.DigitalInOut(_74HC595_LCD_EN, self._sr)
dl4 = self._sr.DigitalInOut(_74HC595_LCD_D4, self._sr)
dl5 = self._sr.DigitalInOut(_74HC595_LCD_D5, self._sr)
dl6 = self._sr.DigitalInOut(_74HC595_LCD_D6, self._sr)
dl7 = self._sr.DigitalInOut(_74HC595_LCD_D7, self._sr)
backlight = self._sr.DigitalInOut(_74HC595_LCD_BACKLIGHT, self._sr)
# Call superclass initializer with shift register pins.
super().__init__(reset, enable, dl4, dl5, dl6, dl7, cols, lines,
reset = self._sr.get_pin(1)
enable = self._sr.get_pin(2)
d4 = self._sr.get_pin(3)
d5 = self._sr.get_pin(4)
d6 = self._sr.get_pin(5)
d7 = self._sr.get_pin(6)
backlight = self._sr.get_pin(7)
super().__init__(reset, enable, d4, d5, d6, d7, cols, lines,
backlight=backlight)

def _write8(self, value, char_mode=False):
# Optimize a command write by changing all GPIO pins at once instead
# of letting the super class try to set each one invidually (far too
# slow with overhead of SPI communication).
gpio = self._sr.gpio
# Make sure enable is low.
gpio = _set_bit(gpio, _74HC595_LCD_EN, False)
# Set character/data bit. (charmode = False).
gpio = _set_bit(gpio, _74HC595_LCD_RS, char_mode)
# Set upper 4 bits.
gpio = _set_bit(gpio, _74HC595_LCD_D4, ((value >> 4) & 1) > 0)
gpio = _set_bit(gpio, _74HC595_LCD_D5, ((value >> 5) & 1) > 0)
gpio = _set_bit(gpio, _74HC595_LCD_D6, ((value >> 6) & 1) > 0)
gpio = _set_bit(gpio, _74HC595_LCD_D7, ((value >> 7) & 1) > 0)
self._sr.gpio = gpio
# Send command.
self._pulse_enable()
# Now repeat for lower 4 bits.
gpio = self._sr.gpio
gpio = _set_bit(gpio, _74HC595_LCD_EN, False)
gpio = _set_bit(gpio, _74HC595_LCD_RS, char_mode)
gpio = _set_bit(gpio, _74HC595_LCD_D4, (value & 1) > 0)
gpio = _set_bit(gpio, _74HC595_LCD_D5, ((value >> 1) & 1) > 0)
gpio = _set_bit(gpio, _74HC595_LCD_D6, ((value >> 2) & 1) > 0)
gpio = _set_bit(gpio, _74HC595_LCD_D7, ((value >> 3) & 1) > 0)
self._sr.gpio = gpio
self._pulse_enable()
228 changes: 118 additions & 110 deletions adafruit_character_lcd/shift_reg_74hc595.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,110 +1,118 @@
"""
`adafruit_character_led.shift_reg_74hc595`
===============================================

74HC595 Serial to Paralllel Shift Register Driver
Bare-bones driver for the 74HC595, as used by the character LCD
backpack. This exposes the 74HC595 and its pins as standard CircuitPython
digitalio pins. Currently this is integrated in the character LCD class for
simplicity and reduction in dependent imports, but it could be broken out
into a standalone library later.

* Author: Tony DiCola
"""
import digitalio

import adafruit_bus_device.spi_device as spi_device


#pylint: disable-msg=too-few-public-methods
#pylint: disable-msg=no-self-use
class ShiftReg74HC595:
"""Shift Register 74LS95 driver class"""
class DigitalInOut:
"""Digital input/output of the 74HC595. The interface is exactly the
same as the digitalio.DigitalInOut class, however note that by design
this device is OUTPUT ONLY! Attempting to read inputs or set
direction as input will raise an exception.
"""

def __init__(self, pin_number, shift_reg_74ls595):
"""Specify the pin number of the shift register (0...7) and
ShiftReg74HC595 instance.
"""
self._pin = pin_number
self._sr = shift_reg_74ls595

# kwargs in switch functions below are _necessary_ for compatibility
# with DigitalInout class (which allows specifying pull, etc. which
# is unused by this class). Do not remove them, instead turn off pylint
# in this case.
#pylint: disable=unused-argument
def switch_to_output(self, value=False, **kwargs):
"""DigitalInOut switch_to_output"""
self.direction = digitalio.Direction.OUTPUT
self.value = value

def switch_to_input(self, **kwargs):
"""do not call switch_to_input"""
raise RuntimeError('Unable to use 74HC595 as digital input!')
#pylint: enable=unused-argument

@property
def value(self):
"""do not call value"""
raise RuntimeError('Unable to use 74HC595 as digital input!')

@value.setter
def value(self, val):
# Only supported operation, writing a digital output.
gpio = self._sr.gpio
if val:
gpio |= (1 << self._pin)
else:
gpio &= ~(1 << self._pin)
self._sr.gpio = gpio

@property
def direction(self):
"""ALWAYS an output!"""
return digitalio.Direction.OUTPUT

@direction.setter
def direction(self, val):
"""Can only be set as OUTPUT!"""
if val != digitalio.Direction.OUTPUT:
raise RuntimeError('Unable to use 74HC595 as digital input!')

@property
def pull(self):
"""Pull-up/down not supported, return NonLiberty e for no pull-up/down."""
return None

@pull.setter
def pull(self, val):
"""Only supports null/no pull state."""
if val is not None:
raise RuntimeError('Unable to set 74HC595 pull!')


def __init__(self, spi, latch):
self._device = spi_device.SPIDevice(spi, latch, baudrate=1000000)
self._gpio = bytearray(1)
self._gpio[0] = 0x00

@property
def gpio(self):
"""Get and set the raw GPIO output register. Each bit represents the
output value of the associated pin (0 = low, 1 = high).
"""
return self._gpio[0]

@gpio.setter
def gpio(self, val):
self._gpio[0] = val & 0xFF
with self._device as spi:
# pylint: disable=no-member
spi.write(self._gpio)

#pylint: enable-msg=no-self-use
#pylint: enable-msg=too-few-public-methods
"""
`adafruit_character_led.shift_reg_74hc595`
===============================================

74HC595 Serial to Paralllel Shift Register Driver
Bare-bones driver for the 74HC595, as used by the character LCD
backpack. This exposes the 74HC595 and its pins as standard CircuitPython
digitalio pins. Currently this is integrated in the character LCD class for
simplicity and reduction in dependent imports, but it could be broken out
into a standalone library later.

* Author: Tony DiCola
"""
import digitalio

import adafruit_bus_device.spi_device as spi_device


#pylint: disable-msg=too-few-public-methods
#pylint: disable-msg=no-self-use
#class ShiftReg74HC595:
# """Shift Register 74LS95 driver class"""
class DigitalInOut:
"""Digital input/output of the 74HC595. The interface is exactly the
same as the digitalio.DigitalInOut class, however note that by design
this device is OUTPUT ONLY! Attempting to read inputs or set
direction as input will raise an exception.
"""

def __init__(self, pin_number, shift_reg_74ls595):
"""Specify the pin number of the shift register (0...7) and
ShiftReg74HC595 instance.
"""
self._pin = pin_number
self._sr = shift_reg_74ls595

# kwargs in switch functions below are _necessary_ for compatibility
# with DigitalInout class (which allows specifying pull, etc. which
# is unused by this class). Do not remove them, instead turn off pylint
# in this case.
#pylint: disable=unused-argument
def switch_to_output(self, value=False, **kwargs):
"""DigitalInOut switch_to_output"""
self.direction = digitalio.Direction.OUTPUT
self.value = value

def switch_to_input(self, **kwargs):
"""do not call switch_to_input"""
raise RuntimeError('Unable to use 74HC595 as digital input!')
#pylint: enable=unused-argument

@property
def value(self):
"""do not call value"""
raise RuntimeError('Unable to use 74HC595 as digital input!')

@value.setter
def value(self, val):
# Only supported operation, writing a digital output.
gpio = self._sr.gpio
if val:
gpio |= (1 << self._pin)
else:
gpio &= ~(1 << self._pin)
self._sr.gpio = gpio

@property
def direction(self):
"""ALWAYS an output!"""
return digitalio.Direction.OUTPUT

@direction.setter
def direction(self, val):
"""Can only be set as OUTPUT!"""
if val != digitalio.Direction.OUTPUT:
raise RuntimeError('Unable to use 74HC595 as digital input!')

@property
def pull(self):
"""Pull-up/down not supported, return NonLiberty e for no pull-up/down."""
return None

@pull.setter
def pull(self, val):
"""Only supports null/no pull state."""
if val is not None:
raise RuntimeError('Unable to set 74HC595 pull!')


class ShiftReg74HC595:
def __init__(self, spi, latch):
self._device = spi_device.SPIDevice(spi, latch, baudrate=1000000)
self._gpio = bytearray(1)
self._gpio[0] = 0x00

@property
def gpio(self):
"""Get and set the raw GPIO output register. Each bit represents the
output value of the associated pin (0 = low, 1 = high).
"""
return self._gpio[0]

@gpio.setter
def gpio(self, val):
self._gpio[0] = val & 0xFF
with self._device as spi:
# pylint: disable=no-member
spi.write(self._gpio)

def get_pin(self, pin):
"""Convenience function to create an instance of the DigitalInOut class
pointing at the specified pin of this MCP23008 device.
"""
assert 0 <= pin <= 7
return DigitalInOut(pin, self)

#pylint: enable-msg=no-self-use
#pylint: enable-msg=too-few-public-methods