adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.06k stars 1.2k forks source link

Displayio.display incorrectly parsing init_sequence #1659

Closed rdagger closed 5 years ago

rdagger commented 5 years ago

I’m using the feather_nrf52840_express-en_US-4.0.0-beta.5.uf2. I’m trying to use the displayio library with an external ST7735 LCD display. I’m using the code from https://github.com/adafruit/Adafruit_CircuitPython_ST7735

The feather is hard crashing with the error: “MicroPython NLR jump failed. Likely memory corruption.” I confirmed the display is OK and is correctly wired by using the Circuitpython Bundle RGB Display library and everything works as expected.

I then compared the SPI init sequence of the working library and the displayio library using an oscilloscope with SPI decoding. It looks like the displayio.display class is only parsing the init sequence SPI commands and not there associated data. For example, the COLMOD command (0x3A) is passed the data value 0x5 in the init sequence but the scope is only showing the 0x3A and not the 0x5.

makermelissa commented 5 years ago

Just out of curiosity, which external ST7735 LCD display are you using? Apparently there are different versions of this display and I'm wondering if you have one of the ones I'm working on adding support to.

makermelissa commented 5 years ago

Also, I just hooked up a ST7735 display to an nRF52840 running the same version you described above and it's not crashing. The one I'm using is one I bought off of Aliexpress. Oh, also what code are you using to test it? Here's a script I wrote that's working for me:

"""
This test will initialize the display using displayio
and draw a solid red background
"""

import board
import displayio
import adafruit_st7735

spi = board.SPI()
tft_cs = board.D5
tft_dc = board.D6

displayio.release_displays()
display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=board.D9)

display = adafruit_st7735.ST7735(display_bus, width=128, height=128)
rdagger commented 5 years ago

@makermelissa I'm using the HiLetgo 1.8" inch ST7735R. Which nrf52840 board are you using? By same version, did you mean the same U2F? Are you getting graphics on the display? Here's my test code:

import adafruit_st7735
import board
import busio
import displayio
import time

displayio.release_displays()
spi = busio.SPI(clock=board.SCK, MOSI=board.MOSI)
bus = displayio.FourWire(spi, chip_select=board.D9, command=board.D10, reset=board.D11)
display = adafruit_st7735.ST7735(bus, width=128, height=160)

time.sleep(5)
spi.deinit
makermelissa commented 5 years ago

The Feather nRF52840 Express (which is what the binary you mentioned was for) and the same binary. Yes I'm getting a little Blinka icon in the corner and text with that. Do you happen to remember if the plastic that came on the display had a red tab, green tab, or some other color? If not, that's ok. I'm trying to determine if it is one of those alternate boards that the driver hasn't been implemented for meaning it's giving the wrong init sequence. You can take a look at the Arduino version of this library if you'd like to know more.

rdagger commented 5 years ago

I don't recall the tab color but the display works great with the ST7735R class in the RGB Display library provided in the CircuitPython bundle.

Can I please see your full code to display the Blinka bitmap.

Are you using the MO and SCK pins on the feather for MOSI and clock respectively?

Are you using an unmodified version of adafruit_st7735.py from https://github.com/adafruit/Adafruit_CircuitPython_ST7735? If not, can you please upload your version.

You're using a slightly different approach to initializing SPI and not calling deinit() afterwards. I tried it your way and the board didn't hard crash but it still did not work and the the scope still showed the same incorrect SPI data.

Can you view the SPI init sequence with a logic analyzer or scope?

deshipu commented 5 years ago

I think the tab color was meaningful only for the displays sourced by Adafruit, if you get them from anywhere else, that color is pretty much random and doesn't correspond to the display version.

There are two versions of those displays (originally designated as "red tab" and "green tab"), which differ in how the actual LCD lines are connected to the ST7725 chip inside — one starts at the upper left corner and has no interleave, and the other starts 32 pixels shifted (it's centered) and has no interleave. They need to have one command changed for the interleave, and different offsets.

But that should be irrelevant to this bug, which apparently happens even before the displays is fully initialized.

makermelissa commented 5 years ago

Thanks for the input @deshipu. That wasn't explained very clearly anywhere I could find.

@rdagger, I'm not loading a full bitmap. It's only displaying the REPL output onto my screen with my code. The board I'm using is a 1.44" 128x128 screen that is very similar to the screen this driver was written for, so I'm not surprised mine is working. I went ahead and ordered the screen you mentioned off amazon and will test it. It should arrive in a couple days. Also yes, I do have access to an oscilloscope that can decode SPI. Hopefully we can get this figured out.

rdagger commented 5 years ago

I did further testing this morning. I was wrong about the SPI data. The command and data are transmitted. There’s a delay between the command and data that threw off my scope. This delay was not present in the RGB Display library initialization.

I’m still not getting anything on the screen. Could someone please post a simple demo to draw anything using displayio.

Here's my current code that does nothing:

import board
import displayio
import adafruit_st7735
from time import sleep
spi = board.SPI()
tft_cs = board.D9
tft_dc = board.D10
displayio.release_displays()
display_bus = displayio.FourWire(spi, command=tft_dc, chip_select=tft_cs, reset=board.D11)
display = adafruit_st7735.ST7735(display_bus, width=128, height=160)
splash = displayio.Group(max_size=10)
display.show(splash)
color_bitmap = displayio.Bitmap(128, 160, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0xFF0000
bg_sprite = displayio.TileGrid(color_bitmap, pixel_shader=color_palette, x=0, y=0)
splash.append(bg_sprite)
sleep(4)
print('done')
rdagger commented 5 years ago

I wasn't having any luck so I wrote an init sequence for the SSD1351 OLED display:

"""
SSD1351
"""

import displayio

_INIT_SEQUENCE = (
    b"\xFD\x01\x12"  # COMMAND_LOCK Unlock IC MCU 
    b"\xFD\x01\xB1"  # COMMAND_LOCK
    b"\xAE\x00"  # DISPLAY_OFF
    b"\xB2\x03\xA4\x00\x00"  # DISPLAY_ENHANCEMENT
    b"\xB3\x01\xF0"  # CLOCK_DIV
    b"\xCA\x01\x7F"  # MUX_RATIO
    b"\xA0\x01\x74"  # SET_REMAP
    b"\xA1\x01\x00"  # START_LINE
    b"\xA2\x01\x00"  # DISPLAY_OFFSET
    b"\xB5\x01\x00"  # SET_GPIO
    b"\xAB\x01\x01"  # FUNCTION_SELECT
    b"\xB1\x01\x32"  # PRECHARGE
    b"\xBB\x01\x1F"  # PRECHARGE_LEVEL
    b"\xBE\x01\x05"  # VCOMH
    b"\xA6\x00"  # NORMAL_DISPLAY
    b"\xC7\x01\x0A"  # CONTRAST_MASTER
    b"\xC1\x03\xFF\xFF\xFF"  # CONTRAST_ABC (RGB)
    b"\xB4\x03\xA0\xB5\x55"  # SET_VSL Set segment low volt
    b"\xB6\x01\x01"  # PRECHARGE2
    b"\xAF\x00"  # DISPLAY_ON  
)

class SSD1351(displayio.Display):
    """SSD1351 driver"""
    def __init__(self, bus):
        super().__init__(bus, _INIT_SEQUENCE, width=128, height=128,
                         set_column_command=0x15, set_row_command=0x75, write_ram_command=0x5C)

It looks like the initialization is working because I can manually clear the display using the displayio.FourWire.send() command. The following manually clears the display to the given color:

# Clear display manually
color = 0
line = color.to_bytes(2, 'big') * 384
for x in range(0, 128):
    display_bus.send(0x15, (x << 8 | x).to_bytes(2, 'big'))
    display_bus.send(0x75, b"\x00\x7F")
    display_bus.send(0x5C, line)

However, I guess I'm just missing something obvious because I can't get the display to respond to any of the displayio sample code.

makermelissa commented 5 years ago

Very nice @rdagger! I've requested for a couple display repos to be created. Once that's complete, would you like to package that driver up into a PR? I can help you out if you would like.

Regarding the displayio sample code, I think it's probably outdated. I recently wrote some sample code for the ILI9341 CP driver that could be minimally repurposed for the other displays. It just basically initializes the display and fills the screen red.

rdagger commented 5 years ago

@makermelissa Please feel free to use the driver in a PR. I would really appreciate if you could post your sample code for the ILI9341. Thanks.

makermelissa commented 5 years ago

Already posted. You can find it here: https://github.com/adafruit/Adafruit_CircuitPython_ILI9341/blob/master/examples/ili9341_simpletest.py

rdagger commented 5 years ago

@makermelissa Thanks, I didn't notice that you had created the sample code in the repo. I did try something very close but got nothing on the display other than snow which occurs regardless after initialization.

import board
import displayio
from ssd1351dio import SSD1351
spi = board.SPI()
displayio.release_displays()
display_bus = displayio.FourWire(spi, command=board.D10,
                                 chip_select=board.D11, reset=board.D9)
display = SSD1351(display_bus)
# Make the display context
splash = displayio.Group(max_size=10)
display.show(splash)
color_bitmap = displayio.Bitmap(128, 128, 1)
color_palette = displayio.Palette(1)
color_palette[0] = 0xFF0000
bg_sprite = displayio.TileGrid(color_bitmap,
                               pixel_shader=color_palette,
                               x=0, y=0)
splash.append(bg_sprite)
while True:
    pass

Snow_20190318_231324

makermelissa commented 5 years ago

Ok, well I plan on knocking out some of these display drivers this week. I just got the 160x80 Mini TFT working tonight and am getting the rest of the init commands plugged into the ST7735 driver right now. Hopefully I can get something for you on Wednesday.

tannewt commented 5 years ago

Sounds like the init sequence is parsed just fine. Please find us on the forums or Discord if you need further help with getting displays going.

rdagger commented 5 years ago

~~I posted the issue to the Adafruit CircuitPython forum: https://forums.adafruit.com/viewtopic.php?f=60&t=149282~~

I moved the issue here: https://github.com/adafruit/Adafruit_CircuitPython_SSD1351/issues/1