From a7d74951881fc44b714de1cd61ef5babfeacb1c5 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Thu, 3 Feb 2022 14:35:48 +0000 Subject: [PATCH 1/4] Support & demo interrupt handlers for buttons --- examples/pygame-button-interrupt.py | 97 +++++++++++++++++++++++++++++ library/displayhatmini/__init__.py | 4 ++ 2 files changed, 101 insertions(+) create mode 100755 examples/pygame-button-interrupt.py diff --git a/examples/pygame-button-interrupt.py b/examples/pygame-button-interrupt.py new file mode 100755 index 0000000..8b5d528 --- /dev/null +++ b/examples/pygame-button-interrupt.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +import os +import sys +import signal +import pygame +import time +import math +from threading import Lock + +from displayhatmini import DisplayHATMini, GPIO + +print("""Display HAT Mini: Basic Pygame Demo""") + + +def _exit(sig, frame): + global running + running = False + print("\nExiting!...\n") + + +def update_display(): + display_hat.st7789.set_window() + # Grab the pygame screen as a bytes object + pixelbytes = pygame.transform.rotate(screen, 180).convert(16, 0).get_buffer() + # Lazy (slow) byteswap: + pixelbytes = bytearray(pixelbytes) + pixelbytes[0::2], pixelbytes[1::2] = pixelbytes[1::2], pixelbytes[0::2] + # Bypass the ST7789 PIL image RGB888->RGB565 conversion + for i in range(0, len(pixelbytes), 4096): + display_hat.st7789.data(pixelbytes[i:i + 4096]) + + +display_hat = DisplayHATMini(None) +event_lock = Lock() + +os.putenv('SDL_VIDEODRIVER', 'dummy') +pygame.display.init() # Need to init for .convert() to work +screen = pygame.Surface((display_hat.WIDTH, display_hat.HEIGHT)) + +signal.signal(signal.SIGINT, _exit) + + +# Plumbing to convert Display HAT Mini button presses into pygame events +def button_callback(pin): + key = { + display_hat.BUTTON_A: 'a', + display_hat.BUTTON_B: 'b', + display_hat.BUTTON_X: 'x', + display_hat.BUTTON_Y: 'y' + }[pin] + event = pygame.KEYDOWN if display_hat.read_button(pin) else pygame.KEYUP + pygame.event.post(pygame.event.Event(event, unicode=key, key=pygame.key.key_code(key))) + + +display_hat.on_button_pressed(button_callback) + +running = True + +while running: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + break + if event.type == pygame.KEYDOWN: + if event.key in (pygame.K_a, pygame.K_ESCAPE): + running = False + break + + # Clear the screen + screen.fill((0, 0, 0)) + + box_w = display_hat.WIDTH // 3 + box_h = display_hat.HEIGHT // 2 + + pygame.draw.rect(screen, (255, 0, 0), (0, 0, box_w, box_h)) + pygame.draw.rect(screen, (0, 255, 0), (box_w, 0, box_w, box_h)) + pygame.draw.rect(screen, (0, 0, 255), (box_w * 2, 0, box_w, box_h)) + + pygame.draw.rect(screen, (255, 255, 0), (0, box_h, box_w, box_h)) + pygame.draw.rect(screen, (255, 0, 255), (box_w, box_h, box_w, box_h)) + pygame.draw.rect(screen, (0, 255, 255), (box_w * 2, box_h, box_w, box_h)) + + r = 50 + x = math.sin(time.time() * 2) * (display_hat.WIDTH - r) / 2 + y = math.cos(time.time()) * (display_hat.HEIGHT - r) / 2 + x += display_hat.WIDTH // 2 + y += display_hat.HEIGHT // 2 + pygame.draw.circle(screen, (0, 0, 0), (int(x), int(y)), r) + + update_display() + + +screen.fill((0, 0, 0)) +update_display() + +pygame.quit() +sys.exit(0) \ No newline at end of file diff --git a/library/displayhatmini/__init__.py b/library/displayhatmini/__init__.py index c3113e1..98e5042 100644 --- a/library/displayhatmini/__init__.py +++ b/library/displayhatmini/__init__.py @@ -81,6 +81,10 @@ def set_led(self, r=0, g=0, b=0): self.led_g_pwm.ChangeDutyCycle((1.0 - g) * 100) self.led_b_pwm.ChangeDutyCycle((1.0 - b) * 100) + def on_button_pressed(self, callback): + for pin in (self.BUTTON_A, self.BUTTON_B, self.BUTTON_X, self.BUTTON_Y): + GPIO.add_event_detect(pin, GPIO.BOTH, callback=callback) + def read_button(self, pin): return not GPIO.input(pin) From 4e03f1b3cd900f14e41b136c578bdcf275d62e1a Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Thu, 3 Feb 2022 14:58:15 +0000 Subject: [PATCH 2/4] Add minimum pygame version check for #9 --- examples/pygame-basic.py | 4 ++++ examples/pygame-button-interrupt.py | 4 ++++ examples/pygame-demo.py | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/examples/pygame-basic.py b/examples/pygame-basic.py index ba25393..1a7775c 100755 --- a/examples/pygame-basic.py +++ b/examples/pygame-basic.py @@ -10,6 +10,10 @@ print("""Display HAT Mini: Basic Pygame Demo""") +if pygame.vernum < (2, 0, 0): + print("Need PyGame >= 2.0.0:\n python3 -m pip install pygame --upgrade") + sys.exit(1) + def _exit(sig, frame): global running diff --git a/examples/pygame-button-interrupt.py b/examples/pygame-button-interrupt.py index 8b5d528..7d1327f 100755 --- a/examples/pygame-button-interrupt.py +++ b/examples/pygame-button-interrupt.py @@ -11,6 +11,10 @@ print("""Display HAT Mini: Basic Pygame Demo""") +if pygame.vernum < (2, 0, 0): + print("Need PyGame >= 2.0.0:\n python3 -m pip install pygame --upgrade") + sys.exit(1) + def _exit(sig, frame): global running diff --git a/examples/pygame-demo.py b/examples/pygame-demo.py index 608e540..0924e9b 100755 --- a/examples/pygame-demo.py +++ b/examples/pygame-demo.py @@ -12,6 +12,10 @@ print("""Display HAT Mini: Pygame Demo""") +if pygame.vernum < (2, 0, 0): + print("Need PyGame >= 2.0.0:\n python3 -m pip install pygame --upgrade") + sys.exit(1) + hue_to_rgb = [] From 9849c0388d2adb35a9d2e374ef04f10a7ef58e4d Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Thu, 3 Feb 2022 15:18:58 +0000 Subject: [PATCH 3/4] Drop unused import --- examples/pygame-button-interrupt.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/pygame-button-interrupt.py b/examples/pygame-button-interrupt.py index 7d1327f..e4c6e09 100755 --- a/examples/pygame-button-interrupt.py +++ b/examples/pygame-button-interrupt.py @@ -7,7 +7,8 @@ import math from threading import Lock -from displayhatmini import DisplayHATMini, GPIO +from displayhatmini import DisplayHATMini + print("""Display HAT Mini: Basic Pygame Demo""") From 1feadd74b8824d4c4bd43f4d7607f94269690587 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Thu, 3 Feb 2022 15:26:39 +0000 Subject: [PATCH 4/4] Add PWM backlight support & demo for #4 This doesn't work so well on a Pi Zero 2, with intermittent flickers being much more obvious in the backlight than the RGB LED... but it's at least *something*. --- examples/pwm-backlight.py | 52 ++++++++++++++++++++++++++++++ library/displayhatmini/__init__.py | 17 ++++++++-- 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100755 examples/pwm-backlight.py diff --git a/examples/pwm-backlight.py b/examples/pwm-backlight.py new file mode 100755 index 0000000..1eb2f97 --- /dev/null +++ b/examples/pwm-backlight.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +import time +from displayhatmini import DisplayHATMini + +try: + from PIL import Image, ImageDraw, ImageFont +except ImportError: + print("""This example requires PIL/Pillow, try: + +sudo apt install python3-pil + +""") + +width = DisplayHATMini.WIDTH +height = DisplayHATMini.HEIGHT +buffer = Image.new("RGB", (width, height)) +draw = ImageDraw.Draw(buffer) +font = ImageFont.load_default() + +displayhatmini = DisplayHATMini(buffer, backlight_pwm=True) +displayhatmini.set_led(0.05, 0.05, 0.05) + +brightness = 1.0 + + +# Plumbing to convert Display HAT Mini button presses into pygame events +def button_callback(pin): + global brightness + + # Only handle presses + if not displayhatmini.read_button(pin): + return + + if pin == displayhatmini.BUTTON_A: + brightness += 0.1 + brightness = min(1, brightness) + + if pin == displayhatmini.BUTTON_B: + brightness -= 0.1 + brightness = max(0, brightness) + + +displayhatmini.on_button_pressed(button_callback) + +draw.rectangle((0, 0, width, height), (255, 255, 255)) +draw.text((10, 70), "Backlight Up", font=font, fill=(0, 0, 0)) +draw.text((10, 160), "Backlight Down", font=font, fill=(0, 0, 0)) + +while True: + displayhatmini.display() + displayhatmini.set_backlight(brightness) + time.sleep(1.0 / 30) diff --git a/library/displayhatmini/__init__.py b/library/displayhatmini/__init__.py index 98e5042..ce4113e 100644 --- a/library/displayhatmini/__init__.py +++ b/library/displayhatmini/__init__.py @@ -27,7 +27,7 @@ class DisplayHATMini(): WIDTH = 320 HEIGHT = 240 - def __init__(self, buffer): + def __init__(self, buffer, backlight_pwm=False): """Initialise displayhatmini """ @@ -55,11 +55,18 @@ def __init__(self, buffer): self.led_b_pwm = GPIO.PWM(self.LED_B, 2000) self.led_b_pwm.start(100) + if backlight_pwm: + GPIO.setup(self.BACKLIGHT, GPIO.OUT) + self.backlight_pwm = GPIO.PWM(self.BACKLIGHT, 500) + self.backlight_pwm.start(100) + else: + self.backlight_pwm = None + self.st7789 = ST7789( port=self.SPI_PORT, cs=self.SPI_CS, dc=self.SPI_DC, - backlight=self.BACKLIGHT, + backlight=None if backlight_pwm else self.BACKLIGHT, width=self.WIDTH, height=self.HEIGHT, rotation=180, @@ -81,6 +88,12 @@ def set_led(self, r=0, g=0, b=0): self.led_g_pwm.ChangeDutyCycle((1.0 - g) * 100) self.led_b_pwm.ChangeDutyCycle((1.0 - b) * 100) + def set_backlight(self, value): + if self.backlight_pwm is not None: + self.backlight_pwm.ChangeDutyCycle(value * 100) + else: + self.st7789.set_backlight(int(value)) + def on_button_pressed(self, callback): for pin in (self.BUTTON_A, self.BUTTON_B, self.BUTTON_X, self.BUTTON_Y): GPIO.add_event_detect(pin, GPIO.BOTH, callback=callback)