1.开发板:ESP32-S3 WROOM-1-N8R8
2.屏幕:ST7789
按下列代码里的GPIO接线:
import machine import time import framebuf import math import gc # ================= 硬件配置 ================= PIN_SCL = 9 PIN_SDA = 8 PIN_CS = 5 PIN_DC = 6 PIN_RST = 7 PIN_BL = 4 SCREEN_W = 240 SCREEN_H = 240 # =========================================== # --- 辅助函数:手动实现画圆 (因为 framebuf 不支持) --- def draw_circle_pixel(fb, x0, y0, radius, color): """使用中点画圆算法绘制空心圆""" x = radius y = 0 err = 0 while x >= y: fb.pixel(x0 + x, y0 + y, color) fb.pixel(x0 + y, y0 + x, color) fb.pixel(x0 - y, y0 + x, color) fb.pixel(x0 - x, y0 + y, color) fb.pixel(x0 - x, y0 - y, color) fb.pixel(x0 - y, y0 - x, color) fb.pixel(x0 + y, y0 - x, color) fb.pixel(x0 + x, y0 - y, color) y += 1 err += 1 + 2 * y if 2 * (err - x) + 1 > 0: x -= 1 err += 1 - 2 * x def fill_circle_pixel(fb, x0, y0, radius, color): """通过绘制水平线填充实心圆 (比逐个像素画快得多)""" x = radius y = 0 err = 0 # 存储需要绘制的线段 (y, x_start, x_end) # 为了节省内存,我们直接在循环里画线,不存储 # 上半圆和下半圆一起处理 while x >= y: # 画四条水平线 (上下对称) # y 行 fb.line(x0 - x, y0 + y, x0 + x, y0 + y, color) fb.line(x0 - x, y0 - y, x0 + x, y0 - y, color) # x 行 (当 x != y 时) if x != y: fb.line(x0 - y, y0 + x, x0 + y, y0 + x, color) fb.line(x0 - y, y0 - x, x0 + y, y0 - x, color) y += 1 err += 1 + 2 * y if 2 * (err - x) + 1 > 0: x -= 1 err += 1 - 2 * x # --- 1. 初始化 SPI 屏幕 (ST7789) --- class ST7789: def __init__(self, spi, cs, dc, rst, w, h): self.spi = spi self.cs = cs; self.dc = dc; self.rst = rst self.w = w; self.h = h for p in [cs, dc, rst]: p.init(machine.Pin.OUT, value=1) rst.value(0); time.sleep_ms(10); rst.value(1); time.sleep_ms(120) cmds = [ (0x01, []), (0x11, []), (0x36, [0x00]), (0x3A, [0x05]), (0xB2, [0x0C, 0x0C, 0x00, 0x33, 0x33]), (0xB7, [0x35]), (0xBB, [0x19]), (0xC0, [0x2C]), (0xC2, [0x01]), (0xC3, [0x12]), (0xC4, [0x20]), (0xC6, [0x0F]), (0xD0, [0xA4, 0xA1]), (0xE0, [0xD0, 0x00, 0x02, 0x07, 0x0A, 0x28, 0x32, 0x44, 0x42, 0x06, 0x0E, 0x12, 0x14, 0x17]), (0xE1, [0xD0, 0x00, 0x02, 0x07, 0x0A, 0x28, 0x31, 0x54, 0x47, 0x0E, 0x1C, 0x17, 0x1B, 0x1E]), (0x21, []), (0x29, []) ] for cmd, data in cmds: self._tx(cmd, False) if data: self._tx(bytes(data), True) if cmd == 0x11: time.sleep_ms(120) self.set_window(0, 0, w-1, h-1) def _tx(self, data, is_data=False): self.dc(is_data) self.cs(0) if isinstance(data, int): data = bytes([data]) self.spi.write(data) self.cs(1) def set_window(self, x0, y0, x1, y1): self._tx(0x2A, False); self._tx(bytes([x0>>8, x0&0xff, x1>>8, x1&0xff]), True) self._tx(0x2B, False); self._tx(bytes([y0>>8, y0&0xff, y1>>8, y1&0xff]), True) self._tx(0x2C, False) def write(self, buffer): self.dc(1) self.cs(0) self.spi.write(buffer) self.cs(1) # --- 2. 主程序逻辑 --- print(f"PSRAM Free: {gc.mem_free() / 1024:.1f} KB") spi = machine.SPI(1, baudrate=60000000, polarity=0, phase=0, sck=machine.Pin(PIN_SCL), mosi=machine.Pin(PIN_SDA)) lcd = ST7789(spi, machine.Pin(PIN_CS), machine.Pin(PIN_DC), machine.Pin(PIN_RST), SCREEN_W, SCREEN_H) machine.Pin(PIN_BL, machine.Pin.OUT).value(1) print("Allocating Framebuffers...") try: buf1 = bytearray(SCREEN_W * SCREEN_H * 2) buf2 = bytearray(SCREEN_W * SCREEN_H * 2) fb1 = framebuf.FrameBuffer(buf1, SCREEN_W, SCREEN_H, framebuf.RGB565) fb2 = framebuf.FrameBuffer(buf2, SCREEN_W, SCREEN_H, framebuf.RGB565) print("Success!") except MemoryError: print("Memory Error!") raise frame_count = 0 start_time = time.ticks_ms() while True: current_time = time.ticks_ms() idx = frame_count % 2 fb = fb1 if idx == 0 else fb2 buf = buf1 if idx == 0 else buf2 # 清屏 fb.fill(0x0000) # 计算动画位置 t = current_time / 1000.0 angle_x = t * 2 * math.pi angle_y = t * 3 * math.pi ball_x = int(120 + 80 * math.sin(angle_x)) ball_y = int(120 + 60 * math.cos(angle_y)) # 使用我们自定义的函数画圆 # 参数: framebuffer, 圆心x, 圆心y, 半径, 颜色 fill_circle_pixel(fb, ball_x, ball_y, 20, 0xF800) # 红色实心 draw_circle_pixel(fb, ball_x, ball_y, 20, 0xFFFF) # 白色边框 # 文字 fb.text("ESP32-S3 PSRAM Demo", 20, 10, 0x07E0) elapsed = time.ticks_diff(current_time, start_time) if elapsed > 0: fps = frame_count * 1000 / elapsed fb.text(f"FPS: {fps:.1f}", 20, 30, 0xFFFF) fb.text(f"Frame: {frame_count}", 20, 50, 0xFFFF) fb.text(f"X:{ball_x} Y:{ball_y}", 20, 70, 0xFFE0) # 发送数据 lcd.set_window(0, 0, SCREEN_W-1, SCREEN_H-1) lcd.write(buf) frame_count += 1 # 控制帧率 (~30 FPS) target_frame_time = 33 current_frame_time = time.ticks_diff(time.ticks_ms(), current_time) sleep_time = target_frame_time - current_frame_time if sleep_time > 0: time.sleep_ms(sleep_time)运行效果: