pimoroni / st7735-python

Python library to control an ST7735 TFT LCD display. Allows simple drawing on the display without installing a kernel module.
MIT License
61 stars 29 forks source link

ST7735 color depth LUTs not defaulted after a SWRESET #16

Open Pachelbel1414 opened 3 years ago

Pachelbel1414 commented 3 years ago

I'm able to get the ST7735 display to be corrupted by doing simultaneous writes from multiple threads (I know this is bad). What occurs is the color depth lookup tables are getting corrupted. According to the Sitronix datasheet, the LUTs are not defaulted on a SWRESET, only on power up or HWRESET. So app restart or even "sudo reboot" is not enough to correct it.

I was able to account for this by adding code to default the LUTs to the ST7735 module's _init() function.

    ST7735_RGBSET = 0x2D

    # update color depth lookup tables for 16-bit 565 color.
    # these are only reset to defaults after a 
    # power on or HW reset (not a SW reset).
    self.command(ST7735_RGBSET)    # Set color LUT (0x2D)
    # red (5-bit, 32 colors)
    for color in range(0, 32, 2):
        self.data(color)
    for color in range(33, 64, 2):
        self.data(color)
    # green (6-bit, 64 colors)
    for color in range(0, 64):
        self.data(color)
    # blue (5-bit, 32 colors)
    for color in range(0, 32, 2):
        self.data(color)
    for color in range(33, 64, 2):
        self.data(color)
ghost commented 2 years ago

I beleive that this is not the full story.

I am working with Automation HAT Mini and the display always gets corrupted after a while of using it. First only colors, later it shows total garbage and stops working entirely. The examples write Press CTRL+C to exit. Maybe there is a chance that it will cut off a command mid SPI transaction and leaves the display ready to be corrupted. I tried to add your fix after init as I don't want to poke in the library itself.

To test if it helps I made a bash loop

while :; do python3 ./kotelnik.py & sleep 10; killall python3; done

and the display crashes almost immediately. I guess that killall doesn't wait for anything and there is a bigger chance that SPI is killed in the middle of a transaction and next time the init breaks everything.


To fix that, it is necessary to properly close the SPI. I have added dummy open and close of the SPI before display init. Seems to me, that it should be done in the spidev library itself.

    tempspi = spidev.SpiDev(0, ST7735.BG_SPI_CS_FRONT)
    tempspi.close() 
    disp = ST7735.ST7735(
        port=0,
        cs=ST7735.BG_SPI_CS_FRONT,
        dc=9,
        backlight=25,
        rotation=270,
        spi_speed_hz=4500000
    )

With that, I have run the kill loop for a while and it worked.

I have also tried to add another reset command before the init. The reasoning is that SPI is killed mid transaction. The first reset merges with previous command, configures some garbage, but it will end the SPI transaction correctly and set CS. The second reset then properly resets the display. I didn't study the datasheet, so I don't know if there is a garbage configuration that might be more risky, like destroy the display or corrupt the color lut as in your case. Plus, this cannot be done easily without modification in the library, only to rerun the _init function after the double reset.


Either way, this is a second Pimoroni product I have and a second problem caused by bad design and bad firmware. Nobody reacted to this issue for almost a year. I can understand bug in a code, but the negligence screams "Never buy again!".