分享我过年时候写的,劣质古早 mcu 的 TFT 屏幕 +ILI9341 亮度控制方案

前言

亮度控制一般有两种方案,DC 调光和 PWM 调光。

但是某些奇怪的单片机设计直接把 TFT 屏的背光灯绑定在电源上,所以你只能开关背光,不能控制电压;所以只能被迫使用 PWM 调光。

然而对于单片机/RTOS 来说,每个 tick 都花一部分性能在控制方面可能有比较大的性能损耗,加上如果设备是无线电相关的话,PWM 带来的噪音可能会辐射到无线电部分导致未知的结果,所以只能采取第三种方案:伪颜色。

伪颜色

本质上 ILI9341 IC 的驱动当中是一个像素点一个像素点地画图

void lcd_write_pixel(ui::Color pixel) {
        lcd_write_data(pixel.v);
    }

这就相当简单了,直接把颜色通过浮点运算化为更“黑”的颜色即可实现“伪”亮度控制

void darken_color(ui::Color& pixel, float darken_level) {
    uint16_t r = (pixel.v >> 11) & 0x1F;
    uint16_t g = (pixel.v >> 5) & 0x3F;
    uint16_t b = pixel.v & 0x1F;

    r = static_cast<uint16_t>(r / darken_level);  // darken
    g = static_cast<uint16_t>(g / darken_level);
    b = static_cast<uint16_t>(b / darken_level);

    pixel.v = (r << 11) | (g << 5) | b;  // combine back
}

但是浮点运算在单片机上相当慢,最后出来而结果可以用,但是渲染速度肉眼可见的下降。最后我的方案是损失一些调控精度,用移位运算代替浮点运算

void darken_color(ui::Color& pixel, uint8_t darken_level_shift) {
        // TODO: 1. do we need edge control?
        // currently didn't see and issue without edge control
        // but maybe hurts screen hardware without one?

        // TODO: 2. de-color mode for accessibility
        // TODO: 3. high contrast mode for accessibility

        uint16_t r = (pixel.v >> 11) & 0x1F;  // extract
        uint16_t g = (pixel.v >> 5) & 0x3F;
        uint16_t b = pixel.v & 0x1F;

        r = r >> darken_level_shift;
        g = g >> darken_level_shift;
        b = b >> darken_level_shift;

        pixel.v = (r << 11) | (g << 5) | b;
    }
其中darken_level_shift是一个uint8,0代表100%亮度,1代表50%亮度,以此类推

最后出来的结果终于可以用了:

void lcd_write_pixels(ui::Color pixel, size_t n) {
        if (get_dark_cover()) {
            darken_color(pixel, get_brightness());  // Darken
        }
void lcd_write_pixels_unrolled8(ui::Color pixel, size_t n) {
        if (get_dark_cover()) {
            darken_color(pixel, get_brightness());  // Darken 
        }

最后欢迎关注我的 GitHub

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...