Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pico Display Pack V2 x 2? #299

Closed
alphanumeric007 opened this issue Mar 16, 2022 · 17 comments
Closed

Pico Display Pack V2 x 2? #299

alphanumeric007 opened this issue Mar 16, 2022 · 17 comments

Comments

@alphanumeric007
Copy link

alphanumeric007 commented Mar 16, 2022

I have a need to run two Pico Display Packs side by side, portrait mode if possible. I get that this is a very unusual request, and that its not designed to be used this way. They will be mounted to proto boards so each one can use a different chip select and backlight pin. I'll just mirror what pins are used on the Pico Breakout Garden SPI slots. And ignore the button and LED connections.
The stock Pimoroni Micro Python Display Pack driver / module doesn't support alternate pin select so I can't use it. The BreakoutColourLCD240x240 Micro python module does, and does work with the Display Pack, with one exception. It limits me too 240 x 240 pixels. It's already in portraited mode because of the different screen orientation of the display pack but I can't draw to the lower 1/4 of the screen. Even if I specify a height=320 and adjust the display buffer to match.
I currently have a 1.5' 240x240 LCD Breakout and Display Pack v2 running side by side, so running two displays is possible.
What I need is **BreakoutColorLCD240x320 module. I don't have the skills to write my own of know where to even begin to modify the current 240x240 module. I may be asking too much, but I figure it can't hurt to at least ask.
@Gadgetoid

from breakout_colourlcd240x240 import BreakoutColourLCD240x240
#import picodisplay2 as display2
display_buffer = bytearray(320 * 240 * 2)  # 2-bytes per pixel (RGB565)
display1 = BreakoutColourLCD240x240(display_buffer, cs=(22), bl=(21))
display2 = BreakoutColourLCD240x240(display_buffer, cs=(17), bl=(20))
#display2.init(display_buffer)
@alphanumeric007
Copy link
Author

It was worth a try, but in hindsight, a bit of an unreasonable request. Do it for one and you'd have to do it for others.
I'm pretty sure its all doable with a Raspberry Pi instead of a Pi Pico so I'll likely go that route. I was trying to avoid having to run an OS in the background but I just can't get there from here using a Pico.

@Gadgetoid
Copy link
Member

It's something I've wanted to fix for a while, since there are some real warts with the module (singleton) driver approach that Display Pack uses. But it's also a lot of effort for very little payoff, so knowing it would also help in cases like this (rare, but relevant) gives me some motivation to prioritise it!

@alphanumeric007
Copy link
Author

An "generic" ST7789 module that wasn't locked into a preset width and height would work for me, and likely others.
The 1.5' LCD Breakout Micro Python driver works with the Display Pack, its just restricted to 240x240. And I can use it to setup two display packs. I was able to use my 1.5' LCD with my Display Pack with that one module. Same way you'd do it on a Breakout Garden, but mine is on a solderless breadboard. I thought I was good to go then noticed the black bottom 1/4 of the display pack.
If you do anything, I think a rotate function would come in very handy for displays that are rectangular.

@Gadgetoid
Copy link
Member

Gadgetoid commented Mar 21, 2022

The ST7789 driver that underpins Pico Display probably does what you want, but is missing the glue that PicoDisplay has to stick it to PicoGraphics (for all the drawing functions). And, of course, the Python bindings 😬

The ST7789 driver could definitely stand to be a little more resolution agnostic, though, it's got a lot of special cases for the different displays we support to avoid having multiple arguments for display region and offsets.

I suspect it would be a lot nicer to initialise an "ST7789" class instance in MicroPython and supply a width, height and rotation instead of the current ugliness of supplying a properly sized bytearray(). It would also combine all of the display product libraries into a single library so we don't have duplicated functionality all over the place (getting progressively worse with every new display product 😬)

I guess TODO is:

  1. Bind ST7789 library + PicoGraphics to MicroPython
  2. Add support for various display orientations to ST7789
  3. Migrate PicoDisplay, and PicoDisplay 2" and all other ST7789-based devices to the generic ST7789
  4. Add shim modules (written in Python and baked into the firmware) to fake the old PicoDisplay and PicoDisplay2 module functionality
  5. Update the examples and mark The Old Way as deprecated

Phase 1 tests:

  1. Pico Display
  2. Pico Display 2.0
  3. Pico Explorer

This would change:

import picodisplay as display

width = display.get_width()
height = display.get_height()

display_buffer = bytearray(width * height * 2)
display.init(display_buffer)

Into something a little more like:

import st7789

width = 320
height = 240

display = st7789.ST7789(width, height, BG_SPI_FRONT)

@Gadgetoid Gadgetoid reopened this Mar 21, 2022
@alphanumeric007
Copy link
Author

That's close to what its like now in Python. Which was why I was headed that way. I could (I think?) use the Pi Mini Display code on the Pico Display Pack, with some editing. And not have to buy new displays.

# Buttons
BUTTON_A = 5
BUTTON_B = 6
BUTTON_X = 16
BUTTON_Y = 24

# Onboard RGB LED
LED_R = 17
LED_G = 27
LED_B = 22

# General
SPI_PORT = 0
SPI_CS = 1
SPI_DC = 9
BACKLIGHT = 13

# Screen dimensions
WIDTH = 320
HEIGHT = 240

display = ST7789(
    port=SPI_PORT,
    cs=SPI_CS,
    dc=SPI_DC,
    backlight=BACKLIGHT,
    width=WIDTH,
    height=HEIGHT,
    rotation=180,
    spi_speed_hz=60 * 1000 * 1000
)

Or the ST7789 breakout driver.

 disp = ST7789.ST7789(
        height=135 if display_type == "rect" else 240,
        rotation=0 if display_type == "rect" else 90,
        port=0,
        cs=ST7789.BG_SPI_CS_FRONT,  # BG_SPI_CS_BACK or BG_SPI_CS_FRONT
        dc=9,
        backlight=19,               # 18 for back BG slot, 19 for front BG slot.
        spi_speed_hz=80 * 1000 * 1000,
        offset_left=0 if display_type == "square" else 40,
        offset_top=53 if display_type == "rect" else 0

@Gadgetoid
Copy link
Member

Welp all my Pico Display boards seem to be completely unresponsive for reasons I can't fathom. Very bizarre!

I have created a very, very rough merged ST7789 driver.

Builds should be available here shortly: https://github.com/pimoroni/pimoroni-pico/actions/runs/2017564653

It's missing a lot of features, but it may... work... and current looks something like this:

import st7789
import time

display = st7789.ST7789(width=240, height=320, slot=0)

print(display)

p = display.create_pen(255, 255, 0)

display.set_backlight(1.0)
display.set_pen(p)

while True:
    display.clear()
    display.update()
    time.sleep(0.5)

This should be able to drive the 240x240 round and square colour LCDs, Pico Display and Pico Display 2, though support for round/square differentiation doesn't exist yet.

I'd kinda like to fold ST7735 support into this somehow, for a combined, driver-agnostic "display" library, but that's a future source of pain.

@alphanumeric007
Copy link
Author

alphanumeric007 commented Mar 21, 2022

The following gets me a yellow background on my display pack V2

import st7789
import time

display1 = st7789.ST7789(width=240, height=320, slot=0)

print(display1)

p1 = display1.create_pen(255, 255, 0)

display1.set_backlight(1.0)
display1.set_pen(p1)

while True:
    display1.clear()
    display1.update()

    time.sleep(0.5)
'''
ST7789(spi = 0, cs = 17, dc = 16, sck = 18, mosi = 19, bl = 20)

And the following gets me a Blue background on my 240x240 LCD Breakout. 

import st7789
import time

display2 = st7789.ST7789(width=240, height=240, slot=1)

print(display2)

p2 = display2.create_pen(0, 0, 255)

display2.set_backlight(1.0)
display2.set_pen(p2)

while True:

display2.clear()
display2.update()
time.sleep(0.5)
ST7789(spi = 0, cs = 22, dc = 16, sck = 18, mosi = 19, bl = 21)

Trying to run them both in the one file gets me a MemoryError: memory allocation failed, allocating 115200 bytes with the display2 = st7789.ST7789(width=240, height=240, slot=1) line.

I had gotten around that by doing all my display text etc for display 1 only (fill buffer)
update display1 (copy buffer to display 1)
clear display1 (empty buffer)
do all my display text ext for display 2 only. (fill buffer)
update display2 (copy buffer to display2)
clear display 2 (empty buffer)
This let me use one buffer for both displays.

@alphanumeric007
Copy link
Author

This is my current working file using the latest official UF2 file.
Wall of text to follow.

'''
This file lets you make a little indoor weather station.
Pi Pico
Large solderless breadboard
1.5" SPI Colour Square LCD (240x240) Breakout
Pico Display Pack V2 (320x240) Colour LCD
BME280 - Temperature, Pressure, Humidity Sensor Breakout
BME688 - Temperature, Pressure, Humidity Sensor Breakout
LTR-559 - Light Sensor Breakout
RV3028 RTC Breakout
'''
import time
import picodisplay2 as display2
from machine import ADC, Pin
from pimoroni_i2c import PimoroniI2C
from breakout_colourlcd240x240 import BreakoutColourLCD240x240
from breakout_bme280 import BreakoutBME280
from breakout_bme68x import BreakoutBME68X
from breakout_ltr559 import BreakoutLTR559
from breakout_rtc import BreakoutRTC

vsys = ADC(29)              # reads the system input voltage
charging = Pin(24, Pin.IN)  # reading GP24 tells us whether or not USB power is connected
conversion_factor = 3 * 3.3 / 65535

full_battery = 4.2                  # these are our reference voltages for a full/empty battery, in volts
empty_battery = 2.8                 # the values could vary by battery size/manufacturer so you might need to adjust them

display_buffer = bytearray(320 * 240 * 2)  # 2-bytes per pixel (RGB565)
display1 = BreakoutColourLCD240x240(display_buffer, cs=(22), bl=(21))
display2.init(display_buffer)
display1.set_backlight(1.0)
display2.set_backlight(1.0)

PINS_BREAKOUT_GARDEN = {"sda": 4, "scl": 5}
PINS_PICO_EXPLORER = {"sda": 20, "scl": 21}
i2c = PimoroniI2C(**PINS_BREAKOUT_GARDEN)

bme_out = BreakoutBME68X(i2c)
bme_in = BreakoutBME280(i2c,0x77)

ltr = BreakoutLTR559(i2c)

rtc = BreakoutRTC(i2c)
rtc.set_backup_switchover_mode(1)
rtc.set_24_hour()
rtc.update_time()

min_temp_in = None
max_temp_in = None
min_temp_out = None
max_temp_out = None
min_press_out = None
max_press_out = None

start_time = time.time()

rtc.enable_periodic_update_interrupt(True)

while True:
    
    if rtc.read_periodic_update_interrupt_flag():
        rtc.clear_periodic_update_interrupt_flag()

        if rtc.update_time():
            rtc_date = rtc.string_date()
            rtc_time = rtc.string_time()
    
    time_elapsed = time.time() - start_time
        
    # read the sensors
    temp_in, press_in, hum_in = bme_in.read()
    temp_out, press_out, hum_out, gas_resistance, status, gas_index, meas_index = bme_out.read() 
            
    # convert pressure to mb
    pressuremb = press_out / 100
    
    # header
    display1.set_pen(200, 200, 200)
    display1.text("in", 30, 0, 240, 3)
    display1.text("out", 170, 0, 240, 3)
    display1.text("min", 95, 78, 240, 3)
    display1.text("max", 92, 100, 240, 3)
       
    # indoor temperature
    temp_in = round(temp_in)
    
    if temp_in < 0:
        display1.set_pen(255, 255, 255)
    elif 0 <= temp_in < 12:
        display1.set_pen(0, 0, 255)
    elif 12 <= temp_in < 17:
        display1.set_pen(255, 255, 0)
    elif 17 <= temp_in < 25:
        display1.set_pen(0, 255, 0)
    elif 25 <= temp_in < 30:
        display1.set_pen(255, 140, 0)
    elif temp_in >= 30:
        display1.set_pen(255, 0, 0)
    else:
        display1.set_pen(0, 0, 0)
        
    display1.text('{:.0f}'.format(temp_in) + '`c', 15, 35, 240, 4)

    # outdoor temperature
    temp_out = round(temp_out)
    if temp_out < 0:
        display1.set_pen(255, 255, 255)
    elif 0 <= temp_out < 12:
        display1.set_pen(0, 0, 255)
    elif 12 <= temp_out < 17:
        display1.set_pen(255, 255, 0)
    elif 17 <= temp_out < 25:
        display1.set_pen(0, 255, 0)
    elif 25 <= temp_out < 30:
        display1.set_pen(255, 140, 0)
    elif temp_out >= 30:
        display1.set_pen(255, 0, 0)
    else:
        display1.set_pen(0, 0, 0)
        
    display1.text('{:.0f}'.format(temp_out) + '`c', 160, 35, 240, 4)

    # indoor min max temperature readings
    if time_elapsed > 5:
        if min_temp_in is not None and max_temp_in is not None:
            if temp_in < min_temp_in:
                min_temp_in = int(temp_in)
            elif temp_in > max_temp_in:
                max_temp_in = int(temp_in)
        else:
            min_temp_in = int(temp_in)
            max_temp_in = int(temp_in)
            
    if min_temp_in is not None and max_temp_in is not None:
        min_string_in = ('{:.0f}'.format(min_temp_in))
        max_string_in = ('{:.0f}'.format(max_temp_in))
    else:
        min_string_in = ""
        max_string_in = ""
        
    if min_temp_in is not None and max_temp_in is not None:
        if min_temp_in < 0:  # very cold
            display1.set_pen(255, 255, 255)
        elif 0 <= min_temp_in < 12:  # cold
            display1.set_pen(0, 0, 255)
        elif 12 <= min_temp_in < 17: # cool
            display1.set_pen(255, 255, 0)
        elif 17 <= min_temp_in < 25: # warm
            display1.set_pen(0, 255, 0)
        elif 25 <= min_temp_in < 30: # hot
            display1.set_pen(255, 140, 0)
        elif min_temp_in >= 30:      # very hot
            display1.set_pen(255, 0, 0)
        else:
            display.set_pen(0, 0, 0)
            
        display1.text(min_string_in, 25, 78, 240, 3)
        
        if max_temp_in < 0:  # very cold
            display1.set_pen(255, 255, 255)
        elif 0 <= max_temp_in < 12:  # cold
            display1.set_pen(0, 0, 255)
        elif 12 <= max_temp_in < 17: # cool
            display1.set_pen(255, 255, 0)
        elif 17 <= max_temp_in < 25: # warm
            display1.set_pen(0, 255, 0)
        elif 25 <= max_temp_in < 30: # hot
            display1.set_pen(255, 140, 0)
        elif max_temp_in >= 30:      # very hot
            display1.set_pen(255, 0, 0)
        else:
            display1.set_pen(0, 0, 0)
            
        display1.text(max_string_in, 25, 100, 240, 3)  

    # outdoor min max temperature readings
    if time_elapsed > 5:
        if min_temp_out is not None and max_temp_out is not None:
            if temp_out < min_temp_out:
                min_temp_out = int(temp_out)
            elif temp_out > max_temp_out:
                max_temp_out = int(temp_out)
        else:
            min_temp_out = int(temp_out)
            max_temp_out = int(temp_out)
            
    if min_temp_out is not None and max_temp_out is not None:
        min_string_out = ('{:.0f}'.format(min_temp_out))
        max_string_out = ('{:.0f}'.format(max_temp_out))
    else:
        min_string_out = ""
        max_string_out = ""
        
    if min_temp_out is not None and max_temp_out is not None:
        if min_temp_out < 0:  # very cold
            display1.set_pen(255, 255, 255)
        elif 0 <= min_temp_out < 12:  # cold
            display1.set_pen(0, 0, 255)
        elif 12 <= min_temp_out < 17: # cool
            display1.set_pen(255, 255, 0)
        elif 17 <= min_temp_out < 25: # warm
            display1.set_pen(0, 255, 0)
        elif 25 <= min_temp_out < 30: # hot
            display1.set_pen(255, 140, 0)
        elif min_temp_out >= 30:      # very hot
            display1.set_pen(255, 0, 0)
        else:
            display1.set_pen(0, 0, 0)
            
        display1.text(min_string_out, 180, 78, 240, 3)
        
        #max_temp_out = round(max_temp_out)
        
        if max_temp_out < 0:  # very cold
            display1.set_pen(255, 255, 255)
        elif 0 <= max_temp_out < 12:  # cold
            display1.set_pen(0, 0, 255)
        elif 12 <= max_temp_out < 17: # cool
            display1.set_pen(255, 255, 0)
        elif 17 <= max_temp_out < 25: # warm
            display1.set_pen(0, 255, 0)
        elif 25 <= max_temp_out < 30: # hot
            display1.set_pen(255, 140, 0)
        elif max_temp_out >= 30:      # very hot
            display1.set_pen(255, 0, 0)
        else:
            display1.set_pen(0, 0, 0)
            
        display1.text(max_string_out, 180, 100, 240, 3)

    # indoor humidity
    
    if hum_in < 30:
        display1.set_pen(255, 140, 0)
    elif 30 <= hum_in < 61:
        display1.set_pen(0, 255,0)
    elif 61 <= hum_in < 81:
        display1.set_pen(255, 255, 0)
    elif hum_in >= 81:
        display1.set_pen(255, 0, 0)
    else:
        display1.set_pen(0, 0, 0)
        
    #display1.text((int(hum_in)), 20, 80, 240, 3)
    display1.text('{:.0f}'.format(hum_in) + '%', 10, 140, 240, 4)
  
    # outdoor humidity
    
    if hum_out < 30:
        display1.set_pen(255, 140, 0)
    elif 30 <= hum_out < 61:
        display1.set_pen(0, 255,0)
    elif 61 <= hum_out < 81:
        display1.set_pen(255, 255, 0)
    elif hum_out >= 81:
        display1.set_pen(255, 0, 0)
    else:
        display1.set_pen(0, 0, 0)
        
    #display1.text((int(hum_in)), 20, 80, 240, 3)
    display1.text('{:.0f}'.format(hum_out) + '%', 160, 140, 240, 4)

    # battery state
    voltage = vsys.read_u16() * conversion_factor
    percentage = 100 * ((voltage - empty_battery) / (full_battery - empty_battery))
    if percentage > 100:
        percentage = 100.00
    
    if charging.value() == 1:         # if it's plugged into USB power...
        display1.set_pen(0, 255, 0)
        display1.text("ok", 20, 200, 240, 3)
        #display1.text('{:.1f}'.format(voltage) + "v", 10, 200, 240, 3)
        display1.text("ok", 170, 200, 240, 3)
        display1.text('{:.0f}%'.format(percentage), 5, 220, 240, 3)
        display1.text('{:.0f}%'.format(percentage), 155, 220, 240, 3)
        display1.set_backlight(1.0)
        display2.set_backlight(1.0)
        display2.set_led(0,0,225)
    else:                             # if not, display the battery stats
        display1.set_pen(255, 255, 0)
        display1.text('{:.1f}'.format(voltage) + "v", 10, 200, 240, 3)
        display1.text('{:.0f}%'.format(percentage), 5, 220, 240, 3)
        display1.text('{:.1f}'.format(voltage) + "v", 160, 200, 240, 3)
        display1.text('{:.0f}%'.format(percentage), 155, 220, 240, 3)
        display1.set_backlight(0.5)
        display2.set_backlight(0.5)
        display2.set_led(125,0,0)
        
    # time to update display 1
    display1.update()
    display1.set_pen(0, 0, 0)
    display1.clear()
                                         
    display2.set_pen(200, 200, 200)
    
    hours = rtc.get_hours()
    minutes = rtc.get_minutes()
    display2.text(rtc.string_date(), 30, 0, 180, 3)
    #display2.text(f"{hours:02}:{minutes:02}", 190, 0, 320, 3)
    
    if hours <12:
        display2.text(f"{hours:2}:{minutes:02}:am", 190, 0, 240, 3)
    elif hours == 12:
        display2.text(f"{hours:2}:{minutes:02}:pm", 190, 0, 240, 3)
    elif hours >12:
        hours = hours - 12
        display2.text(f"{hours:2}:{minutes:02}:pm", 190, 0, 240, 3)
        
    # outddor pressure reading on display 2    
    if pressuremb < 982:
        display2.set_pen(255, 0, 0)
        display2.text('{:.0f}'.format(pressuremb) + 'mb', 0, 110, 240, 3)
        display2.text("very low", 105, 110, 240, 3)
    elif 982 <= pressuremb < 1004:
        display2.set_pen(255, 255, 0)
        display2.text('{:.0f}'.format(pressuremb) + 'mb', 0, 110, 240, 3)
        display2.text("low", 105, 110, 240, 3)
    elif 1004 <= pressuremb < 1026:
        display2.set_pen(0, 255, 0)
        display2.text('{:.0f}'.format(pressuremb) + 'mb', 0, 110, 240, 3)
        display2.text("unsetled", 105, 110, 240, 3)
    elif 1026 <= pressuremb < 1048:
        display2.set_pen(0, 0, 255)
        display2.text('{:.0f}'.format(pressuremb) + 'mb', 0, 110, 240, 3)
        display2.text("high", 105, 110, 240, 3)
    elif pressuremb >= 1048:
        display2.set_pen(255, 140, 0)
        display2.text('{:.0f}'.format(pressuremb) + 'mb', 0, 110, 240, 3)
        display2.text("very high", 105, 110, 240, 3)
    else:
        display2.set_pen(0, 0, 0)
        display2.text('{:.0f}'.format(pressuremb) + 'mb', 0, 110, 240, 3)
        display2.text("", 105, 110, 240, 3)
         
    reading = ltr.get_reading()
    #print("Lux:", reading[BreakoutLTR559.LUX], "Prox:", reading[BreakoutLTR559.PROXIMITY])
    light = reading[BreakoutLTR559.LUX]
    
    display2.set_pen(0, 255, 0)
    display2.text("sun", 0, 70, 240, 3)
    
    #Convert light level in lux to descriptive value.
     
    if light < 50:
        display2.text("dark", 60, 70, 240, 3)
    elif 50 <= light < 100:
        display2.text("dim", 60, 70, 240, 3)
    elif 100 <= light < 500:
        display2.text("light", 60, 70, 240, 3)
    elif light >= 500:
        display2.text("bright", 60, 70, 240, 3)

    # wind speed
    display2.set_pen(255, 255, 255)
    display2.text("wind:", 0, 190, 240, 3)
    display2.text("NE", 80, 190, 240, 3)
    
    # wind direction
    display2.set_pen(255, 255, 255)
    display2.text("36 km/h", 130, 190, 240, 3)
        
    # percipitaion
    display2.set_pen(255, 255, 255)
    display2.text("rain fall:", 0, 220, 240, 3)
    display2.text("30 mm", 150, 220, 240, 3)
    
        
    # update display 2
    display2.update()
    display2.set_pen(0, 0, 0)
    display2.clear()
    
    time.sleep(1.0)
    

@Gadgetoid
Copy link
Member

Gadgetoid commented Mar 22, 2022

My displays - all of them - are still going nothing! But I'm glad this is working for you. Suggests my code is doing what I think it should.

Step two will be to try and flesh out support for various displays in some sensible and easy to use fashion. That may involve breaking the C++ API 😬

(Yay found a display that works)

@Gadgetoid
Copy link
Member

Opened #309 to track this

@alphanumeric007
Copy link
Author

Nice work Phil, very appreciated. One wrinkle is you can't run two displays, not enough memory for two display buffers. I almost hate mentioning it after all the work you've done, and are doing. I wouldn't think its an issue for most people. It would only crop up if you tried to run two displays in the Pico Breakout Garden. Or what I'm trying to do.
Requardless I'm happy to do any testing I can. I have a Pico Display pack V2 and V1. A 240x240 LCD breakout and a ST7735S 160x80 pixel LCD Breakout. Pico Breakout Garden and Quad pack.

@alphanumeric007
Copy link
Author

alphanumeric007 commented Mar 24, 2022

Is there a way to make them both use the same frame_buffer?
I may also have that frame_buffer line wrong?

frame_buffer = bytearray(240 * 240 * 2)  # 2-bytes per pixel (RGB565)
display1 = st7789.ST7789(width=240, height=240, *frame_buffer, slot=0) #front
display2 = st7789.ST7789(width=240, height=240, *frame_buffer, slot=1) #back

MemoryError: memory allocation failed, allocating 81920 bytes

This works with the old driver, letting me use two displays at once

display_buffer = bytearray(240 * 240 * 2)  # 2-bytes per pixel (RGB565)
display1 = BreakoutColourLCD240x240(display_buffer, cs=(17), bl=(20))
display2 = BreakoutColourLCD240x240(display_buffer, cs=(22), bl=(21))

@Gadgetoid
Copy link
Member

Try:

frame_buffer = bytearray(240 * 240 * 2)  # 2-bytes per pixel (RGB565)
display1 = st7789.ST7789(width=240, height=240, buffer=frame_buffer, slot=0) #front
display2 = st7789.ST7789(width=240, height=240, buffer=frame_buffer, slot=1) #back

I appreciate it doesn't constitute documentation, but all the args accepted by the constructor are here:

enum { ARG_width, ARG_height, ARG_slot, ARG_buffer, ARG_spi, ARG_cs, ARG_dc, ARG_sck, ARG_mosi, ARG_bl };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_width, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_height, MP_ARG_REQUIRED | MP_ARG_INT },
{ MP_QSTR_slot, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_buffer, MP_ARG_OBJ, {.u_obj = mp_const_none} },
{ MP_QSTR_spi, MP_ARG_INT, {.u_int = -1} },
{ MP_QSTR_cs, MP_ARG_INT, {.u_int = pimoroni::SPI_BG_FRONT_CS} },
{ MP_QSTR_dc, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_MISO} },
{ MP_QSTR_sck, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_SCK} },
{ MP_QSTR_mosi, MP_ARG_INT, {.u_int = pimoroni::SPI_DEFAULT_MOSI} },
{ MP_QSTR_bl, MP_ARG_INT, {.u_int = pimoroni::SPI_BG_FRONT_PWM} },
};

@alphanumeric007
Copy link
Author

That worked on my test setup, can't thank you enough Phil. I had looked at that file and figured out it was frame_buffer, just couldn't figure out it was "buffer=frame_buffer". What's in that file is Klingon to me, lol
I'll also try it out on my Display Pack setup in a bit.

This is my test file, with your edits added. Pico Breakout Garden with two of the 1.3' 240x240 LCD Breakouts

import time
import st7789
"""
from breakout_colourlcd240x240 import BreakoutColourLCD240x240

display_buffer = bytearray(240 * 240 * 2)  # 2-bytes per pixel (RGB565)
display1 = BreakoutColourLCD240x240(display_buffer, cs=(17), bl=(20))
display2 = BreakoutColourLCD240x240(display_buffer, cs=(22), bl=(21))
"""

frame_buffer = bytearray(240 * 240 * 2)  # 2-bytes per pixel (RGB565)
display1 = st7789.ST7789(width=240, height=240, buffer=frame_buffer, slot=0) #front
display2 = st7789.ST7789(width=240, height=240, buffer=frame_buffer, slot=1) #back


display1.set_backlight(1.0)
display2.set_backlight(1.0)

print(display1)
print(display2)

#BreakoutColourLCD240x240(spi = 0, cs = 27, dc = 16, sck = 18, mosi = 19, bl = 20)
#BreakoutColourLCD240x240(spi = 0, cs = 22, dc = 16, sck = 18, mosi = 19, bl = 21)

while True:
    display1.set_pen(0, 0, 255)
    display1.clear()
    display1.update()
    display1.set_pen(0, 0, 0)
    display1.text("display 1", 10, 10, 240, 5)
    display1.update()
    display1.clear()
    
    display2.set_pen(0, 255, 0)
    display2.clear()
    display2.update()
    display2.set_pen(0, 0, 0)
    display2.text("display 2", 10, 10, 240, 5)
    display2.update()
    display2.clear()
    
    time.sleep(2)
    
    display1.set_pen(0, 255, 0)
    display1.clear()
    display1.update()
    display1.set_pen(0, 0, 0)
    display1.text("BG_front display 1", 10, 10, 240, 5)
    display1.update()
    display1.clear()
    
    display2.set_pen(0, 255, 255)
    display2.clear()
    display2.update()
    display2.set_pen(0, 0, 0)
    display2.text("BG_back display 2", 10, 10, 240, 5)
    display2.update()
    display2.clear()
    
    time.sleep(2)
    
    display1.set_pen(0, 255, 255)
    display1.clear()
    display1.update()
    display1.set_pen(0, 0, 0)
    display1.text("SPI_0 BG_front display 1", 10, 10, 240, 5)
    display1.update()
    display1.clear()
    
    display2.set_pen(255, 0, 0)
    display2.clear()
    display2.update()
    display2.set_pen(0, 0, 0)
    display2.text("SPI_0 BG_back display 2", 10, 10, 240, 5)
    display2.update()
    display2.clear()
    
    time.sleep(2)
    
    display1.set_pen(255, 0, 0)
    display1.clear()
    display1.update()
    display1.set_pen(0, 0, 0)
    display1.text("cs=(17) SPI_0 BG_front display 1", 10, 10, 240, 5)
    display1.update()
    display1.clear()
    
    display2.set_pen(255, 0, 255)
    display2.clear()
    display2.update()
    display2.set_pen(0, 0, 0)
    display2.text("cs=(22) SPI_0 BG_back display 2", 10, 10, 240, 5)
    display2.update()
    display2.clear()
    
    time.sleep(2)
    
    display1.set_pen(255, 0, 255)
    display1.clear()
    display1.update()
    display1.set_pen(0, 0, 0)
    display1.text("bl=(20) cs=(17) SPI_0 BG_front display 1", 10, 10, 240, 5)
    display1.update()
    display1.clear()
    
    display2.set_pen(255, 255, 0)
    display2.clear()
    display2.update()
    display2.set_pen(0, 0, 0)
    display2.text("bl=(21) cs=(22) SPI_0 BG_back display 2", 10, 10, 240, 5)
    display2.update()
    display2.clear()
    
    time.sleep(2)
    
    display1.set_pen(255, 255, 0)
    display1.clear()
    display1.update()
    display1.set_pen(0, 0, 0)
    display1.text("240x240 bl=(20) cs=(17) SPI_0 BG_front display 1", 10, 10, 240, 5)
    display1.update()
    display1.clear()
    
    display2.set_pen(0, 0, 255)
    display2.clear()
    display2.update()
    display2.set_pen(0, 0, 0)
    display2.text("240x240 bl=(21) cs=(22) SPI_0 BG_back display 2", 10, 10, 240, 5)
    display2.update()
    display2.clear()
    
    time.sleep(2)

@alphanumeric007
Copy link
Author

What worked on the 240x135 Pico display pack doesn't want to work so well on the V2 240x320? Landscape worked but portrait is all garbled? Going to plug it into my quad pack and make sure its not my jumpers / wiring?

@alphanumeric007
Copy link
Author

Ok, it looks like an issue with the new unifying driver. portrait mode is cropped to 240 wide and text is all garbled?

import utime

#display = st7789.ST7789(width=240, height=135, slot=0) # Display Pack landscape works
#display = st7789.ST7789(width=135, height=240, slot=0) # Display Pack portrait works

#display = st7789.ST7789(width=320, height=240, slot=0) # Display Pack V2 landscape works
display = st7789.ST7789(width=240, height=320, slot=0) # Display Pack V2 portrait didn't work

@alphanumeric007
Copy link
Author

#327

Currently running two Display Pack V2's side by side in portrait mode without issue. And have access to the full 320 height and 240 width. woot =)
I don't have the buttons or LED's wired up on this setup just yet.
The Display Packs are each mounted to Perma Proto half size, and the Pico to another Perma Proto half size. I just soldered male header on the Perma Proto's and then soldered wires from board to board for SPI and power.

A big big thankyou Phil. A big thumbs up.
My test file is as follows.

import time

import st7789
frame_buffer = bytearray(240 * 320 * 2) # 2-bytes per pixel (RGB565)
display1 = st7789.ST7789(width=240, height=320, buffer=frame_buffer, slot=0)
display2 = st7789.ST7789(width=240, height=320, buffer=frame_buffer, slot=1)
display1.set_backlight(1.0)
display2.set_backlight(1.0)

print(display1)
print(display2)

while True:
display1.set_pen(0, 0, 255)
display1.clear()
display1.update()
display1.set_pen(0, 0, 0) # Set pen to black
display1.text("display 1", 10, 10, 240, 5)
display1.update()
display1.clear()

display2.set_pen(0, 255, 0)
display2.clear()
display2.update()
display2.set_pen(0, 0, 0)  # Set pen to black
display2.text("display 2", 10, 10, 240, 5)  
display2.update()
display2.clear()

time.sleep(2)

display1.set_pen(0, 255, 0)
display1.clear()
display1.update()
display1.set_pen(0, 0, 0)  # Set pen to black
display1.text("display 1 slot 0", 10, 10, 240, 5)  
display1.update()
display1.clear()

display2.set_pen(0, 255, 255)
display2.clear()
display2.update()
display2.set_pen(0, 0, 0)  # Set pen to black
display2.text("display 2 slot 1", 10, 10, 240, 5)  
display2.update()
display2.clear()

time.sleep(2)

display1.set_pen(0, 255, 255)
display1.clear()
display1.update()
display1.set_pen(0, 0, 0)  # Set pen to black
display1.text("display 1 slot 0 BG_front", 10, 10, 240, 5)  
display1.update()
display1.clear()

display2.set_pen(255, 0, 0)
display2.clear()
display2.update()
display2.set_pen(0, 0, 0)  # Set pen to black
display2.text("display 2 slot 1 BG_back", 10, 10, 240, 5)  
display2.update()
display2.clear()

time.sleep(2)

display1.set_pen(255, 0, 0)
display1.clear()
display1.update()
display1.set_pen(0, 0, 0)  # Set pen to black
display1.text("display 1 slot 0 BG_front cs=(17)", 10, 10, 240, 5) 
display1.update()
display1.clear()

display2.set_pen(255, 0, 255)
display2.clear()
display2.update()
display2.set_pen(0, 0, 0)  # Set pen to black
display2.text("display 2 slot 1 BG_back cs=(22)", 10, 10, 240, 5) 
display2.update()
display2.clear()

time.sleep(2)

display1.set_pen(255, 0, 255)
display1.clear()
display1.update()
display1.set_pen(0, 0, 0)  # Set pen to black
display1.text("display 1 slot 0 BG_front cs=(17) bl=(20)", 10, 10, 240, 5) 
display1.update()
display1.clear()

display2.set_pen(255, 255, 0)
display2.clear()
display2.update()
display2.set_pen(0, 0, 0)  # Set pen to black
display2.text("display 2 slot 1 BG_back cs=(22) bl=(21)", 10, 10, 240, 5)
display2.update()
display2.clear()

time.sleep(2)

display1.set_pen(255, 255, 0)
display1.clear()
display1.update()
display1.set_pen(0, 0, 0)  # Set pen to black
display1.text("display 1 slot 0 BG_front cs=(17) bl=(20) st7789", 10, 10, 240, 5)  
display1.update()
display1.clear()

display2.set_pen(0, 0, 255)
display2.clear()
display2.update()
display2.set_pen(0, 0, 0)  # Set pen to black
display2.text("display 2 slot 1 BG_back cs=(22) bl=(21) st7789", 10, 10, 240, 5) 
display2.update()
display2.clear()

time.sleep(2)

display1.set_pen(255, 255, 0)
display1.clear()
display1.update()
display1.set_pen(0, 0, 0)  # Set pen to black
display1.text("display 1 slot 0 BG_front cs=(17) bl=(20) st7789 240 x 320", 10, 10, 240, 5)  
display1.update()
display1.clear()

display2.set_pen(0, 0, 255)
display2.clear()
display2.update()
display2.set_pen(0, 0, 0)  # Set pen to black
display2.text("display 2 slot 1 BG_back cs=(22) bl=(21) st7789 240 x 320", 10, 10, 240, 5) 
display2.update()
display2.clear()

time.sleep(2)

display1.set_pen(255, 255, 0)
display1.clear()
display1.update()
display1.set_pen(0, 0, 0)  # Set pen to black
display1.text("display 1 slot 0 BG_front cs=(17) bl=(20) st7789 240 x 320 portrait", 10, 10, 240, 5)  
display1.update()
display1.clear()

display2.set_pen(0, 0, 255)
display2.clear()
display2.update()
display2.set_pen(0, 0, 0)  # Set pen to black
display2.text("display 2 slot 1 BG_back cs=(22) bl=(21) st7789 240 x 320 portrait", 10, 10, 240, 5) 
display2.update()
display2.clear()

time.sleep(2)    

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants