commit 95f2b8c7c97707fd3e5bc8025a5d8efa91d03fc0 Author: Bartosz Kościów Date: Mon Apr 24 21:52:38 2017 +0200 init afer lib extraction from Doton project diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..67d9e58 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +.idea +.vs +*.pyproj +*.sln +*.pyc +*.pyo +*.bak +env/ +venv/ +ENV/ +build/ +dist/ +sdist/ +*.egg-info/ +*.egg +.python-version +__pycache__/ +.tox diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..cd0d34b --- /dev/null +++ b/.pylintrc @@ -0,0 +1,3 @@ +[REPORTS] +reports=yes +msg-template={path}:{line}: [{msg_id}({symbol}), {obj}] {msg} diff --git a/CHANGELOG.txt b/CHANGELOG.txt new file mode 100644 index 0000000..2e36e69 --- /dev/null +++ b/CHANGELOG.txt @@ -0,0 +1,2 @@ +0.1.0 + - extract library from Doton project \ No newline at end of file diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt new file mode 100644 index 0000000..c60be06 --- /dev/null +++ b/CONTRIBUTORS.txt @@ -0,0 +1,3 @@ +List of contributors: + +- Bartosz Kościów - kosci1@gmail.com diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..23f371a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2017 Bartosz Kościów + +The MIT License + +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. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..ad365ca --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,5 @@ +include CONTRIBUTORS.txt +include LICENSE +include readme.md +include CHANGELOG.txt + diff --git a/gfxlcd/__init__.py b/gfxlcd/__init__.py new file mode 100644 index 0000000..7c2d50b --- /dev/null +++ b/gfxlcd/__init__.py @@ -0,0 +1 @@ +__author__ = 'kosci' diff --git a/gfxlcd/abstract/__init__.py b/gfxlcd/abstract/__init__.py new file mode 100644 index 0000000..7c2d50b --- /dev/null +++ b/gfxlcd/abstract/__init__.py @@ -0,0 +1 @@ +__author__ = 'kosci' diff --git a/gfxlcd/abstract/chip.py b/gfxlcd/abstract/chip.py new file mode 100644 index 0000000..f034abe --- /dev/null +++ b/gfxlcd/abstract/chip.py @@ -0,0 +1,95 @@ +import abc + + +class Chip(metaclass=abc.ABCMeta): + def __init__(self, width, height, driver, auto_flush): + self.options = {} + self._width = width + self._height = height + self.driver = driver + self.options['auto_flush'] = auto_flush + + @property + def width(self): + """get width""" + return self._width + + @property + def height(self): + """get height""" + return self._height + + @abc.abstractmethod + def _converted_background_color(self): + """convert RGB background to available color""" + pass + + @abc.abstractmethod + def _converted_color(self): + """convert RGB color to available color""" + pass + + @property + def color(self): + return self.options['color'] + + @color.setter + def color(self, rgb): + self.options['color'] = { + 'R': rgb[0], 'G': rgb[1], 'B': rgb[2] + } + + @property + def background_color(self): + return self.options['background_color'] + + @background_color.setter + def background_color(self, rgb): + self.options['background_color'] = { + 'R': rgb[0], 'G': rgb[1], 'B': rgb[2] + } + + @property + def auto_flush(self): + """get auto_flush""" + return self.options['auto_flush'] + + @auto_flush.setter + def auto_flush(self, value): + """set auto_flush""" + self.options['auto_flush'] = bool(value) + + @abc.abstractmethod + def init(self): + """init a chipset""" + pass + + @abc.abstractmethod + def draw_pixel(self, x, y): + """draw a pixel at x,y""" + pass + + @abc.abstractmethod + def draw_line(self, x1, y1, x2, y2): + """draw a line from point x1,y1 to x2,y2""" + pass + + @abc.abstractmethod + def draw_rect(self, x1, y1, x2, y2): + """draw a rectangle""" + pass + + @abc.abstractmethod + def draw_circle(self, x, y, r): + """draw a circle""" + pass + + @abc.abstractmethod + def draw_arc(self, x, y, radius, start, end): + """draw an arc""" + pass + + @abc.abstractmethod + def fill_rect(self, x1, y1, x2, y2): + """draw a filled rectangle""" + pass diff --git a/gfxlcd/abstract/driver.py b/gfxlcd/abstract/driver.py new file mode 100644 index 0000000..f928f24 --- /dev/null +++ b/gfxlcd/abstract/driver.py @@ -0,0 +1,23 @@ +import abc + + +class Driver(metaclass=abc.ABCMeta): + @abc.abstractmethod + def init(self): + """initialize a device""" + pass + + @abc.abstractmethod + def reset(self): + """resets a device""" + pass + + @abc.abstractmethod + def cmd(self, data, enable): + """sends command to device""" + pass + + @abc.abstractmethod + def data(self, data, enable): + """sends data to device""" + pass \ No newline at end of file diff --git a/gfxlcd/demos/ili.py b/gfxlcd/demos/ili.py new file mode 100644 index 0000000..631b2a2 --- /dev/null +++ b/gfxlcd/demos/ili.py @@ -0,0 +1,62 @@ +import sys +sys.path.append("../../") +from gfxlcd.driver.ili9325.gpio import GPIO +from gfxlcd.driver.ili9325.ili9325 import ILI9325 + + +def hole(x, y): + o.draw_pixel(x+1, y) + o.draw_pixel(x+2, y) + o.draw_pixel(x+3, y) + o.draw_pixel(x+1, y + 4) + o.draw_pixel(x+2, y + 4) + o.draw_pixel(x+3, y + 4) + o.draw_pixel(x, y + 1) + o.draw_pixel(x+4, y + 1) + o.draw_pixel(x, y + 2) + o.draw_pixel(x+4, y + 2) + o.draw_pixel(x, y + 3) + o.draw_pixel(x+4, y + 3) + + +def draw_net(o): + s = 0 + while s < o.width-1: + o.draw_line(s, 0, s, o.height-1) + s += 10 + s = 0 + while s < o.height-1: + o.draw_line(0, s, o.width-1, s) + s += 10 + +drv = GPIO() +o = ILI9325(240, 320, drv) + +o.init() +# for _ in range(0, 50): +# hole(random.randint(2,o.width-3), random.randint(2,o.height-3)) +# +# draw_net(o) + +o.color = (10, 230, 200) +o.draw_circle(60, 15, 15) +o.color = (0, 250, 20) +o.draw_circle(53, 10, 3) +o.draw_circle(67, 10, 3) +o.color = (250, 150, 20) +o.draw_arc(60, 15, 10, 45, 135) +o.color = (10, 230, 200) +o.draw_line(60, 12, 57, 17) +o.draw_line(60, 12, 63, 17) +o.draw_arc(60, 15, 3, 45, 135) + +o.background_color = (200, 0, 120) +o.fill_rect(2, 2, 42, 29) +o.background_color = (20, 200, 120) +o.fill_rect(119, 2, 109, 12) +o.fill_rect(119, 17, 109, 19) + +o.background_color = (255, 255, 255) +o.fill_rect(77, 6, 105, 16) +o.background_color = (255, 0, 0) +o.fill_rect(77, 16, 105, 25) diff --git a/gfxlcd/demos/ili_2.py b/gfxlcd/demos/ili_2.py new file mode 100644 index 0000000..8280d2b --- /dev/null +++ b/gfxlcd/demos/ili_2.py @@ -0,0 +1,98 @@ +import RPi.GPIO +import sys +RPi.GPIO.setmode(RPi.GPIO.BCM) +sys.path.append("../../") +from gfxlcd.driver.ili9325.gpio import GPIO as ILIGPIO +from gfxlcd.driver.ili9325.ili9325 import ILI9325 +import random + + +def hole(o, x, y): + o.draw_pixel(x+1, y) + o.draw_pixel(x+2, y) + o.draw_pixel(x+3, y) + o.draw_pixel(x+1, y + 4) + o.draw_pixel(x+2, y + 4) + o.draw_pixel(x+3, y + 4) + o.draw_pixel(x, y + 1) + o.draw_pixel(x+4, y + 1) + o.draw_pixel(x, y + 2) + o.draw_pixel(x+4, y + 2) + o.draw_pixel(x, y + 3) + o.draw_pixel(x+4, y + 3) + + +def draw_points(o): + for _ in range(0, 50): + hole(o, random.randint(2,o.width - 10), random.randint(2,o.height-10)) + + +def draw_net(o): + s = 0 + while s < o.width-1: + o.draw_line(s, 0, s, o.height-1) + s += 10 + s = 0 + while s < o.height-1: + o.draw_line(0, s, o.width-1, s) + s += 10 + +lcd_tft = ILI9325(240, 320, ILIGPIO()) +lcd_tft.init() + +# bypass of missing +3v line to power backlight +LED = 6 +RPi.GPIO.setup(LED, RPi.GPIO.OUT) +RPi.GPIO.output(LED, 1) +lcd_tft.background_color = (255, 255, 255) +lcd_tft.fill_rect(0, 0, 240, 320) +lcd_tft.color = (0, 255, 1) +lcd_tft.draw_circle(79, 99, 40) +lcd_tft.draw_circle(60, 80, 7) +lcd_tft.draw_circle(100, 80, 7) + +lcd_tft.draw_line(79, 90, 70, 100) +lcd_tft.draw_line(79, 90, 88, 100) +lcd_tft.draw_arc(79, 91, 12, 45, 135) + +lcd_tft.color = (255, 0, 0) +lcd_tft.draw_arc(79, 90, 40, 45, 135) +lcd_tft.draw_line(51, 117, 105, 117) + +lcd_tft.background_color = (255, 127, 127) +lcd_tft.fill_rect(75, 140, 83, 220) +lcd_tft.draw_line(75, 220, 65, 280) +lcd_tft.draw_line(83, 220, 93, 280) +lcd_tft.draw_line(83, 150, 130, 150) + +lcd_tft.background_color = (0, 255, 0) +lcd_tft.fill_rect(0, 0, 122, 32) +lcd_tft.color = (0, 0, 0) +lcd_tft.background_color = (0, 0, 0) +lcd_tft.draw_circle(60, 15, 15) +lcd_tft.draw_circle(53, 10, 3) +lcd_tft.draw_circle(67, 10, 3) +lcd_tft.draw_arc(60, 15, 10, 45, 135) +lcd_tft.draw_line(60, 12, 57, 17) +lcd_tft.draw_line(60, 12, 63, 17) +lcd_tft.draw_arc(60, 15, 3, 45, 135) +lcd_tft.fill_rect(2, 2, 42, 29) +lcd_tft.fill_rect(119, 2, 109, 12) +lcd_tft.fill_rect(119, 17, 109, 19) +lcd_tft.draw_rect(77, 6, 105, 16) +lcd_tft.fill_rect(77, 16, 105, 25) + +lcd_tft.background_color = (0, 0, 0) +lcd_tft.fill_rect(100, 200, 222, 264) +lcd_tft.color = (43, 212, 255) +lcd_tft.background_color = (43, 212, 255) +lcd_tft.draw_circle(131, 232, 31) +lcd_tft.draw_circle(119, 222, 7) +lcd_tft.draw_circle(143, 222, 7) +lcd_tft.draw_arc(131, 232, 20, 45, 135) +lcd_tft.draw_line(131, 227, 127, 238) +lcd_tft.draw_line(131, 227, 135, 238) +lcd_tft.draw_arc(131, 235, 5, 45, 135) +lcd_tft.fill_rect(195, 204, 205, 210) +lcd_tft.draw_rect(180, 210, 220, 225) +lcd_tft.fill_rect(180, 226, 220, 259) \ No newline at end of file diff --git a/gfxlcd/demos/nju.py b/gfxlcd/demos/nju.py new file mode 100644 index 0000000..0c9f609 --- /dev/null +++ b/gfxlcd/demos/nju.py @@ -0,0 +1,31 @@ +import random +from gfxlcd.driver.nju6450.gpio import GPIO +from gfxlcd.driver.nju6450.nju6450 import NJU6450 + + +def hole(x, y): + o.draw_pixel(x+1, y) + o.draw_pixel(x+2, y) + o.draw_pixel(x+3, y) + o.draw_pixel(x+1, y + 4) + o.draw_pixel(x+2, y + 4) + o.draw_pixel(x+3, y + 4) + o.draw_pixel(x, y + 1) + o.draw_pixel(x+4, y + 1) + o.draw_pixel(x, y + 2) + o.draw_pixel(x+4, y + 2) + o.draw_pixel(x, y + 3) + o.draw_pixel(x+4, y + 3) + +drv = GPIO() +o = NJU6450(122, 32, drv) + +o.init() +o.auto_flush = False +for _ in range(0, 50): + hole(random.randint(2, 115), random.randint(2, 25)) +hole(10, 10) +hole(15, 13) +hole(18, 23) +hole(40, 10) +o.flush(True) diff --git a/gfxlcd/demos/nju_2.py b/gfxlcd/demos/nju_2.py new file mode 100644 index 0000000..03736b9 --- /dev/null +++ b/gfxlcd/demos/nju_2.py @@ -0,0 +1,60 @@ +import RPi.GPIO +import sys +RPi.GPIO.setmode(RPi.GPIO.BCM) +sys.path.append("../../") +from gfxlcd.driver.nju6450.gpio import GPIO +from gfxlcd.driver.nju6450.nju6450 import NJU6450 +import random + + +def hole(o, x, y): + o.draw_pixel(x+1, y) + o.draw_pixel(x+2, y) + o.draw_pixel(x+3, y) + o.draw_pixel(x+1, y + 4) + o.draw_pixel(x+2, y + 4) + o.draw_pixel(x+3, y + 4) + o.draw_pixel(x, y + 1) + o.draw_pixel(x+4, y + 1) + o.draw_pixel(x, y + 2) + o.draw_pixel(x+4, y + 2) + o.draw_pixel(x, y + 3) + o.draw_pixel(x+4, y + 3) + + +def draw_points(o): + for _ in range(0, 50): + hole(o, random.randint(2,o.width - 10), random.randint(2,o.height-10)) + + +def draw_net(o): + s = 0 + while s < o.width-1: + o.draw_line(s, 0, s, o.height-1) + s += 10 + s = 0 + while s < o.height-1: + o.draw_line(0, s, o.width-1, s) + s += 10 + +lcd_nju = NJU6450(122, 32, GPIO()) +lcd_nju.init() +lcd_nju.auto_flush = False + + +lcd_nju.draw_circle(60, 15, 15) +lcd_nju.draw_circle(53, 10, 3) +lcd_nju.draw_circle(67, 10, 3) +lcd_nju.draw_arc(60, 15, 10, 45, 135) +lcd_nju.draw_line(60, 12, 57, 17) +lcd_nju.draw_line(60, 12, 63, 17) +lcd_nju.draw_arc(60, 15, 3, 45, 135) + +lcd_nju.fill_rect(2, 2, 42, 29) +lcd_nju.fill_rect(119, 2, 109, 12) +lcd_nju.fill_rect(119, 17, 109, 19) + +lcd_nju.draw_rect(77, 6, 105, 16) +lcd_nju.fill_rect(77, 16, 105, 25) + +lcd_nju.flush(True) diff --git a/gfxlcd/demos/ssd.py b/gfxlcd/demos/ssd.py new file mode 100644 index 0000000..bc8c230 --- /dev/null +++ b/gfxlcd/demos/ssd.py @@ -0,0 +1,90 @@ +import random +from driver.ssd1306.spi import SPI +from driver.ssd1306.ssd1306 import SSD1306 + + + +def hole(x, y): + o.draw_pixel(x+1, y) + o.draw_pixel(x+2, y) + o.draw_pixel(x+3, y) + o.draw_pixel(x+1, y + 4) + o.draw_pixel(x+2, y + 4) + o.draw_pixel(x+3, y + 4) + o.draw_pixel(x, y + 1) + o.draw_pixel(x+4, y + 1) + o.draw_pixel(x, y + 2) + o.draw_pixel(x+4, y + 2) + o.draw_pixel(x, y + 3) + o.draw_pixel(x+4, y + 3) + +drv = SPI() +o = SSD1306(128, 64, drv) + +o.init() +o.auto_flush = False +for _ in range(0, 50): + hole(random.randint(2,120), random.randint(2,56)) +hole(10, 10) +hole(15, 13) +hole(18, 23) +hole(40, 10) +o.flush(True) +# o.fill(0) +# +# o.fill(random.randint(0, 255)) +# +# o.draw_pixels(2, 0, 128) +# o.draw_pixels(3, 0, 128) +# o.draw_pixels(7, 0, 128) +# o.draw_pixels(8, 0, 128) +# o.draw_pixels(1, 9, 7) +# o.draw_pixels(9, 9, 7) +# o.draw_pixels(2, 9, 8) +# o.draw_pixels(3, 9, 16) +# o.draw_pixels(4, 9, 33) +# o.draw_pixels(5, 9, 66) +# o.draw_pixels(6, 9, 33) +# o.draw_pixels(7, 9, 16) +# o.draw_pixels(8, 9, 8) +# +# o.draw_pixels(15, 9, 127) +# o.draw_pixels(16, 9, 65) +# o.draw_pixels(17, 9, 65) +# o.draw_pixels(18, 9, 62) +# +# o.draw_pixels(20, 9, 38) +# o.draw_pixels(21, 9, 73) +# o.draw_pixels(22, 9, 73) +# o.draw_pixels(23, 9, 50) +# +# o.draw_pixels(25, 9, 127) +# o.draw_pixels(26, 9, 9) +# o.draw_pixels(27, 9, 9) +# o.draw_pixels(28, 9, 6) +# +# o.draw_pixels(30, 9, 98) +# o.draw_pixels(31, 9, 81) +# o.draw_pixels(32, 9, 73) +# o.draw_pixels(33, 9, 70) +# +# o.draw_pixels(35, 9, 62) +# o.draw_pixels(36, 9, 65) +# o.draw_pixels(37, 9, 65) +# o.draw_pixels(38, 9, 62) +# +# o.draw_pixels(40, 9, 4) +# o.draw_pixels(41, 9, 2+64) +# o.draw_pixels(42, 9, 127) +# o.draw_pixels(43, 9, 64) +# +# o.draw_pixels(40, 9, 4) +# o.draw_pixels(41, 9, 2+64) +# o.draw_pixels(42, 9, 127) +# o.draw_pixels(43, 9, 64) +# +# o.draw_pixels(45, 9, 97) +# o.draw_pixels(46, 9, 25) +# o.draw_pixels(47, 9, 5) +# o.draw_pixels(48, 9, 3) + diff --git a/gfxlcd/demos/ssd_2.py b/gfxlcd/demos/ssd_2.py new file mode 100644 index 0000000..a5faba5 --- /dev/null +++ b/gfxlcd/demos/ssd_2.py @@ -0,0 +1,56 @@ +import RPi.GPIO +import sys +RPi.GPIO.setmode(RPi.GPIO.BCM) +sys.path.append("../../") +from gfxlcd.driver.ssd1306.spi import SPI +from gfxlcd.driver.ssd1306.ssd1306 import SSD1306 +import random + + +def hole(o, x, y): + o.draw_pixel(x+1, y) + o.draw_pixel(x+2, y) + o.draw_pixel(x+3, y) + o.draw_pixel(x+1, y + 4) + o.draw_pixel(x+2, y + 4) + o.draw_pixel(x+3, y + 4) + o.draw_pixel(x, y + 1) + o.draw_pixel(x+4, y + 1) + o.draw_pixel(x, y + 2) + o.draw_pixel(x+4, y + 2) + o.draw_pixel(x, y + 3) + o.draw_pixel(x+4, y + 3) + + +def draw_points(o): + for _ in range(0, 50): + hole(o, random.randint(2,o.width - 10), random.randint(2,o.height-10)) + + +def draw_net(o): + s = 0 + while s < o.width-1: + o.draw_line(s, 0, s, o.height-1) + s += 10 + s = 0 + while s < o.height-1: + o.draw_line(0, s, o.width-1, s) + s += 10 + +lcd_oled = SSD1306(128, 64, SPI()) +lcd_oled.init() +lcd_oled.auto_flush = False + +lcd_oled.draw_circle(31, 32, 31) +lcd_oled.draw_circle(19, 22, 7) +lcd_oled.draw_circle(43, 22, 7) +lcd_oled.draw_arc(31, 32, 20, 45, 135) +lcd_oled.draw_line(31, 27, 27, 38) +lcd_oled.draw_line(31, 27, 35, 38) +lcd_oled.draw_arc(31, 35, 5, 45, 135) + +lcd_oled.fill_rect(95, 4, 105, 10) +lcd_oled.draw_rect(80, 10, 120, 25) +lcd_oled.fill_rect(80, 26, 120, 59) + +lcd_oled.flush(True) diff --git a/gfxlcd/drawing/__init__.py b/gfxlcd/drawing/__init__.py new file mode 100644 index 0000000..7c2d50b --- /dev/null +++ b/gfxlcd/drawing/__init__.py @@ -0,0 +1 @@ +__author__ = 'kosci' diff --git a/gfxlcd/drawing/area.py b/gfxlcd/drawing/area.py new file mode 100644 index 0000000..4bdb7a1 --- /dev/null +++ b/gfxlcd/drawing/area.py @@ -0,0 +1,115 @@ +import itertools +from gfxlcd.drawing.pixel import Pixel + + +class Area(Pixel): + """Page drawing algorithm""" + def __init__(self): + Pixel.__init__(self) + + def init(self): + pass + + def draw_pixel(self, x, y): + """draw one pixel""" + self._set_area(x, y, x, y) + self.driver.data(self._converted_color(), None) + + def _set_area(self, x1, y1, x2, y2): + """select area to work with""" + self.driver.cmd_data(0x0020, x1) + self.driver.cmd_data(0x0021, y1) + self.driver.cmd_data(0x0050, x1) + self.driver.cmd_data(0x0052, y1) + self.driver.cmd_data(0x0051, x2) + self.driver.cmd_data(0x0053, y2) + self.driver.cmd(0x0022, None) + + + def _draw_vertical_line(self, x, y, length): + """draw vertical line""" + self._set_area(x, y, x, y + length) + color = self._converted_color() + for _ in itertools.repeat(None, length): + self.driver.data(color, None) + + def _draw_horizontal_line(self, x, y, length): + """draw horizontal line""" + self._set_area(x, y, x + length, y) + color = self._converted_color() + for _ in itertools.repeat(None, length): + self.driver.data(color, None) + + def _calculate_steps(self, length, step, required_length): + """calculate lineparts - helper""" + steps = [length for _ in range(0, step)] + if step * length < required_length: + for idx in range(0, required_length - step * length): + steps[idx] += 1 + + return steps + + def draw_line(self, x1, y1, x2, y2): + """draw diagonal line""" + width = abs(x2 - x1) + height = abs(y2 - y1) + if x1 == x2: + steps = [height] + horizontal = False + offset_x = offset_y = 0 + elif y1 == y2: + steps = [width] + horizontal = True + offset_x = offset_y = 0 + elif width > height: + if x2 < x1: + x1, x2 = x2, x1 + y1, y2 = y2, y1 + offset_y = 1 if y2 > y1 else -1 + offset_x = 1 if x2 > x1 else -1 + horizontal = True + step = height + length = width / step + steps = self._calculate_steps(length, step, width) + + else: + if y2 < y1: + x1, x2 = x2, x1 + y1, y2 = y2, y1 + offset_y = 1 if y2 > y1 else -1 + offset_x = 1 if x2 > x1 else -1 + horizontal = False + step = width + length = height / step + steps = self._calculate_steps(length, step, height) + + dy = 0 + dx = 0 + for idx, step in enumerate(steps): + if horizontal: + self._draw_horizontal_line( + int(x1 + dx), + int(y1 + (idx * offset_y)), + int(step) + ) + dx += step * offset_x + else: + self._draw_vertical_line( + int(x1 + (idx * offset_x)), + int(y1 + dy), + int(step) + ) + dy += step * offset_y + + def fill_rect(self, x1, y1, x2, y2): + """fill an area""" + size = (abs(x2 - x1) + 1) * (abs(y2 - y1) + 1) + self._set_area( + min(x1, x2), + min(y1, y2), + max(x1, x2), + max(y1, y2) + ) + color = self._converted_background_color() + for _ in range(0, size): + self.driver.data(color, None) diff --git a/gfxlcd/drawing/page.py b/gfxlcd/drawing/page.py new file mode 100644 index 0000000..0844018 --- /dev/null +++ b/gfxlcd/drawing/page.py @@ -0,0 +1,114 @@ +import abc +from gfxlcd.drawing.pixel import Pixel + + +class Page(Pixel, metaclass=abc.ABCMeta): + """Page drawing algorithm""" + def __init__(self): + Pixel.__init__(self) + self.buffer = [] + + def init(self): + """init page""" + self.buffer = [[0] * (self.height // 8) for x in range(self.width)] + + def draw_pixel(self, x, y): + """draw a pixel at x,y""" + self.buffer[x][y//8] |= 1 << (y % 8) + self.flush() + + def _calculate_steps(self, length, step, required_length): + """calculate lineparts - helper""" + steps = [length for _ in range(0, step)] + if step * length < required_length: + for idx in range(0, required_length - step * length): + steps[idx] += 1 + + return steps + + def draw_line(self, x1, y1, x2, y2): + """draw diagonal line""" + width = abs(x2 - x1) + height = abs(y2 - y1) + if x1 == x2: + steps = [height] + horizontal = False + offset_x = offset_y = 0 + elif y1 == y2: + steps = [width] + horizontal = True + offset_x = offset_y = 0 + elif width > height: + if x2 < x1: + x1, x2 = x2, x1 + y1, y2 = y2, y1 + offset_y = 1 if y2 > y1 else -1 + offset_x = 1 if x2 > x1 else -1 + horizontal = True + step = height + length = width / step + steps = self._calculate_steps(length, step, width) + + else: + if y2 < y1: + x1, x2 = x2, x1 + y1, y2 = y2, y1 + offset_y = 1 if y2 > y1 else -1 + offset_x = 1 if x2 > x1 else -1 + horizontal = False + step = width + length = height / step + steps = self._calculate_steps(length, step, height) + dy = 0 + dx = 0 + for idx, step in enumerate(steps): + if horizontal: + for appendix in range(int(step)+1): + self.draw_pixel( + int(x1 + dx + appendix), + int(y1 + (idx * offset_y)) + ) + dx += step * offset_x + else: + for appendix in range(int(step)+1): + self.draw_pixel( + int(x1 + (idx * offset_x)), + int(y1 + dy + appendix) + ) + dy += step * offset_y + + def fill_rect(self, x1, y1, x2, y2): + """draw a filled rectangle""" + if y2 < y1: + y1, y2 = y2, y1 + if x2 < x1: + x1, x2 = x2, x1 + start_page = y1 // 8 + start_bit = y1 % 8 + end_page = y2 // 8 + end_bit = y2 % 8 + rows = [] + first_page = int(('0' * start_bit).rjust(8, '1'), 2) + last_page = int('1' * (end_bit+1), 2) + if start_page != end_page: + rows.append(first_page) + for _ in range(end_page - start_page - 1): + rows.append(255) + rows.append(last_page) + else: + rows.append(first_page & last_page) + + page = start_page + for v in rows: + for x in range(x2-x1+1): + self.buffer[x1+x][page] |= v + page += 1 + + def get_page_value(self, column, page): + """returns value""" + return self.buffer[column][page] + + @abc.abstractmethod + def flush(self, force=None): + """flush buffer to the screen""" + pass diff --git a/gfxlcd/drawing/pixel.py b/gfxlcd/drawing/pixel.py new file mode 100644 index 0000000..2f6e81f --- /dev/null +++ b/gfxlcd/drawing/pixel.py @@ -0,0 +1,73 @@ +import math + + +class Pixel(object): + def __init__(self): + self.options['color'] = { + 'R': 255, 'G': 255, 'B': 255 + } + self.options['background_color'] = { + 'R': 0, 'G': 0, 'B': 0, + } + + def draw_rect(self, x1, y1, x2, y2): + """draw a rectangle""" + self.draw_line(x1, y1, x2, y1) + self.draw_line(x1, y2, x2, y2) + self.draw_line(x1, y1, x1, y2) + self.draw_line(x2, y1, x2, y2) + + def draw_circle(self, x, y, radius): + """draw a circle""" + err = 0 + offset_x = radius + offset_y = 0 + while offset_x >= offset_y: + self.draw_pixel(x + offset_x, y + offset_y) + self.draw_pixel(x + offset_y, y + offset_x) + self.draw_pixel(x - offset_y, y + offset_x) + self.draw_pixel(x - offset_x, y + offset_y) + self.draw_pixel(x - offset_x, y - offset_y) + self.draw_pixel(x - offset_y, y - offset_x) + self.draw_pixel(x + offset_y, y - offset_x) + self.draw_pixel(x + offset_x, y - offset_y) + if err <= 0: + offset_y += 1 + err += 2*offset_y + 1 + else: + offset_x -= 1 + err -= 2*offset_x + 1 + + def draw_arc(self, x, y, radius, start, end): + """draw an arc""" + start = start * math.pi / 180 + end = end * math.pi / 180 + + err = 0 + offset_x = radius + offset_y = 0 + while offset_x >= offset_y: + if start <= math.atan2(offset_y, offset_x) <= end: + self.draw_pixel(x + offset_x, y + offset_y) + if start <= math.atan2(offset_x, offset_y) <= end: + self.draw_pixel(x + offset_y, y + offset_x) + if start <= math.atan2(offset_x, -offset_y) <= end: + self.draw_pixel(x - offset_y, y + offset_x) + if start <= math.atan2(offset_y, -offset_x) <= end: + self.draw_pixel(x - offset_x, y + offset_y) + + if start <= math.atan2(-offset_y, -offset_x) + 2*math.pi <= end: + self.draw_pixel(x - offset_x, y - offset_y) + if start <= math.atan2(-offset_x, -offset_y) + 2*math.pi <= end: + self.draw_pixel(x - offset_y, y - offset_x) + if start <= math.atan2(-offset_x, offset_y) + 2*math.pi <= end: + self.draw_pixel(x + offset_y, y - offset_x) + if start <= math.atan2(-offset_y, offset_x) + 2*math.pi <= end: + self.draw_pixel(x + offset_x, y - offset_y) + + if err <= 0: + offset_y += 1 + err += 2*offset_y + 1 + else: + offset_x -= 1 + err -= 2*offset_x + 1 diff --git a/gfxlcd/driver/__init__.py b/gfxlcd/driver/__init__.py new file mode 100644 index 0000000..7c2d50b --- /dev/null +++ b/gfxlcd/driver/__init__.py @@ -0,0 +1 @@ +__author__ = 'kosci' diff --git a/gfxlcd/driver/ili9325/__init__.py b/gfxlcd/driver/ili9325/__init__.py new file mode 100644 index 0000000..7c2d50b --- /dev/null +++ b/gfxlcd/driver/ili9325/__init__.py @@ -0,0 +1 @@ +__author__ = 'kosci' diff --git a/gfxlcd/driver/ili9325/gpio.py b/gfxlcd/driver/ili9325/gpio.py new file mode 100644 index 0000000..afdf757 --- /dev/null +++ b/gfxlcd/driver/ili9325/gpio.py @@ -0,0 +1,72 @@ +import time +import RPi.GPIO +from gfxlcd.abstract.driver import Driver + +RPi.GPIO.setmode(RPi.GPIO.BCM) + + +class GPIO(Driver): + def __init__(self): + self.pins = { + 'RS': 27, + 'W': 17, + 'DB8': 22, + 'DB9': 23, + 'DB10': 24, + 'DB11': 5, + 'DB12': 12, + 'DB13': 16, + 'DB14': 20, + 'DB15': 21, + 'RST': 25, + } + self.data_pins = [ + 'DB8', 'DB9', 'DB10', 'DB11', 'DB12', 'DB13', 'DB14', 'DB15', + ] + + def init(self): + """initialize pins""" + for pin in self.pins: + RPi.GPIO.setup(self.pins[pin], RPi.GPIO.OUT) + RPi.GPIO.output(self.pins[pin], 0) + + def reset(self): + """reset a display""" + RPi.GPIO.output(self.pins['RST'], 1) + time.sleep(0.005) + RPi.GPIO.output(self.pins['RST'], 0) + time.sleep(0.005) + RPi.GPIO.output(self.pins['RST'], 1) + time.sleep(0.005) + + def _set_pins(self, bits): + """set rpi pins""" + for pin in self.data_pins: + value = bits & 0x01 + RPi.GPIO.output(self.pins[pin], value) + bits >>= 1 + + def cmd(self, char, enable): + """send command to display""" + RPi.GPIO.output(self.pins['RS'], 0) + self.send(char, enable) + + def send(self, char, enable): + """send 16bit as 2*8bit""" + self._set_pins(char >> 8) + RPi.GPIO.output(self.pins['W'], 0) + RPi.GPIO.output(self.pins['W'], 1) + self._set_pins(char) + RPi.GPIO.output(self.pins['W'], 0) + RPi.GPIO.output(self.pins['W'], 1) + + def data(self, data, enable): + """send data to display""" + RPi.GPIO.output(self.pins['RS'], 1) + self.send(data, enable) + + def cmd_data(self, cmd, data): + RPi.GPIO.output(self.pins['RS'], 0) + self.send(cmd, None) + RPi.GPIO.output(self.pins['RS'], 1) + self.send(data, None) \ No newline at end of file diff --git a/gfxlcd/driver/ili9325/ili9325.py b/gfxlcd/driver/ili9325/ili9325.py new file mode 100644 index 0000000..8282c8a --- /dev/null +++ b/gfxlcd/driver/ili9325/ili9325.py @@ -0,0 +1,174 @@ +import time +from gfxlcd.drawing.area import Area +from gfxlcd.abstract.chip import Chip + + +class ILI9325(Area, Chip): + """CLass for ILI9325 based LCD""" + def __init__(self, width, height, driver): + Chip.__init__(self, width, height, driver, True) + Area.__init__(self) + + def _converted_background_color(self): + """color from 8-8-8 to 5-6-5""" + rgb = self.options['background_color']['R'] << 16 | \ + self.options['background_color']['G'] << 8 | \ + self.options['background_color']['B'] + return ((rgb & 0x00f80000) >> 8) | ((rgb & 0x0000fc00) >> 5) | ((rgb & 0x000000f8) >> 3) + + def _converted_color(self): + """color from 8-8-8 to 5-6-5""" + rgb = self.options['color']['R'] << 16 | \ + self.options['color']['G'] << 8 | \ + self.options['color']['B'] + return ((rgb & 0x00f80000) >> 8) | ((rgb & 0x0000fc00) >> 5) | ((rgb & 0x000000f8) >> 3) + + def init(self): + """init display""" + self.driver.init() + Area.init(self) + Chip.init(self) + self.driver.reset() + + # ************* ILI9325C/D ********** + # set SS and SM bit + self.driver.cmd(0x0001, None) + self.driver.data(0x0100, None) + # set 1 line inversion + self.driver.cmd(0x0002, None) + self.driver.data(0x0200, None) + # set GRAM write direction and BGR=1 + self.driver.cmd(0x0003, None) + self.driver.data(0x1030, None) + # Resize register + self.driver.cmd(0x0004, None) + self.driver.data(0x0000, None) + # set the back porch and front porch + self.driver.cmd(0x0008, None) + self.driver.data(0x0207, None) + # set non-display area refresh cycle ISC[3:0] + self.driver.cmd(0x0009, None) + self.driver.data(0x0000, None) + # FMARK function + self.driver.cmd(0x000A, None) + self.driver.data(0x0000, None) + # RGB interface setting + self.driver.cmd(0x000C, None) + self.driver.data(0x0000, None) + # Frame marker Position + self.driver.cmd(0x000D, None) + self.driver.data(0x0000, None) + # RGB interface polarity + self.driver.cmd(0x000F, None) + self.driver.data(0x0000, None) + + # ************* Power On sequence **************** + # SAP, BT[3:0], AP, DSTB, SLP, STB + self.driver.cmd(0x0010, None) + self.driver.data(0x0000, None) + # DC1[2:0], DC0[2:0], VC[2:0] + self.driver.cmd(0x0011, None) + self.driver.data(0x0007, None) + # VREG1OUT voltage + self.driver.cmd(0x0012, None) + self.driver.data(0x0000, None) + # VDV[4:0] for VCOM amplitude + self.driver.cmd(0x0013, None) + self.driver.data(0x0000, None) + self.driver.cmd(0x0007, None) + self.driver.data(0x0001, None) + time.sleep(0.2) # Dis-charge capacitor power voltage + # SAP, BT[3:0], AP, DSTB, SLP, STB + self.driver.cmd(0x0010, None) + self.driver.data(0x1690, None) + # Set DC1[2:0], DC0[2:0], VC[2:0] + self.driver.cmd(0x0011, None) + self.driver.data(0x0227, None) + time.sleep(0.05) + self.driver.cmd(0x0012, None) + self.driver.data(0x000D, None) + time.sleep(0.05) + # VDV[4:0] for VCOM amplitude + self.driver.cmd(0x0013, None) + self.driver.data(0x1200, None) + # 04 VCM[5:0] for VCOMH + self.driver.cmd(0x0029, None) + self.driver.data(0x000A, None) + # Set Frame Rate + self.driver.cmd(0x002B, None) + self.driver.data(0x000D, None) + time.sleep(0.05) + # GRAM horizontal Address + self.driver.cmd(0x0020, None) + self.driver.data(0x0000, None) + # GRAM Vertical Address + self.driver.cmd(0x0021, None) + self.driver.data(0x0000, None) + + # ************* Adjust the Gamma Curve ************* + self.driver.cmd(0x0030, None) + self.driver.data(0x0000, None) + self.driver.cmd(0x0031, None) + self.driver.data(0x0404, None) + self.driver.cmd(0x0032, None) + self.driver.data(0x0003, None) + self.driver.cmd(0x0035, None) + self.driver.data(0x0405, None) + self.driver.cmd(0x0036, None) + self.driver.data(0x0808, None) + self.driver.cmd(0x0037, None) + self.driver.data(0x0407, None) + self.driver.cmd(0x0038, None) + self.driver.data(0x0303, None) + self.driver.cmd(0x0039, None) + self.driver.data(0x0707, None) + self.driver.cmd(0x003C, None) + self.driver.data(0x0504, None) + self.driver.cmd(0x003D, None) + self.driver.data(0x0808, None) + + # ************* Set GRAM area ************* + # Horizontal GRAM Start Address + self.driver.cmd(0x0050, None) + self.driver.data(0x0000, None) + # Horizontal GRAM End Address + self.driver.cmd(0x0051, None) + self.driver.data(0x00EF, None) + # Vertical GRAM Start Address + self.driver.cmd(0x0052, None) + self.driver.data(0x0000, None) + # Vertical GRAM Start Address + self.driver.cmd(0x0053, None) + self.driver.data(0x013F, None) + # Gate Scan Line + self.driver.cmd(0x0060, None) + self.driver.data(0xA700, None) + # NDL, VLE, REV + self.driver.cmd(0x0061, None) + self.driver.data(0x0001, None) + # set scrolling line + self.driver.cmd(0x006A, None) + self.driver.data(0x0000, None) + + # ************* Partial Display Control ************* + self.driver.cmd(0x0080, None) + self.driver.data(0x0000, None) + self.driver.cmd(0x0081, None) + self.driver.data(0x0000, None) + self.driver.cmd(0x0082, None) + self.driver.data(0x0000, None) + self.driver.cmd(0x0083, None) + self.driver.data(0x0000, None) + self.driver.cmd(0x0084, None) + self.driver.data(0x0000, None) + self.driver.cmd(0x0085, None) + self.driver.data(0x0000, None) + + # ************* Panel Control ************* + self.driver.cmd(0x0090, None) + self.driver.data(0x0010, None) + self.driver.cmd(0x0092, None) + self.driver.data(0x0000, None) + # 262K color and display ON + self.driver.cmd(0x0007, None) + self.driver.data(0x0133, None) diff --git a/gfxlcd/driver/nju6450/__init__.py b/gfxlcd/driver/nju6450/__init__.py new file mode 100644 index 0000000..7c2d50b --- /dev/null +++ b/gfxlcd/driver/nju6450/__init__.py @@ -0,0 +1 @@ +__author__ = 'kosci' diff --git a/gfxlcd/driver/nju6450/gpio.py b/gfxlcd/driver/nju6450/gpio.py new file mode 100644 index 0000000..7f1a27a --- /dev/null +++ b/gfxlcd/driver/nju6450/gpio.py @@ -0,0 +1,69 @@ +import time +import RPi.GPIO +from gfxlcd.abstract.driver import Driver + +RPi.GPIO.setmode(RPi.GPIO.BCM) + + +class GPIO(Driver): + def __init__(self): + self.pins = { + 'A0': 17, + 'E1': 22, + 'E2': 21, + 'D0': 23, + 'D1': 24, + 'D2': 25, + 'D3': 12, + 'D4': 16, + 'D5': 20, + 'D6': 26, + 'D7': 19, + 'RST': 5, + } + + self.data_pins = [ + 'D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7' + ] + + def init(self): + """init a device""" + for pin in self.pins: + RPi.GPIO.setup(self.pins[pin], RPi.GPIO.OUT) + RPi.GPIO.output(self.pins[pin], 0) + + def reset(self): + """resets a device""" + RPi.GPIO.output(self.pins['RST'], 1) + time.sleep(0.025) + RPi.GPIO.output(self.pins['RST'], 0) + time.sleep(0.025) + RPi.GPIO.output(self.pins['RST'], 1) + time.sleep(0.025) + RPi.GPIO.output(self.pins['A0'], 0) + RPi.GPIO.output(self.pins['E1'], 1) + RPi.GPIO.output(self.pins['E2'], 1) + + def cmd(self, char, enable): + """send command""" + RPi.GPIO.output(self.pins['A0'], 0) + self.send(char, enable) + + def data(self, char, enable): + """send data""" + RPi.GPIO.output(self.pins['A0'], 1) + self.send(char, enable) + + def send(self, data, enable): + """Write to gpio""" + RPi.GPIO.output(self.pins['E1'], 0) + RPi.GPIO.output(self.pins['E2'], 0) + for i in self.data_pins: + value = data & 0x01 + RPi.GPIO.output(self.pins[i], value) + data >>= 1 + + RPi.GPIO.output(self.pins['E'+str(enable+1)], 1) + time.sleep(0.00025) + RPi.GPIO.output(self.pins['E1'], 0) + RPi.GPIO.output(self.pins['E2'], 0) \ No newline at end of file diff --git a/gfxlcd/driver/nju6450/nju6450.py b/gfxlcd/driver/nju6450/nju6450.py new file mode 100644 index 0000000..cb8128e --- /dev/null +++ b/gfxlcd/driver/nju6450/nju6450.py @@ -0,0 +1,53 @@ +from gfxlcd.drawing.page import Page +from gfxlcd.abstract.chip import Chip + + +class NJU6450(Page, Chip): + """Class for an LCD with NJU6450 chip""" + def __init__(self, width, height, driver, auto_flush=True): + Chip.__init__(self, width, height, driver, auto_flush) + Page.__init__(self) + + def init(self): + """initialize display""" + self.driver.init() + Page.init(self) + Chip.init(self) + self.driver.reset() + + init_sequence = [0xae, 0xa4, 0xa9, 0xe2, 0xa0, 0xaf] + for cmd in init_sequence: + self.driver.cmd(cmd, 0) + self.driver.cmd(cmd, 1) + + def set_xy(self, x, y): + """set xy pos""" + if x < self.width/2: + self.driver.cmd(0xB8 | y, 0) + self.driver.cmd(0x00 | x, 0) + else: + self.driver.cmd(0xB8 | y, 1) + self.driver.cmd(0x00 | (x - self.width//2), 1) + + def _converted_background_color(self): + """convert RGB background to available color""" + return 1 + + def _converted_color(self): + """convert RGB color to available color""" + return 1 + + def flush(self, force=None): + """flush buffer to device + :force - boolean|None""" + if force is None: + force = self.options['auto_flush'] + + if force: + for j in range(0, self.height//8): + for i in range(0, self.width): + self.set_xy(i, j) + if i < self.width/2: + self.driver.data(self.get_page_value(i, j), 0) + else: + self.driver.data(self.get_page_value(i, j), 1) \ No newline at end of file diff --git a/gfxlcd/driver/ssd1306/__init__.py b/gfxlcd/driver/ssd1306/__init__.py new file mode 100644 index 0000000..7c2d50b --- /dev/null +++ b/gfxlcd/driver/ssd1306/__init__.py @@ -0,0 +1 @@ +__author__ = 'kosci' diff --git a/gfxlcd/driver/ssd1306/spi.py b/gfxlcd/driver/ssd1306/spi.py new file mode 100644 index 0000000..38e8e2c --- /dev/null +++ b/gfxlcd/driver/ssd1306/spi.py @@ -0,0 +1,49 @@ +import spidev +import RPi.GPIO as GPIO + + +GPIO.setmode(GPIO.BCM) +import time +from gfxlcd.abstract.driver import Driver + + +class SPI(Driver): + def __init__(self): + self.pins = { + 'RST': 13, + 'DC': 6, + } + self.spi = None + + def init(self): + """init sequence""" + for pin in self.pins: + GPIO.setup(self.pins[pin], GPIO.OUT) + GPIO.output(self.pins[pin], 0) + + spi = spidev.SpiDev() + spi.open(0,0) + spi.max_speed_hz = 8000000 + spi.mode = 0 + self.spi = spi + + def reset(self): + """reset device""" + GPIO.output(self.pins['RST'], 1) + time.sleep(0.025) + GPIO.output(self.pins['RST'], 0) + time.sleep(0.025) + GPIO.output(self.pins['RST'], 1) + time.sleep(0.025) + + def cmd(self, data, enable=None): + """send command to device""" + GPIO.output(self.pins['DC'], 0) + self.spi.xfer2([data]) + + def data(self, data, enable=None): + """send data to device""" + GPIO.output(self.pins['DC'], 1) + self.spi.xfer2([data]) + GPIO.output(self.pins['DC'], 0) + diff --git a/gfxlcd/driver/ssd1306/ssd1306.py b/gfxlcd/driver/ssd1306/ssd1306.py new file mode 100644 index 0000000..52574f9 --- /dev/null +++ b/gfxlcd/driver/ssd1306/ssd1306.py @@ -0,0 +1,80 @@ +from gfxlcd.drawing.page import Page +from gfxlcd.abstract.chip import Chip + + +class SSD1306(Page, Chip): + """Class for an LCD with SSD306 chip""" + def __init__(self, width, height, driver, auto_flush=True): + Chip.__init__(self, width, height, driver, auto_flush) + Page.__init__(self) + + def init(self): + """inits a device""" + self.driver.init() + Page.init(self) + Chip.init(self) + self.driver.reset() + self.driver.cmd(0xae) # turn off panel + self.driver.cmd(0x00) # set low column address + self.driver.cmd(0x10) # set high column address + self.driver.cmd(0x40) # set start line address + + self.driver.cmd(0x20) # addr mode + self.driver.cmd(0x02) # horizontal + + self.driver.cmd(0xb0) # set page address + self.driver.cmd(0x81) # set contrast control register + self.driver.cmd(0xff) + self.driver.cmd(0xa1) # a0/a1 set segment re-map 127 to 0 a0:0 to seg127 + self.driver.cmd(0xc8) # c8/c0 set com(N-1)to com0 c0:com0 to com(N-1) + self.driver.cmd(0xa6) # set normal display, a6 - normal, a7 - inverted + + self.driver.cmd(0xa8) # set multiplex ratio(16to63) + self.driver.cmd(0x3f) # 1/64 duty + + self.driver.cmd(0xd3) # set display offset + self.driver.cmd(0x00) # not offset + + self.driver.cmd(0xd5) # set display clock divide ratio/oscillator frequency + self.driver.cmd(0x80) # set divide ratio + + self.driver.cmd(0xd9) # set pre-charge period + self.driver.cmd(0xf1) + self.driver.cmd(0xda) # set com pins hardware configuration + self.driver.cmd(0x12) + + self.driver.cmd(0xdb) # set vcomh + self.driver.cmd(0x40) + + self.driver.cmd(0x8d) # charge pump + self.driver.cmd(0x14) # enable charge pump + self.driver.cmd(0xaf) # turn on panel + + def _converted_background_color(self): + """convert RGB background to available color""" + return 1 + + def _converted_color(self): + """convert RGB color to available color""" + return 1 + + def flush(self, force=None): + """flush buffer to device + :force - boolean|None""" + if force is None: + force = self.options['auto_flush'] + + if force: + for j in range(0, self.height//8): + self.set_area(0, j, self.width-1, j+1) + for i in range(0, self.width): + self.driver.data(self.get_page_value(i, j)) + + def set_area(self, x1, y1, x2, y2): + """set area to work on""" + self.driver.cmd(0x22) + self.driver.cmd(0xb0 + y1) + self.driver.cmd(0xb0 + y2) + self.driver.cmd(0x21) + self.driver.cmd(x1) + self.driver.cmd(x2) diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..90cec37 --- /dev/null +++ b/readme.md @@ -0,0 +1,183 @@ +What it is +=== + +Library for graphical LCDs for Python on Raspberry Pi. Creates a united interface for supported devices + +Supported: + +- ili325 via GPIO +- ssd1306 via SPI +- nju6450 via GPIO + +On NJU and SSD uses buffer to keep current content as help for page operations. + +Wiring is below + + +Initialization +=== +##SSD1306 +### SPI + + from driver.ssd1306.spi import SPI + from driver.ssd1306.ssd1306 import SSD1306 + drv = SPI() + o = SSD1306(128, 64, drv) + o.init() + +If you want to set your own pins: + + drv = SPI() + drv.pins = { + 'RST': 13, + 'DC': 6, + } + o = SSD1306(128, 64, drv) + o.init() + +##NJU6450 +### GPIO + + from gfxlcd.driver.nju6450.gpio import GPIO + from gfxlcd.driver.nju6450.nju6450 import NJU6450 + drv = GPIO() + o = NJU6450(122, 32, drv) + o.init() + +Custom wiring: + + from gfxlcd.driver.nju6450.gpio import GPIO + from gfxlcd.driver.nju6450.nju6450 import NJU6450 + drv = GPIO() + drv.pins = { + 'A0': 17, + 'E1': 22, + 'E2': 21, + 'D0': 23, + 'D1': 24, + 'D2': 25, + 'D3': 12, + 'D4': 16, + 'D5': 20, + 'D6': 26, + 'D7': 19, + 'RST': 5, + } + o = NJU6450(122, 32, drv) + o.init() + +##ILI9325 +### GPIO + + from gfxlcd.driver.ili9325.gpio import GPIO + from gfxlcd.driver.ili9325.ili9325 import ILI9325 + drv = GPIO() + o = ILI9325(240, 320, drv) + o.init() + +Custom pins: + + from gfxlcd.driver.ili9325.gpio import GPIO + from gfxlcd.driver.ili9325.ili9325 import ILI9325 + drv = GPIO() + drv.pins = { + 'RS': 27, + 'W': 17, + 'DB8': 22, + 'DB9': 23, + 'DB10': 24, + 'DB11': 5, + 'DB12': 12, + 'DB13': 16, + 'DB14': 20, + 'DB15': 21, + 'RST': 25, + } + o = ILI9325(240, 320, drv) + o.init() + + +Drawing functions +=== +draw_pixel + +draw_line + +draw_rect + +draw_circle + +draw_arc + +fill_rect + + +Colours +=== +lcd.color = (r, g, b) +lcd.background_color = (r, g ,b) + + +Wiring +=== + +##SSD1306 +### SPI +SPI wiring + 2 additional pins. Defaults: + + LCD Raspberry Pi + GND ----------- GND + +3.3V ----------- +3.3V + SCL ----------- G11 + SDA ----------- G10 + RST ----------- G13 + D/C ----------- G6 + + +##NJU6450 +### GPIO +Default wiring: + + LCD Raspberry Pi + 1 (Vss) ------- GND + 2 (Vdd) ------- +5V + 3 (V0) ---[-\-] 10k + \--- GND + 4 (A0) ---------------------- G17 + 5 (E1) ---------------------- G22 + 6 (E2) ---------------------- G21 + 7 (R/W) ------- GND + 8 (D0) ---------------------- G23 + 9 (D1) ---------------------- G24 + 10 (D2) ---------------------- G25 + 11 (D3) ---------------------- G12 + 12 (D4) ---------------------- G16 + 13 (D5) ---------------------- G20 + 14 (D6) ---------------------- G26 + 15 (D7) ---------------------- G19 + 16 (RST) ------- +5V + 17 (A) ------- +5V + 18 (K) ------- GND + +##ILI9325 +### GPIO +Default: + + TFT Raspberry Pi 2B + + GND ------------------------ GND + Vcc ------------------------ 3.3 + RS ------------------------ G27 (data[H]/cmd[L]) + WR ------------------------ G17 + RD ------------------------ 3.3 (never read from screen) + DB8 ------------------------ G22 + DB9 ------------------------ G23 + DB10 ------------------------ G24 + DB11 ------------------------ G5 + DB12 ------------------------ G12 + DB13 ------------------------ G16 + DB14 ------------------------ G20 + DB15 ------------------------ G21 + CS ------------------------ GND (always selected) + REST ------------------------ G25 + LED_A ------------------------ 3.3 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..1dfc65a --- /dev/null +++ b/setup.cfg @@ -0,0 +1,4 @@ +[bdist_wheel] +universal = 1 +[metadata] +description-file = readme.md \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..d2016fa --- /dev/null +++ b/setup.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +"""setup for GfxLCD package""" + +import os +from setuptools import setup, find_packages + + +def read(*paths): + """Build a file path from *paths* and return the contents.""" + with open(os.path.join(*paths), 'r') as file_handler: + return file_handler.read() + +setup( + name='gfxlcd', + version='0.1.0', + description='gfxlcd is a handler for grpahical lcds: ILI9328, SSD1306, NJU6450 @ Raspberry Pi.', + keywords=['gfxlcd', 'raspberry pi' ,'ili9328' ,'ssd1306', 'nju6450', 'lcd', 'graphical lcd'], + long_description=(read('readme.md')), + url='https://bitbucket.org/kosci/charlcd.git', + license='MIT', + author='Bartosz Kościów', + author_email='kosci1@gmail.com', + py_modules=['gfxlcd'], + include_package_data=True, + classifiers=[ + 'Development Status :: 4 - Beta', + 'Environment :: Console', + 'Intended Audience :: Developers', + 'Natural Language :: English', + 'License :: OSI Approved :: MIT License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Topic :: Software Development :: Libraries :: Python Modules', + 'Topic :: Home Automation' + ], + packages=find_packages(exclude=['tests*']), +) diff --git a/tests3 b/tests3 new file mode 100644 index 0000000..b9eb61a --- /dev/null +++ b/tests3 @@ -0,0 +1,2 @@ +chmod 644 gfxlcd/tests/*.py +python3 -m nose --with-specplugin diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..0b0eeda --- /dev/null +++ b/tox.ini @@ -0,0 +1,20 @@ +[tox] +envlist = py35 +skipsdist = True +[testenv] +setenv = VIRTUAL_ENV={envdir} +deps= + nose + mock + future + flake8 + pylint +whitelist_externals = /bin/bash +commands= nosetests --with-xunit --xunit-file=junit-{envname}.xml charlcd/tests + rm flake8-{envname}.log + /bin/bash -c "flake8 --output-file=flake8-{envname}.log gfxlcd || :" + /bin/bash -c "pylint gfxlcd > pylint-{envname}.log || :" +[flake8] +show-source = True +exclude = .git,.venv,.tox,dist,doc,build,*egg,*/tests/* +