ESP32S3/MicroPython display bmp on ili9341 LCD
Last post introduced setup and basic of
ESP32-S3-DevKitC-1 running MicroPython v1.24.1 using 3.2" 320x240 IPS LCD
(ILI9341 SPI) with Cap. Touch (FT6336U) and Micro SD Slot. This post further exercise to load 240x240 RGB888/RGB565 bmp from
device/SD, display on ILI9341 LCD.
Remark for ESP32-S3-DevKitC-1:
For ESP32-S3-DevKitC-1 with Octal SPI flash/PSRAM memory, use the "spiram-oct" variant such as ESP32_GENERIC_S3-SPIRAM_OCT-20241129-v1.24.1.bin (ref: https://micropython.org/download/ESP32_GENERIC_S3/). If use a in-correct firmware such as ESP32_GENERIC_S3-20241129-v1.24.1.bin, "MemoryError: memory allocation failed" will be raised.
For boards with Octal SPI flash/PSRAM memory embedded ESP32-S3-WROOM-1/1U modules, and boards with ESP32-S3-WROOM-2 modules, the pins GPIO35, GPIO36 and GPIO37 are used for the internal communication between ESP32-S3 and SPI flash/PSRAM memory, thus not available for external use.
(ref: https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32s3/esp32-s3-devkitc-1/user_guide_v1.0.html)
Connection and library setup, read the last post, ILI9341/FT6336U/SD on ESP32S3/MicroPython.
Exercise Code:
mpy_s3_ili9341_image.py
mpy_s3_ili9341_images_loop.py
mpy_s3_ili9341_images_touch.py
Related:
~ Raspberry Pi Pico 2/MicroPython display bmp images on 240x240 ST7789 SPI Display.
Remark for ESP32-S3-DevKitC-1:
For ESP32-S3-DevKitC-1 with Octal SPI flash/PSRAM memory, use the "spiram-oct" variant such as ESP32_GENERIC_S3-SPIRAM_OCT-20241129-v1.24.1.bin (ref: https://micropython.org/download/ESP32_GENERIC_S3/). If use a in-correct firmware such as ESP32_GENERIC_S3-20241129-v1.24.1.bin, "MemoryError: memory allocation failed" will be raised.
For boards with Octal SPI flash/PSRAM memory embedded ESP32-S3-WROOM-1/1U modules, and boards with ESP32-S3-WROOM-2 modules, the pins GPIO35, GPIO36 and GPIO37 are used for the internal communication between ESP32-S3 and SPI flash/PSRAM memory, thus not available for external use.
(ref: https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32s3/esp32-s3-devkitc-1/user_guide_v1.0.html)
Connection and library setup, read the last post, ILI9341/FT6336U/SD on ESP32S3/MicroPython.
All test images were generated using "Image Creatoe in Bing". Converted to 240x240 RGB888/RGB565 bmp using Python/GIMP. To resize and convert jpg to bmp, read "Resize jpg and convert to bmp in RGB888 and RGB565 mode, using Python/GIMP".
Exercise Code:
mpy_s3_ili9341_image.py
"""
ESP32-S3-DevKitC-1 running MicroPython v1.24.1
3.2" 320x240 IPS LCD (ILI9341 SPI) with Cap. Touch (FT6336) and Micro SD Slot
Display 240x240 RGB888/RGB565 bmp on ILI9341 LCD
For ESP32-S3-DevKitC-1 has Octal SPIRAM,
makesure using MicroPython firmware of "spiram-oct" variant,
such as ESP32_GENERIC_S3-SPIRAM_OCT-20241129-v1.24.1.bin.
Libraries needed, install to /lib:
- ili9341.py
- sdcard (remark: NOT machine.SDCard)
ili9341.py download from:
rdagger/micropython-ili9341
https://github.com/rdagger/micropython-ili9341
micropython-lib/sdcard.py can be download from
https://github.com/micropython/micropython-lib/blob/master/micropython/drivers/storage/sdcard/sdcard.py
or using Thonny > Tools > Manager Package...
All test images were generated using "Image Creator in Bing",
converted to 240x240 RGB888/RGB565 bmp using GIMP or Python.
To resize using GIMP/Python, read:
https://coxxect.blogspot.com/2024/11/resize-jpg-and-convert-to-bmp-in-rgb888.html
"""
import os, sys
from time import ticks_ms, ticks_diff
from machine import Pin, SPI
import sdcard
import gc
from ili9341 import Display, color565
print("====================================")
print(sys.implementation[0], os.uname()[3],
"\nrun on", os.uname()[4])
print("====================================")
LCD_CS=14 # low to select
LCD_RST=13
LCD_RS=12
LCD_SDI=11 #MOSI
LCD_SCK=10
LCD_LED=9
LCD_SDO=8 #MISO
SD_CS=18 # low to select
# un-select both LCD and SD in power-up,
# to prevent un-used cs unstable and false selected.
pin_cs_lcd = Pin(LCD_CS, Pin.OUT)
pin_cs_lcd.value(1)
pin_cs_sd = Pin(SD_CS, Pin.OUT)
pin_cs_sd.value(1)
pin_backlight=Pin(LCD_LED, Pin.OUT)
pin_backlight.value(0)
# rot and rot_set
# are used to better manager rotation/width/height of display
rot=0 # 0 rotation = 0
# 1 rotation = 90
# 2 rotation = 180
# 3 rotation = 270
# rot_set[x][0] : ili9341 Display rotation
# rot_set[x][1] : ili9341 Display width
# rot_set[x][2] : ili9341 Display height
rot_set = [[0, 240, 320],
[90, 320, 240],
[180, 240, 320],
[270, 320, 240]]
# spi share by LCD and SD
spi = SPI(1, baudrate=40000000, sck=LCD_SCK, mosi=LCD_SDI, miso=LCD_SDO)
display = Display(spi,
rotation=rot_set[rot][0],
width=rot_set[rot][1], height=rot_set[rot][2],
dc=Pin(LCD_RS), cs=Pin(LCD_CS), rst=Pin(LCD_RST))
display.invert(enable=True)
# remark:
# It's sdcard.SDCard
# NOT machine.SDCard
sd = sdcard.SDCard(spi, Pin(SD_CS))
vfs = os.VfsFat(sd)
os.mount(vfs, "/sd")
pin_backlight.value(1)
print("Display:", display.width, "x", display.height)
display.clear()
# read SD and draw on display
def listImages(path):
image_files = os.listdir(path)
print(len(image_files), "files in", path)
for f in image_files:
print(f)
# Read bmp
def read_bmp(filename):
print("Read:", filename)
gc.collect()
print(gc.mem_free())
tick_ms_read_bmp_start = ticks_ms()
with open(filename, 'rb') as f:
bmp_data = f.read()
tick_ms_read_bmp_end = ticks_ms()
msg_read_bmp = "read bmp: " + str(ticks_diff(tick_ms_read_bmp_end, tick_ms_read_bmp_start)) + " ms"
display.draw_text8x8(0, 290,
msg_read_bmp,
color565(255, 255, 255))
print(msg_read_bmp)
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")
def display_bmp(src_path, src_file):
bmp_file = src_path + '/' + src_file
display.clear()
display.draw_text8x8(0, 250, src_path, color565(255, 255, 255))
display.draw_text8x8(0, 260, src_file, color565(255, 255, 255))
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 = 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 = ticks_ms()
display.block(0, 0, 239, 239, buffer)
tick_ms_block_end = ticks_ms()
msg_formbuffer = "form buffer: " +\
str(ticks_diff(tick_ms_formbuffer_end, tick_ms_formbuffer_start)) +\
" ms"
msg_displayblock = "display.block(): " +\
str(ticks_diff(tick_ms_block_end, tick_ms_formbuffer_end)) +\
" ms"
print(msg_formbuffer)
print(msg_displayblock)
display.draw_text8x8(0, 300,
msg_formbuffer,
color565(255, 255, 255))
display.draw_text8x8(0, 310,
msg_displayblock,
color565(255, 255, 255))
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 = 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 = ticks_ms()
display.block(0, 0, 239, 239, buffer)
tick_ms_block_end = ticks_ms()
msg_formbuffer = "form buffer: " +\
str(ticks_diff(tick_ms_formbuffer_end, tick_ms_formbuffer_start)) +\
" ms"
msg_displayblock = "display.block(): " +\
str(ticks_diff(tick_ms_block_end, tick_ms_formbuffer_end)) +\
" ms"
print(msg_formbuffer)
print(msg_displayblock)
display.draw_text8x8(0, 300,
msg_formbuffer,
color565(255, 255, 255))
display.draw_text8x8(0, 310,
msg_displayblock,
color565(255, 255, 255))
else:
print("Must be 16 or 24 bit per pixel")
#==========================
img_path = '/sd/images' #load from SD
#img_path = '/images' #load from MicroPython device
#img_file = 'img_001_gimp_240_rgb888.bmp'
#img_file = 'img_002_gimp_240_rgb565.bmp'
img_file = 'img_003_240x240_rgb888.bmp'
#img_file = 'img_004_240x240_rgb565.bmp'
IMG_WIDTH = 240
IMG_HEIGHT = 240
listImages(img_path)
print()
display_bmp(img_path, img_file)
print("~ bye ~")
mpy_s3_ili9341_images_loop.py
"""
ESP32-S3-DevKitC-1 running MicroPython v1.24.1
3.2" 320x240 IPS LCD (ILI9341 SPI) with Cap. Touch (FT6336) and Micro SD Slot
Loop to display 240x240 RGB888/RGB565 bmp images on ILI9341 LCD
For ESP32-S3-DevKitC-1 has Octal SPIRAM,
makesure using MicroPython firmware of "spiram-oct" variant,
such as ESP32_GENERIC_S3-SPIRAM_OCT-20241129-v1.24.1.bin.
Libraries needed, install to /lib:
- ili9341.py
- sdcard (remark: NOT machine.SDCard)
ili9341.py download from:
rdagger/micropython-ili9341
https://github.com/rdagger/micropython-ili9341
micropython-lib/sdcard.py can be download from
https://github.com/micropython/micropython-lib/blob/master/micropython/drivers/storage/sdcard/sdcard.py
or using Thonny > Tools > Manager Package...
All test images were generated using "Image Creator in Bing",
converted to 240x240 RGB888/RGB565 bmp using GIMP or Python.
To resize using GIMP/Python, read:
https://coxxect.blogspot.com/2024/11/resize-jpg-and-convert-to-bmp-in-rgb888.html
"""
import os, sys
from time import sleep, ticks_ms, ticks_diff
from machine import Pin, SPI
import sdcard
import gc
from ili9341 import Display, color565
print("====================================")
print(sys.implementation[0], os.uname()[3],
"\nrun on", os.uname()[4])
print("====================================")
LCD_CS=14 # low to select
LCD_RST=13
LCD_RS=12
LCD_SDI=11 #MOSI
LCD_SCK=10
LCD_LED=9
LCD_SDO=8 #MISO
SD_CS=18 # low to select
# un-select both LCD and SD in power-up,
# to prevent un-used cs unstable and false selected.
pin_cs_lcd = Pin(LCD_CS, Pin.OUT)
pin_cs_lcd.value(1)
pin_cs_sd = Pin(SD_CS, Pin.OUT)
pin_cs_sd.value(1)
pin_backlight=Pin(LCD_LED, Pin.OUT)
pin_backlight.value(0)
# rot and rot_set
# are used to better manager rotation/width/height of display
rot=0 # 0 rotation = 0
# 1 rotation = 90
# 2 rotation = 180
# 3 rotation = 270
# rot_set[x][0] : ili9341 Display rotation
# rot_set[x][1] : ili9341 Display width
# rot_set[x][2] : ili9341 Display height
rot_set = [[0, 240, 320],
[90, 320, 240],
[180, 240, 320],
[270, 320, 240]]
# spi share by LCD and SD
spi = SPI(1, baudrate=40000000, sck=LCD_SCK, mosi=LCD_SDI, miso=LCD_SDO)
display = Display(spi,
rotation=rot_set[rot][0],
width=rot_set[rot][1], height=rot_set[rot][2],
dc=Pin(LCD_RS), cs=Pin(LCD_CS), rst=Pin(LCD_RST))
display.invert(enable=True)
# remark:
# It's sdcard.SDCard
# NOT machine.SDCard
sd = sdcard.SDCard(spi, Pin(SD_CS))
vfs = os.VfsFat(sd)
os.mount(vfs, "/sd")
pin_backlight.value(1)
print("Display:", display.width, "x", display.height)
display.clear()
# Read bmp
def read_bmp(filename):
print("Read:", filename)
gc.collect()
print(gc.mem_free())
tick_ms_read_bmp_start = ticks_ms()
with open(filename, 'rb') as f:
bmp_data = f.read()
tick_ms_read_bmp_end = ticks_ms()
msg_read_bmp = "read bmp: " + str(ticks_diff(tick_ms_read_bmp_end, tick_ms_read_bmp_start)) + " ms"
display.draw_text8x8(0, 290,
msg_read_bmp,
color565(255, 255, 255))
print(msg_read_bmp)
return bmp_data
def display_bmp(src_path, src_file):
bmp_file = src_path + '/' + src_file
display.clear()
display.draw_text8x8(0, 250, src_path+'/', color565(255, 255, 255))
display.draw_text8x8(0, 260, src_file, color565(255, 255, 255))
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')
#==========================
# 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 = 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 = ticks_ms()
display.block(0, 0, 239, 239, buffer)
tick_ms_block_end = ticks_ms()
msg_formbuffer = "form buffer: " +\
str(ticks_diff(tick_ms_formbuffer_end, tick_ms_formbuffer_start)) +\
" ms"
msg_displayblock = "display.block(): " +\
str(ticks_diff(tick_ms_block_end, tick_ms_formbuffer_end)) +\
" ms"
print(msg_formbuffer)
print(msg_displayblock)
display.draw_text8x8(0, 300,
msg_formbuffer,
color565(255, 255, 255))
display.draw_text8x8(0, 310,
msg_displayblock,
color565(255, 255, 255))
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 = 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 = ticks_ms()
display.block(0, 0, 239, 239, buffer)
tick_ms_block_end = ticks_ms()
msg_formbuffer = "form buffer: " +\
str(ticks_diff(tick_ms_formbuffer_end, tick_ms_formbuffer_start)) +\
" ms"
msg_displayblock = "display.block(): " +\
str(ticks_diff(tick_ms_block_end, tick_ms_formbuffer_end)) +\
" ms"
print(msg_formbuffer)
print(msg_displayblock)
display.draw_text8x8(0, 300,
msg_formbuffer,
color565(255, 255, 255))
display.draw_text8x8(0, 310,
msg_displayblock,
color565(255, 255, 255))
else:
print("Must be 16 or 24 bit per pixel")
#==========================
#img_path = '/sd/images' #load from SD
img_path = '/images' #load from MicroPython device
IMG_WIDTH = 240
IMG_HEIGHT = 240
while True:
files = os.listdir(img_path)
bmp_files = [file for file in files if file.endswith('.bmp')]
bmp_file_list = sorted(bmp_files)
for f in bmp_file_list:
print()
display_bmp(img_path, f)
sleep(2)
mpy_s3_ili9341_images_touch.py
"""
ESP32-S3-DevKitC-1 running MicroPython v1.24.1
3.2" 320x240 IPS LCD (ILI9341 SPI) with Cap. Touch (FT6336) and Micro SD Slot
Display 240x240 RGB888/RGB565 bmp images on ILI9341 LCD.
Touch left 1/3 of LCD to show previous image.
Touch right 1/3 of LCD to show next image.
For ESP32-S3-DevKitC-1 has Octal SPIRAM,
makesure using MicroPython firmware of "spiram-oct" variant,
such as ESP32_GENERIC_S3-SPIRAM_OCT-20241129-v1.24.1.bin.
Libraries needed, install to /lib:
- ili9341.py
- sdcard (remark: NOT machine.SDCard)
- ft6x36.py
ili9341.py download from:
rdagger/micropython-ili9341
https://github.com/rdagger/micropython-ili9341
micropython-lib/sdcard.py can be download from
https://github.com/micropython/micropython-lib/blob/master/micropython/drivers/storage/sdcard/sdcard.py
or using Thonny > Tools > Manager Package...
lbuque/micropython-ft6x36
https://github.com/lbuque/micropython-ft6x36
All test images were generated using "Image Creator in Bing",
converted to 240x240 RGB888/RGB565 bmp using GIMP or Python.
To resize using GIMP/Python, read:
https://coxxect.blogspot.com/2024/11/resize-jpg-and-convert-to-bmp-in-rgb888.html
"""
import os, sys
from time import sleep, ticks_ms, ticks_diff
from machine import Pin, SPI, I2C
import sdcard
import gc
from ili9341 import Display, color565
from ft6x36 import FT6x36
print("====================================")
print(sys.implementation[0], os.uname()[3],
"\nrun on", os.uname()[4])
print("====================================")
LCD_CS=14 # low to select
LCD_RST=13
LCD_RS=12
LCD_SDI=11 #MOSI
LCD_SCK=10
LCD_LED=9
LCD_SDO=8 #MISO
CTP_SCL=7
CTP_RST=6
CTP_SDA=5
CTP_INT=4 #Not used
SD_CS=18 # low to select
# un-select both LCD and SD in power-up,
# to prevent un-used cs unstable and false selected.
pin_cs_lcd = Pin(LCD_CS, Pin.OUT)
pin_cs_lcd.value(1)
pin_cs_sd = Pin(SD_CS, Pin.OUT)
pin_cs_sd.value(1)
pin_backlight=Pin(LCD_LED, Pin.OUT)
pin_backlight.value(0)
# output a negative pulse to CTP_RST pin, to reset FT6336U
pin_CTP_RST=Pin(CTP_RST, Pin.OUT)
pin_CTP_RST.value(1)
sleep(0.1)
pin_CTP_RST.value(0)
sleep(0.1)
pin_CTP_RST.value(1)
sleep(0.1)
# rot and rot_set
# are used to better manager rotation/width/height of display and touch
rot=0 # 0 rotation = 0
# 1 rotation = 90
# 2 rotation = 180
# 3 rotation = 270
# rot_set[x][0] : ili9341 Display rotation
# rot_set[x][1] : ili9341 Display width
# rot_set[x][2] : ili9341 Display height
# rot_set[x][3] : ft6336U Touch rotation
# rot_set[x][4] : ft6336U Touch width
# rot_set[x][5] : ft6336U Touch height
rot_set = [[0, 240, 320, FT6x36.PORTRAIT_INVERTED, 240, 320],
[90, 320, 240, FT6x36.LANDSCAPE_INVERTED, 240, 320],
[180, 240, 320, FT6x36.PORTRAIT, 240, 320],
[270, 320, 240, FT6x36.LANDSCAPE, 240, 320]]
# spi share by LCD and SD
spi = SPI(1, baudrate=40000000, sck=LCD_SCK, mosi=LCD_SDI, miso=LCD_SDO)
display = Display(spi,
rotation=rot_set[rot][0],
width=rot_set[rot][1], height=rot_set[rot][2],
dc=Pin(LCD_RS), cs=Pin(LCD_CS), rst=Pin(LCD_RST))
display.invert(enable=True)
# i2c
i2c = I2C(0, scl=Pin(CTP_SCL), sda=Pin(CTP_SDA))
print("i2c", i2c)
print("I2C Scan:", i2c.scan())
touch = FT6x36(i2c,
width=rot_set[rot][4],
height=rot_set[rot][5],
rotation=rot_set[rot][3],)
print("Touch:", touch._width, "x", touch._height)
# remark:
# It's sdcard.SDCard
# NOT machine.SDCard
sd = sdcard.SDCard(spi, Pin(SD_CS))
vfs = os.VfsFat(sd)
os.mount(vfs, "/sd")
pin_backlight.value(1)
print("Display:", display.width, "x", display.height)
display.clear()
# Read bmp
def read_bmp(filename):
print("Read:", filename)
gc.collect()
print(gc.mem_free())
tick_ms_read_bmp_start = ticks_ms()
with open(filename, 'rb') as f:
bmp_data = f.read()
tick_ms_read_bmp_end = ticks_ms()
msg_read_bmp = "read bmp: " + str(ticks_diff(tick_ms_read_bmp_end, tick_ms_read_bmp_start)) + " ms"
display.draw_text8x8(0, 290,
msg_read_bmp,
color565(255, 255, 255))
print(msg_read_bmp)
return bmp_data
def display_bmp(src_path, src_file):
bmp_file = src_path + '/' + src_file
display.clear()
display.draw_text8x8(0, 250, src_path+'/', color565(255, 255, 255))
display.draw_text8x8(0, 260, src_file, color565(255, 255, 255))
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')
#==========================
# 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 = 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 = ticks_ms()
display.block(0, 0, 239, 239, buffer)
tick_ms_block_end = ticks_ms()
msg_formbuffer = "form buffer: " +\
str(ticks_diff(tick_ms_formbuffer_end, tick_ms_formbuffer_start)) +\
" ms"
msg_displayblock = "display.block(): " +\
str(ticks_diff(tick_ms_block_end, tick_ms_formbuffer_end)) +\
" ms"
print(msg_formbuffer)
print(msg_displayblock)
display.draw_text8x8(0, 300,
msg_formbuffer,
color565(255, 255, 255))
display.draw_text8x8(0, 310,
msg_displayblock,
color565(255, 255, 255))
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 = 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 = ticks_ms()
display.block(0, 0, 239, 239, buffer)
tick_ms_block_end = ticks_ms()
msg_formbuffer = "form buffer: " +\
str(ticks_diff(tick_ms_formbuffer_end, tick_ms_formbuffer_start)) +\
" ms"
msg_displayblock = "display.block(): " +\
str(ticks_diff(tick_ms_block_end, tick_ms_formbuffer_end)) +\
" ms"
print(msg_formbuffer)
print(msg_displayblock)
display.draw_text8x8(0, 300,
msg_formbuffer,
color565(255, 255, 255))
display.draw_text8x8(0, 310,
msg_displayblock,
color565(255, 255, 255))
else:
print("Must be 16 or 24 bit per pixel")
#==========================
#img_path = '/sd/images' #load from SD
img_path = '/images' #load from MicroPython device
IMG_WIDTH = 240
IMG_HEIGHT = 240
files = os.listdir(img_path)
bmp_files = [file for file in files if file.endswith('.bmp')]
bmp_file_list = sorted(bmp_files)
index = 0
while True:
print()
f = bmp_file_list[index]
display_bmp(img_path, f)
while True:
p = touch.get_positions()
if len(p) > 0:
print(p)
x = p[0][0]
if x < display.width//3:
print(p, ": Prev")
index = index-1
if index < 0:
index = len(bmp_file_list)-1
break
elif x > display.width*2//3:
print(p, ": Next")
index = index + 1
if index == len(bmp_file_list):
index = 0
break
else:
print(p, ": No op")
sleep(0.1)
Related:
~ Raspberry Pi Pico 2/MicroPython display bmp images on 240x240 ST7789 SPI Display.
Comments
Post a Comment