Skip to content

Labels on RGBMatrix flicker when writing/reading from SD card (sdcardio) #7682

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

Open
chef-wally opened this issue Mar 5, 2023 · 2 comments
Open

Comments

@chef-wally
Copy link

chef-wally commented Mar 5, 2023

CircuitPython version

Adafruit CircuitPython 8.0.3 on 2023-02-23; Adafruit Grand Central M4 Express with samd51p20

adafruit-circuitpython-grandcentral_m4_express-en_GB-8.0.3.uf2
update-bootloader-grandcentral_m4-v3.15.0.uf2

Code/REPL

import displayio
import rgbmatrix
import board
import framebufferio
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text.label import Label
import digitalio
import busio
import sdcardio
import storage
import adafruit_debouncer

displayio.release_displays()

matrix = rgbmatrix.RGBMatrix(
    width=64,
    height=32,
    bit_depth=4,
    rgb_pins=[board.D8, board.D9, board.D10, board.D11, board.D12, board.D13],
    addr_pins=[board.D4, board.D6, board.D3, board.D5],
    clock_pin=board.D1,
    latch_pin=board.D2,
    output_enable_pin=board.D0
)
display = framebufferio.FramebufferDisplay(matrix)

font = bitmap_font.load_font("/fonts/test.pcf")

mg = displayio.Group()
mg_a = Label(font, text="A.A.A.A", color=0x00FF00, x=15, y=6)
mg_h = Label(font, text="H.H.H.H", color=0x00FF00, x=14, y=17)
mg_b = Label(font, text="B.B.B.B", color=0x00FF00, x=15, y=28)
mg.append(mg_a)
mg.append(mg_h)
mg.append(mg_b)

class HiscoreController:
    def __init__(self):
        spi = busio.SPI(board.SD_SCK, board.SD_MOSI, board.SD_MISO)
        sdcard = sdcardio.SDCard(spi, board.SD_CS)
        vfs = storage.VfsFat(sdcard)
        storage.mount(vfs, "/")

    def hiscore(self, value=-1):
        if value >= 0:
            try:
                with open("/hiscore.txt", "w") as hiscore_file:
                    if value > 9999:
                        hiscore_file.write(str(value - 10000))
                    else:
                        hiscore_file.write(str(value))
            except OSError:
                pass
        else:
            if "hiscore.txt" in os.listdir("/"):
                try:
                    with open("/hiscore.txt", "r") as hiscore_file:
                        score = hiscore_file.read()
                        return score
                except OSError:
                    pass

class ButtonController:
    def __init__(self, btns_and_names):
        self.buttons = {}

        for name, btn in btns_and_names.items():
            button_pin = digitalio.DigitalInOut(btn["obj"])
            button_pin.switch_to_input(pull=digitalio.Pull.UP)
            self.buttons[name] = adafruit_debouncer.Debouncer(button_pin)

    def is_pressed(self, btn_name):
        return self.buttons[btn_name].fell

    def reset(self):
        for btn in self.buttons.values():
            btn.update()

    def update(self):
        for btn in self.buttons.values():
            btn.update()

hiscore_ctrl = HiscoreController()

buttons = ButtonController(
    btns_and_names = {
        "reset": {"num": 1, "obj": board.D27}
    }
)

def menu(screen_state):
    reset = False

    display.show(mg)

    buttons.reset()

    while screen_state == menu:
        buttons.update()

        if buttons.is_pressed("reset"):
            hiscore_ctrl.hiscore(value=0)
            reset = True

screens = {
    menu: menu
}

screen_state = menu

while True:
    current_screen = screens[screen_state]
    screen_state = current_screen(screen_state)

Behavior

I switched to reading/writing to a file on an SD card because writing to CIRCUITPY would lock up other tasks. However, now when using the SD card whenever I try to write/read from the SD card the labels on the RGBMatrix flicker.
hiscore_ctrl.hiscore(value=0)

I used to write to the CIRCUITPY drive but it would cause other tasks to lock up, but I wouldn't get a flicker of the labels.

import board
import digitalio
import storage
  
switch = digitalio.DigitalInOut(board.D36)
switch.direction = digitalio.Direction.INPUT
switch.pull = digitalio.Pull.UP
  
storage.remount("/", switch.value)
class HiscoreController:
    def __init__(self):
        pass

    def hiscore(self, value=-1):
        if value >= 0:
            hiscore_file = open("/hiscore/hiscore.txt", "w")
            if value > 9999:
                hiscore_file.write(str(value - 10000))
            else:
                hiscore_file.write(str(value))
            hiscore_file.close()
        else:
            hiscore_file = open("/hiscore/hiscore.txt", "r")
            score = hiscore_file.read()
            hiscore_file.close()
            return score

Description

No response

Additional information

SanDisk Ultra 32GB microSDHC Memory Card

Example of flicker when pressing a button, easiest way to replicate.

flicker.mp4
@chef-wally chef-wally added the bug label Mar 5, 2023
@chef-wally
Copy link
Author

chef-wally commented Mar 6, 2023

The RGBmatrix is powered on its own 5V line with an electrolytic capacitor.
The SDCard is the one on the GrandCentral M4 Express, powered by a 12V line.

The flickering of the matrix happens when mounting/writing the SDCard from my tests, this code causes the flicker.

class HiscoreController:
    def __init__(self):
        pass

    def hiscore(self):
        spi = busio.SPI(board.SD_SCK, board.SD_MOSI, board.SD_MISO)
        sdcard = sdcardio.SDCard(spi, board.SD_CS)
        vfs = storage.VfsFat(sdcard)
        storage.mount(vfs, "/")

Thought might be something to do with baudrate, but can't set it

in init
TypeError: extra keyword arguments given

class HiscoreController:
    def __init__(self):
        pass

    def hiscore(self):
        spi = busio.SPI(board.SD_SCK, board.SD_MOSI, board.SD_MISO, baudrate=500000)
        sdcard = sdcardio.SDCard(spi, board.SD_CS)
        vfs = storage.VfsFat(sdcard)
        storage.mount(vfs, "/")

some benchmarks

>>> import mount_sd
>>> 
paste mode; Ctrl-C to cancel, Ctrl-D to finish
=== import time
=== import os
=== 
=== # First, just write the file 'hello.txt' to the card
=== with open("/sd/hello.txt", "w") as f:
===     print("hello world", file=f)
=== print()
=== print("SD card I/O benchmarks")
=== # Test read and write speed in several scenarios:
=== #  * 512 or 4096 bytes at a time
=== #  * Writing 1 time or 16 times
=== # First write the content to the SD card, then read it back, reporting the
=== # time taken.
=== for sz in 512, 4096:
===     b = bytearray(sz)
===     for i in range(sz):
===         b[i] = 0xaa
===     for n in (1, 16):
===         with open("/sd/hello.bin", "wb") as f:
===             t0 = time.monotonic_ns()
===             for i in range(n):
===                 f.write(b)
===             t1 = time.monotonic_ns()
===         dt = (t1-t0) / 1e9
===         print(f"write {len(b)} x {n} in {dt}s {n * len(b) / dt / 1000:.1f}Kb/s")
===         with open("/sd/hello.bin", "rb") as f:
===             t0 = time.monotonic_ns()
===             for i in range(n):
===                 f.readinto(b)
===             t1 = time.monotonic_ns()
===         dt = (t1-t0) / 1e9
===         print(f"read  {len(b)} x {n} in {dt}s {n * len(b) / dt / 1000:.1f}Kb/s")
===         print()
=== # Test "logging" speed and report the time to write each line.
=== # Note that in this test the file is not closed or flushed after each
=== # line, so in the event of power loss some lines would be lost.  However,
=== # it allows much more frequent logging overall.
=== #
=== # If keeping data integrity is your highest concern, follow the logging
=== # example, not this logging benchmark!
=== print("logging test")
=== with open("/sd/log.txt", "wt") as logfile:
===     t0 = time.monotonic_ns()
===     for i in range(10000):
===         t1 = time.monotonic_ns()
===         dt = (t1-t0) / 1e9
===         print(f"Line {i}, {dt:2f}s elapsed", file=logfile)
=== t1 = time.monotonic_ns()
=== dt = (t1-t0) / 1e9
=== print(f"Logged 10000 lines in {dt} seconds, {dt*100:.0f}us/line")
=== sz = os.stat('/sd/log.txt')[6]
=== print(f"{sz} bytes written, {sz/dt/1000:.1f}Kb/s")

SD card I/O benchmarks
512
write 512 x 1 in 0.00317382s 161.3Kb/s
512
read  512 x 1 in 0.0109863s 46.6Kb/s

512
512
512
512
512
512
512
512
512
512
512
512
512
512
512
512
write 512 x 16 in 0.0296631s 276.2Kb/s
512
512
512
512
512
512
512
512
512
512
512
512
512
512
512
512
read  512 x 16 in 0.036377s 225.2Kb/s

4096
write 4096 x 1 in 0.00537109s 762.6Kb/s
4096
read  4096 x 1 in 0.0141601s 289.3Kb/s

4096
4096
4096
4096
4096
4096
4096
4096
4096
4096
4096
4096
4096
4096
4096
4096
write 4096 x 16 in 0.13147s 498.5Kb/s
4096
4096
4096
4096
4096
4096
4096
4096
4096
4096
4096
4096
4096
4096
4096
4096
read  4096 x 16 in 0.132568s 494.4Kb/s

logging test
Logged 10000 lines in 8.06909 seconds, 807us/line
288890 bytes written, 35.8Kb/s
>>> 

@dhalbert
Copy link
Collaborator

dhalbert commented Mar 6, 2023

I don't think it's so likely this is an electrical problem, but rather it's a timing problem. Running an RGB Matrix requires real-time updates of the matrix, and missing an update can create flicker. It takes about 1/3 of the CPU to do it. It's done at interrupt level, so it should be capable of overriding most other things going on. but writing to an SD card may add uninterruptable things, or may cause contention that introduces latency and therefor flickering. We'll need to do some instrumentation to find out what is going on.

@tannewt tannewt added this to the Support milestone Mar 6, 2023
@tannewt tannewt modified the milestones: Support, Long term Mar 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants