Python 3 fixes, and fixes to support pillow library.

This commit is contained in:
Tony DiCola
2016-04-15 00:04:03 +00:00
parent 54d52bde26
commit dbb9b33482
6 changed files with 250 additions and 239 deletions

View File

@@ -22,8 +22,8 @@ import numbers
import time
import numpy as np
import Image
import ImageDraw
from PIL import Image
from PIL import ImageDraw
import Adafruit_GPIO as GPIO
import Adafruit_GPIO.SPI as SPI
@@ -94,233 +94,233 @@ ILI9341_RED = 0xF800
ILI9341_GREEN = 0x07E0
ILI9341_CYAN = 0x07FF
ILI9341_MAGENTA = 0xF81F
ILI9341_YELLOW = 0xFFE0
ILI9341_YELLOW = 0xFFE0
ILI9341_WHITE = 0xFFFF
def color565(r, g, b):
"""Convert red, green, blue components to a 16-bit 565 RGB value. Components
should be values 0 to 255.
"""
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)
"""Convert red, green, blue components to a 16-bit 565 RGB value. Components
should be values 0 to 255.
"""
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)
def image_to_data(image):
"""Generator function to convert a PIL image to 16-bit 565 RGB bytes."""
#NumPy is much faster at doing this. NumPy code provided by:
#Keith (https://www.blogger.com/profile/02555547344016007163)
pb = np.array(image.convert('RGB')).astype('uint16')
color = ((pb[:,:,0] & 0xF8) << 8) | ((pb[:,:,1] & 0xFC) << 3) | (pb[:,:,2] >> 3)
return np.dstack(((color >> 8) & 0xFF, color & 0xFF)).flatten().tolist()
"""Generator function to convert a PIL image to 16-bit 565 RGB bytes."""
#NumPy is much faster at doing this. NumPy code provided by:
#Keith (https://www.blogger.com/profile/02555547344016007163)
pb = np.array(image.convert('RGB')).astype('uint16')
color = ((pb[:,:,0] & 0xF8) << 8) | ((pb[:,:,1] & 0xFC) << 3) | (pb[:,:,2] >> 3)
return np.dstack(((color >> 8) & 0xFF, color & 0xFF)).flatten().tolist()
class ILI9341(object):
"""Representation of an ILI9341 TFT LCD."""
"""Representation of an ILI9341 TFT LCD."""
def __init__(self, dc, spi, rst=None, gpio=None, width=ILI9341_TFTWIDTH,
height=ILI9341_TFTHEIGHT):
"""Create an instance of the display using SPI communication. Must
provide the GPIO pin number for the D/C pin and the SPI driver. Can
optionally provide the GPIO pin number for the reset pin as the rst
parameter.
"""
self._dc = dc
self._rst = rst
self._spi = spi
self._gpio = gpio
self.width = width
self.height = height
if self._gpio is None:
self._gpio = GPIO.get_platform_gpio()
# Set DC as output.
self._gpio.setup(dc, GPIO.OUT)
# Setup reset as output (if provided).
if rst is not None:
self._gpio.setup(rst, GPIO.OUT)
# Set SPI to mode 0, MSB first.
spi.set_mode(0)
spi.set_bit_order(SPI.MSBFIRST)
spi.set_clock_hz(64000000)
# Create an image buffer.
self.buffer = Image.new('RGB', (width, height))
def __init__(self, dc, spi, rst=None, gpio=None, width=ILI9341_TFTWIDTH,
height=ILI9341_TFTHEIGHT):
"""Create an instance of the display using SPI communication. Must
provide the GPIO pin number for the D/C pin and the SPI driver. Can
optionally provide the GPIO pin number for the reset pin as the rst
parameter.
"""
self._dc = dc
self._rst = rst
self._spi = spi
self._gpio = gpio
self.width = width
self.height = height
if self._gpio is None:
self._gpio = GPIO.get_platform_gpio()
# Set DC as output.
self._gpio.setup(dc, GPIO.OUT)
# Setup reset as output (if provided).
if rst is not None:
self._gpio.setup(rst, GPIO.OUT)
# Set SPI to mode 0, MSB first.
spi.set_mode(0)
spi.set_bit_order(SPI.MSBFIRST)
spi.set_clock_hz(64000000)
# Create an image buffer.
self.buffer = Image.new('RGB', (width, height))
def send(self, data, is_data=True, chunk_size=4096):
"""Write a byte or array of bytes to the display. Is_data parameter
controls if byte should be interpreted as display data (True) or command
data (False). Chunk_size is an optional size of bytes to write in a
single SPI transaction, with a default of 4096.
"""
# Set DC low for command, high for data.
self._gpio.output(self._dc, is_data)
# Convert scalar argument to list so either can be passed as parameter.
if isinstance(data, numbers.Number):
data = [data & 0xFF]
# Write data a chunk at a time.
for start in range(0, len(data), chunk_size):
end = min(start+chunk_size, len(data))
self._spi.write(data[start:end])
def send(self, data, is_data=True, chunk_size=4096):
"""Write a byte or array of bytes to the display. Is_data parameter
controls if byte should be interpreted as display data (True) or command
data (False). Chunk_size is an optional size of bytes to write in a
single SPI transaction, with a default of 4096.
"""
# Set DC low for command, high for data.
self._gpio.output(self._dc, is_data)
# Convert scalar argument to list so either can be passed as parameter.
if isinstance(data, numbers.Number):
data = [data & 0xFF]
# Write data a chunk at a time.
for start in range(0, len(data), chunk_size):
end = min(start+chunk_size, len(data))
self._spi.write(data[start:end])
def command(self, data):
"""Write a byte or array of bytes to the display as command data."""
self.send(data, False)
def command(self, data):
"""Write a byte or array of bytes to the display as command data."""
self.send(data, False)
def data(self, data):
"""Write a byte or array of bytes to the display as display data."""
self.send(data, True)
def data(self, data):
"""Write a byte or array of bytes to the display as display data."""
self.send(data, True)
def reset(self):
"""Reset the display, if reset pin is connected."""
if self._rst is not None:
self._gpio.set_high(self._rst)
time.sleep(0.005)
self._gpio.set_low(self._rst)
time.sleep(0.02)
self._gpio.set_high(self._rst)
time.sleep(0.150)
def reset(self):
"""Reset the display, if reset pin is connected."""
if self._rst is not None:
self._gpio.set_high(self._rst)
time.sleep(0.005)
self._gpio.set_low(self._rst)
time.sleep(0.02)
self._gpio.set_high(self._rst)
time.sleep(0.150)
def _init(self):
# Initialize the display. Broken out as a separate function so it can
# be overridden by other displays in the future.
self.command(0xEF)
self.data(0x03)
self.data(0x80)
self.data(0x02)
self.command(0xCF)
self.data(0x00)
self.data(0XC1)
self.data(0X30)
self.command(0xED)
self.data(0x64)
self.data(0x03)
self.data(0X12)
self.data(0X81)
self.command(0xE8)
self.data(0x85)
self.data(0x00)
self.data(0x78)
self.command(0xCB)
self.data(0x39)
self.data(0x2C)
self.data(0x00)
self.data(0x34)
self.data(0x02)
self.command(0xF7)
self.data(0x20)
self.command(0xEA)
self.data(0x00)
self.data(0x00)
self.command(ILI9341_PWCTR1) # Power control
self.data(0x23) # VRH[5:0]
self.command(ILI9341_PWCTR2) # Power control
self.data(0x10) # SAP[2:0];BT[3:0]
self.command(ILI9341_VMCTR1) # VCM control
self.data(0x3e)
self.data(0x28)
self.command(ILI9341_VMCTR2) # VCM control2
self.data(0x86) # --
self.command(ILI9341_MADCTL) # Memory Access Control
self.data(0x48)
self.command(ILI9341_PIXFMT)
self.data(0x55)
self.command(ILI9341_FRMCTR1)
self.data(0x00)
self.data(0x18)
self.command(ILI9341_DFUNCTR) # Display Function Control
self.data(0x08)
self.data(0x82)
self.data(0x27)
self.command(0xF2) # 3Gamma Function Disable
self.data(0x00)
self.command(ILI9341_GAMMASET) # Gamma curve selected
self.data(0x01)
self.command(ILI9341_GMCTRP1) # Set Gamma
self.data(0x0F)
self.data(0x31)
self.data(0x2B)
self.data(0x0C)
self.data(0x0E)
self.data(0x08)
self.data(0x4E)
self.data(0xF1)
self.data(0x37)
self.data(0x07)
self.data(0x10)
self.data(0x03)
self.data(0x0E)
self.data(0x09)
self.data(0x00)
self.command(ILI9341_GMCTRN1) # Set Gamma
self.data(0x00)
self.data(0x0E)
self.data(0x14)
self.data(0x03)
self.data(0x11)
self.data(0x07)
self.data(0x31)
self.data(0xC1)
self.data(0x48)
self.data(0x08)
self.data(0x0F)
self.data(0x0C)
self.data(0x31)
self.data(0x36)
self.data(0x0F)
self.command(ILI9341_SLPOUT) # Exit Sleep
time.sleep(0.120)
self.command(ILI9341_DISPON) # Display on
def _init(self):
# Initialize the display. Broken out as a separate function so it can
# be overridden by other displays in the future.
self.command(0xEF)
self.data(0x03)
self.data(0x80)
self.data(0x02)
self.command(0xCF)
self.data(0x00)
self.data(0XC1)
self.data(0X30)
self.command(0xED)
self.data(0x64)
self.data(0x03)
self.data(0X12)
self.data(0X81)
self.command(0xE8)
self.data(0x85)
self.data(0x00)
self.data(0x78)
self.command(0xCB)
self.data(0x39)
self.data(0x2C)
self.data(0x00)
self.data(0x34)
self.data(0x02)
self.command(0xF7)
self.data(0x20)
self.command(0xEA)
self.data(0x00)
self.data(0x00)
self.command(ILI9341_PWCTR1) # Power control
self.data(0x23) # VRH[5:0]
self.command(ILI9341_PWCTR2) # Power control
self.data(0x10) # SAP[2:0];BT[3:0]
self.command(ILI9341_VMCTR1) # VCM control
self.data(0x3e)
self.data(0x28)
self.command(ILI9341_VMCTR2) # VCM control2
self.data(0x86) # --
self.command(ILI9341_MADCTL) # Memory Access Control
self.data(0x48)
self.command(ILI9341_PIXFMT)
self.data(0x55)
self.command(ILI9341_FRMCTR1)
self.data(0x00)
self.data(0x18)
self.command(ILI9341_DFUNCTR) # Display Function Control
self.data(0x08)
self.data(0x82)
self.data(0x27)
self.command(0xF2) # 3Gamma Function Disable
self.data(0x00)
self.command(ILI9341_GAMMASET) # Gamma curve selected
self.data(0x01)
self.command(ILI9341_GMCTRP1) # Set Gamma
self.data(0x0F)
self.data(0x31)
self.data(0x2B)
self.data(0x0C)
self.data(0x0E)
self.data(0x08)
self.data(0x4E)
self.data(0xF1)
self.data(0x37)
self.data(0x07)
self.data(0x10)
self.data(0x03)
self.data(0x0E)
self.data(0x09)
self.data(0x00)
self.command(ILI9341_GMCTRN1) # Set Gamma
self.data(0x00)
self.data(0x0E)
self.data(0x14)
self.data(0x03)
self.data(0x11)
self.data(0x07)
self.data(0x31)
self.data(0xC1)
self.data(0x48)
self.data(0x08)
self.data(0x0F)
self.data(0x0C)
self.data(0x31)
self.data(0x36)
self.data(0x0F)
self.command(ILI9341_SLPOUT) # Exit Sleep
time.sleep(0.120)
self.command(ILI9341_DISPON) # Display on
def begin(self):
"""Initialize the display. Should be called once before other calls that
interact with the display are called.
"""
self.reset()
self._init()
def set_window(self, x0=0, y0=0, x1=None, y1=None):
"""Set the pixel address window for proceeding drawing commands. x0 and
x1 should define the minimum and maximum x pixel bounds. y0 and y1
should define the minimum and maximum y pixel bound. If no parameters
are specified the default will be to update the entire display from 0,0
to 239,319.
"""
if x1 is None:
x1 = self.width-1
if y1 is None:
y1 = self.height-1
self.command(ILI9341_CASET) # Column addr set
self.data(x0 >> 8)
self.data(x0) # XSTART
self.data(x1 >> 8)
self.data(x1) # XEND
self.command(ILI9341_PASET) # Row addr set
self.data(y0 >> 8)
self.data(y0) # YSTART
self.data(y1 >> 8)
self.data(y1) # YEND
self.command(ILI9341_RAMWR) # write to RAM
def begin(self):
"""Initialize the display. Should be called once before other calls that
interact with the display are called.
"""
self.reset()
self._init()
def display(self, image=None):
"""Write the display buffer or provided image to the hardware. If no
image parameter is provided the display buffer will be written to the
hardware. If an image is provided, it should be RGB format and the
same dimensions as the display hardware.
"""
# By default write the internal buffer to the display.
if image is None:
image = self.buffer
# Set address bounds to entire display.
self.set_window()
# Convert image to array of 16bit 565 RGB data bytes.
# Unfortunate that this copy has to occur, but the SPI byte writing
# function needs to take an array of bytes and PIL doesn't natively
# store images in 16-bit 565 RGB format.
pixelbytes = list(image_to_data(image))
# Write data to hardware.
self.data(pixelbytes)
def set_window(self, x0=0, y0=0, x1=None, y1=None):
"""Set the pixel address window for proceeding drawing commands. x0 and
x1 should define the minimum and maximum x pixel bounds. y0 and y1
should define the minimum and maximum y pixel bound. If no parameters
are specified the default will be to update the entire display from 0,0
to 239,319.
"""
if x1 is None:
x1 = self.width-1
if y1 is None:
y1 = self.height-1
self.command(ILI9341_CASET) # Column addr set
self.data(x0 >> 8)
self.data(x0) # XSTART
self.data(x1 >> 8)
self.data(x1) # XEND
self.command(ILI9341_PASET) # Row addr set
self.data(y0 >> 8)
self.data(y0) # YSTART
self.data(y1 >> 8)
self.data(y1) # YEND
self.command(ILI9341_RAMWR) # write to RAM
def clear(self, color=(0,0,0)):
"""Clear the image buffer to the specified RGB color (default black)."""
width, height = self.buffer.size
self.buffer.putdata([color]*(width*height))
def display(self, image=None):
"""Write the display buffer or provided image to the hardware. If no
image parameter is provided the display buffer will be written to the
hardware. If an image is provided, it should be RGB format and the
same dimensions as the display hardware.
"""
# By default write the internal buffer to the display.
if image is None:
image = self.buffer
# Set address bounds to entire display.
self.set_window()
# Convert image to array of 16bit 565 RGB data bytes.
# Unfortunate that this copy has to occur, but the SPI byte writing
# function needs to take an array of bytes and PIL doesn't natively
# store images in 16-bit 565 RGB format.
pixelbytes = list(image_to_data(image))
# Write data to hardware.
self.data(pixelbytes)
def draw(self):
"""Return a PIL ImageDraw instance for 2D drawing on the image buffer."""
return ImageDraw.Draw(self.buffer)
def clear(self, color=(0,0,0)):
"""Clear the image buffer to the specified RGB color (default black)."""
width, height = self.buffer.size
self.buffer.putdata([color]*(width*height))
def draw(self):
"""Return a PIL ImageDraw instance for 2D drawing on the image buffer."""
return ImageDraw.Draw(self.buffer)

View File

@@ -18,4 +18,4 @@
# 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.
from ILI9341 import *
from .ILI9341 import *

View File

@@ -18,7 +18,7 @@
# 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 Image
from PIL import Image
import Adafruit_ILI9341 as TFT
import Adafruit_GPIO as GPIO
@@ -44,12 +44,12 @@ disp = TFT.ILI9341(DC, rst=RST, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_h
disp.begin()
# Load an image.
print 'Loading image...'
print('Loading image...')
image = Image.open('cat.jpg')
# Resize the image and rotate it so it's 240x320 pixels.
image = image.rotate(90).resize((240, 320))
# Draw the image on the display hardware.
print 'Drawing image'
print('Drawing image')
disp.display(image)

View File

@@ -18,7 +18,7 @@
# 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 Image
from PIL import Image
import time
import Adafruit_ILI9341 as TFT
import Adafruit_GPIO as GPIO
@@ -44,19 +44,19 @@ disp = TFT.ILI9341(DC, rst=RST, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_h
disp.begin()
# Load an image.
print 'Loading image...'
print('Loading image...')
image = Image.open('cat.jpg')
# Resize the image and rotate it so it's 240x320 pixels.
image = image.rotate(90).resize((240, 320))
print 'Press Ctrl-C to exit'
print('Press Ctrl-C to exit')
while(True):
# Draw the image on the display hardware.
print 'Drawing image'
print('Drawing image')
start_time = time.time()
disp.display(image)
end_time = time.time()
print 'Time to draw image: ' + str(end_time - start_time)
print('Time to draw image: ' + str(end_time - start_time))
disp.clear((0, 0, 0))
disp.display()

View File

@@ -18,9 +18,9 @@
# 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 Image
import ImageDraw
import ImageFont
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
import Adafruit_ILI9341 as TFT
import Adafruit_GPIO as GPIO
@@ -77,21 +77,21 @@ font = ImageFont.load_default()
#font = ImageFont.truetype('Minecraftia.ttf', 16)
# Define a function to create rotated text. Unfortunately PIL doesn't have good
# native support for rotated fonts, but this function can be used to make a
# native support for rotated fonts, but this function can be used to make a
# text image and rotate it so it's easy to paste in the buffer.
def draw_rotated_text(image, text, position, angle, font, fill=(255,255,255)):
# Get rendered font width and height.
draw = ImageDraw.Draw(image)
width, height = draw.textsize(text, font=font)
# Create a new image with transparent background to store the text.
textimage = Image.new('RGBA', (width, height), (0,0,0,0))
# Render the text.
textdraw = ImageDraw.Draw(textimage)
textdraw.text((0,0), text, font=font, fill=fill)
# Rotate the text image.
rotated = textimage.rotate(angle, expand=1)
# Paste the text into the image, using it as a mask for transparency.
image.paste(rotated, position, rotated)
# Get rendered font width and height.
draw = ImageDraw.Draw(image)
width, height = draw.textsize(text, font=font)
# Create a new image with transparent background to store the text.
textimage = Image.new('RGBA', (width, height), (0,0,0,0))
# Render the text.
textdraw = ImageDraw.Draw(textimage)
textdraw.text((0,0), text, font=font, fill=fill)
# Rotate the text image.
rotated = textimage.rotate(angle, expand=1)
# Paste the text into the image, using it as a mask for transparency.
image.paste(rotated, position, rotated)
# Write two lines of white text on the buffer, rotated 90 degrees counter clockwise.
draw_rotated_text(disp.buffer, 'Hello World!', (150, 120), 90, font, fill=(255,255,255))

View File

@@ -9,12 +9,23 @@ from ez_setup import use_setuptools
use_setuptools()
from setuptools import setup, find_packages
classifiers = ['Development Status :: 4 - Beta',
'Operating System :: POSIX :: Linux',
'License :: OSI Approved :: MIT License',
'Intended Audience :: Developers',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Topic :: Software Development',
'Topic :: System :: Hardware']
setup(name = 'Adafruit_ILI9341',
version = '1.5.0',
author = 'Tony DiCola',
author_email = 'tdicola@adafruit.com',
description = 'Library to control an ILI9341 TFT LCD display.',
license = 'MIT',
classifiers = classifiers,
url = 'https://github.com/adafruit/Adafruit_Python_ILI9341/',
dependency_links = ['https://github.com/adafruit/Adafruit_Python_GPIO/tarball/master#egg=Adafruit-GPIO-0.6.5'],
install_requires = ['Adafruit-GPIO>=0.6.5'],