Raspberry Pi Pico 2 W/CircuitPython 10.0.3: Load animated GIFs from MicroSD, display them on a 240*240 GC9A01 LCD using gifio.OnDiskGif.

Previous posts show Basic setup of 1.28 inch 240x240 Round Display with GC9A01 SPI on Raspberry Pi Pico 2 W/CircuitPython 10.0.3 and display bmp images from SD using displayio.OnDiskBitmap()/adafruit_imageload.load() This post shows how to display animated GIFs from MicroSD.



The connection between Raspberry Pi Pico 2 W, GC9A01 LCD and the MicroSD Module refers to the last post, display bmp images from SD using displayio.OnDiskBitmap()/adafruit_imageload.load() 

Ref:
https://docs.circuitpython.org/en/latest/shared-bindings/gifio/index.html#gifio.OnDiskGif

Exercise Code:

cpy_rpPicoW_gc9a01_sd_OnDiskGif.py. Cooperate with displayio, this mode is relatively slow.
"""
Raspberry Pi Pico 2 W/CircuitPython 10.0.3
with 1.28" 240x240 Round GC9A01 SPI IPS LCD
Display animated GIF in SD, using gifio.OnDiskGif.
Cooperate with displayio, this mode is relatively slow.

https://coxxect.blogspot.com/2026/02/raspberry-pi-pico-2-wcircuitpython-1003.html

ref:
https://docs.circuitpython.org/en/latest/shared-bindings/gifio/index.html#gifio.OnDiskGif

Prepare Libraries
-----------------
CircuitPython Libraries Bundle for Version 9.x needed:
(https://circuitpython.org/libraries)
- adafruit_gc9a01a.mpy

To install libraries using circup, enter the command:
> circup install adafruit_gc9a01a

For CircUp, read:
https://coxxect.blogspot.com/2024/12/install-and-using-circup-circuitpython.html


"""
import os, sys
import board
import time
import displayio
import busio
import adafruit_gc9a01a
from fourwire import FourWire

import sdcardio
import storage
import gifio

displayio.release_displays()

tft_blk = board.GP22
tft_cs  = board.GP17
tft_dc  = board.GP21
tft_res = board.GP20
tft_sda = board.GP19
tft_scl = board.GP18

sd_miso = board.GP12
sd_cs   = board.GP13
sd_clk  = board.GP14
sd_mosi = board.GP15

# Release any resources currently in use for the displays
displayio.release_displays()

info = os.uname()[4] + "\n" + \
       sys.implementation[0] + " " + os.uname()[3] + "\n" + \
       adafruit_gc9a01a.__name__ + " " + adafruit_gc9a01a.__version__
print("=======================================")
print(info)
print("=======================================")
print()
    
disp_spi = busio.SPI(clock=tft_scl, MOSI=tft_sda)

display_bus = FourWire(spi_bus=disp_spi,
                       command=tft_dc,
                       chip_select=tft_cs,
                       reset=tft_res)
display = adafruit_gc9a01a.GC9A01A(display_bus, width=240, height=240, backlight_pin=tft_blk)

#=======================================
# Init SD
sd_spi = busio.SPI(clock=sd_clk, MOSI=sd_mosi, MISO=sd_miso)

sd = sdcardio.SDCard(sd_spi, sd_cs)
vfs = storage.VfsFat(sd)
storage.mount(vfs, '/sd')

def list_files_with_ext(path, ext):
    """Return sorted list of files in path with given extension."""
    files = []
    for name in os.listdir(path):
        full_path = path + "/" + name
        st = os.stat(full_path)
        mode = st[0]

        # check regular file (0x8000) only
        if mode & 0x8000:
            if name.lower().endswith("." + ext.lower()):
                files.append(name)

    files.sort()
    return files
            
gif_folder = "/sd/GIFs_10/"
gif_files = list_files_with_ext(gif_folder, "gif")
print(gif_files)

def showGIF(g_folder, g_file):
    print("------------------")
    print("GIF:", g_file)

    odg = gifio.OnDiskGif(g_folder+g_file)  # Create an OnDiskGif object with the given file.

    print("W x H:", odg.width, "x", odg.height)
    print("duration:", odg.duration, "sec")
    print("frame_count:", odg.frame_count)

    start = time.monotonic()
    next_delay  = odg.next_frame() # Load the first frame
    end = time.monotonic()
    overhead = end - start

    face = displayio.TileGrid(
        odg.bitmap,
        pixel_shader=displayio.ColorConverter(
            input_colorspace=displayio.Colorspace.RGB565_SWAPPED
        ),
        x = int((display.width-odg.width)/2),
        y = int((display.height-odg.height)/2),
    )
    
    if len(main_group) > 0:
        main_group[0] = face
    else:
        main_group.append(face)
    display.refresh()

    for r in range(odg.frame_count-1):
        time.sleep(max(0, next_delay - overhead))
        next_delay = odg.next_frame() #load next frame
        
    print("- showGIF ended -")
    
main_group = displayio.Group()
display.root_group = main_group

while True:
    for f in gif_files:
        showGIF(gif_folder, f)



cpy_rpPicoW_gc9a01_sd_OnDiskGif_direct_blit.py. Directly blitted to the full screen, faster.
"""
Raspberry Pi Pico 2 W/CircuitPython 10.0.3
with 1.28" 240x240 Round GC9A01 SPI IPS LCD
Display animated GIF in SD, using gifio.OnDiskGif.
Directly blitted to the full screen, faster.

https://coxxect.blogspot.com/2026/02/raspberry-pi-pico-2-wcircuitpython-1003.html

Ref:
https://docs.circuitpython.org/en/latest/shared-bindings/gifio/index.html#gifio.OnDiskGif

Prepare Libraries
-----------------
CircuitPython Libraries Bundle for Version 9.x needed:
(https://circuitpython.org/libraries)
- adafruit_gc9a01a.mpy

To install libraries using circup, enter the command:
> circup install adafruit_gc9a01a

For CircUp, read:
https://coxxect.blogspot.com/2024/12/install-and-using-circup-circuitpython.html


"""
import os, sys
import board
import time
import displayio
import busio
import adafruit_gc9a01a
from fourwire import FourWire

import sdcardio
import storage
import gifio

import struct, gc

displayio.release_displays()

tft_blk = board.GP22
tft_cs  = board.GP17
tft_dc  = board.GP21
tft_res = board.GP20
tft_sda = board.GP19
tft_scl = board.GP18

sd_miso = board.GP12
sd_cs   = board.GP13
sd_clk  = board.GP14
sd_mosi = board.GP15

# Release any resources currently in use for the displays
displayio.release_displays()

info = os.uname()[4] + "\n" + \
       sys.implementation[0] + " " + os.uname()[3] + "\n" + \
       adafruit_gc9a01a.__name__ + " " + adafruit_gc9a01a.__version__
print("=======================================")
print(info)
print("=======================================")
print()

def change_spi_baudrate(spi, rate):
    while not spi.try_lock():
        pass
    
    spi.configure(baudrate=rate)
    print("Updated SPI settings:")
    print("Baudrate:", spi.frequency)
    
    spi.unlock()
    
disp_spi = busio.SPI(clock=tft_scl, MOSI=tft_sda)

display_bus = FourWire(spi_bus=disp_spi,
                       command=tft_dc,
                       chip_select=tft_cs,
                       reset=tft_res)
display = adafruit_gc9a01a.GC9A01A(display_bus, width=240, height=240, backlight_pin=tft_blk)

#=======================================

# Init SD
sd_spi = busio.SPI(clock=sd_clk, MOSI=sd_mosi, MISO=sd_miso)

sd = sdcardio.SDCard(sd_spi, sd_cs)
vfs = storage.VfsFat(sd)
storage.mount(vfs, '/sd')



def list_files_with_ext(path, ext):
    """Return sorted list of files in path with given extension."""
    files = []
    for name in os.listdir(path):
        full_path = path + "/" + name
        st = os.stat(full_path)
        mode = st[0]

        # check regular file (0x8000) only
        if mode & 0x8000:
            if name.lower().endswith("." + ext.lower()):
                files.append(name)

    files.sort()
    return files
            
gif_folder = "/sd/GIFs_10/"
gif_files = list_files_with_ext(gif_folder, "gif")
print(gif_files)

# Take over display to drive directly
display.auto_refresh = False
display_bus = display.bus

def showGIF(g_folder, g_file):
    print("------------------")
    print("GIF:", g_file)

    # Create OnDiskGif object
    odg = gifio.OnDiskGif(g_folder + g_file)

    print("W x H:", odg.width, "x", odg.height)
    print("duration:", odg.duration, "sec")
    print("frame_count:", odg.frame_count)
    
    start = time.monotonic()
    next_delay  = odg.next_frame() # Load the first frame
    end = time.monotonic()
    overhead = end - start

    # Loop through all frames
    for frame in range(odg.frame_count-1):
        # Sleep for the frame delay specified by the GIF
        time.sleep(max(0, next_delay - overhead))

        # Advance to next frame
        next_delay = odg.next_frame()

        # Send frame directly to display
        display_bus.send(42, struct.pack(">hh", 0, odg.bitmap.width - 1))   # column range
        display_bus.send(43, struct.pack(">hh", 0, odg.bitmap.height - 1))  # row range
        display_bus.send(44, odg.bitmap)                                    # pixel data

    print("- showGIF ended -")

    # Free resources
    odg.deinit()
    odg = None
    gc.collect()
    
# Example main loop
while True:
    for f in gif_files:  # gif_files is list containing GIF filenames
        showGIF(gif_folder, f)




Comments

Popular posts from this blog

Drive 320x240 ILI9341 SPI TFT using ESP32-S3 (NodeMCU ESP-S3-12K-Kit) using TFT_eSPI library, in Arduino Framework.

480x320 TFT/ILI9488 SPI wih EP32C3 (arduino-esp32) using Arduino_GFX Library