我爱点屏之:神奇的“单色RGB屏幕”

最近从老王店铺里入手了一块很神奇的单色屏幕:它拥有RGB三个背光LED,可以通过屏幕显示的内容配合背光的切换,实现伪彩色显示。

一般的RGB屏幕,都是使用白色的背光,通过液晶的偏转控制RGB三种颜色的通过率来显示彩色的,但是这次我们玩的屏幕,却换了一个方法:使用一块单色的液晶面板,配合不断变换的RGB背光,通过在某种颜色背光点亮的瞬间显示对应内容的方式,用单色屏幕实现了伪彩色。

https://img.yuanze.wang/posts/st7049a-rgb-backlight-tn-lcd/lcd-front.jpg
屏幕正面照片

https://img.yuanze.wang/posts/st7049a-rgb-backlight-tn-lcd/lcd-back.jpg
屏幕背面照片

从正面看,这块屏幕好像就是一块平平无奇的单色TN屏幕。但是翻到背面之后,会发现这块屏幕有三个背光LED,并且有各自的驱动电路连接到主控芯片。通过查阅手册得知,这块屏幕使用的驱动芯片是ST7049A,它具有4x320点的驱动电路,并且可以输出RGB背光的驱动信号。通过在三种颜色背光点亮的同时点亮液晶像素,这颗驱动芯片最多可以模拟8种颜色,实现了彩色的显示。

数据 颜色
000 白色
001 黄色
010 紫色
011 红色
100 青色
101 绿色
110 蓝色
111 黑色

使用群友提供的初始化代码,初始化好LCD,并向其中写入不同的颜色数据,可以看到如下图的效果。

https://img.yuanze.wang/posts/st7049a-rgb-backlight-tn-lcd/color-strip.jpg
点亮色带

点亮全部像素之后,可以数出屏幕的物理像素数量是128x32,是一块标准的TN屏幕面板。但是通过尝试更改写入屏幕的数据可以发现,屏幕上半部分与下半部分只能显示16个像素高的色条,并不能按照像素点显示不同的内容。通过研究,发现群友发的驱动是针对ST7045驱动芯片的,但屏幕的实际驱动芯片是ST7049AST7045相比ST7049A,少了3组COM引脚,也就是只能支持1x320个像素点的驱动。因此,本来纵向应该分成4个像素的上半部分,被合成了一个像素,造成了上下半部分色带的现象。同时,该显示器物理上将四个像素合为一个,128x32的物理分辨率变成了64x16的逻辑分辨率。

通过比对ST7045与ST7049A的手册,发现ST7049A多了一个B0H寄存器,功能为Duty set。该寄存器在群友提供的代码中被设置了0,因此4个COM引脚均保持了统一电平,将该寄存器设置为3之后,终于可以让上半屏的16个像素显示8个不同的内容了。但是由于Duty大于1,驱动芯片工作在扫描模式下,所有的参数(LCD面板驱动电压、设置LCD驱动模式等)都需要重新调试,才能达到一个比较好的显示状态。

https://img.yuanze.wang/posts/st7049a-rgb-backlight-tn-lcd/hello-world.jpg
点亮效果

随着调试,更多问题被暴露了出来:这块屏幕的像素连接顺序非常奇怪,向连续的地址写入连续的数据,会显示在屏幕的各个位置上。通过依次向屏幕写入数据,试探各个块的边界,最终得到了下图所示的显示映射表。其中,数字代表该位置的像素,在4x320/2大小的显存中所处的位置(纵向相同颜色的行中的两个像素,共同使用一个字节表示,高低4位分别表示两个像素)。由于控制芯片支持最大1280点显示,但是屏幕本身只有16x64=1024个点,因此从0开始表示的显存地址会有一些空像素。

https://img.yuanze.wang/posts/st7049a-rgb-backlight-tn-lcd/remap.png
显示映射表

代码表示如下:

esp_err_t lcd_set_point(uint8_t x, uint8_t y, lcd_color_t color)
{
    ESP_RETURN_ON_FALSE(x < LCD_X_PIXELS && y < LCD_Y_PIXELS, ESP_ERR_INVALID_ARG, TAG, "invalid point position");
    ESP_RETURN_ON_FALSE(color < lcd_color_max, ESP_ERR_INVALID_ARG, TAG, "invalid point color");

    uint8_t *gram_target = NULL;

    if (y < LCD_Y_PIXELS/2) { //上半屏比较复杂
        if (x < LCD_X_PIXELS/2) { //左半屏
            if (y == 3 || y == 4) {
                gram_target = &lcd_gram[112] + x;
            } else if (y == 2 || y == 5) {
                gram_target = &lcd_gram[272] + x;
            } else if (y == 1 || y == 6) {
                gram_target = &lcd_gram[432] + x;
            } else if (y == 0 || y == 7) {
                gram_target = &lcd_gram[592] + x;
            }
        } else { //右半屏
            if (y == 3 || y == 4) {
                gram_target = &lcd_gram[16] + (x-LCD_X_PIXELS/2);
            } else if (y == 2 || y == 5) {
                gram_target = &lcd_gram[176] + (x-LCD_X_PIXELS/2);
            } else if (y == 1 || y == 6) {
                gram_target = &lcd_gram[336] + (x-LCD_X_PIXELS/2);
            } else if (y == 0 || y == 7) {
                gram_target = &lcd_gram[496] + (x-LCD_X_PIXELS/2);
            }
        }
    } else { //是下半屏
        if (y == 11 || y == 12) {
            gram_target = &lcd_gram[48] + (LCD_X_PIXELS-1-x);
        } else if (y == 10 || y == 13) {
            gram_target = &lcd_gram[208] + (LCD_X_PIXELS-1-x);
        } else if (y == 9 || y == 14) {
            gram_target = &lcd_gram[368] + (LCD_X_PIXELS-1-x);
        } else if (y == 8 || y == 15) {
            gram_target = &lcd_gram[528] + (LCD_X_PIXELS-1-x);
        }
        y -= LCD_Y_PIXELS/2; //方便后续处理
    }

    if (x < LCD_X_PIXELS/2) { //是左半屏
        if (y < 4) { //前半个像素
            *gram_target &= 0xF0;
            *gram_target |= color;
        } else { //后半个像素
            *gram_target &= 0x0F;
            *gram_target |= color<<4;
        }
    } else { //是右半屏
        if (y < 4) { //前半个像素
            *gram_target &= 0x0F;
            *gram_target |= color<<4;
        } else { //后半个像素
            *gram_target &= 0xF0;
            *gram_target |= color;
        }
    }

    return ESP_OK;
}

最终的点亮效果如下。

https://img.yuanze.wang/posts/st7049a-rgb-backlight-tn-lcd/video.gif
最终点亮效果

 ESP32工程

:为方便搜索,在文末放上该屏幕的淘宝宝贝名“全新3.2寸12864 LCD rgb三色背光 貌似主控带rgb控制器”以及“清仓 以前买过的rgb点阵屏 旧链接找不到了新上”,背面PCB上的丝印为“GV6416GFSL011-16677A_V02(PCB)”。