├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── Adafruit_ILI9341 ├── ILI9341.py └── __init__.py ├── LICENSE ├── README.md ├── examples ├── cat.jpg ├── image.py ├── image_timed.py └── shapes.py ├── ez_setup.py └── setup.py /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for opening an issue on an Adafruit Python library repository. To 2 | improve the speed of resolution please review the following guidelines and 3 | common troubleshooting steps below before creating the issue: 4 | 5 | - **Do not use GitHub issues for troubleshooting projects and issues.** Instead use 6 | the forums at http://forums.adafruit.com to ask questions and troubleshoot why 7 | something isn't working as expected. In many cases the problem is a common issue 8 | that you will more quickly receive help from the forum community. GitHub issues 9 | are meant for known defects in the code. If you don't know if there is a defect 10 | in the code then start with troubleshooting on the forum first. 11 | 12 | - **If following a tutorial or guide be sure you didn't miss a step.** Carefully 13 | check all of the steps and commands to run have been followed. Consult the 14 | forum if you're unsure or have questions about steps in a guide/tutorial. 15 | 16 | - **For Python/Raspberry Pi projects check these very common issues to ensure they don't apply**: 17 | 18 | - If you are receiving an **ImportError: No module named...** error then a 19 | library the code depends on is not installed. Check the tutorial/guide or 20 | README to ensure you have installed the necessary libraries. Usually the 21 | missing library can be installed with the `pip` tool, but check the tutorial/guide 22 | for the exact command. 23 | 24 | - **Be sure you are supplying adequate power to the board.** Check the specs of 25 | your board and power in an external power supply. In many cases just 26 | plugging a board into your computer is not enough to power it and other 27 | peripherals. 28 | 29 | - **Double check all soldering joints and connections.** Flakey connections 30 | cause many mysterious problems. See the [guide to excellent soldering](https://learn.adafruit.com/adafruit-guide-excellent-soldering/tools) for examples of good solder joints. 31 | 32 | If you're sure this issue is a defect in the code and checked the steps above 33 | please fill in the following fields to provide enough troubleshooting information. 34 | You may delete the guideline and text above to just leave the following details: 35 | 36 | - Platform/operating system (i.e. Raspberry Pi with Raspbian operating system, 37 | Windows 32-bit, Windows 64-bit, Mac OSX 64-bit, etc.): **INSERT PLATFORM/OPERATING 38 | SYSTEM HERE** 39 | 40 | - Python version (run `python -version` or `python3 -version`): **INSERT PYTHON 41 | VERSION HERE** 42 | 43 | - Error message you are receiving, including any Python exception traces: **INSERT 44 | ERROR MESAGE/EXCEPTION TRACES HERE*** 45 | 46 | - List the steps to reproduce the problem below (if possible attach code or commands 47 | to run): **LIST REPRO STEPS BELOW** 48 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Thank you for creating a pull request to contribute to Adafruit's GitHub code! 2 | Before you open the request please review the following guidelines and tips to 3 | help it be more easily integrated: 4 | 5 | - **Describe the scope of your change--i.e. what the change does and what parts 6 | of the code were modified.** This will help us understand any risks of integrating 7 | the code. 8 | 9 | - **Describe any known limitations with your change.** For example if the change 10 | doesn't apply to a supported platform of the library please mention it. 11 | 12 | - **Please run any tests or examples that can exercise your modified code.** We 13 | strive to not break users of the code and running tests/examples helps with this 14 | process. 15 | 16 | Thank you again for contributing! We will try to test and integrate the change 17 | as soon as we can, but be aware we have many GitHub repositories to manage and 18 | can't immediately respond to every request. There is no need to bump or check in 19 | on a pull request (it will clutter the discussion of the request). 20 | 21 | Also don't be worried if the request is closed or not integrated--sometimes the 22 | priorities of Adafruit's GitHub code (education, ease of use) might not match the 23 | priorities of the pull request. Don't fret, the open source community thrives on 24 | forks and GitHub makes it easy to keep your changes in a forked repo. 25 | 26 | After reviewing the guidelines above you can delete this text from the pull request. 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | *.egg-info 4 | *.pyc 5 | setuptools-* 6 | -------------------------------------------------------------------------------- /Adafruit_ILI9341/ILI9341.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | import numbers 22 | import time 23 | import numpy as np 24 | 25 | from PIL import Image 26 | from PIL import ImageDraw 27 | 28 | import Adafruit_GPIO as GPIO 29 | import Adafruit_GPIO.SPI as SPI 30 | 31 | 32 | # Constants for interacting with display registers. 33 | ILI9341_TFTWIDTH = 240 34 | ILI9341_TFTHEIGHT = 320 35 | 36 | ILI9341_NOP = 0x00 37 | ILI9341_SWRESET = 0x01 38 | ILI9341_RDDID = 0x04 39 | ILI9341_RDDST = 0x09 40 | 41 | ILI9341_SLPIN = 0x10 42 | ILI9341_SLPOUT = 0x11 43 | ILI9341_PTLON = 0x12 44 | ILI9341_NORON = 0x13 45 | 46 | ILI9341_RDMODE = 0x0A 47 | ILI9341_RDMADCTL = 0x0B 48 | ILI9341_RDPIXFMT = 0x0C 49 | ILI9341_RDIMGFMT = 0x0A 50 | ILI9341_RDSELFDIAG = 0x0F 51 | 52 | ILI9341_INVOFF = 0x20 53 | ILI9341_INVON = 0x21 54 | ILI9341_GAMMASET = 0x26 55 | ILI9341_DISPOFF = 0x28 56 | ILI9341_DISPON = 0x29 57 | 58 | ILI9341_CASET = 0x2A 59 | ILI9341_PASET = 0x2B 60 | ILI9341_RAMWR = 0x2C 61 | ILI9341_RAMRD = 0x2E 62 | 63 | ILI9341_PTLAR = 0x30 64 | ILI9341_MADCTL = 0x36 65 | ILI9341_PIXFMT = 0x3A 66 | 67 | ILI9341_FRMCTR1 = 0xB1 68 | ILI9341_FRMCTR2 = 0xB2 69 | ILI9341_FRMCTR3 = 0xB3 70 | ILI9341_INVCTR = 0xB4 71 | ILI9341_DFUNCTR = 0xB6 72 | 73 | ILI9341_PWCTR1 = 0xC0 74 | ILI9341_PWCTR2 = 0xC1 75 | ILI9341_PWCTR3 = 0xC2 76 | ILI9341_PWCTR4 = 0xC3 77 | ILI9341_PWCTR5 = 0xC4 78 | ILI9341_VMCTR1 = 0xC5 79 | ILI9341_VMCTR2 = 0xC7 80 | 81 | ILI9341_RDID1 = 0xDA 82 | ILI9341_RDID2 = 0xDB 83 | ILI9341_RDID3 = 0xDC 84 | ILI9341_RDID4 = 0xDD 85 | 86 | ILI9341_GMCTRP1 = 0xE0 87 | ILI9341_GMCTRN1 = 0xE1 88 | 89 | ILI9341_PWCTR6 = 0xFC 90 | 91 | ILI9341_BLACK = 0x0000 92 | ILI9341_BLUE = 0x001F 93 | ILI9341_RED = 0xF800 94 | ILI9341_GREEN = 0x07E0 95 | ILI9341_CYAN = 0x07FF 96 | ILI9341_MAGENTA = 0xF81F 97 | ILI9341_YELLOW = 0xFFE0 98 | ILI9341_WHITE = 0xFFFF 99 | 100 | 101 | def color565(r, g, b): 102 | """Convert red, green, blue components to a 16-bit 565 RGB value. Components 103 | should be values 0 to 255. 104 | """ 105 | return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3) 106 | 107 | def image_to_data(image): 108 | """Generator function to convert a PIL image to 16-bit 565 RGB bytes.""" 109 | #NumPy is much faster at doing this. NumPy code provided by: 110 | #Keith (https://www.blogger.com/profile/02555547344016007163) 111 | pb = np.array(image.convert('RGB')).astype('uint16') 112 | color = ((pb[:,:,0] & 0xF8) << 8) | ((pb[:,:,1] & 0xFC) << 3) | (pb[:,:,2] >> 3) 113 | return np.dstack(((color >> 8) & 0xFF, color & 0xFF)).flatten().tolist() 114 | 115 | class ILI9341(object): 116 | """Representation of an ILI9341 TFT LCD.""" 117 | 118 | def __init__(self, dc, spi, rst=None, gpio=None, width=ILI9341_TFTWIDTH, 119 | height=ILI9341_TFTHEIGHT): 120 | """Create an instance of the display using SPI communication. Must 121 | provide the GPIO pin number for the D/C pin and the SPI driver. Can 122 | optionally provide the GPIO pin number for the reset pin as the rst 123 | parameter. 124 | """ 125 | self._dc = dc 126 | self._rst = rst 127 | self._spi = spi 128 | self._gpio = gpio 129 | self.width = width 130 | self.height = height 131 | if self._gpio is None: 132 | self._gpio = GPIO.get_platform_gpio() 133 | # Set DC as output. 134 | self._gpio.setup(dc, GPIO.OUT) 135 | # Setup reset as output (if provided). 136 | if rst is not None: 137 | self._gpio.setup(rst, GPIO.OUT) 138 | # Set SPI to mode 0, MSB first. 139 | spi.set_mode(0) 140 | spi.set_bit_order(SPI.MSBFIRST) 141 | spi.set_clock_hz(64000000) 142 | # Create an image buffer. 143 | self.buffer = Image.new('RGB', (width, height)) 144 | 145 | def send(self, data, is_data=True, chunk_size=4096): 146 | """Write a byte or array of bytes to the display. Is_data parameter 147 | controls if byte should be interpreted as display data (True) or command 148 | data (False). Chunk_size is an optional size of bytes to write in a 149 | single SPI transaction, with a default of 4096. 150 | """ 151 | # Set DC low for command, high for data. 152 | self._gpio.output(self._dc, is_data) 153 | # Convert scalar argument to list so either can be passed as parameter. 154 | if isinstance(data, numbers.Number): 155 | data = [data & 0xFF] 156 | # Write data a chunk at a time. 157 | for start in range(0, len(data), chunk_size): 158 | end = min(start+chunk_size, len(data)) 159 | self._spi.write(data[start:end]) 160 | 161 | def command(self, data): 162 | """Write a byte or array of bytes to the display as command data.""" 163 | self.send(data, False) 164 | 165 | def data(self, data): 166 | """Write a byte or array of bytes to the display as display data.""" 167 | self.send(data, True) 168 | 169 | def reset(self): 170 | """Reset the display, if reset pin is connected.""" 171 | if self._rst is not None: 172 | self._gpio.set_high(self._rst) 173 | time.sleep(0.005) 174 | self._gpio.set_low(self._rst) 175 | time.sleep(0.02) 176 | self._gpio.set_high(self._rst) 177 | time.sleep(0.150) 178 | 179 | def _init(self): 180 | # Initialize the display. Broken out as a separate function so it can 181 | # be overridden by other displays in the future. 182 | self.command(0xEF) 183 | self.data(0x03) 184 | self.data(0x80) 185 | self.data(0x02) 186 | self.command(0xCF) 187 | self.data(0x00) 188 | self.data(0XC1) 189 | self.data(0X30) 190 | self.command(0xED) 191 | self.data(0x64) 192 | self.data(0x03) 193 | self.data(0X12) 194 | self.data(0X81) 195 | self.command(0xE8) 196 | self.data(0x85) 197 | self.data(0x00) 198 | self.data(0x78) 199 | self.command(0xCB) 200 | self.data(0x39) 201 | self.data(0x2C) 202 | self.data(0x00) 203 | self.data(0x34) 204 | self.data(0x02) 205 | self.command(0xF7) 206 | self.data(0x20) 207 | self.command(0xEA) 208 | self.data(0x00) 209 | self.data(0x00) 210 | self.command(ILI9341_PWCTR1) # Power control 211 | self.data(0x23) # VRH[5:0] 212 | self.command(ILI9341_PWCTR2) # Power control 213 | self.data(0x10) # SAP[2:0];BT[3:0] 214 | self.command(ILI9341_VMCTR1) # VCM control 215 | self.data(0x3e) 216 | self.data(0x28) 217 | self.command(ILI9341_VMCTR2) # VCM control2 218 | self.data(0x86) # -- 219 | self.command(ILI9341_MADCTL) # Memory Access Control 220 | self.data(0x48) 221 | self.command(ILI9341_PIXFMT) 222 | self.data(0x55) 223 | self.command(ILI9341_FRMCTR1) 224 | self.data(0x00) 225 | self.data(0x18) 226 | self.command(ILI9341_DFUNCTR) # Display Function Control 227 | self.data(0x08) 228 | self.data(0x82) 229 | self.data(0x27) 230 | self.command(0xF2) # 3Gamma Function Disable 231 | self.data(0x00) 232 | self.command(ILI9341_GAMMASET) # Gamma curve selected 233 | self.data(0x01) 234 | self.command(ILI9341_GMCTRP1) # Set Gamma 235 | self.data(0x0F) 236 | self.data(0x31) 237 | self.data(0x2B) 238 | self.data(0x0C) 239 | self.data(0x0E) 240 | self.data(0x08) 241 | self.data(0x4E) 242 | self.data(0xF1) 243 | self.data(0x37) 244 | self.data(0x07) 245 | self.data(0x10) 246 | self.data(0x03) 247 | self.data(0x0E) 248 | self.data(0x09) 249 | self.data(0x00) 250 | self.command(ILI9341_GMCTRN1) # Set Gamma 251 | self.data(0x00) 252 | self.data(0x0E) 253 | self.data(0x14) 254 | self.data(0x03) 255 | self.data(0x11) 256 | self.data(0x07) 257 | self.data(0x31) 258 | self.data(0xC1) 259 | self.data(0x48) 260 | self.data(0x08) 261 | self.data(0x0F) 262 | self.data(0x0C) 263 | self.data(0x31) 264 | self.data(0x36) 265 | self.data(0x0F) 266 | self.command(ILI9341_SLPOUT) # Exit Sleep 267 | time.sleep(0.120) 268 | self.command(ILI9341_DISPON) # Display on 269 | 270 | def begin(self): 271 | """Initialize the display. Should be called once before other calls that 272 | interact with the display are called. 273 | """ 274 | self.reset() 275 | self._init() 276 | 277 | def set_window(self, x0=0, y0=0, x1=None, y1=None): 278 | """Set the pixel address window for proceeding drawing commands. x0 and 279 | x1 should define the minimum and maximum x pixel bounds. y0 and y1 280 | should define the minimum and maximum y pixel bound. If no parameters 281 | are specified the default will be to update the entire display from 0,0 282 | to 239,319. 283 | """ 284 | if x1 is None: 285 | x1 = self.width-1 286 | if y1 is None: 287 | y1 = self.height-1 288 | self.command(ILI9341_CASET) # Column addr set 289 | self.data(x0 >> 8) 290 | self.data(x0) # XSTART 291 | self.data(x1 >> 8) 292 | self.data(x1) # XEND 293 | self.command(ILI9341_PASET) # Row addr set 294 | self.data(y0 >> 8) 295 | self.data(y0) # YSTART 296 | self.data(y1 >> 8) 297 | self.data(y1) # YEND 298 | self.command(ILI9341_RAMWR) # write to RAM 299 | 300 | def display(self, image=None): 301 | """Write the display buffer or provided image to the hardware. If no 302 | image parameter is provided the display buffer will be written to the 303 | hardware. If an image is provided, it should be RGB format and the 304 | same dimensions as the display hardware. 305 | """ 306 | # By default write the internal buffer to the display. 307 | if image is None: 308 | image = self.buffer 309 | # Set address bounds to entire display. 310 | self.set_window() 311 | # Convert image to array of 16bit 565 RGB data bytes. 312 | # Unfortunate that this copy has to occur, but the SPI byte writing 313 | # function needs to take an array of bytes and PIL doesn't natively 314 | # store images in 16-bit 565 RGB format. 315 | pixelbytes = list(image_to_data(image)) 316 | # Write data to hardware. 317 | self.data(pixelbytes) 318 | 319 | def clear(self, color=(0,0,0)): 320 | """Clear the image buffer to the specified RGB color (default black).""" 321 | width, height = self.buffer.size 322 | self.buffer.putdata([color]*(width*height)) 323 | 324 | def draw(self): 325 | """Return a PIL ImageDraw instance for 2D drawing on the image buffer.""" 326 | return ImageDraw.Draw(self.buffer) 327 | -------------------------------------------------------------------------------- /Adafruit_ILI9341/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | from .ILI9341 import * 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Adafruit Industries 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This library has been archived and replaced by a new library that handles multiple displays: 2 | 3 | https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display 4 | 5 | This library is no longer supported. Please use the new library 6 | -------------------------------------------------------------------------------- /examples/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adafruit/Adafruit_Python_ILI9341/f1fb7ec1614e3fde8fa25ad1324cdaa8fdb416a8/examples/cat.jpg -------------------------------------------------------------------------------- /examples/image.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | from PIL import Image 22 | 23 | import Adafruit_ILI9341 as TFT 24 | import Adafruit_GPIO as GPIO 25 | import Adafruit_GPIO.SPI as SPI 26 | 27 | 28 | # Raspberry Pi configuration. 29 | DC = 18 30 | RST = 23 31 | SPI_PORT = 0 32 | SPI_DEVICE = 0 33 | 34 | # BeagleBone Black configuration. 35 | # DC = 'P9_15' 36 | # RST = 'P9_12' 37 | # SPI_PORT = 1 38 | # SPI_DEVICE = 0 39 | 40 | # Create TFT LCD display class. 41 | disp = TFT.ILI9341(DC, rst=RST, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=64000000)) 42 | 43 | # Initialize display. 44 | disp.begin() 45 | 46 | # Load an image. 47 | print('Loading image...') 48 | image = Image.open('cat.jpg') 49 | 50 | # Resize the image and rotate it so it's 240x320 pixels. 51 | image = image.rotate(90).resize((240, 320)) 52 | 53 | # Draw the image on the display hardware. 54 | print('Drawing image') 55 | disp.display(image) 56 | -------------------------------------------------------------------------------- /examples/image_timed.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | from PIL import Image 22 | import time 23 | import Adafruit_ILI9341 as TFT 24 | import Adafruit_GPIO as GPIO 25 | import Adafruit_GPIO.SPI as SPI 26 | 27 | 28 | # Raspberry Pi configuration. 29 | DC = 18 30 | RST = 23 31 | SPI_PORT = 0 32 | SPI_DEVICE = 0 33 | 34 | # BeagleBone Black configuration. 35 | #DC = 'P9_15' 36 | #RST = 'P9_12' 37 | #SPI_PORT = 1 38 | #SPI_DEVICE = 0 39 | 40 | # Create TFT LCD display class. 41 | disp = TFT.ILI9341(DC, rst=RST, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=64000000)) 42 | 43 | # Initialize display. 44 | disp.begin() 45 | 46 | # Load an image. 47 | print('Loading image...') 48 | image = Image.open('cat.jpg') 49 | 50 | # Resize the image and rotate it so it's 240x320 pixels. 51 | image = image.rotate(90).resize((240, 320)) 52 | 53 | print('Press Ctrl-C to exit') 54 | while(True): 55 | # Draw the image on the display hardware. 56 | print('Drawing image') 57 | start_time = time.time() 58 | disp.display(image) 59 | end_time = time.time() 60 | print('Time to draw image: ' + str(end_time - start_time)) 61 | disp.clear((0, 0, 0)) 62 | disp.display() 63 | -------------------------------------------------------------------------------- /examples/shapes.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2014 Adafruit Industries 2 | # Author: Tony DiCola 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | from PIL import Image 22 | from PIL import ImageDraw 23 | from PIL import ImageFont 24 | 25 | import Adafruit_ILI9341 as TFT 26 | import Adafruit_GPIO as GPIO 27 | import Adafruit_GPIO.SPI as SPI 28 | 29 | 30 | # Raspberry Pi configuration. 31 | DC = 18 32 | RST = 23 33 | SPI_PORT = 0 34 | SPI_DEVICE = 0 35 | 36 | # BeagleBone Black configuration. 37 | # DC = 'P9_15' 38 | # RST = 'P9_12' 39 | # SPI_PORT = 1 40 | # SPI_DEVICE = 0 41 | 42 | # Create TFT LCD display class. 43 | disp = TFT.ILI9341(DC, rst=RST, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=64000000)) 44 | 45 | # Initialize display. 46 | disp.begin() 47 | 48 | # Clear the display to a red background. 49 | # Can pass any tuple of red, green, blue values (from 0 to 255 each). 50 | disp.clear((255, 0, 0)) 51 | 52 | # Alternatively can clear to a black screen by calling: 53 | # disp.clear() 54 | 55 | # Get a PIL Draw object to start drawing on the display buffer. 56 | draw = disp.draw() 57 | 58 | # Draw some shapes. 59 | # Draw a blue ellipse with a green outline. 60 | draw.ellipse((10, 10, 110, 80), outline=(0,255,0), fill=(0,0,255)) 61 | 62 | # Draw a purple rectangle with yellow outline. 63 | draw.rectangle((10, 90, 110, 160), outline=(255,255,0), fill=(255,0,255)) 64 | 65 | # Draw a white X. 66 | draw.line((10, 170, 110, 230), fill=(255,255,255)) 67 | draw.line((10, 230, 110, 170), fill=(255,255,255)) 68 | 69 | # Draw a cyan triangle with a black outline. 70 | draw.polygon([(10, 275), (110, 240), (110, 310)], outline=(0,0,0), fill=(0,255,255)) 71 | 72 | # Load default font. 73 | font = ImageFont.load_default() 74 | 75 | # Alternatively load a TTF font. 76 | # Some other nice fonts to try: http://www.dafont.com/bitmap.php 77 | #font = ImageFont.truetype('Minecraftia.ttf', 16) 78 | 79 | # Define a function to create rotated text. Unfortunately PIL doesn't have good 80 | # native support for rotated fonts, but this function can be used to make a 81 | # text image and rotate it so it's easy to paste in the buffer. 82 | def draw_rotated_text(image, text, position, angle, font, fill=(255,255,255)): 83 | # Get rendered font width and height. 84 | draw = ImageDraw.Draw(image) 85 | width, height = draw.textsize(text, font=font) 86 | # Create a new image with transparent background to store the text. 87 | textimage = Image.new('RGBA', (width, height), (0,0,0,0)) 88 | # Render the text. 89 | textdraw = ImageDraw.Draw(textimage) 90 | textdraw.text((0,0), text, font=font, fill=fill) 91 | # Rotate the text image. 92 | rotated = textimage.rotate(angle, expand=1) 93 | # Paste the text into the image, using it as a mask for transparency. 94 | image.paste(rotated, position, rotated) 95 | 96 | # Write two lines of white text on the buffer, rotated 90 degrees counter clockwise. 97 | draw_rotated_text(disp.buffer, 'Hello World!', (150, 120), 90, font, fill=(255,255,255)) 98 | draw_rotated_text(disp.buffer, 'This is a line of text.', (170, 90), 90, font, fill=(255,255,255)) 99 | 100 | # Write buffer to display hardware, must be called to make things visible on the 101 | # display! 102 | disp.display() 103 | -------------------------------------------------------------------------------- /ez_setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Bootstrap setuptools installation 3 | 4 | To use setuptools in your package's setup.py, include this 5 | file in the same directory and add this to the top of your setup.py:: 6 | 7 | from ez_setup import use_setuptools 8 | use_setuptools() 9 | 10 | To require a specific version of setuptools, set a download 11 | mirror, or use an alternate download directory, simply supply 12 | the appropriate options to ``use_setuptools()``. 13 | 14 | This file can also be run as a script to install or upgrade setuptools. 15 | """ 16 | import os 17 | import shutil 18 | import sys 19 | import tempfile 20 | import zipfile 21 | import optparse 22 | import subprocess 23 | import platform 24 | import textwrap 25 | import contextlib 26 | 27 | from distutils import log 28 | 29 | try: 30 | from site import USER_SITE 31 | except ImportError: 32 | USER_SITE = None 33 | 34 | DEFAULT_VERSION = "3.5.1" 35 | DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/" 36 | 37 | def _python_cmd(*args): 38 | """ 39 | Return True if the command succeeded. 40 | """ 41 | args = (sys.executable,) + args 42 | return subprocess.call(args) == 0 43 | 44 | 45 | def _install(archive_filename, install_args=()): 46 | with archive_context(archive_filename): 47 | # installing 48 | log.warn('Installing Setuptools') 49 | if not _python_cmd('setup.py', 'install', *install_args): 50 | log.warn('Something went wrong during the installation.') 51 | log.warn('See the error message above.') 52 | # exitcode will be 2 53 | return 2 54 | 55 | 56 | def _build_egg(egg, archive_filename, to_dir): 57 | with archive_context(archive_filename): 58 | # building an egg 59 | log.warn('Building a Setuptools egg in %s', to_dir) 60 | _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) 61 | # returning the result 62 | log.warn(egg) 63 | if not os.path.exists(egg): 64 | raise IOError('Could not build the egg.') 65 | 66 | 67 | def get_zip_class(): 68 | """ 69 | Supplement ZipFile class to support context manager for Python 2.6 70 | """ 71 | class ContextualZipFile(zipfile.ZipFile): 72 | def __enter__(self): 73 | return self 74 | def __exit__(self, type, value, traceback): 75 | self.close 76 | return zipfile.ZipFile if hasattr(zipfile.ZipFile, '__exit__') else \ 77 | ContextualZipFile 78 | 79 | 80 | @contextlib.contextmanager 81 | def archive_context(filename): 82 | # extracting the archive 83 | tmpdir = tempfile.mkdtemp() 84 | log.warn('Extracting in %s', tmpdir) 85 | old_wd = os.getcwd() 86 | try: 87 | os.chdir(tmpdir) 88 | with get_zip_class()(filename) as archive: 89 | archive.extractall() 90 | 91 | # going in the directory 92 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) 93 | os.chdir(subdir) 94 | log.warn('Now working in %s', subdir) 95 | yield 96 | 97 | finally: 98 | os.chdir(old_wd) 99 | shutil.rmtree(tmpdir) 100 | 101 | 102 | def _do_download(version, download_base, to_dir, download_delay): 103 | egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg' 104 | % (version, sys.version_info[0], sys.version_info[1])) 105 | if not os.path.exists(egg): 106 | archive = download_setuptools(version, download_base, 107 | to_dir, download_delay) 108 | _build_egg(egg, archive, to_dir) 109 | sys.path.insert(0, egg) 110 | 111 | # Remove previously-imported pkg_resources if present (see 112 | # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details). 113 | if 'pkg_resources' in sys.modules: 114 | del sys.modules['pkg_resources'] 115 | 116 | import setuptools 117 | setuptools.bootstrap_install_from = egg 118 | 119 | 120 | def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 121 | to_dir=os.curdir, download_delay=15): 122 | to_dir = os.path.abspath(to_dir) 123 | rep_modules = 'pkg_resources', 'setuptools' 124 | imported = set(sys.modules).intersection(rep_modules) 125 | try: 126 | import pkg_resources 127 | except ImportError: 128 | return _do_download(version, download_base, to_dir, download_delay) 129 | try: 130 | pkg_resources.require("setuptools>=" + version) 131 | return 132 | except pkg_resources.DistributionNotFound: 133 | return _do_download(version, download_base, to_dir, download_delay) 134 | except pkg_resources.VersionConflict as VC_err: 135 | if imported: 136 | msg = textwrap.dedent(""" 137 | The required version of setuptools (>={version}) is not available, 138 | and can't be installed while this script is running. Please 139 | install a more recent version first, using 140 | 'easy_install -U setuptools'. 141 | 142 | (Currently using {VC_err.args[0]!r}) 143 | """).format(VC_err=VC_err, version=version) 144 | sys.stderr.write(msg) 145 | sys.exit(2) 146 | 147 | # otherwise, reload ok 148 | del pkg_resources, sys.modules['pkg_resources'] 149 | return _do_download(version, download_base, to_dir, download_delay) 150 | 151 | def _clean_check(cmd, target): 152 | """ 153 | Run the command to download target. If the command fails, clean up before 154 | re-raising the error. 155 | """ 156 | try: 157 | subprocess.check_call(cmd) 158 | except subprocess.CalledProcessError: 159 | if os.access(target, os.F_OK): 160 | os.unlink(target) 161 | raise 162 | 163 | def download_file_powershell(url, target): 164 | """ 165 | Download the file at url to target using Powershell (which will validate 166 | trust). Raise an exception if the command cannot complete. 167 | """ 168 | target = os.path.abspath(target) 169 | cmd = [ 170 | 'powershell', 171 | '-Command', 172 | "(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)" % vars(), 173 | ] 174 | _clean_check(cmd, target) 175 | 176 | def has_powershell(): 177 | if platform.system() != 'Windows': 178 | return False 179 | cmd = ['powershell', '-Command', 'echo test'] 180 | devnull = open(os.path.devnull, 'wb') 181 | try: 182 | try: 183 | subprocess.check_call(cmd, stdout=devnull, stderr=devnull) 184 | except Exception: 185 | return False 186 | finally: 187 | devnull.close() 188 | return True 189 | 190 | download_file_powershell.viable = has_powershell 191 | 192 | def download_file_curl(url, target): 193 | cmd = ['curl', url, '--silent', '--output', target] 194 | _clean_check(cmd, target) 195 | 196 | def has_curl(): 197 | cmd = ['curl', '--version'] 198 | devnull = open(os.path.devnull, 'wb') 199 | try: 200 | try: 201 | subprocess.check_call(cmd, stdout=devnull, stderr=devnull) 202 | except Exception: 203 | return False 204 | finally: 205 | devnull.close() 206 | return True 207 | 208 | download_file_curl.viable = has_curl 209 | 210 | def download_file_wget(url, target): 211 | cmd = ['wget', url, '--quiet', '--output-document', target] 212 | _clean_check(cmd, target) 213 | 214 | def has_wget(): 215 | cmd = ['wget', '--version'] 216 | devnull = open(os.path.devnull, 'wb') 217 | try: 218 | try: 219 | subprocess.check_call(cmd, stdout=devnull, stderr=devnull) 220 | except Exception: 221 | return False 222 | finally: 223 | devnull.close() 224 | return True 225 | 226 | download_file_wget.viable = has_wget 227 | 228 | def download_file_insecure(url, target): 229 | """ 230 | Use Python to download the file, even though it cannot authenticate the 231 | connection. 232 | """ 233 | try: 234 | from urllib.request import urlopen 235 | except ImportError: 236 | from urllib2 import urlopen 237 | src = dst = None 238 | try: 239 | src = urlopen(url) 240 | # Read/write all in one block, so we don't create a corrupt file 241 | # if the download is interrupted. 242 | data = src.read() 243 | dst = open(target, "wb") 244 | dst.write(data) 245 | finally: 246 | if src: 247 | src.close() 248 | if dst: 249 | dst.close() 250 | 251 | download_file_insecure.viable = lambda: True 252 | 253 | def get_best_downloader(): 254 | downloaders = [ 255 | download_file_powershell, 256 | download_file_curl, 257 | download_file_wget, 258 | download_file_insecure, 259 | ] 260 | 261 | for dl in downloaders: 262 | if dl.viable(): 263 | return dl 264 | 265 | def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 266 | to_dir=os.curdir, delay=15, downloader_factory=get_best_downloader): 267 | """ 268 | Download setuptools from a specified location and return its filename 269 | 270 | `version` should be a valid setuptools version number that is available 271 | as an egg for download under the `download_base` URL (which should end 272 | with a '/'). `to_dir` is the directory where the egg will be downloaded. 273 | `delay` is the number of seconds to pause before an actual download 274 | attempt. 275 | 276 | ``downloader_factory`` should be a function taking no arguments and 277 | returning a function for downloading a URL to a target. 278 | """ 279 | # making sure we use the absolute path 280 | to_dir = os.path.abspath(to_dir) 281 | zip_name = "setuptools-%s.zip" % version 282 | url = download_base + zip_name 283 | saveto = os.path.join(to_dir, zip_name) 284 | if not os.path.exists(saveto): # Avoid repeated downloads 285 | log.warn("Downloading %s", url) 286 | downloader = downloader_factory() 287 | downloader(url, saveto) 288 | return os.path.realpath(saveto) 289 | 290 | def _build_install_args(options): 291 | """ 292 | Build the arguments to 'python setup.py install' on the setuptools package 293 | """ 294 | return ['--user'] if options.user_install else [] 295 | 296 | def _parse_args(): 297 | """ 298 | Parse the command line for options 299 | """ 300 | parser = optparse.OptionParser() 301 | parser.add_option( 302 | '--user', dest='user_install', action='store_true', default=False, 303 | help='install in user site package (requires Python 2.6 or later)') 304 | parser.add_option( 305 | '--download-base', dest='download_base', metavar="URL", 306 | default=DEFAULT_URL, 307 | help='alternative URL from where to download the setuptools package') 308 | parser.add_option( 309 | '--insecure', dest='downloader_factory', action='store_const', 310 | const=lambda: download_file_insecure, default=get_best_downloader, 311 | help='Use internal, non-validating downloader' 312 | ) 313 | parser.add_option( 314 | '--version', help="Specify which version to download", 315 | default=DEFAULT_VERSION, 316 | ) 317 | options, args = parser.parse_args() 318 | # positional arguments are ignored 319 | return options 320 | 321 | def main(): 322 | """Install or upgrade setuptools and EasyInstall""" 323 | options = _parse_args() 324 | archive = download_setuptools( 325 | version=options.version, 326 | download_base=options.download_base, 327 | downloader_factory=options.downloader_factory, 328 | ) 329 | return _install(archive, _build_install_args(options)) 330 | 331 | if __name__ == '__main__': 332 | sys.exit(main()) 333 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Workaround for issue in Python 2.7.3 2 | # See http://bugs.python.org/issue15881#msg170215 3 | try: 4 | import multiprocessing 5 | except ImportError: 6 | pass 7 | 8 | try: 9 | # Try using ez_setup to install setuptools if not already installed. 10 | from ez_setup import use_setuptools 11 | use_setuptools() 12 | except ImportError: 13 | # Ignore import error and assume Python 3 which already has setuptools. 14 | pass 15 | 16 | from setuptools import setup, find_packages 17 | 18 | 19 | classifiers = ['Development Status :: 4 - Beta', 20 | 'Operating System :: POSIX :: Linux', 21 | 'License :: OSI Approved :: MIT License', 22 | 'Intended Audience :: Developers', 23 | 'Programming Language :: Python :: 2.7', 24 | 'Programming Language :: Python :: 3', 25 | 'Topic :: Software Development', 26 | 'Topic :: System :: Hardware'] 27 | 28 | setup(name = 'Adafruit_ILI9341', 29 | version = '1.5.1', 30 | author = 'Tony DiCola', 31 | author_email = 'tdicola@adafruit.com', 32 | description = 'Library to control an ILI9341 TFT LCD display.', 33 | license = 'MIT', 34 | classifiers = classifiers, 35 | url = 'https://github.com/adafruit/Adafruit_Python_ILI9341/', 36 | dependency_links = ['https://github.com/adafruit/Adafruit_Python_GPIO/tarball/master#egg=Adafruit-GPIO-0.6.5'], 37 | install_requires = ['Adafruit-GPIO>=0.6.5'], 38 | packages = find_packages()) 39 | --------------------------------------------------------------------------------