# MicroPython+ESP32 C3+ST7735S LCD屏 WIFI联网并显示时间 **Repository Path**: whltaoin_admin/MP_ESP32_ST7735S- ## Basic Information - **Project Name**: MicroPython+ESP32 C3+ST7735S LCD屏 WIFI联网并显示时间 - **Description**: MicroPython+ESP32 C3+ST7735S LCD屏 WIFI联网并显示时间 - **Primary Language**: Python - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 5 - **Forks**: 1 - **Created**: 2024-06-20 - **Last Updated**: 2025-09-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 案例地址:[https://gitee.com/whltaoin_admin/MP_ESP32_ST7735S-](https://gitee.com/whltaoin_admin/MP_ESP32_ST7735S-) # 展示效果 ![image.png](https://cdn.nlark.com/yuque/0/2024/png/38516294/1718890217272-67b09c3d-765d-47d9-90ed-473346f0e156.png#averageHue=%2330393f&clientId=u3332f8dd-f2c8-4&from=paste&height=474&id=u720afaf1&originHeight=474&originWidth=705&originalType=binary&ratio=1&rotation=0&showTitle=false&size=394802&status=done&style=none&taskId=uba8d33a0-a746-46f3-b471-b1c5e326104&title=&width=705)
![image.png](https://cdn.nlark.com/yuque/0/2024/png/38516294/1718890232680-c6a99b35-9500-494b-884f-a459db554645.png#averageHue=%232a2f36&clientId=u3332f8dd-f2c8-4&from=paste&height=442&id=uc2a7d330&originHeight=442&originWidth=765&originalType=binary&ratio=1&rotation=0&showTitle=false&size=461803&status=done&style=none&taskId=u6bca48b8-5f36-4634-be34-b7a9e275d60&title=&width=765) # TFT LCD模块参数介绍 ![image.png](https://cdn.nlark.com/yuque/0/2024/png/38516294/1718715582085-7f5ab813-5bcd-4ec3-8045-574aedb13f0e.png#averageHue=%23bebfbc&clientId=ud949da8e-0b3f-4&from=paste&height=396&id=uefa68353&originHeight=479&originWidth=517&originalType=binary&ratio=1&rotation=0&showTitle=false&size=150427&status=done&style=none&taskId=uc9f236c7-046e-419d-a1f2-2a769f4faad&title=&width=427)
![image.png](https://cdn.nlark.com/yuque/0/2024/png/38516294/1718715596955-60fd7fdb-f488-4dfc-8d13-b7742abed05f.png#averageHue=%23e0e2de&clientId=ud949da8e-0b3f-4&from=paste&height=657&id=C8VoP&originHeight=657&originWidth=568&originalType=binary&ratio=1&rotation=0&showTitle=false&size=219413&status=done&style=none&taskId=uef746d47-7526-48aa-ae44-bca3adc10c7&title=&width=568) 1. 名称:1.8 128*160 RGB_TFT 2. 驱动芯片:ST7735S # ESP32 C3 参数介绍(经典款) 1. 外观及引脚分布 ![image.png](https://cdn.nlark.com/yuque/0/2024/png/38516294/1718432999416-71e3f552-717c-4997-9143-3e7412d991e0.png#averageHue=%23acaa55&clientId=u30eba982-09a9-4&from=paste&height=727&id=u0b83248b&originHeight=727&originWidth=1040&originalType=binary&ratio=1&rotation=0&showTitle=false&size=337324&status=done&style=none&taskId=u74879def-8a59-479c-8288-3f9b6f8855e&title=&width=1040) # 接线 | LCD屏接口 | 功能 | ESP32 C3 对应连接引脚 | | --- | --- | --- | | GND | 电源(接地) | GND | | VCC | 电源(正极3.3V) | 3.3V | | SCL | SPI时钟线(与单片机SPI_CLK对接) | IO02 | | SDA | SPI数据线(与单片机SPI_MOSI对接) | IO03 | | RES | 复位 | IO10 | | DC | 数据/命令 | IO06 | | CS | SPI片选 | IO07 | | BL | 背光控制开发,默认可不接 | IO08 | # 项目初始化 ### 一、项目结构 ![image.png](https://cdn.nlark.com/yuque/0/2024/png/38516294/1718886904464-f3f0e7ac-fa18-4006-8e8a-be8d95102b71.png#averageHue=%2320221d&clientId=ue454659c-a402-4&from=paste&height=216&id=u01ff43b0&originHeight=216&originWidth=439&originalType=binary&ratio=1&rotation=0&showTitle=false&size=14871&status=done&style=none&taskId=ude3a6b99-e50d-4c20-8f44-3c048f40629&title=&width=439) ```shell boot.py:用于连接WIFI,打印初始化信息等。 font.bmf: 文字库 st7735.py: ST7735驱动 test.py: 测试效果文件 ufont.py: 字体文件 ``` ### 二、st7735.py ```python """ 使用方法(以合宙ESP32C3为例): from machine import SPI, Pin from st7735 import ST7735 spi = SPI(1, 30000000, sck=Pin(2), mosi=Pin(3)) ST7735(spi, rst=10, dc=6, cs=7, bl=11, width=160, height=80, rotate=1) # 直插横屏显示 ST7735(spi, rst=10, dc=6, cs=7, bl=11, width=160, height=80, rotate=0) # 直插竖屏显示 """ import gc import time import math import machine import framebuf from micropython import const SWRESET = const(0x01) SLPOUT = const(0x11) NORON = const(0x13) INVOFF = const(0x20) DISPON = const(0x29) CASET = const(0x2A) RASET = const(0x2B) RAMWR = const(0x2C) MADCTL = const(0x36) COLMOD = const(0x3A) FRMCTR1 = const(0xB1) FRMCTR2 = const(0xB2) FRMCTR3 = const(0xB3) INVCTR = const(0xB4) PWCTR1 = const(0xC0) PWCTR2 = const(0xC1) PWCTR3 = const(0xC2) PWCTR4 = const(0xC3) PWCTR5 = const(0xC4) VMCTR1 = const(0xC5) GMCTRP1 = const(0xE0) GMCTRN1 = const(0xE1) ROTATIONS = [0x00,0x60] # 只给了两个旋转方向 def color(r, g, b): i = (((b & 0xF8) << 8) | ((g & 0xFC) << 3) | (r >> 3)).to_bytes(2, "little") return (i[0] << 8) + i[1] RED = color(255, 0, 0) GREEN = color(0, 255, 0) BLUE = color(0, 0, 255) WHITE = color(255, 255, 255) BLACK = color(0, 0, 0) class ST7735(framebuf.FrameBuffer): def __init__(self, spi, rst, dc, cs, bl=None, width=None, height=None, offset=None, rotate=1, rgb=True): """ :param spi: :param rst: :param dc: :param cs: 使能 :param bl: 背光 :param width: 宽度 :param height: 高度 :param offset: 偏移 (x, y): (23, -1)|(-1, 23) :param rotate: 旋转 0 横屏 1 竖屏 :param rgb: RGB 色彩模式 """ # 根据方向自动设置偏移 self.rotate = rotate self.offset = offset self.rgb = rgb if offset is None and rotate == 1: self.offset = (-1, 23) elif offset is None and rotate == 0: self.offset = (23, -1) self.width = width self.height = height self.spi = spi self.rst = machine.Pin(rst, machine.Pin.OUT, machine.Pin.PULL_DOWN) self.dc = machine.Pin(dc, machine.Pin.OUT, machine.Pin.PULL_DOWN) self.cs = machine.Pin(cs, machine.Pin.OUT, machine.Pin.PULL_DOWN) if bl is not None: self.bl = machine.PWM(machine.Pin(bl)) gc.collect() self.buffer = bytearray(self.height * self.width * 2) super().__init__(self.buffer, self.width, self.height, framebuf.RGB565) self.init() self.set_windows() self.clear() def set_windows(self, x_start=None, y_start=None, x_end=None, y_end=None): """ 设置窗口 :return: """ x_start = (x_start + self.offset[0] + 1) if x_start is not None else (self.offset[0] + 1) x_end = x_end + self.rotate + self.offset[0] if x_end is not None else self.width + self.rotate + \ self.offset[0] y_start = y_start + self.offset[1] + 1 if y_start is not None else self.offset[1] + 1 y_end = y_end + self.rotate + self.offset[1] if y_end is not None else self.height + self.rotate + \ self.offset[1] self.write_cmd(CASET) self.write_data(bytearray([0x00, x_start, 0x00, x_end])) self.write_cmd(RASET) self.write_data(bytearray([0x00, y_start, 0x00, y_end])) self.write_cmd(RAMWR) def init(self): self.reset() self.write_cmd(SWRESET) time.sleep_us(150) self.write_cmd(SLPOUT) time.sleep_us(300) self.write_cmd(FRMCTR1) self.write_data(bytearray([0x01, 0x2C, 0x2D])) self.write_cmd(FRMCTR2) self.write_data(bytearray([0x01, 0x2C, 0x2D])) self.write_cmd(FRMCTR3) self.write_data(bytearray([0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D])) time.sleep_us(10) self.write_cmd(INVCTR) self.write_data(bytearray([0x07])) self.write_cmd(PWCTR1) self.write_data(bytearray([0xA2, 0x02, 0x84])) self.write_cmd(PWCTR2) self.write_data(bytearray([0xC5])) self.write_cmd(PWCTR3) self.write_data(bytearray([0x0A, 0x00])) self.write_cmd(PWCTR4) self.write_data(bytearray([0x8A, 0x2A])) self.write_cmd(PWCTR5) self.write_data(bytearray([0x8A, 0xEE])) self.write_cmd(VMCTR1) self.write_data(bytearray([0x0E])) self.write_cmd(INVOFF) self.write_cmd(MADCTL) self.write_data(bytearray([ROTATIONS[self.rotate] | 0x00 if self.rgb else 0x08])) self.write_cmd(COLMOD) self.write_data(bytearray([0x05])) self.write_cmd(GMCTRP1) self.write_data( bytearray([0x02, 0x1c, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2d, 0x29, 0x25, 0x2b, 0x39, 0x00, 0x01, 0x03, 0x10])) self.write_cmd(GMCTRN1) self.write_data( bytearray([0x03, 0x1d, 0x07, 0x06, 0x2e, 0x2c, 0x29, 0x2d, 0x2e, 0x2e, 0x37, 0x3f, 0x00, 0x00, 0x02, 0x10])) self.write_cmd(NORON) time.sleep_us(10) self.write_cmd(DISPON) time.sleep_us(100) self.cs(1) def reset(self): """ 设备重置 :return: """ self.rst(1) time.sleep(0.2) self.rst(0) time.sleep(0.2) self.rst(1) time.sleep(0.2) def write_cmd(self, cmd): self.dc(0) self.cs(0) self.spi.write(bytearray([cmd])) self.cs(1) def write_data(self, buf): self.dc(1) self.cs(0) self.spi.write(buf) self.cs(1) def back_light(self, value): """ 背光调节 :param value: 背光���级 0 ~ 256 :return: """ self.bl.freq(1000) if value >= 0xff: value = 0xff data = value * 0xffff >> 8 self.bl.duty_u16(data) def clear(self): """ 清屏 :return: """ self.fill(0) self.show() def show(self): """ 显示 :return: """ self.set_windows() # 如果没有这行就会偏移 self.write_data(self.buffer) def circle(self, center, radius, c=color(255, 255, 255), section=100): """ 画圆 :param c: 颜色 :param center: 中心(x, y) :param radius: 半径 :param section: 分段 :return: """ arr = [] for m in range(section + 1): x = round(radius * math.cos((2 * math.pi / section) * m - math.pi) + center[0]) y = round(radius * math.sin((2 * math.pi / section) * m - math.pi) + center[1]) arr.append([x, y]) for i in range(len(arr) - 1): self.line(*arr[i], *arr[i + 1], c) def image(self, file_name): with open(file_name, "rb") as bmp: for b in range(0, 80 * 160 * 2, 1024): self.buffer[b:b + 1024] = bmp.read(1024) self.show() ``` ### 三、ufont.py ```python __version__ = 3 import time import struct import framebuf DEBUG = False def timeit(f, *args, **kwargs): try: myname = str(f).split(' ')[1] except: myname = "UNKONW" def new_func(*args, **kwargs): if DEBUG: try: t = time.ticks_us() result = f(*args, **kwargs) delta = time.ticks_diff(time.ticks_us(), t) print('Function {} Time = {:6.3f}ms'.format(myname, delta / 1000)) except AttributeError: t = time.perf_counter_ns() result = f(*args, **kwargs) delta = time.perf_counter_ns() - t print('Function {} Time = {:6.3f}ms'.format(myname, delta / 1000000)) return result else: return f(*args, **kwargs) return new_func class BMFont: @staticmethod def _list_to_byte(arr): b = 0 for a in arr: b = (b << 1) + a return bytes([b]) @timeit def _bit_list_to_byte_data(self, bit_list): """将点阵转换为字节数据 Args: bit_list: Returns: """ byte_data = b'' for _col in bit_list: for i in range(0, len(_col), 8): byte_data += self._list_to_byte(_col[i:i + 8]) return byte_data @timeit def __init__(self, font_file): self.font_file = font_file self.font = open(font_file, "rb", buffering=0xff) self.bmf_info = self.font.read(16) if self.bmf_info[0:2] != b"BM": raise TypeError("字体文件格式不正确: " + font_file) self.version = self.bmf_info[2] if self.version != 3: raise TypeError("字体文件版本不正确: " + str(self.version)) self.map_mode = self.bmf_info[3] # 映射方式 self.start_bitmap = struct.unpack(">I", b'\x00' + self.bmf_info[4:7])[0] # 位图开始字节 self.font_size = self.bmf_info[7] # 字体大小 self.bitmap_size = self.bmf_info[8] # 点阵所占字节 @timeit def _to_bit_list(self, byte_data, font_size, *, _height=None, _width=None): """将字节数据转��为点阵数据 Args: byte_data: 字节数据 font_size: 字号大小 _height: 字体原高度 _width: 字体原宽度 Returns: """ _height = _height or self.font_size _width = _width or self.bitmap_size // self.font_size * 8 new_bitarray = [[0 for j in range(font_size)] for i in range(font_size)] for _col in range(len(new_bitarray)): for _row in range(len(new_bitarray[_col])): _index = int(_col / (font_size / _height)) * _width + int(_row / (font_size / _width)) new_bitarray[_col][_row] = byte_data[_index // 8] >> (7 - _index % 8) & 1 return new_bitarray @timeit def _color_render(self, bit_list, color): """将二值点阵图像转换为 RGB565 彩色字节图像 Args: bit_list: color: Returns: """ color_array = b"" for _col in range(len(bit_list)): for _row in range(len(bit_list)): color_array += struct.pack("H", self.font.read(2))[0] if word_code == target_code: return (mid - 16) >> 1 elif word_code < target_code: end = mid - 2 else: start = mid + 2 return -1 @timeit def get_bitmap(self, word): """获取点阵图 Args: word: 字符 Returns: bytes 字符点阵 """ index = self._get_index(word) if index == -1: return b'\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x0f\xcf\xf3\xcf\xf3\xff\xf3\xff\xcf\xff?\xff?\xff\xff\xff' \ b'?\xff?\xff\xff\xff\xff' self.font.seek(self.start_bitmap + index * self.bitmap_size, 0) return self.font.read(self.bitmap_size) @timeit def text(self, display, string, x, y, color=1, *, font_size=None, reverse=False, clear=False, show=False, half_char=True, auto_wrap=False, **kwargs): """通过显示屏显示文字 使用此函数显示文字,必须先确认显示对象是否继承与 framebuf.FrameBuffer。 如果显示对象没有 clear 方法,需要自行调用 fill 清屏 Args: display: 显示实例 string: 字符串 x: 字体左上角 x 轴 y: 字体左上角 y 轴 color: 颜色 font_size: 字号 reverse: 是否反转背景 clear: 是否清��之前显示的内容 show: 是否立刻显示 half_char: 是否半字节显示 ASCII 字符 auto_wrap: 自动换行 **kwargs: Returns: None """ font_size = font_size or self.font_size initial_x = x # 清屏 try: display.clear() if clear else 0 except AttributeError: print("请自行调用 display.fill(*) 清屏") for char in range(len(string)): # 是否自动换行 if auto_wrap: if auto_wrap and ((x + font_size // 2 >= 128 and ord(string[char]) < 128 and half_char) or (x + font_size >= 128 and (not half_char or ord(string[char]) > 128))): y += font_size x = initial_x # 回车 if string[char] == '\n': y += font_size x = initial_x continue # Tab elif string[char] == '\t': x = ((x // font_size) + 1) * font_size + initial_x % font_size continue # 其它的控制字符不显示 elif ord(string[char]) < 16: continue # 超过范围的字符不会显示* if x > 160 or y > 80: continue byte_data = list(self.get_bitmap(string[char])) # 反��� if reverse: for _pixel in range(len(byte_data)): byte_data[_pixel] = ~byte_data[_pixel] & 0xff # 缩放和色彩* if color > 1 or font_size != self.font_size: bit_data = self._to_bit_list(byte_data, font_size) if color > 1: display.blit( framebuf.FrameBuffer(bytearray(self._color_render(bit_data, color)), font_size, font_size, framebuf.RGB565), x, y) else: display.blit( framebuf.FrameBuffer(bytearray(self._bit_list_to_byte_data(bit_data)), font_size, font_size, framebuf.MONO_HLSB), x, y) else: display.blit(framebuf.FrameBuffer(bytearray(byte_data), font_size, font_size, framebuf.MONO_HLSB), x, y) # 英文字符半格显示 if ord(string[char]) < 128 and half_char: x += font_size // 2 else: x += font_size display.show() if show else 0 def char(self, char, color=1, font_size=None, reverse=False): """ 获取字体字��数据 在没有继承 framebuf.FrameBuffer 的显示驱动,或者内存不足以将一整个屏幕载入缓存帧时 可以直接获取单字的字节数据,局部更新 Args: char: 单个字符 color: 颜色 font_size: 字体大小 reverse: 反转 Returns: bytearray """ font_size = font_size or self.font_size byte_data = list(self.get_bitmap(char)) # 反转 if reverse: for _pixel in range(len(byte_data)): byte_data[_pixel] = ~byte_data[_pixel] & 0xff if color > 1 or font_size != self.font_size: bit_data = self._to_bit_list(byte_data, font_size) if color > 1: return self._color_render(bit_data, color) else: return self._bit_list_to_byte_data(bit_data) else: return bytearray(byte_data) ``` ### 四、字库font.bmf 下载地址:[https://gitee.com/whltaoin_admin/MP_ESP32_ST7735S-](https://gitee.com/whltaoin_admin/MP_ESP32_ST7735S-) # WIFI连接初始化屏幕 ```python # This file is executed on every boot (including wake-boot from deepsleep) #import esp #esp.osdebug(None) #import webrepl #webrepl.start() # print("varin") from machine import Pin,SPI from st7735 import ST7735,color from ufont import BMFont import time import random import network import ntptime font = BMFont("font.bmf") # WIFI名称和连接密码 ssid="aaa" password="12345678" spi = SPI(1,baudrate = 60_000_000,polarity = 0,sck = Pin(2),mosi = Pin(3),miso = None) tft = ST7735(spi,rst = 10,dc = 6,cs = 7,bl = 8, width = 160, height = 127, offset=(-1,0) ,rotate = 1, rgb = True) def network_connect(): font.text(tft,"WIFI网络连接中~" ,0,0,color(random.randint(1,256),random.randint(1,256),random.randint(1,256)),font_size = 16,reverse = False,clear = False,show = True,half_char = True,auto_wrap = False) sta = network.WLAN(network.STA_IF) sta.active(True) # 判断WIF是否连接,如无连接通过循环等待WIFI连接后再执行后续代码 if not sta.isconnected(): sta.connect(ssid,password) while not sta.isconnected(): pass print(sta.ifconfig()) ntptime.settime() print() font.text(tft,"WIFI网络连接成功~" ,0,0,color(random.randint(1,256),random.randint(1,256),random.randint(1,256)),font_size = 16,reverse = False,clear = False,show = True,half_char = True,auto_wrap = False) font.text(tft,"WIFI名称为:"+ssid ,0,20,color(random.randint(1,256),random.randint(1,256),random.randint(1,256)),font_size = 16,reverse = False,clear = False,show = True,half_char = True,auto_wrap = False) font.text(tft,"IP地址:"+sta.ifconfig()[0] ,0,40,color(random.randint(1,256),random.randint(1,256),random.randint(1,256)),font_size = 16,reverse = False,clear = False,show = True,half_char = True,auto_wrap = False) while True: # 实时更新屏幕时间, t=time.localtime() timeStr= '当前时间是:{}:{}:{}'.format( t[3], t[4], t[5]) font.text(tft,timeStr ,0,60,color(random.randint(1,256),random.randint(1,256),random.randint(1,256)),font_size = 16,reverse = False,clear = False,show = True,half_char = True,auto_wrap = False) font.text(tft,"今天又是加油的一天呀" ,0,80,color(random.randint(1,256),random.randint(1,256),random.randint(1,256)),font_size = 16,reverse = False,clear = False,show = True,half_char = True,auto_wrap = False) if __name__ == "__main__": network_connect() ``` ```python # This file is executed on every boot (including wake-boot from deepsleep) #import esp #esp.osdebug(None) #import webrepl #webrepl.start() # print("varin") from machine import Pin,SPI from st7735 import ST7735,color from ufont import BMFont import time import random import network import ntptime from machine import RTC import time rtc = RTC() font = BMFont("font.bmf") # WIFI名称和连接密码 ssid="aaa" password="12345678" spi = SPI(1,baudrate = 60_000_000,polarity = 0,sck = Pin(2),mosi = Pin(3),miso = None) tft = ST7735(spi,rst = 10,dc = 6,cs = 7,bl = 8, width = 160, height = 127, offset=(-1,0) ,rotate = 1, rgb = True) def network_connect(): font.text(tft,"WIFI网络连接中~" ,0,0,color(random.randint(1,256),random.randint(1,256),random.randint(1,256)),font_size = 16,reverse = False,clear = False,show = True,half_char = True,auto_wrap = False) sta = network.WLAN(network.STA_IF) sta.active(True) # 判断WIF是否连接,如无连接通过循环等待WIFI连接后再执行后续代码 if not sta.isconnected(): sta.connect(ssid,password) while not sta.isconnected(): pass print(sta.ifconfig()) ntptime.settime() print() font.text(tft,"WIFI网络连接成功~" ,0,0,color(random.randint(1,256),random.randint(1,256),random.randint(1,256)),font_size = 16,reverse = False,clear = False,show = True,half_char = True,auto_wrap = False) font.text(tft,"WIFI名称为:"+ssid ,0,20,color(random.randint(1,256),random.randint(1,256),random.randint(1,256)),font_size = 16,reverse = False,clear = False,show = True,half_char = True,auto_wrap = False) font.text(tft,"IP地址:"+sta.ifconfig()[0] ,0,40,color(random.randint(1,256),random.randint(1,256),random.randint(1,256)),font_size = 16,reverse = False,clear = False,show = True,half_char = True,auto_wrap = False) while True: # 实时更新屏幕时间 t= rtc.datetime() timeStr= '当前时间是:{}:{}:{}'.format( t[4]+8, t[5], t[6]) font.text(tft,timeStr ,0,60,color(random.randint(1,256),random.randint(1,256),random.randint(1,256)),font_size = 16,reverse = False,clear = False,show = True,half_char = True,auto_wrap = False) font.text(tft,"今天又是加油的一天呀" ,0,80,color(random.randint(1,256),random.randint(1,256),random.randint(1,256)),font_size = 16,reverse = False,clear = False,show = True,half_char = True,auto_wrap = False) time.sleep(1) if __name__ == "__main__": network_connect() ``` # 注意 本案例初始化模块代码是参照以下作者,若侵权,请联系本作者。
[https://blog.csdn.net/zhusongziye/article/details/135757477](https://blog.csdn.net/zhusongziye/article/details/135757477)