You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改ESP32-nesemu项目以通过SPI寄存器实现ILI9486的18位彩色显示?

解决ESP32-NESEmu适配ILI9486 18位色的像素错乱问题

首先得明确你遇到像素错乱的核心原因:原来的代码是为RGB565(16位,2字节/像素)设计的,它把两个16位像素打包成一个32位值批量发送;但ILI9486的18位色是RGB666(3字节/像素),像素的字节边界和旧逻辑完全不匹配,强行套用旧打包方式会直接破坏RGB字节顺序,自然就出现错位了。

下面是具体的修改方案,分步骤落地:

1. 先确认ILI9486的显示模式配置

在发送图像数据前,必须确保ILI9486已被设置为18位RGB666模式。你需要发送对应的屏幕指令(比如0x3A像素格式指令,设置参数为0x66,具体参考ILI9486 datasheet),这一步是基础,否则即使数据正确,屏幕也会解析错误。

2. 实现RGB565到RGB666的正确转换

先写一个简单的转换函数,把原来的16位RGB565值拆成18位RGB666的三个字节:

// 将RGB565(16位)转换为RGB666的R/G/B单字节分量
void rgb565_to_rgb666(uint16_t color, uint8_t *r_byte, uint8_t *g_byte, uint8_t *b_byte) {
    // RGB565结构:R(5位) G(6位) B(5位)
    // 转成RGB666:把5位分量左移1位补0,凑成6位
    *r_byte = (color >> 11) << 1;   // 提取5位R,左移1位补0
    *g_byte = (color >> 5) & 0x3F;  // 直接提取6位G
    *b_byte = (color & 0x1F) << 1;   // 提取5位B,左移1位补0
}

3. 重构SPI数据打包逻辑

ESP32的SPI W寄存器是32位(4字节)的,我们需要把3字节的像素按顺序填充到这些寄存器里,保证每个像素的R/G/B字节连续发送,不跨像素打乱顺序。

修改原来的循环代码,改成按8个像素为一组(8×3=24字节,刚好填满6个32位SPI寄存器)处理:

#include <stdint.h>
#include <string.h>
#include "driver/spi_common.h"

// 假设SPI_NUM是你使用的SPI端口(比如SPI2_HOST)
#define SPI_NUM SPI2_HOST
#define MIN(a,b) ((a)<(b)?(a):(b))

// ... 其他原有代码 ...

while (x < width) {
    // 每次最多处理8个像素(凑24字节,适配6个32位寄存器)
    int pixels_to_process = MIN(8, width - x);
    uint32_t spi_reg_data[6] = {0}; // 存储要写入SPI寄存器的32位数据
    int byte_pos = 0; // 当前填充的字节位置(0~23)

    for (int i = 0; i < pixels_to_process; i++) {
        if (data == NULL) {
            // 空数据时填充黑色(0x000000)
            uint8_t empty_bytes[3] = {0, 0, 0};
            // 把三个空字节填充到spi_reg_data中
            for (int j = 0; j < 3; j++) {
                int reg_idx = byte_pos / 4;
                int bit_shift = (3 - (byte_pos % 4)) * 8; // SPI高位字节先发送
                spi_reg_data[reg_idx] |= (uint32_t)empty_bytes[j] << bit_shift;
                byte_pos++;
            }
            x++;
            continue;
        }

        // 从调色板获取当前像素的RGB565值
        uint16_t rgb565_color = myPalette[(unsigned char)(data[y][x])];
        x++;

        // 转换为RGB666的三个字节
        uint8_t r, g, b;
        rgb565_to_rgb666(rgb565_color, &r, &g, &b);

        // 按顺序把R/G/B字节填充到SPI寄存器数组中
        uint8_t rgb_bytes[3] = {r, g, b};
        for (int j = 0; j < 3; j++) {
            int reg_idx = byte_pos / 4;
            int bit_shift = (3 - (byte_pos % 4)) * 8; // 高位字节优先,匹配ILI9486接收顺序
            spi_reg_data[reg_idx] |= (uint32_t)rgb_bytes[j] << bit_shift;
            byte_pos++;
        }
    }

    // 等待SPI传输完成,确保寄存器空闲
    while (READ_PERI_REG(SPI_CMD_REG(SPI_NUM)) & SPI_USR);

    // 把填充好的32位数据写入SPI的W0~W5寄存器
    for (int i = 0; i < 6; i++) {
        // 处理行尾剩余像素时,跳过未使用的寄存器
        if (i * 4 >= byte_pos) break;
        WRITE_PERI_REG(SPI_W0_REG(SPI_NUM) + (i << 2), spi_reg_data[i]);
    }

    // 启动SPI传输
    SET_PERI_REG_MASK(SPI_CMD_REG(SPI_NUM), SPI_USR);
}

关键细节说明

  • 字节顺序:代码中用(3 - (byte_pos % 4)) * 8的移位方式,是因为SPI默认高位字节先发送,这样能保证每个像素的R→G→B字节按顺序被ILI9486接收;如果你的屏幕要求低位字节先,把移位逻辑改成(byte_pos % 4) * 8即可。
  • 寄存器填充:按8个像素一组处理,刚好凑齐24字节(6×4),完美适配ESP32的32位SPI寄存器,避免了跨寄存器的像素拆分,这是解决错位的核心。
  • 边界处理:用MIN(8, width - x)处理行尾剩余像素,避免越界访问。

这样修改后,就能正确把RGB565的NES画面转换成18位色,通过SPI发送给ILI9486,不会再出现像素错乱的问题了。

内容的提问来源于stack exchange,提问作者Ilya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 14:57:41