V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zxkmm
V2EX  ›  嵌入式开发

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

  •  
  •   zxkmm ·
    zxkmm · 219 天前 · 951 次点击
    这是一个创建于 219 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    亮度控制一般有两种方案,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

    https://github.com/zxkmm

    1 条回复    2024-04-29 10:58:06 +08:00
    cd605426287
        1
    cd605426287  
       206 天前
    6666 关注一 bo
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3406 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 11:03 · PVG 19:03 · LAX 03:03 · JFK 06:03
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.