adafruit / Adafruit_CircuitPython_RGB_Display

Drivers for RGB displays for Adafruit CircuitPython.
MIT License
133 stars 52 forks source link

chip select issue with multiple screens #95

Closed kmtm closed 2 years ago

kmtm commented 3 years ago

Hi, I'm trying to control two 2.2 TFT screens, both on SPI 0 using the two chip selects (CE0 and CE1).

Whenever I display to the first screen on CE0, it's fine (i.e. only displays on the first screen), but displaying to the second screen (CE1) pushes the same image to both screens. I can't seem to figure out why. Trying to manually throw CE0 high hasn't appeared to work.

Here's the implementation (adjusted from the sample code):

import digitalio
import board
from PIL import Image, ImageDraw, ImageFont
import adafruit_rgb_display.ili9341 as ili9341
import time

# First define some constants to allow easy resizing of shapes.
BORDER = 0
FONTSIZE = 24

# Configuration for CS and DC pins (these are PiTFT defaults):
cs_pin = digitalio.DigitalInOut(board.CE0)
cs2_pin = digitalio.DigitalInOut(board.CE1)
dc_pin = digitalio.DigitalInOut(board.D25)
dc2_pin = digitalio.DigitalInOut(board.D25)
reset_pin = digitalio.DigitalInOut(board.D24)
reset_pin2 = digitalio.DigitalInOut(board.D24)

# Config for display baudrate (default max is 24mhz):
BAUDRATE = 24000000

# Setup SPI bus using hardware SPI:
spi = board.SPI()
print('spi set up')

# pylint: disable=line-too-long
# Create the display:
disp = ili9341.ILI9341(
    spi,
    rotation=90,  # 2.2", 2.4", 2.8", 3.2" ILI9341
    cs=cs_pin,
    dc=dc_pin,
    rst=reset_pin,
    baudrate=BAUDRATE,
)

disp2 = ili9341.ILI9341(
    spi,
    rotation=90,  # 2.2", 2.4", 2.8", 3.2" ILI9341
    cs=cs2_pin,
    dc=dc2_pin,
    rst=reset_pin2,
    baudrate=BAUDRATE,
)
# pylint: enable=line-too-long

# Create blank image for drawing.
# Make sure to create image with mode 'RGB' for full color.
if disp.rotation % 180 == 90:
    height = disp.width  # we swap height/width to rotate it to landscape!
    width = disp.height
else:
    width = disp.width  # we swap height/width to rotate it to landscape!
    height = disp.height

image = Image.new("RGB", (width, height))

if disp2.rotation % 180 == 90:
    height2 = disp2.width  # we swap height/width to rotate it to landscape!
    width2 = disp2.height
else:
    width2 = disp2.width # we swap height/width to rotate it to landscape!
    height2 = disp2.height 

image2 = Image.new("RGB", (width2, height2))
print('width2:', width2, 'height2:', height2)

# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)
draw2 = ImageDraw.Draw(image2)

# Draw a white filled box as the background
draw.rectangle((00, 0, disp.width+100, disp.height+100), fill=(225, 225, 225))
disp.image(image)

input('draw blue box on second screen only')

#Draw a blue box only on the second screen
draw2.rectangle((0, 0, disp2.width+100, disp2.height+100), fill=(0, 225, 225))
disp2.image(image2)

Essentially the blue box appears on both screens instead of just the second one!

I'm not sure if I'm doing something wrong with the chip selects/setup, or if this library just can't support multiple SPI devices.

TrevorFSmith commented 2 years ago

I have exactly this same issue! I've checked everything that I can think of. A single board works fine but when I try to use both board.CE0 and board.CE1 as CS lines the display using CE0 responds to both drawing commands. In the code below, the CE0 display draws "Left" and then redraws to "Right" while the display using CE1 only draws "Right".

I've been working on this for a few hours and I'm a pretty stumped.

@kmtm Did you find a solution?

I'm using today's Blinka on a Raspberry Pi 4 running bullseye and I followed the directions in this learn guide.

import digitalio
import board
import time
import busio
from PIL import Image, ImageDraw, ImageFont
from adafruit_rgb_display import ili9341
from adafruit_rgb_display import st7789  # pylint: disable=unused-import
from adafruit_rgb_display import hx8357  # pylint: disable=unused-import
from adafruit_rgb_display import st7735  # pylint: disable=unused-import
from adafruit_rgb_display import ssd1351  # pylint: disable=unused-import
from adafruit_rgb_display import ssd1331  # pylint: disable=unused-import

BORDER = 20
FONTSIZE = 24

left_cs_pin = digitalio.DigitalInOut(board.CE0)
right_cs_pin = digitalio.DigitalInOut(board.CE1)
dc_pin = digitalio.DigitalInOut(board.D25)
reset_pin = digitalio.DigitalInOut(board.D24)

BAUDRATE = 24000000

spi = board.SPI()

left_disp = ili9341.ILI9341(
    spi,
    rotation=0, 
    cs=left_cs_pin,
    dc=dc_pin,
    #rst=reset_pin,
    baudrate=BAUDRATE,
)

right_disp = ili9341.ILI9341(
    spi,
    rotation=0,
    cs=right_cs_pin,
    dc=dc_pin,
    #rst=reset_pin,
    baudrate=BAUDRATE,
        )

if left_disp.rotation % 180 == 90:
    height = left_disp.width
    width = left_disp.height
else:
    width = left_disp.width
    height = left_disp.height

left_image = Image.new("RGB", (width, height))
left_draw = ImageDraw.Draw(left_image)
left_text = "Left"

right_image = Image.new("RGB", (width, height))
right_draw = ImageDraw.Draw(right_image)
right_text = "Right"

font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", FONTSIZE)
text_color = 128 

while True:
    right_draw.rectangle((0, 0, width, height), fill=(0,0,0))
    (font_width, font_height) = font.getsize(right_text)
    right_draw.text(
        (width // 2 - font_width // 2, height // 2 - font_height // 2),
        right_text,
        font=font,
        fill=(text_color, 255 - text_color, 255 - text_color),
    )
    right_disp.image(right_image)

    left_draw.rectangle((0, 0, width, height), fill=(0,0,0))
    (font_width, font_height) = font.getsize(left_text)
    left_draw.text(
            (width // 2 - font_width // 2, height // 2 - font_height // 2),
            left_text,
            font=font,
            fill=(text_color, 255 - text_color, 255 - text_color),
            )
    left_disp.image(left_image)

    text_color = (text_color + 10) % 255
    time.sleep(0.5)
makermelissa commented 2 years ago

It may be that Linux is messing with the Chip Enable lines. We have instruction on how to reassign them here: https://learn.adafruit.com/circuitpython-on-raspberrypi-linux/spi-sensors-devices#reassigning-the-spi-chip-enable-lines-3097985-4.

TrevorFSmith commented 2 years ago

Hi, @makermelissa! Thank you for the suggestion.

I reassigned CE0 and CE1 to GPIO 5 and 6 using the method in the learning guide that you linked. It did change the CS lines over to those pins but sadly they behave the same as when it was using the original CE0 and CE1 pins.

makermelissa commented 2 years ago

Ok, it was worth a shot.

TrevorFSmith commented 2 years ago

So, I found a work-around by using both SPI0 and SPI1 on the RPi 4. They're separate SPI peripherals so it takes more wires and won't work for devices that don't have two SPI peripherals but it'll get the job done for my immediate use case.

Just in case anyone else needs this, to turn on SPI0 and SPI1 go to /boot/config.txt and make sure that dtparam=spi-on and dtoverlay=spi1-3cs are set. (you'll need to reboot after changing them)

Then hook each display up to the various SPI0 and SPI1 pin with completely separate wiring (no shared MOSI, MISO, DC, etc) and program them separately by using busio.SPI() and busio.SPI(board.SCK_1, MISO=board.MISO_1, MOSI=board.MOSI_1)

kmtm commented 2 years ago

Hey, y'all! Glad this thread is useful a few months later :) I tried so many different things that didn't work (all the standard suggestions) and then finally hacked something new that worked.

Essentially, I actually ended up modifying a non-adafruit driver and manually threading the way the pixel data was being sent to two screens so they behave simultaneously. No need for more wiring on two separate SPIs (or, in my case, a lag between each eye when pushing new data serially)! My little robot eyes are now fully individually controllable in parallel.

If you give me a couple of days I can clean up my code and throw it up on git. Might need a little poke of a reminder as it's a busy week!

On Mon, Nov 15, 2021 at 7:37 PM Trevor Flowers @.***> wrote:

So, I found a work-around by using both SPI0 and SPI1 on the RPi 4. They're separate SPI peripherals so it takes more wires and won't work for devices that don't have two SPI peripherals but it'll get the job done for my immediate use case.

Just in case anyone else needs this, to turn on SPI0 and SPI1 go to /boot/config.txt and make sure that dtparam=spi-on and dtoverlay=spi1-3cs are set. (you'll need to reboot after changing them)

Then hook each display up to the various SPI0 and SPI1 pin with completely separate wiring (no shared MOSI, MISO, DC, etc) and program them separately by using busio.SPI() and busio.SPI(board.SCK_1, MISO=board.MISO_1, MOSI=board.MOSI_1)

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/adafruit/Adafruit_CircuitPython_RGB_Display/issues/95#issuecomment-969519975, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACK76ICZHPEXGKF22FTR32DUMGRWDANCNFSM5AL6SGHQ . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

Pastel-Industries commented 2 years ago

Hey, what about four screens? We need to make them work, but rpi has only two SPI interfaces. Is there any fix for CS pins behaviour?

makermelissa commented 2 years ago

I was able to get 2 displays working at the same time. First of all, please don't use CE0 and CE1. The reassignment of pins was only if you absolutely needed to use those because the hardware couldn't easily be reconfigured.

Additionally, this would easily let you use more than 2 displays.

In my test, I used D5 and D6 as the chip select lines and D24 as the DC line. Here's the code I used:

import time
import random
import busio
import digitalio
import board

from adafruit_rgb_display.rgb import color565
from adafruit_rgb_display import ili9341

# Configuratoin for CS and DC pins (these are FeatherWing defaults on M0/M4):
cs_pin = digitalio.DigitalInOut(board.D5)
cs2_pin = digitalio.DigitalInOut(board.D6)
dc_pin = digitalio.DigitalInOut(board.D24)

# Config for display baudrate (default max is 24mhz):
BAUDRATE = 24000000

# Setup SPI bus using hardware SPI:
spi = busio.SPI(clock=board.SCK, MOSI=board.MOSI, MISO=board.MISO)

# Create the ILI9341 display:
display = ili9341.ILI9341(spi, cs=cs_pin, dc=dc_pin, baudrate=BAUDRATE)
display2 = ili9341.ILI9341(spi, cs=cs2_pin, dc=dc_pin, baudrate=BAUDRATE)

# Fill the screen red, green, blue, then black:
for color in ((255, 0, 0), (0, 255, 0), (0, 0, 255)):
    display.fill(color565(color))
    display2.fill(color565(color))
# Clear the display
display.fill(0)
# Draw a red pixel in the center.
display.pixel(display.width // 2, display.height // 2, color565(255, 0, 0))
# Pause 2 seconds.
time.sleep(2)
# Clear the screen a random color
display.fill(
    color565(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
)
display2.fill(
    color565(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
)

I'm closing this because no modifications to the library were necessary to get this working.

TrevorFSmith commented 2 years ago

Thank you for looking into this, @makermelissa! It'll be useful for future projects.

BioRyajenka commented 6 months ago

Hi @makermelissa ! Your code doesn't work for my ST7789 screens. I can draw on one or another but not on both at the same time (specifically, after I create another ST7789 object, previous one stops working).

Here is my code adapted from your example

import time
import busio
import digitalio
import board

from adafruit_rgb_display.rgb import color565
from adafruit_rgb_display.st7789 import ST7789

# Configuratoin for CS and DC pins (these are FeatherWing defaults on M0/M4):
cs_pin = digitalio.DigitalInOut(board.GP10)
cs2_pin = digitalio.DigitalInOut(board.GP11)
dc_pin = digitalio.DigitalInOut(board.GP12)
rs_pin = digitalio.DigitalInOut(board.GP13)

# Config for display baudrate (default max is 24mhz):
BAUDRATE = 24000000

# Setup SPI bus using hardware SPI:
spi = busio.SPI(clock=board.GP14, MOSI=board.GP15)

# Create the ILI9341 display:
display = ST7789(spi, rotation=0, width=240, height=240, x_offset=0, y_offset=80, baudrate=BAUDRATE,
                 cs=cs_pin,
                 dc=dc_pin,
                 rst=rs_pin)
display2 = ST7789(spi, rotation=0, width=240, height=240, x_offset=0, y_offset=80, baudrate=BAUDRATE,
                 cs=cs2_pin,
                 dc=dc_pin,
                 rst=rs_pin)

# Fill the screen red, green, blue, then black:
for color in ((255, 0, 0), (0, 255, 0), (0, 0, 255)):
    display.fill(color565(color))
    display2.fill(color565(color))
# Clear the display
display.fill(0)
# Draw a red pixel in the center.
display.pixel(display.width // 2, display.height // 2, color565(255, 0, 0))
# Pause 2 seconds.
time.sleep(2)
# Clear the screen a random color
display.fill(
    color565(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
)
display2.fill(
    color565(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
)

Moreover, If I put the following lines instead:

display2 = ST7789(...)
display2.fill(color565(255, 0, 0))
time.sleep(2)
display1 = initDisplay(...)
display1.fill(color565(0, 255, 0))

Then the behaviour is to display red (actually, yellow. I think its because both CS are enabled by default until I initialized driver?), then wait for 2 sec, they put the first screen to black and then display green on the second screen. I would expect both screen to work at the same time?

makermelissa commented 6 months ago

@BioRyajenka please create a new issue referencing this issue. Thanks.

BioRyajenka commented 6 months ago

Hi @makermelissa , done (link). I also fixed a bunch of grammar and code errors in this new issue compared to my last message here :) So I hope it will make more sense now

makermelissa commented 6 months ago

Thank you. :)