深入理解电子墨水屏的编程与应用

电子墨水屏是一种现代电子设备,它使用实际的墨水来渲染图形和文本,类似于常见的喷墨打印机。然而,与打印机不同的是,它们可以无限期地重复使用相同的表面,这使得它们可以作为电子阅读器等设备的显示屏使用。尽管如此,这些显示屏的特性与传统的TFT、LCD或OLED显示屏截然不同,因此在使用它们时需要格外小心。

首先,电子墨水显示屏通常是单色的,即黑白显示。大多数电子墨水显示屏没有灰度显示能力,因为与基于光的显示屏不同,不能简单地改变颜色的强度。

其次,彩色的电子墨水显示屏通常价格昂贵,并且颜色调色板非常有限,通常只有3种颜色,见过的最昂贵的显示屏可以达到7种颜色。没有混合墨水的能力,所以不能将黄色和蓝色混合成绿色,例如。

最后,显示屏的刷新率可能需要几秒钟,尤其是对于彩色显示屏。这些显示屏无法进行动画显示,并且在使用GFX时必须小心,以避免因刷新率问题而导致的性能问题。

概念

将使用目前编写的两种电子墨水显示屏的驱动程序。GFX的电子墨水显示屏驱动程序可以通过一种称为抖动的技术来虚拟化灰度和颜色混合,这是一种通过相邻像素混合颜色来欺骗眼睛的技术。例如,不是一系列灰色像素,而是一系列交替的黑色和白色像素,以欺骗眼睛看到一条灰色线条。颜色的工作原理相同。要绘制紫色,可以交替红色和蓝色。

在GFX中,这是通过虚拟化比设备原生支持的“更深层次”的像素类型来实现的。例如,而不是暴露一个单色像素(gsc_pixel<1>),可以将其扩展为灰度,如gsc_pixel<4>或gsc_pixel<8>。黑白显示屏只允许灰度虚拟化,而彩色显示屏可以虚拟化任何像素类型,除了索引像素。

这个特性使电子墨水驱动程序在显示能力方面成为同类产品中的佼佼者。如果有其他驱动程序支持物联网的这种功能,不知道。

成本是内存使用和速度,尤其是对于彩色显示屏。帧缓冲区在内存中,其大小基于虚拟化的像素,这意味着如果在3色电子墨水显示屏上虚拟化16位颜色,帧缓冲区最终将是8倍大小!因此,当使用虚拟化时,尤其是在彩色驱动程序上,不会有太多的RAM用于其他事情,除非有一个ESP32 WROVER 8MB或类似的设备。

即使选择通过虚拟化放弃抖动,GFX在转换为索引像素格式时总是进行最近颜色匹配,这包括彩色电子墨水显示屏调色板。因此,在黑色、白色和红色显示屏上,如果加载了一个包含红色的JPEG图像,JPEG的红色部分将在显示屏上显示为红色。

实际操作

让看看这个在实际中的应用

这是一个著名的沃霍尔作品的打印件,将加载到一个3色,黑白和红色显示屏上:

请原谅凌乱的工作台,但这里是在3色显示屏上没有通过虚拟化抖动显示的图像:

这里是通过虚拟化16位RGB颜色像素进行抖动的同一图像:

最后,让看看在黑白显示屏上通过抖动显示的安迪·沃霍尔的脸,这样就能看到灰色是如何呈现的:

看到他脸上的定义了吗?如果没有虚拟化,只会看到太阳镜和嘴巴。

使用虚拟化

尽量让这个过程尽可能简单。代码中只需要一个更改,那就是在驱动程序类型实例化中:

C++ // 实例化一个带有虚拟化的彩色电子墨水驱动程序 using lcd_type = gdeh0154z90< PIN_NUM_CS, PIN_NUM_DC, PIN_NUM_RST, PIN_NUM_BUSY, rgb_pixel< 16 > >;

注意有一个最终参数指定了一个16位RGB像素类型。指定像素类型是通过抖动启用虚拟化的方式。如果没有指定这个,只会使用最近颜色匹配。

对于黑白驱动程序,情况略有不同,因为不能指定任意像素类型。在这种情况下,只需将位深度作为最终参数。例如,指定8将通过虚拟化/抖动得到256色的灰度。

C++ // 实例化一个带有虚拟化的黑白电子墨水驱动程序 using lcd_type = depg0290b< PIN_NUM_CS, PIN_NUM_DC, PIN_NUM_RST, PIN_NUM_BUSY, 8 >;

之后,就像平常一样使用它们,只是现在可以有效地获得更多的颜色和灰度。

处理刷新

电子墨水显示屏的刷新速度不快。由于这个原因,强烈建议在绘制时暂停,只有在完成整个帧的绘制后才恢复。

C++ draw::suspend(lcd); draw::filled_rectangle(lcd,(srect16)lcd.bounds(),lcd_color::white); rect16 image_bounds( 0, 0, 335, 255 ); rect16 crop_bounds( 0, 0, 127, 127 ); File fs = SPIFFS.open( "/image3.jpg" ); draw::image(lcd,(srect16)crop_bounds,&fs,crop_bounds.center(image_bounds)); fs.close(); fs = SPIFFS.open( "/image3.jpg" ); draw::image(lcd,{ 0, lcd.height-128, 127, lcd.height-1 },&fs,crop_bounds.center(image_bounds)); fs.close(); const font& f = Bm437_Acer_VGA_8x8_FON; const char* text = "GFX Demo by\r\n honey the\r\n codewitch" ; ssize16 fd=f.measure_text({ 128, 128 },text); draw::text(lcd,fd.bounds().center((srect16)lcd.bounds()),text,f,lcd_color::black); draw::resume(lcd);

注意绘图调用是如何被suspend()和resume()调用包围的吗?如果没有这样做,显示屏在尝试渲染那个帧时会刷新好几次。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485