数码之家

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信登录

微信扫一扫,快速登录

搜索
查看: 443|回复: 31

[other] 求解:16位并口与SPI口速度

[复制链接]
发表于 2025-9-27 16:57:13 | 显示全部楼层 |阅读模式

爱科技、爱创意、爱折腾、爱极致,我们都是技术控

您需要 登录 才可以下载或查看,没有账号?立即注册 微信登录

x
网购一个3.5寸并口彩屏,
开发板用esp32-s3-nano驱动,
代码就是很简单的让彩屏全屏红绿蓝3色循环显示。
由于本人刚刚学习micropython
不管咋调试,也问了豆包,
显示速度总是慢,
最快时也就和SPI口驱动速度差不多。

网上也查不到相关资料,
但是都说并口比spi口速度快。
所以请教大家:怎样才能叫显示速度快起来?
谢谢 !
发表于 2025-9-27 18:42:43 来自手机浏览器 | 显示全部楼层
我乱说,首先把单片机的时钟加大,其次也要看相关屏幕资料手册,每条指令最快能到多少ns。并口按理是比spi快
回复 支持 反对

使用道具 举报

发表于 2025-9-27 19:29:56 | 显示全部楼层
测试一下屏幕SPI接口的最高速度是多少,然后在这个速度略微低一点的速度下,使用ESP32单片机的DMA+SPI功能,可以提高速度。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-9-28 01:02:34 | 显示全部楼层
兔包公 发表于 2025-9-27 18:42
我乱说,首先把单片机的时钟加大,其次也要看相关屏幕资料手册,每条指令最快能到多少ns。并口按理是比spi ...

我的esp32-s3-nano最大时钟240M。再不能大了。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-9-28 01:03:46 | 显示全部楼层
ahyu99 发表于 2025-9-27 19:29
测试一下屏幕SPI接口的最高速度是多少,然后在这个速度略微低一点的速度下,使用ESP32单片机的DMA+SPI功能 ...

我的彩屏是并口。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-9-28 01:04:14 | 显示全部楼层
多谢以上2位坛友的解答。
回复 支持 反对

使用道具 举报

发表于 2025-9-30 00:02:54 | 显示全部楼层
楼上的应该没说到点上,觉着并口慢的多半是硬件没安排好,导致用软件模拟并口。

正常单片机 8bit/16bit 并口,一条 PORT=XXX 指令就会同步送出 8/16 个电平信号,这类并口配合并口屏就会很快;
但像 esp 这类单片机,跑 Arduino 时为了兼顾灵活性,会做一次 io 映射:把 io 编号和实际口通过查表对应起来。
结果为了输出 8bit/16bit 并行数据,后台软件一个个 io 查表过去,每次仅更新一个电平,这就比串口还慢。

我 72MHz 的 GD32 带一片 320x240 并口屏,纯软件(无DMA)全屏刷新率轻松上到 20fps
等效到串口的话,SPI 需要 20Hz*320*240*16bit = 24.5MHz,显然串口会很吃力。

打赏

参与人数 1家元 +70 收起 理由
lxa0 + 70 精彩回帖

查看全部打赏

回复 支持 2 反对 0

使用道具 举报

发表于 2025-9-30 01:07:11 | 显示全部楼层
并口屏常用的接口是6800/8080/FSMC,好像ESP-32没有这些接口,只能用普通I/O模拟,实际速度取决于CPU频率和I/O速率。
回复 支持 反对

使用道具 举报

发表于 2025-9-30 12:24:02 | 显示全部楼层
以前用STM32驱动并口屏,同样有这个问题,刷纯色也不是那种秒刷的感觉,图片就更慢了。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册 微信登录

x

打赏

参与人数 1家元 +70 收起 理由
lxa0 + 70 謝謝分享

查看全部打赏

回复 支持 1 反对 0

使用道具 举报

发表于 2025-9-30 14:44:40 | 显示全部楼层
frival 发表于 2025-9-30 12:24
以前用STM32驱动并口屏,同样有这个问题,刷纯色也不是那种秒刷的感觉,图片就更慢了。
...

测下帧时间,超过100ms (<10fps) 就已经该优化了
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-9-30 19:25:07 | 显示全部楼层
frival 发表于 2025-9-30 12:24
以前用STM32驱动并口屏,同样有这个问题,刷纯色也不是那种秒刷的感觉,图片就更慢了。
...

你这个比我的快多了 。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-9-30 19:25:38 | 显示全部楼层
感谢以上各位坛友的解答。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-9-30 19:27:54 | 显示全部楼层
我把代码发出来 ,

请大家帮我出出主意?
谢谢 !


from machine import Pin, PWM
import time
import os
import _thread

# 硬件配置(精简到最小必要)
DATA_PINS = [Pin(i, Pin.OUT, value=0) for i in [1,2,3,4,5,6,7,8,9,10,11,12,13,14,45,46]]
CS = Pin(15, Pin.OUT, value=1)
RS = Pin(16, Pin.OUT, value=0)
WR = Pin(17, Pin.OUT, value=1)
RST = Pin(21, Pin.OUT, value=1)
BACKLIGHT = PWM(Pin(38), freq=1000, duty=0)

# 引脚操作函数(直接引用,不做额外缓存)
def set_data(data):
    for i in range(16):
        DATA_PINS[i].value((data >> i) & 1)

# 屏幕基础操作(极简版)
def send_cmd(cmd):
    CS.value(0)
    RS.value(0)
    set_data(cmd)
    WR.value(0)
    time.sleep_us(2)
    WR.value(1)
    CS.value(1)
    time.sleep_us(2)

def send_dat(data):
    CS.value(0)
    RS.value(1)
    set_data(data)
    WR.value(0)
    time.sleep_us(2)
    WR.value(1)
    CS.value(1)

def set_window(x0, y0, x1, y1):
    send_cmd(0x2A)
    send_dat(x0>>8); send_dat(x0&0xFF); send_dat(x1>>8); send_dat(x1&0xFF)
    send_cmd(0x2B)
    send_dat(y0>>8); send_dat(y0&0xFF); send_dat(y1>>8); send_dat(y1&0xFF)
    send_cmd(0x2C)

# 清屏函数
def clear_screen():
    set_window(0, 0, 479, 319)  # 480x320屏幕
    CS.value(0)
    RS.value(1)
    set_data(0x0000)  # 黑色
    for _ in range(480*320):
        WR.value(0)
        WR.value(1)
    CS.value(1)

# BMP加载函数(仅保留核心功能)
def load_bmp(path):
    try:
        with open(path, 'rb') as f:
            f.seek(10)
            offset = int.from_bytes(f.read(4), 'little')
            f.seek(18)
            w = int.from_bytes(f.read(4), 'little')
            h = int.from_bytes(f.read(4), 'little')
            f.seek(28)
            if int.from_bytes(f.read(2), 'little') != 24:
                return (0,0,[])
            
            h_abs = abs(h)
            data = []
            row_size = (w*3 +3) & ~3
            
            for y in range(h_abs):
                f.seek(offset + ((h_abs-1-y) if h>0 else y)*row_size)
                row = f.read(row_size)
                for x in range(w-1, -1, -1):  # 水平翻转
                    b, g, r = row[x*3], row[x*3+1], row[x*3+2]
                    rgb565 = ((b&0xF8)<<8)|((g&0xFC)<<3)|((r&0xF8)>>3)
                    data.append(rgb565)
            
            return (w, h_abs, data)
    except:
        return (0,0,[])

# 绘制函数
def draw_bmp(bmp):
    w, h, data = bmp
    if w ==0 or h ==0 or not data:
        return False
    x = (480 - w)//2
    y = (320 - h)//2
    set_window(x, y, x+w-1, y+h-1)
    CS.value(0)
    RS.value(1)
    for pixel in data:
        set_data(pixel)
        WR.value(0)
        WR.value(1)
    CS.value(1)
    return True

# 主函数(完全重写,逻辑极简)
def main():
    # 初始化所有变量(强制赋值)
    files = []
    current_idx = 0
    next_data = (0,0,[])
    load_ready = False
    lock = _thread.allocate_lock()
    running = True

    # 屏幕初始化
    RST.value(0)
    time.sleep_ms(50)
    RST.value(1)
    time.sleep_ms(100)
    send_cmd(0x01); time.sleep_ms(50)
    send_cmd(0x11); time.sleep_ms(60)
    send_cmd(0x36); send_dat(0x60)
    send_cmd(0x3A); send_dat(0x05)
    send_cmd(0x29); time.sleep_ms(20)
    clear_screen()
    print("屏幕初始化完成")

    # 开启背光
    BACKLIGHT.duty(444)
    print("系统启动成功")

    # 获取BMP文件
    if "bmp" in os.listdir():
        files = [f"bmp/{f}" for f in os.listdir("bmp") if f.lower().endswith(".bmp")]
        files.sort()
    if not files:
        print("无BMP文件,退出")
        return
    print(f"找到{len(files)}个BMP文件")

    # 预加载线程函数(内部定义,避免全局变量混乱)
    def preload():
        nonlocal next_data, load_ready, current_idx, running
        while running:
            target = (current_idx + 1) % len(files)
            bmp = load_bmp(files[target])
            with lock:
                next_data = bmp
                load_ready = True
            # 等待当前索引推进
            while running and current_idx == (target -1) % len(files):
                time.sleep_ms(100)

    # 启动预加载线程
    _thread.start_new_thread(preload, ())

    # 显示第一张图
    first_bmp = load_bmp(files[0])
    if first_bmp[0] ==0:
        print("第一张图加载失败")
        return
    print(f"[1/{len(files)}] 显示: {files[0].split('/')[-1]}")
    draw_bmp(first_bmp)

    # 主循环(极简逻辑)
    try:
        while running:
            time.sleep(3)  # 显示3秒
            
            # 准备下一张
            current_bmp = (0,0,[])  # 强制初始化
            with lock:
                if load_ready:
                    current_bmp = next_data
                    load_ready = False
                    current_idx = (current_idx +1) % len(files)
                else:
                    print("下一张未准备好,跳过")
                    current_idx = (current_idx +1) % len(files)
                    continue
            
            # 清屏并显示
            clear_screen()
            fname = files[current_idx].split('/')[-1]
            print(f"[{current_idx+1}/{len(files)}] 显示: {fname}")
            draw_bmp(current_bmp)
    except KeyboardInterrupt:
        print("\n用户中断")
    except Exception as e:
        print(f"错误: {e}")
    finally:
        running = False
        time.sleep_ms(200)
        BACKLIGHT.duty(0)
        print("程序已退出")

if __name__ == "__main__":
    main()



回复 支持 反对

使用道具 举报

发表于 2025-10-1 22:07:38 | 显示全部楼层
lxa0 发表于 2025-9-30 19:27
我把代码发出来 ,

请大家帮我出出主意?

你看看 set_data 函数,就是一个位一个位的更新并口,这跟串口一个鬼样子。

如果 esp32 找不出一个完整的 Px15:0,建议放弃并口,直接换 SPI 串口版屏
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-10-2 22:55:16 | 显示全部楼层
t3486784401 发表于 2025-10-1 22:07
你看看 set_data 函数,就是一个位一个位的更新并口,这跟串口一个鬼样子。

如果 esp32 找不出一个完整 ...

原本想着:开发板用的双核240主频,还带又8M psram驱动16位并口,应该比spi口快很多。做个小小的电子相册,速度上应该是比spi快。
唉。。。。。没想到实际上还不如spi速度快,晕乎乎。

那现在只好放弃并口了?还有别的办法吗?
回复 支持 反对

使用道具 举报

发表于 2025-10-2 23:00:56 | 显示全部楼层
lxa0 发表于 2025-10-2 22:55
原本想着:开发板用的双核240主频,还带又8M psram驱动16位并口,应该比spi口快很多。做个小小的电子相册 ...

过了下这片 esp32,只能是spi 走起了。 想要速度应该去买大封装的 arm
回复 支持 反对

使用道具 举报

 楼主| 发表于 2025-10-3 22:41:58 | 显示全部楼层
t3486784401 发表于 2025-10-2 23:00
过了下这片 esp32,只能是spi 走起了。 想要速度应该去买大封装的 arm

很无奈:我只会一点点micropython,玩玩ESP32,还会一点 51单片机。别的都不会了,咋办呢?
回复 支持 反对

使用道具 举报

发表于 2025-10-5 01:43:28 | 显示全部楼层
lxa0 发表于 2025-10-3 22:41
很无奈:我只会一点点micropython,玩玩ESP32,还会一点 51单片机。别的都不会了,咋办呢? ...

arm 也不算难,两个路径: keil-c 或者 Arduino-C++
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2025-10-5 22:49:12 | 显示全部楼层
t3486784401 发表于 2025-10-5 01:43
arm 也不算难,两个路径: keil-c 或者 Arduino-C++

突发奇想:用 51单片机驱动的话速度会如何?
回复 支持 反对

使用道具 举报

发表于 2025-10-7 12:55:23 | 显示全部楼层
lxa0 发表于 2025-10-5 22:49
突发奇想:用 51单片机驱动的话速度会如何?

51 的话速度勉强够,但内存是一大问题。

320x240 彩屏,如果只是简单画几个线条文字,还能勉强够用;
如果要渲染动态图片、显示效果,内存肯定要吃紧
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册 微信登录

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2025-11-14 23:34 , Processed in 0.234000 second(s), 13 queries , Gzip On, Redis On.

Powered by Discuz!

© 2006-2025 MyDigit.Net

快速回复 返回顶部 返回列表