diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 2e36e69..1e2fcfe 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,2 +1,6 @@ +0.2.1 + - CS & LED pin do ILI drv +0.2.0 + - add AD7843 touch panel driver 0.1.0 - - extract library from Doton project \ No newline at end of file + - extract library from Doton project diff --git a/gfxlcd/abstract/chip.py b/gfxlcd/abstract/chip.py index 36eb407..03bf3c0 100644 --- a/gfxlcd/abstract/chip.py +++ b/gfxlcd/abstract/chip.py @@ -99,3 +99,8 @@ class Chip(metaclass=abc.ABCMeta): def fill_rect(self, pos_x1, pos_y1, pos_x2, pos_y2): """draw a filled rectangle""" pass + + @abc.abstractmethod + def draw_image(self, pos_x, pos_y, image): + """draw a PIL image""" + pass diff --git a/gfxlcd/demos/assets/dsp2017_101_64.png b/gfxlcd/demos/assets/dsp2017_101_64.png new file mode 100644 index 0000000..1f9beb1 Binary files /dev/null and b/gfxlcd/demos/assets/dsp2017_101_64.png differ diff --git a/gfxlcd/demos/assets/dsp2017_122_29.png b/gfxlcd/demos/assets/dsp2017_122_29.png new file mode 100644 index 0000000..298425f Binary files /dev/null and b/gfxlcd/demos/assets/dsp2017_122_29.png differ diff --git a/gfxlcd/demos/assets/japan_temple_240x320.jpg b/gfxlcd/demos/assets/japan_temple_240x320.jpg new file mode 100644 index 0000000..0946e24 Binary files /dev/null and b/gfxlcd/demos/assets/japan_temple_240x320.jpg differ diff --git a/gfxlcd/demos/assets/numbers.jpg b/gfxlcd/demos/assets/numbers.jpg new file mode 100644 index 0000000..f8904ad Binary files /dev/null and b/gfxlcd/demos/assets/numbers.jpg differ diff --git a/gfxlcd/demos/ili.py b/gfxlcd/demos/ili9325.py similarity index 97% rename from gfxlcd/demos/ili.py rename to gfxlcd/demos/ili9325.py index aa89fd6..23ee492 100644 --- a/gfxlcd/demos/ili.py +++ b/gfxlcd/demos/ili9325.py @@ -31,6 +31,8 @@ def draw_net(o): drv = GPIO() +drv.pins['LED'] = 6 +drv.pins['CS'] = 18 o = ILI9325(240, 320, drv) o.init() diff --git a/gfxlcd/demos/ili_2.py b/gfxlcd/demos/ili9325_2.py similarity index 94% rename from gfxlcd/demos/ili_2.py rename to gfxlcd/demos/ili9325_2.py index 4d8d91d..49957d6 100644 --- a/gfxlcd/demos/ili_2.py +++ b/gfxlcd/demos/ili9325_2.py @@ -37,14 +37,12 @@ def draw_net(o): o.draw_line(0, s, o.width-1, s) s += 10 - -lcd_tft = ILI9325(240, 320, ILIGPIO()) +drv = ILIGPIO() +drv.pins['LED'] = 6 +drv.pins['CS'] = 18 +lcd_tft = ILI9325(240, 320, drv) 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) diff --git a/gfxlcd/demos/ili9325_image.py b/gfxlcd/demos/ili9325_image.py new file mode 100644 index 0000000..7c58fc6 --- /dev/null +++ b/gfxlcd/demos/ili9325_image.py @@ -0,0 +1,22 @@ +import RPi.GPIO +import sys +from PIL import Image +sys.path.append("../../") +from gfxlcd.driver.ili9325.gpio import GPIO as ILIGPIO +from gfxlcd.driver.ili9325.ili9325 import ILI9325 +RPi.GPIO.setmode(RPi.GPIO.BCM) + + +drv = ILIGPIO() +drv.pins['LED'] = 6 +drv.pins['CS'] = 18 +lcd_tft = ILI9325(240, 320, drv) +lcd_tft.init() + + +image_file = Image.open("assets/japan_temple_240x320.jpg") +lcd_tft.draw_image(0, 0, image_file) + +numbers_image = Image.open("assets/dsp2017_101_64.png") +lcd_tft.transparency_color = (0, 0, 0) +lcd_tft.draw_image(10, 10, numbers_image) diff --git a/gfxlcd/demos/ili9486.py b/gfxlcd/demos/ili9486.py new file mode 100644 index 0000000..58a531e --- /dev/null +++ b/gfxlcd/demos/ili9486.py @@ -0,0 +1,76 @@ +import sys +sys.path.append("../../") +from gfxlcd.driver.ili9486.spi import SPI +from gfxlcd.driver.ili9486.ili9486 import ILI9486 +import RPi.GPIO +RPi.GPIO.setmode(RPi.GPIO.BCM) +import random + +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 = SPI() +o = ILI9486(320, 480, drv) + +print("init") +o.init() +# o.driver.reset() + +# exit() +o.color = (255, 255, 255) +o.background_color = (0, 0, 0) +o.fill_rect(10, 150, 205, 205) + +# exit() +# for _ in range(0, 50): +# hole(random.randint(2,o.width-3), random.randint(2,o.height-3)) + +# draw_net(o) +print("draw") +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) +print("end") +# o.driver.reset() diff --git a/gfxlcd/demos/ili9486_image.py b/gfxlcd/demos/ili9486_image.py new file mode 100644 index 0000000..5c957fd --- /dev/null +++ b/gfxlcd/demos/ili9486_image.py @@ -0,0 +1,19 @@ +import RPi.GPIO +import sys +from PIL import Image +sys.path.append("../../") +from gfxlcd.driver.ili9486.spi import SPI +from gfxlcd.driver.ili9486.ili9486 import ILI9486 +RPi.GPIO.setmode(RPi.GPIO.BCM) + + +drv = SPI() +lcd_tft = ILI9486(320, 480, drv) +lcd_tft.init() + +image_file = Image.open("assets/japan_temple_240x320.jpg") +lcd_tft.draw_image(0, 0, image_file) + +numbers_image = Image.open("assets/dsp2017_101_64.png") +lcd_tft.transparency_color = (0, 0, 0) +lcd_tft.draw_image(10, 10, numbers_image) diff --git a/gfxlcd/demos/nju_image.py b/gfxlcd/demos/nju_image.py new file mode 100644 index 0000000..fb348aa --- /dev/null +++ b/gfxlcd/demos/nju_image.py @@ -0,0 +1,20 @@ +import RPi.GPIO +import sys +import random +from PIL import Image +sys.path.append("../../") +from gfxlcd.driver.nju6450.gpio import GPIO +from gfxlcd.driver.nju6450.nju6450 import NJU6450 +RPi.GPIO.setmode(RPi.GPIO.BCM) + + +lcd_nju = NJU6450(122, 32, GPIO()) +lcd_nju.init() +lcd_nju.auto_flush = False + +image_file = Image.open("assets/dsp2017_122_29.png") +# lcd_nju.threshold = 50 +# lcd_nju.transparency_color = [110, 57] +lcd_nju.draw_image(0, 0, image_file) + +lcd_nju.flush(True) diff --git a/gfxlcd/demos/ssd_image.py b/gfxlcd/demos/ssd_image.py new file mode 100644 index 0000000..011aff0 --- /dev/null +++ b/gfxlcd/demos/ssd_image.py @@ -0,0 +1,23 @@ +import RPi.GPIO +import sys +from PIL import Image +sys.path.append("../../") +from gfxlcd.driver.ssd1306.spi import SPI +from gfxlcd.driver.ssd1306.ssd1306 import SSD1306 +RPi.GPIO.setmode(RPi.GPIO.BCM) + +lcd_oled = SSD1306(128, 64, SPI()) +lcd_oled.init() +lcd_oled.auto_flush = False + +image_file = Image.open("assets/dsp2017_101_64.png") + +lcd_oled.threshold = 50 + +lcd_oled.threshold = 0 +# lcd_oled.transparency_color = [110, 57] #110 #[110, 57] +# lcd_oled.threshold = 255 + +lcd_oled.draw_image(10, 0, image_file) + +lcd_oled.flush(True) diff --git a/gfxlcd/demos/touch.py b/gfxlcd/demos/touch.py new file mode 100644 index 0000000..a63dd56 --- /dev/null +++ b/gfxlcd/demos/touch.py @@ -0,0 +1,29 @@ +import RPi.GPIO +import sys +import time +sys.path.append("../../") +from gfxlcd.driver.ili9325.gpio import GPIO as ILIGPIO +from gfxlcd.driver.ili9325.ili9325 import ILI9325 +from gfxlcd.driver.ad7843.ad7843 import AD7843 +RPi.GPIO.setmode(RPi.GPIO.BCM) + + +lcd_tft = ILI9325(240, 320, ILIGPIO()) +lcd_tft.init() + + +def callback(position): + print('(x,y)', position) + +touch = AD7843(240, 320, 26, callback) + +touch.init() + +while True: + try: + time.sleep(1) + + except KeyboardInterrupt: + touch.close() + # RPi.GPIO.cleanup() + diff --git a/gfxlcd/demos/touch_loop.py b/gfxlcd/demos/touch_loop.py new file mode 100644 index 0000000..a0bb1de --- /dev/null +++ b/gfxlcd/demos/touch_loop.py @@ -0,0 +1,32 @@ +import RPi.GPIO +import sys +import time +sys.path.append("../../") +from gfxlcd.driver.ili9325.gpio import GPIO as ILIGPIO +from gfxlcd.driver.ili9325.ili9325 import ILI9325 +from gfxlcd.driver.ad7843.ad7843 import AD7843 +RPi.GPIO.setmode(RPi.GPIO.BCM) + + +lcd_tft = ILI9325(240, 320, ILIGPIO()) +lcd_tft.init() + + +def callback(position): + print('(x,y)', position) + +touch = AD7843(240, 320) + +touch.init() + +while True: + try: + time.sleep(0.05) + ret = touch.get_position() + if ret: + print(ret[0], ret[1]) + + except KeyboardInterrupt: + touch.close() + RPi.GPIO.cleanup() + diff --git a/gfxlcd/drawing/area.py b/gfxlcd/drawing/area.py index 8fc1d19..418a84b 100644 --- a/gfxlcd/drawing/area.py +++ b/gfxlcd/drawing/area.py @@ -18,22 +18,6 @@ class Area(Pixel): self._set_area(pos_x, pos_y, pos_x, pos_y) self.driver.data(self._converted_color(), None) - def _set_area(self, pos_x1, pos_y1, pos_x2, pos_y2): - """select area to work with""" - self.driver.cmd(0x0020, None) - self.driver.data(pos_x1, None) - self.driver.cmd(0x0021, None) - self.driver.data(pos_y1, None) - self.driver.cmd(0x0050, None) - self.driver.data(pos_x1, None) - self.driver.cmd(0x0052, None) - self.driver.data(pos_y1, None) - self.driver.cmd(0x0051, None) - self.driver.data(pos_x2, None) - self.driver.cmd(0x0053, None) - self.driver.data(pos_y2, None) - self.driver.cmd(0x0022, None) - def _draw_vertical_line(self, pos_x, pos_y, length): """draw vertical line""" self._set_area(pos_x, pos_y, pos_x, pos_y + length) @@ -121,3 +105,61 @@ class Area(Pixel): color = self._converted_background_color() for _ in range(0, size): self.driver.data(color, None) + + def draw_image(self, pos_x, pos_y, image): + """draw a PIL image""" + image_file = image.convert('RGB') + width, height = image_file.size + self._set_area( + pos_x, + pos_y, + pos_x + width - 1, + pos_y + height - 1 + ) + row = 0 + col = 0 + area = None + temporary_area = None + for red, green, blue in list(image_file.getdata()): + if self._is_transparent((red, green, blue)): + area = ( + pos_x, + pos_y + row + 1, + pos_x + width - 1, + pos_y + height - 1 + ) + temporary_area = ( + pos_x + col + 1, + pos_y + row, + pos_x + width - 1, + pos_y + row + ) + else: + if temporary_area is not None: + self._set_area(*temporary_area) + temporary_area = None + self.color = (red, green, blue) + self.driver.data(self._converted_color(), None) + + col += 1 + if col > width - 1: + col = 0 + row += 1 + if area is not None: + self._set_area(*area) + area = None + temporary_area = None + + def _is_transparent(self, color): + """check if color is a transparency color""" + if self.options['transparency_color'] is None: + return False + elif type(self.options['transparency_color'][0]) == int \ + and color == self.options['transparency_color']: + return True + elif (type(self.options['transparency_color'][0]) == list or + type(self.options['transparency_color'][0]) == tuple) \ + and color in self.options['transparency_color']: + return True + + return False diff --git a/gfxlcd/drawing/page.py b/gfxlcd/drawing/page.py index 6e6ca58..d9c899c 100644 --- a/gfxlcd/drawing/page.py +++ b/gfxlcd/drawing/page.py @@ -113,3 +113,29 @@ class Page(Pixel, metaclass=abc.ABCMeta): def flush(self, force=None): """flush buffer to the screen""" pass + + def draw_image(self, pos_x, pos_y, image): + """draw a PIL image""" + image_file = image.convert('L') + width, height = image_file.size + offset_x = 0 + offset_y = 0 + for stream in list(image_file.getdata()): + if stream > self.options['threshold'] \ + and not self._is_transparent(stream): + self.draw_pixel(pos_x + offset_x, pos_y + offset_y) + offset_x += 1 + if offset_x > width - 1: + offset_x = 0 + offset_y += 1 + + def _is_transparent(self, color): + """check if color is a transparency color""" + if type(self.options['transparency_color']) == int \ + and color == self.options['transparency_color']: + return True + elif type(self.options['transparency_color']) == list \ + and color in self.options['transparency_color']: + return True + + return False diff --git a/gfxlcd/drawing/pixel.py b/gfxlcd/drawing/pixel.py index c2c22ac..a3a7339 100644 --- a/gfxlcd/drawing/pixel.py +++ b/gfxlcd/drawing/pixel.py @@ -12,6 +12,28 @@ class Pixel(object): self.options['background_color'] = { 'R': 0, 'G': 0, 'B': 0, } + self.options['threshold'] = 50 + self.options['transparency_color'] = None + + @property + def threshold(self): + """get threshold for B&W conversion""" + return self.options['threshold'] + + @threshold.setter + def threshold(self, threshold): + """set B&W threshold for conversion """ + self.options['threshold'] = threshold + + @property + def transparency_color(self): + """get transparency color""" + return self.options['transparency_color'] + + @transparency_color.setter + def transparency_color(self, transparency_color): + """set transparency color """ + self.options['transparency_color'] = transparency_color def draw_pixel(self, pos_x, pos_y): """dummy fuction""" diff --git a/gfxlcd/driver/ad7843/__init__.py b/gfxlcd/driver/ad7843/__init__.py new file mode 100644 index 0000000..cf13795 --- /dev/null +++ b/gfxlcd/driver/ad7843/__init__.py @@ -0,0 +1,2 @@ +"""driver/ad7843 module""" +__author__ = 'Bartosz Kosciow' diff --git a/gfxlcd/driver/ad7843/ad7843.py b/gfxlcd/driver/ad7843/ad7843.py new file mode 100644 index 0000000..6e107fe --- /dev/null +++ b/gfxlcd/driver/ad7843/ad7843.py @@ -0,0 +1,89 @@ +import spidev # pylint: disable=I0011,F0401 +import RPi.GPIO + + +class AD7843(object): + """AD7843 class""" + def __init__(self, width, height, int_pin=None, callback=None, cs_pin=None, spi=0, speed=2000000): + self.width = width + self.height = height + self.spi = spidev.SpiDev() + self.spi.open(spi, 0) + self.spi.max_speed_hz = speed + self.spi.mode = 0 + self.correction = { + 'x': 364, + 'y': 430, + 'ratio_x': 14.35, + 'ratio_y': 10.59 + } + self.cs_pin = cs_pin + self.int_pin = int_pin + self.callback = callback + self.bouncetime = 500 + + def init(self): + """some init functions""" + if self.int_pin: + RPi.GPIO.setup(self.int_pin, RPi.GPIO.IN) + RPi.GPIO.add_event_detect( + self.int_pin, RPi.GPIO.BOTH, callback=self._interrupt, bouncetime=self.bouncetime + ) + if self.cs_pin: + RPi.GPIO.setup(self.cs_pin, RPi.GPIO.OUT) + RPi.GPIO.output(self.cs_pin, 1) + + def get_x(self, value): + """correct value to x""" + return self.width - int((value - self.correction['x']) / self.correction['ratio_x']) + + def get_y(self, value): + """correct value to y""" + return self.height - int((value - self.correction['y']) / self.correction['ratio_y']) + + def _interrupt(self, channel): + """call users callback""" + if self.cs_pin: + RPi.GPIO.output(self.cs_pin, 0) + self.callback(self.get_position()) + if self.cs_pin: + RPi.GPIO.output(self.cs_pin, 1) + + def get_position(self): + """get touch coords""" + buffer = [] + while len(buffer) < 20: + self.spi.xfer2([0xd0]) + recvx = self.spi.readbytes(2) + self.spi.xfer2([0x90]) + recvy = self.spi.readbytes(2) + + tc_rx = recvx[0] << 5 + tc_rx |= recvx[1] >> 3 + + tc_ry = recvy[0] << 5 + tc_ry |= recvy[1] >> 3 + + pos_x = self.get_x(tc_rx) + pos_y = self.get_y(tc_ry) + if 0 <= pos_x <= self.width and 0 <= pos_y <= self.height: + buffer.append((pos_x, pos_y)) + + return self._calculate_avr(buffer) + + def _calculate_avr(self, points): + """calculate x,y by average""" + sum_x = 0 + sum_y = 0 + for point in points: + sum_x += point[0] + sum_y += point[1] + + return int(sum_x / len(points)), int(sum_y / len(points)) + + def close(self): + """close action""" + if self.int_pin: + RPi.GPIO.remove_event_detect(self.int_pin) + self.spi.close() + diff --git a/gfxlcd/driver/ili9325/gpio.py b/gfxlcd/driver/ili9325/gpio.py index 4da591b..4d26d80 100644 --- a/gfxlcd/driver/ili9325/gpio.py +++ b/gfxlcd/driver/ili9325/gpio.py @@ -20,6 +20,8 @@ class GPIO(Driver): 'DB14': 20, 'DB15': 21, 'RST': 25, + 'LED': None, + 'CS': None } self.data_pins = [ 'DB8', 'DB9', 'DB10', 'DB11', 'DB12', 'DB13', 'DB14', 'DB15', @@ -28,11 +30,22 @@ class GPIO(Driver): 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) + if self.pins[pin] is not None: + RPi.GPIO.setup(self.pins[pin], RPi.GPIO.OUT) + RPi.GPIO.output(self.pins[pin], 0) + + if self.pins['LED']: + RPi.GPIO.output(self.pins['LED'], 1) + + if self.pins['CS']: + RPi.GPIO.output(self.pins['CS'], 1) def reset(self): """reset a display""" + if self.pins['LED']: + RPi.GPIO.output(self.pins['LED'], 1) + if self.pins['CS']: + RPi.GPIO.output(self.pins['CS'], 1) RPi.GPIO.output(self.pins['RST'], 1) time.sleep(0.005) RPi.GPIO.output(self.pins['RST'], 0) @@ -54,12 +67,16 @@ class GPIO(Driver): def send(self, char, enable): """send 16bit as 2*8bit""" + if self.pins['CS']: + RPi.GPIO.output(self.pins['CS'], 0) 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) + if self.pins['CS']: + RPi.GPIO.output(self.pins['CS'], 1) def data(self, data, enable): """send data to display""" diff --git a/gfxlcd/driver/ili9325/ili9325.py b/gfxlcd/driver/ili9325/ili9325.py index b260f01..27562b5 100644 --- a/gfxlcd/driver/ili9325/ili9325.py +++ b/gfxlcd/driver/ili9325/ili9325.py @@ -6,9 +6,33 @@ from gfxlcd.abstract.chip import Chip class ILI9325(Area, Chip): """Class for ILI9325 based LCD""" + rotations = { + 0: { + 'output': 0x0100, + 'mode': 0x1030, + 'output2': 0xa700 + }, + 90: { + 'output': 0x0000, + 'mode': 0x1038, + 'output2': 0xa700 + }, + 180: { + 'output': 0x0000, + 'mode': 0x1030, + 'output2': 0x2700 + }, + 270: { + 'output': 0x0100, + 'mode': 0x1038, + 'output2': 0x2700 + } + } + def __init__(self, width, height, driver): Chip.__init__(self, width, height, driver, True) Area.__init__(self, driver) + self.rotation = 0 def _converted_background_color(self): """color from 8-8-8 to 5-6-5""" @@ -36,13 +60,16 @@ class ILI9325(Area, Chip): # ************* ILI9325C/D ********** # set SS and SM bit self.driver.cmd(0x0001, None) - self.driver.data(0x0100, None) + self.driver.data(self.rotations[self.rotation]['output'], 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) + self.driver.data(self.rotations[self.rotation]['mode'], None) + # Resize register self.driver.cmd(0x0004, None) self.driver.data(0x0000, None) @@ -145,7 +172,8 @@ class ILI9325(Area, Chip): self.driver.data(0x013F, None) # Gate Scan Line self.driver.cmd(0x0060, None) - self.driver.data(0xA700, None) + self.driver.data(self.rotations[self.rotation]['output2'], None) + # NDL, VLE, REV self.driver.cmd(0x0061, None) self.driver.data(0x0001, None) @@ -175,3 +203,21 @@ class ILI9325(Area, Chip): # 262K color and display ON self.driver.cmd(0x0007, None) self.driver.data(0x0133, None) + + def _set_area(self, pos_x1, pos_y1, pos_x2, pos_y2): + """select area to work with""" + if self.rotation == 90 or self.rotation == 270: + pos_x1, pos_y1, pos_x2, pos_y2 = pos_y1, pos_x1, pos_y2, pos_x2 + self.driver.cmd(0x0020, None) + self.driver.data(pos_x1, None) + self.driver.cmd(0x0021, None) + self.driver.data(pos_y1, None) + self.driver.cmd(0x0050, None) + self.driver.data(pos_x1, None) + self.driver.cmd(0x0052, None) + self.driver.data(pos_y1, None) + self.driver.cmd(0x0051, None) + self.driver.data(pos_x2, None) + self.driver.cmd(0x0053, None) + self.driver.data(pos_y2, None) + self.driver.cmd(0x0022, None) diff --git a/gfxlcd/driver/ili9486/__init__.py b/gfxlcd/driver/ili9486/__init__.py new file mode 100644 index 0000000..7a53ea9 --- /dev/null +++ b/gfxlcd/driver/ili9486/__init__.py @@ -0,0 +1,2 @@ +"""driver/ili9486 module""" +__author__ = 'Bartosz Kosciow' diff --git a/gfxlcd/driver/ili9486/ili9486.py b/gfxlcd/driver/ili9486/ili9486.py new file mode 100644 index 0000000..58ed1c3 --- /dev/null +++ b/gfxlcd/driver/ili9486/ili9486.py @@ -0,0 +1,126 @@ +"""ILI9486 chip driver""" +import time +from gfxlcd.drawing.area import Area +from gfxlcd.abstract.chip import Chip + + +class ILI9486(Area, Chip): + """Class for ILI9486 based LCD""" + rotations = {0: 0x80, 90: 0xf0, 180: 0x40, 270: 0x20} + + def __init__(self, width, height, driver): + Chip.__init__(self, width, height, driver, True) + Area.__init__(self, driver) + self.rotation = 0 + + 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() + + self.driver.cmd(0x0b, None) + self.driver.data(0x00, None) + self.driver.data(0x00, None) + + self.driver.cmd(0x11, None) + + self.driver.cmd(0x3a, None) + self.driver.data(0x55, None) #0x66 + + self.driver.cmd(0x36, None) + self.driver.data(self.rotations[self.rotation], None) + + self.driver.cmd(0xc2, None) + self.driver.data(0x44, None) + + self.driver.cmd(0xc5, None) + self.driver.data(0x00, None) + self.driver.data(0x00, None) + self.driver.data(0x00, None) + self.driver.data(0x00, None) + + self.driver.cmd(0xe0, None) + self.driver.data(0x0F, None) + self.driver.data(0x1F, None) + self.driver.data(0x1C, None) + self.driver.data(0x0C, None) + self.driver.data(0x0F, None) + self.driver.data(0x08, None) + self.driver.data(0x48, None) + self.driver.data(0x98, None) + self.driver.data(0x37, None) + self.driver.data(0x0A, None) + self.driver.data(0x13, None) + self.driver.data(0x04, None) + self.driver.data(0x11, None) + self.driver.data(0x0D, None) + self.driver.data(0x00, None) + + self.driver.cmd(0xe1, None) + self.driver.data(0x0F, None) + self.driver.data(0x32, None) + self.driver.data(0x2E, None) + self.driver.data(0x0B, None) + self.driver.data(0x0D, None) + self.driver.data(0x05, None) + self.driver.data(0x47, None) + self.driver.data(0x75, None) + self.driver.data(0x37, None) + self.driver.data(0x06, None) + self.driver.data(0x10, None) + self.driver.data(0x03, None) + self.driver.data(0x24, None) + self.driver.data(0x20, None) + self.driver.data(0x00, None) + + self.driver.cmd(0xe2, None) + self.driver.data(0x0F, None) + self.driver.data(0x32, None) + self.driver.data(0x2E, None) + self.driver.data(0x0B, None) + self.driver.data(0x0D, None) + self.driver.data(0x05, None) + self.driver.data(0x47, None) + self.driver.data(0x75, None) + self.driver.data(0x37, None) + self.driver.data(0x06, None) + self.driver.data(0x10, None) + self.driver.data(0x03, None) + self.driver.data(0x24, None) + self.driver.data(0x20, None) + self.driver.data(0x00, None) + + self.driver.cmd(0x11, None) + self.driver.cmd(0x29, None) + + def _set_area(self, pos_x1, pos_y1, pos_x2, pos_y2): + """select area to work with""" + self.driver.cmd(0x2a, None) + self.driver.data(pos_x1 >> 8, None) + self.driver.data(pos_x1 & 0xff, None) + self.driver.data(pos_x2 >> 8, None) + self.driver.data(pos_x2 & 0xff, None) + self.driver.cmd(0x2b, None) + self.driver.data(pos_y1 >> 8, None) + self.driver.data(pos_y1 & 0xff, None) + self.driver.data(pos_y2 >> 8, None) + self.driver.data(pos_y2 & 0xff, None) + self.driver.cmd(0x2c, None) diff --git a/gfxlcd/driver/ili9486/spi.py b/gfxlcd/driver/ili9486/spi.py new file mode 100644 index 0000000..fab722c --- /dev/null +++ b/gfxlcd/driver/ili9486/spi.py @@ -0,0 +1,65 @@ +"""SPI communication driver""" +import time +import spidev +import RPi.GPIO # pylint: disable=I0011,F0401 +from gfxlcd.abstract.driver import Driver +RPi.GPIO.setmode(RPi.GPIO.BCM) + + +class SPI(Driver): + """SPI communication driver""" + def __init__(self, spi=0, speed=2000000): + self.pins = { + 'CS': 8, + 'RST': 25, + 'RS': 24, + 'LED': None + } + self.spi = spidev.SpiDev() + self.spi.open(spi, 0) + self.spi.max_speed_hz = speed + self.spi.mode = 0 + + def init(self): + """initialize pins""" + for pin in self.pins: + if self.pins[pin] is not None: + RPi.GPIO.setup(self.pins[pin], RPi.GPIO.OUT) + RPi.GPIO.output(self.pins[pin], 0) + + if self.pins['CS']: + RPi.GPIO.output(self.pins['CS'], 1) + + if self.pins['LED']: + RPi.GPIO.output(self.pins['LED'], 1) + + def reset(self): + """reset a display""" + if self.pins['LED']: + RPi.GPIO.output(self.pins['LED'], 1) + if self.pins['CS']: + RPi.GPIO.output(self.pins['CS'], 1) + 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 cmd(self, data, enable): + """send command to display""" + RPi.GPIO.output(self.pins['RS'], 0) + if self.pins['CS']: + RPi.GPIO.output(self.pins['CS'], 0) + self.spi.xfer2([data]) + if self.pins['CS']: + RPi.GPIO.output(self.pins['CS'], 1) + + def data(self, data, enable): + """send data to display""" + RPi.GPIO.output(self.pins['RS'], 1) + if self.pins['CS']: + RPi.GPIO.output(self.pins['CS'], 0) + self.spi.xfer2([data]) + if self.pins['CS']: + RPi.GPIO.output(self.pins['CS'], 1) diff --git a/gfxlcd/driver/nju6450/nju6450.py b/gfxlcd/driver/nju6450/nju6450.py index 200ed15..a99c77e 100644 --- a/gfxlcd/driver/nju6450/nju6450.py +++ b/gfxlcd/driver/nju6450/nju6450.py @@ -8,6 +8,7 @@ class NJU6450(Page, Chip): def __init__(self, width, height, driver, auto_flush=True): Chip.__init__(self, width, height, driver, auto_flush) Page.__init__(self, driver) + self.rotation = 0 def init(self): """initialize display""" diff --git a/gfxlcd/driver/ssd1306/__init__.py b/gfxlcd/driver/ssd1306/__init__.py index 31c4620..a9428b5 100644 --- a/gfxlcd/driver/ssd1306/__init__.py +++ b/gfxlcd/driver/ssd1306/__init__.py @@ -1,2 +1,2 @@ """driver/ssd1306 module""" -__author__ = 'Bartosz Kosciow' \ No newline at end of file +__author__ = 'Bartosz Kosciow' diff --git a/gfxlcd/driver/ssd1306/ssd1306.py b/gfxlcd/driver/ssd1306/ssd1306.py index 4b595a1..dfe6078 100644 --- a/gfxlcd/driver/ssd1306/ssd1306.py +++ b/gfxlcd/driver/ssd1306/ssd1306.py @@ -8,6 +8,7 @@ class SSD1306(Page, Chip): def __init__(self, width, height, driver, auto_flush=True): Chip.__init__(self, width, height, driver, auto_flush) Page.__init__(self, driver) + self.rotation = 0 def init(self): """inits a device""" diff --git a/readme.md b/readme.md index f260d85..18c87b4 100644 --- a/readme.md +++ b/readme.md @@ -9,12 +9,18 @@ Supported: - ssd1306 via SPI - nju6450 via GPIO +And for touch panels: + +- ad7843 via SPI, uses irq or not + + On NJU and SSD uses buffer to keep current content as help for page operations. Wiring is below +Demos are in demos directory -Initialization +LCD initialization === ## SSD1306 ### SPI @@ -92,6 +98,8 @@ Custom pins: 'DB14': 20, 'DB15': 21, 'RST': 25, + 'LED': None, + 'CS': None } o = ILI9325(240, 320, drv) o.init() @@ -111,13 +119,60 @@ draw_arc(x1, y1, radius, from_angle, to_angle fill_rect(x1, y1, x2, y2) +draw_image(x, y, PIL.Image) Colours === lcd.color = (r, g, b) + lcd.background_color = (r, g ,b) +lcd.threshold = 255 - for images a threshold between black and white (on monochrome) +lcd.transparency_color = [110, 57] #110 - color(s) that are skipped during drawing an image + + +Touch panels +=== + +## AD7843 + +Constructor: + + AD7843(width, height, (T_INT), (callback)) + +Can be used with T_INT + + def callback(position): + print('(x,y)', position) + + touch = AD7843(240, 320, 26, callback) + touch.init() + +or without: + + touch = AD7843(240, 320) + touch.init() + + while True: + try: + time.sleep(0.05) + ret = touch.get_position() + if ret: + print(ret[0], ret[1]) + + except KeyboardInterrupt: + touch.close() + +There is no automatic calibration. It must be done manually. + + self.correction = { + 'x': 364, + 'y': 430, + 'ratio_x': 14.35, + 'ratio_y': 10.59 + } + Wiring === @@ -178,6 +233,6 @@ Default: DB13 ------------------------ G16 DB14 ------------------------ G20 DB15 ------------------------ G21 - CS ------------------------ GND (always selected) + CS ------------------------ GND (always selected) (or connect to GPIO pin) REST ------------------------ G25 - LED_A ------------------------ 3.3 \ No newline at end of file + LED_A ------------------------ 3.3 (can be connected to GPIO pin) diff --git a/setup.py b/setup.py index d2016fa..57f673b 100644 --- a/setup.py +++ b/setup.py @@ -13,11 +13,11 @@ def read(*paths): 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'], + version='0.2.1', + description='gfxlcd is a handler for graphical lcds: ILI9328, SSD1306, NJU6450, touch panel: AD7843 @ Raspberry Pi.', + keywords=['gfxlcd', 'raspberry pi' ,'ili9328' ,'ssd1306', 'nju6450', 'lcd', 'graphical lcd', 'touch panel', 'ad7843'], long_description=(read('readme.md')), - url='https://bitbucket.org/kosci/charlcd.git', + url='https://github.com/bkosciow/gfxlcd', license='MIT', author='Bartosz Kościów', author_email='kosci1@gmail.com', diff --git a/tox.ini b/tox.ini index 41710dd..0ad5206 100644 --- a/tox.ini +++ b/tox.ini @@ -16,5 +16,5 @@ commands= nosetests --with-xunit --xunit-file=junit-{envname}.xml gfxlcd/tests /bin/bash -c "pylint gfxlcd > pylint-{envname}.log || :" [flake8] show-source = True -exclude = .git,.venv,.tox,dist,doc,build,*egg,*/tests/* +exclude = .git,.venv,.tox,dist,doc,build,*egg,*/tests/*,*/demos/*