环境

开发环境:VSCODE + ESP-IDF

开发板:信盈达ESP32-S3-N16*R8开发板

使用的是D200C2407V0液晶屏,液晶屏的IC是ST7789V

屏幕是如何显示的

屏幕是由一个个像素组成的,一个像素一般由三原色组成,红绿蓝,RGB,要显示一张图片,实际上就是通过控制每个像素点中红、绿、蓝三个子像素的亮度,混合出不同的颜色。成千上万个像素点各自显示指定的颜色,拼在一起,就形成了我们看到的完整图像。

不过我们并不需要去分别把每一个RGB的亮度都定义好来决定颜色,显示IC已经帮我们完成了这件事,所以我们只需要输入色彩信息

色彩的位数

1位

2色

非黑即白,像老式电报

4位

16色

早期游戏机,色彩很"复古"

8位

256色

调色板模式:从256个"油漆桶"里选颜色填充

16位

6.5万色

高彩:日常看图基本够用

24位

1677万色

真彩色:RGB各8位,人眼几乎分不出差别

32位

1677万色+透明

真彩色 + Alpha通道:多8位"透明度"

为了节省存储量,最大化的利用位数的颜色还诞生出了

RGB233 即8位中 (B: 2 bits; G: 3 bits; R: 3 bits)。

RGB565 即16 位中(B: 5 bits, G: 6 bits, R: 5 bits)

ST7789V显示IC使用的就是RGB556进行显示

取个色把!

R = 178  1011 0010 取5

G = 121  0111 1001 取6

B = 240  1111 0000 取5

从高位开始取,相当于去掉小数点一样,低位的数字舍弃对颜色的影响相对较小

得到 1011 0011 1101 1110

0xB3DE 但这个邪门的屏幕需要颜色反转,所以使用0xDEB3

从RGB颜色到RGB565

根据上面

颜色就可以正确映射了。

uint16_t RGB_Convert(uint8_t R, uint8_t G, uint8_t B)

{

    uint16_t r5 = (R >> 3) & 0x1F;  // Keep 5 bits

    uint16_t g6 = (G >> 2) & 0x3F;  // Keep 6 bits

    uint16_t b5 = (B >> 3) & 0x1F;  // Keep 5 bits

    uint16_t rgb565 = (r5 << 11) | (g6 << 5) | b5;

    uint16_t swapped = ((rgb565 & 0xFF00) >> 8) | ((rgb565 & 0x00FF) << 8);

   

    return swapped;

}

设置全屏颜色代码

void lcd_set_color(uint16_t color)

{

    // 分配内存 这里分配了液晶屏一行数据需要的大小

    uint16_t *buffer = (uint16_t *)heap_caps_malloc(BSP_LCD_H_RES * sizeof(uint16_t), MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);

   

    if (NULL == buffer){

        ESP_LOGE(TAG, "Memory for bitmap is not enough");

    }else{

        for (size_t i = 0; i < BSP_LCD_H_RES; i++){ // 给缓存中放入颜色数据

            buffer[i] = color;

        }

        for (int y = 0; y < BSP_LCD_V_RES; y++){ // 显示整屏颜色

            esp_lcd_panel_draw_bitmap(panel_handle, 0, y, BSP_LCD_H_RES, y+1, buffer);

        }

        free(buffer); // 释放内存

    }

}

字库

其实我更愿意把这些都称之为点阵图,每个字/字母,都用一个固定大小的"像素网格"表示,不过为了方便存储,一般取8的倍数,通过16进制存储会很方便,如果你的字号是12或者19等的非8倍数,取模软件一般就会向上取整,

字号12会用16bit存储,剩下4bit就空出来了

字号20会用24bit存储。

使用的是PctoLCD2002这个小软件,需要设置一下,1那里是字号,3那里的点阵会影响数据的分行,可以修改看看导出的效果。

生成字模待用,创建一个数组。注意这里的48位,计算方法是按照24高 12宽,24*16/8 = 48(12按照8的倍数取16)

这里以24x12的字体为例。

void lcd_draw_char24(int x_start, int y_start, uint8_t Ch, uint16_t fc, uint16_t bc)

{

    // 分配内存 分配了需要的字节大小 且指定在外部SPIRAM中分配

    size_t pixels_byte_size = 24 * 16 * 2;

    uint16_t *pixels = (uint16_t *)heap_caps_malloc(pixels_byte_size, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM);

    if (NULL == pixels){

        ESP_LOGE(TAG, "Memory for char is not enough");

        return;

    }

    unsigned char Temp;

    Ch = Ch - '0';

    for (int i=0;i<48;i++){

        Temp = ascii_2412[Ch][i];

        for(int j=0;j<8;j++){

            if(Temp&(0x01<<j)) pixels[i*8+j] = fc;

            else pixels[i*8+j] = bc;

        }

    }

   

    esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start+16, y_start+24, (uint16_t *)pixels); // 显示一个字符数据

    heap_caps_free(pixels);  // 释放内存

}

如果用其他规格,需要改的地方挺多的,图中标出

汉字也一样,取模方法类似,如果太长可能会出现分行的情况,改点阵输入999就可以了,就不会分行了。

结束

然后最后差不多就有这样效果啦~

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐