Python编程 + SD存储卡 + TFT屏幕放映图片
必备
话说 ESP32 ————— ESP32官方文档
,
,
,
绪论
【 ESP32读取SD卡图片并放映到TFT显示屏 】
本章:结合Python库函数,实现ESP-WROOM-32 芯片通过SPI通信,将SD卡 存储的图片显示在TFT-ST7735S彩色显示屏 上。
① 、 ESP32有两种使用SD卡的方法,一种是使用SPI接口访问SD卡,另一种是使用SDMMC接口访问SD卡 。
,
,
1、设备、软件与库函数源
1.1 所用到的设备:
- ESP-WROOM-32 芯片;
- SD卡模块 + 存储卡;
- TFT - ST7735S显示屏 128X160;
- 面包板+杜邦线若干
电脑、Type-C数据线······☺☺
1.2 所用到的软件:
- Thonny
1.3 库函数
-
SD卡驱动库:
驱动函数: sdcard.py- Github上的源码:sdcard.py
-
TFT-ST7735S显示屏库:
驱动函数: ST7735.py -
本章源码
- 我的分享:点击获取实验的 Python 程序源码
注意: 与GitHub上下载的源码不同,本章涉及到两个从机设备(SD卡、TFT屏),同时用到两个 SPI协议。 因此,相关引脚的对接有所改动,请参考源码,或者文章论述。
,
,
,
2、实验一、读写SD卡、挂载目录
能够对TF存储卡的根目录、子目录的任意文件(.txt)读写;
还能将存储卡的目录挂在到MicroPython设备目录中。
2.1、接线
ESP32引脚 | SD卡引脚 | 简诉 |
---|---|---|
D14 | CS | 片选信号 |
D17 | SCK | 时钟 |
D15 | MOSI | SPI 主机输出 从机接受 |
D13 | MISO | SPI 主机接受 从机输出 |
VN | VCC | 本模块应接5V |
GND | GND | 地管教 |
2.2 文件操作指令:
2.2-1. 打开文件:
open(filename, mode,encoding): 打开指定路径的文件
①filename为文件名,<相对路径或绝对路径+文件名+格式>
②mode为打开模式('r’为只读,'w’为写入,'a’为追加,'r+'读写,'w+'读写,'a+'追加读写 等)。
③encoding为打开文件编码格式
2.2-2、读取文件:
read(size)读取指定数量的数据。
readline()读取一行数据。
readlines()读取所有行数据并返回一个列表。
2.2-3、写入文件:
write(data)写入数据到文件。
writelines(lines)将列表中的所有行写入文件。
2.2-4、文件操作:
seek(offset, whence)移动文件指针到指定位置。
tell()返回当前文件指针位置。
flush()刷新文件缓冲区。
(1、f.seek(offset, whence)
offset参数用于指定需要移动的偏移量,可以是正数、负数或零。具体含义如下:
当offset为正数时,表示从当前位置向文件尾移动的偏移量。
当offset为负数时,表示从当前位置向文件头移动的偏移量。
当offset为零时,表示从文件头移动到指定位置。
whence参数用于指定offset的起始位置,可以接受三个常量:
0:表示起始位置为文件头。
1:表示起始位置为当前位置。
2:表示起始位置为文件尾。
(2、f.tell()
返回当前文件指针的位置,(按字节计算)
中文大部分占用3 字节
(3、f.flush()
flush()方法可以手动地将缓冲区中的数据写入到磁盘文件中,
从而避免缓冲区满了才写入的问题,同时也可以确保文件的实时性
2.2-5. 关闭文件:
close()关闭当前操作的文件。
例1: fb=open("/sd/test.txt",mode="r",encoding="utf-8")
ct = fb.read()
print(ct)
fb.close()
当然,如果怕会忘记了使用 close() ,也可以换一种操作打开文件:
【 with open(文件地址)as 函数名: 】
这样就不需要你手动调用fb.close(),自动帮你关闭文件。
例2: with open("/sdtest.txt",mode="r",encoding="utf-8") as fb:
ct = fb.read()
print(ct)
,
,
2.3、上传
,
2.3、读写SD卡文件
运行程序 (一): SDmain.py
注意!仔细审核 程序和实际 中 SPI接线, 不要接错了
import time
import os, sdcard, machine
from machine import SPI
from machine import Pin
def Time_is():
Ntime = time.localtime(time.time()) #获得本地日期(元组)
str_time = "%s-%s-%s %s:%s:%s" % (Ntime[0],Ntime[1],Ntime[2],
Ntime[3],Ntime[4],Ntime[5],) #转换字符串并按格式保存
return str_time
SD_CS = Pin(14)`在这里插入代码片`
SD_sck = Pin(17)
SD_mosi = Pin(15)
SD_miso = Pin(13)
sd = sdcard.SDCard(SPI(2, sck=SD_sck, mosi=SD_mosi, miso=SD_miso), SD_CS)
def sdtest_1():
os.mount(sd,"/sd")
print(os.listdir("/sd"))
file_name="/sd/Notifications/text.txt" #操作的文件路径
message = "/sd/Notifications/mymsg.txt" #文件路径
w_txt="大家好,人生苦短,我选Python和MicroPython"
f=open(file_name,"w") #打开文件,进行写操作
print(f.write(w_txt)) #写入
f.close() #关闭文件
f=open(file_name) #打开文件,进行读操作
r_txt=f.read()
print(r_txt)
f.close() #关闭文件
a_txt = ",是的,这是追加写入的内容!\r\n当然,如果你想要更多,请输入:"
f=open(file_name,"a") #打开文件,进行读操作
f.write(a_txt)
f.write("%s" % Time_is())
f.close() #关闭文件
f=open(file_name,"r") #打开文件,进行读操作
r_linetxt=f.readline() #读一行
print(r_linetxt)
f.close() #关闭文件
f=open(file_name,"r+") #打开文件,进行读操作
print(f"********第一次读取前的指针位置:{f.tell()}")
r_stxt_1=f.read(4) #读取前 8 个 (指针也指到这儿【中文占 3 字节,英文占 1 字节】)
print(r_stxt_1)
print(f"******写前的指针位置:{f.tell()}")
f.write(r_stxt_1)
print(f"******写后的指针位置:{f.tell()}")
r_stxt_2 = f.read()
print(f"******第二次读取后的指针位置:{f.tell()}")
print(r_stxt_2)
f.seek(-17,1) #指针从当前位置,往后偏移17位
print(f"******偏移后的指针位置:{f.tell()}")
print(f.read())
f.close() #关闭文件
f=open(file_name,"r") #打开文件,进行读操作
r_alltxt=f.read()
print("******最后重新打开文件读取完整的内容:\n"+r_alltxt)
f.close() #关闭文件
if __name__ == '__main__':
sdtest_1()
运行结果 (一)
2.4 挂载SD卡目录
运行程序 (二): mount_sd.py
from machine import Pin,SPI
from sdcard import SDCard
import time,os,esp
cs = Pin(14,Pin.OUT)
spi = SPI(2,sck = Pin(17),mosi = Pin(15),miso = Pin(13))
sd = SDCard(spi,cs)
#挂载SD到MicroPython设备目录
def mount_sd():
os.VfsFat(sd)
os.mount(sd,"/sd") # 挂载SD卡
dirs=os.listdir('/sd')
fb = os.statvfs('/sd')
print("SD capacity = %d B %d M"%(fb[0] * fb[2],fb[0] * fb[2]/1024/1024))
print("SD Remaining = %d B %d M"%(fb[0] * fb[3],fb[0] * fb[3]/1024/1024))
print(dirs)
print("esp32 Flash容量: %d M"%(esp.flash_size()/1024/1024))
#检测SD卡
def test_sd():
vfs = os.VfsFat(sd)
os.mount(vfs, "/fc")
line = "abcdefghijklmnopqrstuvwxyz\n"
lines = line * 200 # 5400 chars
short = "1234567890\n"
fn = "/fc/rats.txt"
print()
print("Multiple block read/write")
with open(fn, "w") as f:
n = f.write(lines)
print(n, "bytes written")
n = f.write(short)
print(n, "bytes written")
n = f.write(lines)
print(n, "bytes written")
with open(fn, "r") as f:
result1 = f.read()
print(len(result1), "bytes read")
fn = "/fc/rats1.txt"
print()
print("Single block read/write")
with open(fn, "w") as f:
n = f.write(short) # one block
print(n, "bytes written")
with open(fn, "r") as f:
result2 = f.read()
print(len(result2), "bytes read")
os.umount("/fc")
print()
print("Verifying data read back")
success = True
if result1 == "".join((lines, short, lines)):
print("Large file Pass")
else:
print("Large file Fail")
success = False
if result2 == short:
print("Small file Pass")
else:
print("Small file Fail")
success = False
print()
print("Tests", "passed" if success else "failed")
if __name__ == "__main__":
mount_sd()
test_sd()
运行结果 (二)
再者, 对MicroPython设备区刷新一下,你就能看到SD卡的目录了:
,
,
,
,
,
,
3、实验二,驱动ST7735S显示屏
3.1 有关 TFT 显示屏的
3.2 接线
ESP32引脚 | ST7735引脚 | 简诉 |
---|---|---|
D18 | SLK | SIP时钟线 |
D21 | SDA | SPI数据线 ( mosi ) |
D23 | RST | 复位 |
D16 | DC | 数据/命令选择 |
D5 | CS | 片选信号 |
BLK | 留空 | 背光控制开关 |
3V3 | VCC | 2.8V~3.3V |
GND | GND | 地管教 |
3.3 上传
程序运行 (三):tftbmp.py
注意!仔细审核 程序和实际 中 SPI接线, 不要接错了
from ST7735 import TFT,TFTColor
from machine import SPI,Pin
spi_TFT = SPI(2, baudrate=20000000, polarity=0, phase=0, sck=Pin(18), mosi=Pin(21))
tft=TFT(spi_TFT,16,23,5) # DC,REA,CS
tft.initr()
tft.rgb(True)
tft.fill(TFT.BLACK)
f=open('test128x160.bmp', 'rb')
if f.read(2) == b'BM': #header
dummy = f.read(8) #file size(4), creator bytes(4)
offset = int.from_bytes(f.read(4), 'little')
hdrsize = int.from_bytes(f.read(4), 'little')
width = int.from_bytes(f.read(4), 'little')
height = int.from_bytes(f.read(4), 'little')
if int.from_bytes(f.read(2), 'little') == 1: #planes must be 1
depth = int.from_bytes(f.read(2), 'little')
if depth == 24 and int.from_bytes(f.read(4), 'little') == 0:#compress method == uncompressed
print("Image size:", width, "x", height)
rowsize = (width * 3 + 3) & ~3
if height < 0:
height = -height
flip = False
else:
flip = True
w, h = width, height
if w > 128: w = 128
if h > 160: h = 160
tft._setwindowloc((0,0),(w - 1,h - 1))
for row in range(h):
if flip:
pos = offset + (height - 1 - row) * rowsize
else:
pos = offset + row * rowsize
if f.tell() != pos:
dummy = f.seek(pos)
for col in range(w):
bgr = f.read(3)
tft._pushcolor(TFTColor(bgr[2],bgr[1],bgr[0]))
spi_TFT.deinit()
运行结果 (三):
得到:
,
,
,
,
,
,
4、实验三、 读取存储卡中的图片到FT7735S显示屏
4.1接线
就是结合 2.1 和3.1 的接线
要保证与程序路线一致!
4.2 上传
首先,将 下载的资源中文件夹 pictures 里的5个照片 都 复制到 SD卡根目录下 Pictures 里,(注意目录的字母大小写)
-
方法1
通过USB读卡器,插入电脑复制粘贴所有图片到目标位置; -
方法2
直接通过Thonny上传图片到目录 /sd/Pictures 。
(前提是已经通过“实验一”将SD卡目录挂载上去,在【MicroPython设备】中点击并打开进入SD目录到 /sd/Pictures,随后在【此电脑】找到图片并上传)
这也是挂载SD卡目录的好处之一吧,避开插拔USB读卡器上传图片。
其次,上传库函数到设备:
4.2 综合读写SD卡和TFT屏幕显示
运行程序 (四): main.py
import os, esp, time
from machine import Pin,SPI
from sdcard import SDCard
from ST7735 import TFT,TFTColor
#####################配置SD卡的SPI通信,调用SD卡相关库函数###############
SD_cs = Pin(14,Pin.OUT)
spi_SD = SPI(1,sck = Pin(17),mosi = Pin(15),miso = Pin(13))
sd = SDCard(spi_SD,SD_cs)
os.VfsFat(sd)
os.mount(sd,"/sd") # 挂载SD卡
#####################配置TFT7735s显示屏的SPI通信,调用TFT相关库函数###############
spi_TFT = SPI(2, baudrate=20000000, polarity=0, phase=0, sck=Pin(18), mosi=Pin(21))
tft=TFT(spi_TFT,16,23,5) # DC,REA,CS
tft.initr() #初始化红色选项版本
tft.rgb(True) #RGB/BGR 格式 (反转颜色设置)
tft.fill(TFT.BLACK) #用给定的颜色填充屏幕
#.bmp格式的一些照片路径:(第一个是单片机内存的bmp图片,后面的都是SD卡目录的BMP图片)
path = ['test128x160.bmp', '/sd/Pictures/test1.bmp', '/sd/Pictures/test2.bmp', '/sd/Pictures/test3.bmp', '/sd/Pictures/test4.bmp', '/sd/Pictures/jpg1.jpg']
def send_picture(Photo_path):
with open(Photo_path, 'rb') as f:
if f.read(2) == b'BM': #header
dummy = f.read(8) #file size(4), creator bytes(4)
offset = int.from_bytes(f.read(4), 'little')
hdrsize = int.from_bytes(f.read(4), 'little')
width = int.from_bytes(f.read(4), 'little')
height = int.from_bytes(f.read(4), 'little')
if int.from_bytes(f.read(2), 'little') == 1: #planes must be 1
depth = int.from_bytes(f.read(2), 'little')
if depth == 24 and int.from_bytes(f.read(4), 'little') == 0: #compress method == uncompressed 压缩方法 == 未压缩
print("Image size:", width, "x", height)
rowsize = (width * 3 + 3) & ~3
if height < 0:
height = -height
flip = False
else:
flip = True
w, h = width, height
if w > 128: w = 128
if h > 160: h = 160
tft._setwindowloc((0,0),(w - 1,h - 1)) # 设置绘制颜色的矩形区域(起始坐标,终点坐标)¥¥¥¥¥¥¥¥¥¥¥
for row in range(h):
if flip:
pos = offset + (height - 1 - row) * rowsize
else:
pos = offset + row * rowsize
if f.tell() != pos: #读图片文件的当前指针位置
dummy = f.seek(pos) #就 将当前指针位置 设置为 pos,且赋值给 dummy
for col in range(w):
bgr = f.read(3) #每次读取图片文件中的 3 字节
tft._pushcolor(TFTColor(bgr[2],bgr[1],bgr[0])) #向设备推送读取到的颜色
# spi.deinit()
def TFT_show():
for i in range(1,len(path),1):
print(f'第{i}张图片:',end='')
send_picture(path[i])
def Time_is():
Ntime = time.localtime(time.time()) #获得本地日期(元组)
str_time = "%s-%s-%s %s:%s:%s" % (Ntime[0],Ntime[1],Ntime[2],Ntime[3],Ntime[4],Ntime[5],) #转换字符串并按格式保存
return str_time
def sdlog_1():
file_name="/sd/runniglog.txt" #日志文件路径
w_txt = "欢迎来到SD卡存储空间!————%s \n" % Time_is() #欢迎词+日期
f=open(file_name,"a") #打开文件,进行追加写入操作
print(f.write(w_txt)) #写入
f.close() #关闭文件
with open(file_name,"r", encoding="uft-8") as f: #打开文件,进行只读操作
r_txt=f.read()
print("获取到记录内容:\n"+r_txt)
def mount_sd():
dirs=os.listdir('/sd')
fb = os.statvfs('/sd')
print("SD capacity = %d B %d M"%(fb[0] * fb[2],fb[0] * fb[2]/1024/1024))
print("SD Remaining = %d B %d M"%(fb[0] * fb[3],fb[0] * fb[3]/1024/1024))
print(dirs)
print("esp32 Flash容量: %d M"%(esp.flash_size()/1024/1024))
if __name__ == '__main__':
sdlog_1()
mount_sd()
TFT_show()
运行结果 (四):
读取存储卡换图效果(视频):
ESP32读取SD卡图片,播放于ST7735S彩屏
告诉:
1、microPython不能显示中文文件名和路径名
2、