Raspberry Pi Pico 2/MicroPython display bmp images on 240x240 ST7789 SPI Display
Previous exercise "Raspberry Pi Pico 2/MicroPython read bmp images and draw on ST7789 display
pixel-by-pixel" is a slow approach. Because it draw in pixel-by-pixel, each involve a write
sequency. But it help me understand bmp much more.
Here is another
approach, form a bytearray passing to st7789pt blit_buffer() method, such
that the driver write the whole buffer in one sequency to improve the speed
much more.
For connection and installing st7789py_mpy on Raspberry Pi Pico 2/MicroPython,
read:
https://coxxect.blogspot.com/2024/10/raspberry-pi-pico-2micropython-display.html
To
prepare 240x240 RGB565/RGB888 bmp using Python or GIMP, read:
https://coxxect.blogspot.com/2024/11/resize-jpg-and-convert-to-bmp-in-rgb888.html
Exercise code:
mpy_pico2_bmp_buffer.py
"""
Raspberry Pi Pico/MicroPython exercise
to display on 1.54" IPS 240x240 with SPI ST7789 driver
Read, decode bmp in /images/ directory, and disply on 240x240 SPI ST7789 display.
Target:
- 240x240 RGB565 .bmp
- 240x240 RGB888 .bmp
Calling blit_buffer() with bmp data in buffer, to make it more efficiency.
Actually, I don't know the format of buffer or bitmap for st7789py_mpy, or any method to
generate buffer or bitmap.
I find out my approach to form the buffer for blit_buffer() method by guessing and trying.
ref:
Using library: russhughes/st7789py_mpy
https://github.com/russhughes/st7789py_mpy
to install st7789py_mpy on Raspberry Pi Pico 2/MicroPython,
https://coxxect.blogspot.com/2024/10/raspberry-pi-pico-2micropython-display.html
to know more about bmp structure:
https://coxxect.blogspot.com/2024/10/python-code-to-read-bmp-info-from.html
To prepare the 240x240 bmp RGB565/RGB888 using Python or GIMP, read te post:
https://coxxect.blogspot.com/2024/11/resize-jpg-and-convert-to-bmp-in-rgb888.html
Connection:
-----------
GND GND
VCC 3V3
SCL GP18
SDA GP19
RES GP20
DC GP21
CS GP17
BLK GP22
GP16 (dummy, not used)
"""
import os, sys
from machine import Pin, SPI
import time
import st7789py as st7789
disp_sck = 18 # default SCK of SPI(0)
disp_mosi = 19 # default MOSI of SPI(0)
disp_miso = 16 # not use
disp_res = 20
disp_dc = 21
disp_cs = 17
disp_blk = 22
print("====================================")
print(sys.implementation[0], os.uname()[3],
"\nrun on", os.uname()[4])
print("====================================")
DISP_WIDTH = 240
DISP_HEIGHT = 240
# it's found that:
# - cannot set baudrate
# - even I set miso=None, miso will be assigned deault GP16 or previous assigned miso
disp_spi = SPI(0, baudrate=60000000, sck=Pin(disp_sck), mosi=Pin(disp_mosi), miso=None)
print(disp_spi)
display = st7789.ST7789(disp_spi,
DISP_WIDTH, DISP_HEIGHT,
reset=Pin(disp_res, Pin.OUT),
cs=Pin(disp_cs, Pin.OUT),
dc=Pin(disp_dc, Pin.OUT),
backlight=Pin(disp_blk, Pin.OUT))
print(st7789.__name__, display.width, "x", display.height)
display.fill(st7789.WHITE)
time.sleep(0.5)
display.fill(st7789.BLACK)
img_path = '/images/'
#img_file = 'img_a_240_rgb565.bmp'
img_file = 'img_b_240_rgb888.bmp'
# Read bmp
def read_bmp(filename):
print("Read:", filename)
with open(filename, 'rb') as f:
bmp_data = f.read()
return bmp_data
def get_compression_method(num):
switcher = {
0: "BI_RGB",
1: "BI_RLE8",
2: "BI_RLE4",
3: "BI_BITFIELDS",
4: "BI_JPEG",
5: "BI_PNG",
6: "BI_ALPHABITFIELDS",
11: "BI_CMYK",
12: "BI_CMYKRLE8",
13: "BI_CMYKRLE4",
}
return switcher.get(num, "unknown")
IMG_WIDTH = 240
IMG_HEIGHT = 240
def displat_bmp(bmp_file):
bmp_data = read_bmp(bmp_file)
print("bmp_data", type(bmp_data), len(bmp_data))
offset = int.from_bytes(bmp_data[10:14], 'little')
image_width = int.from_bytes(bmp_data[18:22], 'little')
image_height = int.from_bytes(bmp_data[22:26], 'little')
num_of_bits_per_pixel = int.from_bytes(bmp_data[28:30], 'little')
compression_method = int.from_bytes(bmp_data[30:34], 'little')
print("=============================================")
print("What concerned are:")
print("Signature:",chr(bmp_data[0]), chr(bmp_data[1]))
print("offset:", offset)
print("image_width:", image_width)
print("image_height:", image_height)
print("num_of_bits_per_pixel:", num_of_bits_per_pixel)
print("compression_method:", compression_method, get_compression_method(compression_method))
print("=============================================")
#==========================
# Verify the bmp imag
if chr(bmp_data[0]) != 'B' or chr(bmp_data[1]) != 'M':
print("Must be bmp")
elif image_width != IMG_WIDTH or image_height != IMG_HEIGHT:
print("Must be 240 x 240")
else:
if num_of_bits_per_pixel == 24:
print("Assume " + bmp_file + " is 240x240 RGB888 bmp, continuous.")
buffer = bytearray(IMG_WIDTH*IMG_HEIGHT*2) # for RGB565, each pixel two bytes
byte_per_pixel_src = 3
byte_per_pixel_buffer = 2
byte_per_line_src = IMG_WIDTH*byte_per_pixel_src
byte_per_line_buffer = IMG_WIDTH*byte_per_pixel_buffer
tick_ms_formbuffer_start = time.ticks_ms()
for y in range(IMG_HEIGHT):
src_y_offset = (y*byte_per_line_src) + offset
# Because bmp start from bottom-left,
# so we have to invert the y order (IMG_HEIGHT-1-y).
buffer_y_offset_0 = (IMG_HEIGHT-1-y)*byte_per_line_buffer
buffer_y_offset_1 = (IMG_HEIGHT-1-y)*byte_per_line_buffer + 1
for x in range(IMG_WIDTH):
i = (x*byte_per_pixel_src) + src_y_offset
r_565 = (bmp_data[i+2] >> 3) & 0b00011111
g_565 = (bmp_data[i+1] >> 2) & 0b00111111
b_565 = (bmp_data[i] >> 3) & 0b00011111
rgb565 = (r_565 << 11) | (g_565 << 5) | b_565
buffer[x*2 + buffer_y_offset_0] = (rgb565 >> 8) & 0xFF
buffer[x*2 + buffer_y_offset_1] = rgb565 & 0xFF
tick_ms_formbuffer_end = time.ticks_ms()
display.blit_buffer(buffer, 0, 0, image_width, image_height)
tick_ms_blitbuffer_end = time.ticks_ms()
print("time to form buffer:",
time.ticks_diff(tick_ms_formbuffer_end, tick_ms_formbuffer_start),
"ms")
print("time to blit_buffer():",
time.ticks_diff(tick_ms_blitbuffer_end, tick_ms_formbuffer_end),
"ms")
elif num_of_bits_per_pixel == 16:
print("Assume " + bmp_file + " is 240x240 RGB565 bmp, continuous.")
buffer = bytearray(IMG_WIDTH*IMG_HEIGHT*2) # for RGB565, each pixel two bytes
byte_per_pixel_src = 2
byte_per_pixel_buffer = 2
byte_per_line_src = IMG_WIDTH*byte_per_pixel_src
byte_per_line_buffer = IMG_WIDTH*byte_per_pixel_buffer
tick_ms_formbuffer_start = time.ticks_ms()
for y in range(IMG_HEIGHT):
src_y_offset_0 = (y*byte_per_line_src) + offset
src_y_offset_1 = (y*byte_per_line_src) + offset + 1
# Because bmp start from bottom-left,
# so we have to invert the y order (IMG_HEIGHT-1-y).
buffer_y_offset_0 = (IMG_HEIGHT-1-y)*byte_per_line_buffer
buffer_y_offset_1 = (IMG_HEIGHT-1-y)*byte_per_line_buffer + 1
for x in range(IMG_WIDTH):
#notice: HIGH/LOW byte reversed
buffer[x*2 + buffer_y_offset_0] = bmp_data[x*2 + src_y_offset_1]
buffer[x*2 + buffer_y_offset_1] = bmp_data[x*2 + src_y_offset_0]
tick_ms_formbuffer_end = time.ticks_ms()
display.blit_buffer(buffer, 0, 0, image_width, image_height)
tick_ms_blitbuffer_end = time.ticks_ms()
print("time to form buffer:",
time.ticks_diff(tick_ms_formbuffer_end, tick_ms_formbuffer_start),
"ms")
print("time to blit_buffer():",
time.ticks_diff(tick_ms_blitbuffer_end, tick_ms_formbuffer_end),
"ms")
else:
print("Must be 16 or 24 bit per pixel")
#==========================
displat_bmp(img_path + img_file)
mpy_pico2_bmp_buffer_slideshow.py
"""
Raspberry Pi Pico/MicroPython exercise
to display on 1.54" IPS 240x240 with SPI ST7789 driver
Read, decode bmp in /images/ directory, and disply on 240x240 SPI ST7789 display.
In SlideShow.
Target:
- 240x240 RGB565 .bmp
- 240x240 RGB888 .bmp
Calling blit_buffer() with bmp data in buffer, to make it more efficiency.
Actually, I don't know the format of buffer or bitmap for st7789py_mpy, or any method to
generate buffer or bitmap.
I find out my approach to form the buffer for blit_buffer() method by guessing and trying.
ref:
Using library: russhughes/st7789py_mpy
https://github.com/russhughes/st7789py_mpy
to install st7789py_mpy on Raspberry Pi Pico 2/MicroPython,
https://coxxect.blogspot.com/2024/10/raspberry-pi-pico-2micropython-display.html
to know more about bmp structure:
https://coxxect.blogspot.com/2024/10/python-code-to-read-bmp-info-from.html
To prepare the 240x240 bmp RGB565/RGB888 using Python or GIMP, read te post:
https://coxxect.blogspot.com/2024/11/resize-jpg-and-convert-to-bmp-in-rgb888.html
Connection:
-----------
GND GND
VCC 3V3
SCL GP18
SDA GP19
RES GP20
DC GP21
CS GP17
BLK GP22
GP16 (dummy, not used)
"""
import os, sys
from machine import Pin, SPI
import time
import st7789py as st7789
disp_sck = 18 # default SCK of SPI(0)
disp_mosi = 19 # default MOSI of SPI(0)
disp_miso = 16 # not use
disp_res = 20
disp_dc = 21
disp_cs = 17
disp_blk = 22
print("====================================")
print(sys.implementation[0], os.uname()[3],
"\nrun on", os.uname()[4])
print("====================================")
DISP_WIDTH = 240
DISP_HEIGHT = 240
# it's found that:
# - cannot set baudrate
# - even I set miso=None, miso will be assigned deault GP16 or previous assigned miso
disp_spi = SPI(0, baudrate=60000000, sck=Pin(disp_sck), mosi=Pin(disp_mosi), miso=None)
print(disp_spi)
display = st7789.ST7789(disp_spi,
DISP_WIDTH, DISP_HEIGHT,
reset=Pin(disp_res, Pin.OUT),
cs=Pin(disp_cs, Pin.OUT),
dc=Pin(disp_dc, Pin.OUT),
backlight=Pin(disp_blk, Pin.OUT))
print(st7789.__name__, display.width, "x", display.height)
display.fill(st7789.WHITE)
time.sleep(0.5)
display.fill(st7789.BLACK)
img_path = '/images/'
img_file = 'img_a_240_rgb565.bmp'
#img_file = 'img_b_240_rgb888.bmp'
# Read bmp
def read_bmp(filename):
print("Read:", filename)
with open(filename, 'rb') as f:
bmp_data = f.read()
return bmp_data
def get_compression_method(num):
switcher = {
0: "BI_RGB",
1: "BI_RLE8",
2: "BI_RLE4",
3: "BI_BITFIELDS",
4: "BI_JPEG",
5: "BI_PNG",
6: "BI_ALPHABITFIELDS",
11: "BI_CMYK",
12: "BI_CMYKRLE8",
13: "BI_CMYKRLE4",
}
return switcher.get(num, "unknown")
IMG_WIDTH = 240
IMG_HEIGHT = 240
def displat_bmp(bmp_file):
bmp_data = read_bmp(bmp_file)
print("bmp_data", type(bmp_data), len(bmp_data))
offset = int.from_bytes(bmp_data[10:14], 'little')
image_width = int.from_bytes(bmp_data[18:22], 'little')
image_height = int.from_bytes(bmp_data[22:26], 'little')
num_of_bits_per_pixel = int.from_bytes(bmp_data[28:30], 'little')
compression_method = int.from_bytes(bmp_data[30:34], 'little')
print("=============================================")
print("What concerned are:")
print("Signature:",chr(bmp_data[0]), chr(bmp_data[1]))
print("offset:", offset)
print("image_width:", image_width)
print("image_height:", image_height)
print("num_of_bits_per_pixel:", num_of_bits_per_pixel)
print("compression_method:", compression_method, get_compression_method(compression_method))
print("=============================================")
#==========================
# Verify the bmp imag
if chr(bmp_data[0]) != 'B' or chr(bmp_data[1]) != 'M':
print("Must be bmp")
elif image_width != IMG_WIDTH or image_height != IMG_HEIGHT:
print("Must be 240 x 240")
else:
if num_of_bits_per_pixel == 24:
print("Assume " + bmp_file + " is 240x240 RGB888 bmp, continuous.")
buffer = bytearray(IMG_WIDTH*IMG_HEIGHT*2) # for RGB565, each pixel two bytes
byte_per_pixel_src = 3
byte_per_pixel_buffer = 2
byte_per_line_src = IMG_WIDTH*byte_per_pixel_src
byte_per_line_buffer = IMG_WIDTH*byte_per_pixel_buffer
tick_ms_formbuffer_start = time.ticks_ms()
for y in range(IMG_HEIGHT):
src_y_offset = (y*byte_per_line_src) + offset
# Because bmp start from bottom-left,
# so we have to invert the y order (IMG_HEIGHT-1-y).
buffer_y_offset_0 = (IMG_HEIGHT-1-y)*byte_per_line_buffer
buffer_y_offset_1 = (IMG_HEIGHT-1-y)*byte_per_line_buffer + 1
for x in range(IMG_WIDTH):
i = (x*byte_per_pixel_src) + src_y_offset
r_565 = (bmp_data[i+2] >> 3) & 0b00011111
g_565 = (bmp_data[i+1] >> 2) & 0b00111111
b_565 = (bmp_data[i] >> 3) & 0b00011111
rgb565 = (r_565 << 11) | (g_565 << 5) | b_565
buffer[x*2 + buffer_y_offset_0] = (rgb565 >> 8) & 0xFF
buffer[x*2 + buffer_y_offset_1] = rgb565 & 0xFF
tick_ms_formbuffer_end = time.ticks_ms()
display.blit_buffer(buffer, 0, 0, image_width, image_height)
tick_ms_blitbuffer_end = time.ticks_ms()
print("time to form buffer:",
time.ticks_diff(tick_ms_formbuffer_end, tick_ms_formbuffer_start),
"ms")
print("time to blit_buffer():",
time.ticks_diff(tick_ms_blitbuffer_end, tick_ms_formbuffer_end),
"ms")
elif num_of_bits_per_pixel == 16:
print("Assume " + bmp_file + " is 240x240 RGB565 bmp, continuous.")
buffer = bytearray(IMG_WIDTH*IMG_HEIGHT*2) # for RGB565, each pixel two bytes
byte_per_pixel_src = 2
byte_per_pixel_buffer = 2
byte_per_line_src = IMG_WIDTH*byte_per_pixel_src
byte_per_line_buffer = IMG_WIDTH*byte_per_pixel_buffer
tick_ms_formbuffer_start = time.ticks_ms()
for y in range(IMG_HEIGHT):
src_y_offset_0 = (y*byte_per_line_src) + offset
src_y_offset_1 = (y*byte_per_line_src) + offset + 1
# Because bmp start from bottom-left,
# so we have to invert the y order (IMG_HEIGHT-1-y).
buffer_y_offset_0 = (IMG_HEIGHT-1-y)*byte_per_line_buffer
buffer_y_offset_1 = (IMG_HEIGHT-1-y)*byte_per_line_buffer + 1
for x in range(IMG_WIDTH):
#notice: HIGH/LOW byte reversed
buffer[x*2 + buffer_y_offset_0] = bmp_data[x*2 + src_y_offset_1]
buffer[x*2 + buffer_y_offset_1] = bmp_data[x*2 + src_y_offset_0]
tick_ms_formbuffer_end = time.ticks_ms()
display.blit_buffer(buffer, 0, 0, image_width, image_height)
tick_ms_blitbuffer_end = time.ticks_ms()
print("time to form buffer:",
time.ticks_diff(tick_ms_formbuffer_end, tick_ms_formbuffer_start),
"ms")
print("time to blit_buffer():",
time.ticks_diff(tick_ms_blitbuffer_end, tick_ms_formbuffer_end),
"ms")
else:
print("Must be 16 or 24 bit per pixel")
#==========================
while True:
for b_file in os.listdir(img_path):
if b_file.endswith('.bmp'):
print()
bmp_file_full_path = img_path + b_file
displat_bmp(bmp_file_full_path)
time.sleep(4)
Comments
Post a Comment