#Funpack2-3 #ESP32-E #任务二 lvgl图形库和应用-颜色拾取器
在本项目中,选用FireBeetle-E开发板加TSC3200颜色传感器以及串口彩色TFT显示屏模块,配合LVGL图形库实现了一个“颜色拾取器”小工具
标签
Funpack活动
Geralt
更新2023-01-03
河南大学
638

#Funpack2-3#ESP32-E#任务二-lvgl图形库和应用-颜色拾取器

本项目实现了Funpack第二季第3期的任务二,即“用FireBeetle-E开发板作为控制单元,搭配显示屏移植lvgl图形库,动态显示任意一个及以上传感器采集到的数据,或者使用触摸屏对执行器进行控制。”

在本项目中,选用FireBeetle-E开发板加TSC3200颜色传感器以及一个分辨率为QVGA的ST7789串口彩色TFT显示屏模块,配合LVGL图形库实现了一个“颜色拾取器”小工具

得益于FireBeetle-E所使用的ESP32-E核心的强大和LVGL的充分优化,本项目以最高35FPS的高帧率实现颜色(透射和反射)的实时拾取和显示

Frx3uCQRud3dtjBEgdYETEBE1y9i

  硬件说明

 

本项目使用的开发板是DFRobot出品的FireBeetle ESP32-E(简称FireBeetle-E),此开发板以ESP32-WROOM-32E为核心,该核心拥有双核240MHz的LX6主控,520KB的SRAM,4MB的外置Flash空间,支持WiFi/BT/BLE.性能和功能都十分强大。除此之外,板子上还板载了一颗RGB-LED灯,两个按键、锂电池充放电管理电路和USB-UART通讯/自动下载电路,十分适合电子爱好者快速开发项目原型。

FlTqfg6bNxXYGHLWHVhCqSGCLMDh

传感器采用的型号为DFRobot的TCS3200颜色传感器,此传感器可以将接收到的光信号转化为与对应RGB分量强度成比例关系的方波信号输出。

FnvAVCJaDIK1tnEMg-v0UIV6ebgB

显示屏部分选用了开源的2.4寸TFT屏模块,面板使用的是ST7789控制器QVGA分辨率的2.4寸TFT面板,采用SPI接口与开发板通讯。

FirEk_PUHvPhS26Q4a1-Yf65nUcC

早期开发时,笔者使用杜邦线来连接模组和开发板,但是随即发现这样连接不够方便和可靠。于是使用万用板制作了一个底座用来连接以上的三个模块

FpW_Axt2zYvVAN_ZUvT0-y8nbk5qFsy8G4TYCfLq1e2NQju9RjWNOas3Ft6rIpUcvu0b8F9DEF9ceSnIf4Ay

开发板与模组的IO映射关系如下:

TCS3200 S0 GPIO19 TCS3200输出频率缩放选择输入1
TCS3200 S1 GPIO25 TCS3200输出频率缩放选择输入2
TCS3200 S2 GPIO17 TCS3200光电二极管类型选择输入1
TCS3200 S3 GPIO16 TCS3200光电二极管类型选择输入2
TCS3200 OE GPIO23 TCS3200的输出使能信号
TCS3200 LED GPIO18 TCS3200传感器模组上的LED照明灯控制信号
TCS3200 OUT GPIO4 TCS3200信号输出引脚
LCD SCLK GPIO14 LCD的时钟信号
LCD MOSI GPIO13 LCD的数据信号
LCD CS GPIO22 LCD的片选信号
LCD DC GPIO21 LCD的数据/指令选择信号
LCD RST GPIO26 LCD的复位控制引脚

  实现思路

 

开发环境选择

FireBeetle-E支持基于ESP32的所有开发环境,包括但不限于ESP-IDF/Arduino/MicroPython等。官方的推荐开发环境为Arduino。

在本项目中,为了更高的自由度和更加精细化的性能优化,本项目没有使用Arduino,而是使用了乐鑫官方出品的基于Eclipse的ESP32-IDE配合ESP-IDF来进行开发。

Fkfr4Spk-VIzrHGd3jV5Z5FmiDwN

项目模板搭建

本项目需要驱动LCD,因为LCD与开发板通讯的方式是SPI,所以使用ESP-IDF的SPI-LCD项目示例模板来作为项目的基础模板。

显示屏的驱动

LCD与开发板通过SPI方式进行通讯,所以我们需要初始化ESP32的SPI外设。ESP32拥有三个SPI主机,分别为SPI_HOST、HSPI_HOST和VSPI_HOST。本项目中使用速度最快的HSPI_HOST(原因下表)

SPI的速度设置上,则需要进行一些运算。已知本项目中使用的LCD的分辨率为QVGA即320x240,颜色深度为16bit(RGB565格式),则可计算出全屏刷新的数据量是

320 x 240 x 16bit = 1,228,800 bits = 153,600 Bytes

流畅显示的底线是30FPS以上的刷新率,则1秒的数据量为

320 x 240 x 16bit x 30 = 36,864,000 bits = 4,608,000 Bytes

可见,SPI主机(ESP32)的速度必须要达到36.8Mbps以上,才能实现理论30FPS的全屏刷新速率。但是实际上,因为不可能把所有的算力都用来与LCD通讯,ESP32还要负责图形的运算、传感器数据的采集等任务。如果LCD的刷新率都是堪堪达到下限,则可以肯定无法终实现30FPS以上的刷新率。所以,为了保证最终效果,我们需要将这个理论值翻倍,达到60FPS也就是73.7Mbps的通讯速度。LCD使用的是单线SPI,在一个时钟周期只能传输一个bit,这也就意味着SPI的时钟频率也必须设置到73.7MHz以上

ESP32的HSPI_HOST支持最高80Mhz的频率,所以我们选择此SPI主机,并将其通讯速度设置为最高的80MHz。使用以下代码就可以初始化HSPI_HOST外设。因为LCD与主机的通讯是单向的,所以无需初始化MISO引脚。

此外需要注意的是,此速度下,SPI关键IO引脚的选择只能使用GPIO14(SCLK)和GPIO13(MOSI)

static void bsp_spi_init(){
    esp_err_t ret;
    spi_bus_config_t buscfg = {
            .miso_io_num = LCD_PIN_MUM_MISO,
            .mosi_io_num = LCD_PIN_NUM_MOSI,
            .sclk_io_num = LCD_PIN_NUM_CLK,
            .quadwp_io_num = -1,
            .quadhd_io_num = -1,
            .max_transfer_sz = LV_SINGLE_BUF_SIZE_IN_BYTES + 8
    };

    spi_device_interface_config_t devcfg = {
            .clock_speed_hz = 80 * 1000 * 1000,       // Clock out at 80 MHz
            .mode = 0,                                // SPI mode 0
            .spics_io_num = LCD_PIN_NUM_CS,           // CS pin
            .queue_size = 7,                          // We want to be able to queue 7 transactions at a time
            .pre_cb = lcd_spi_pre_transfer_callback,  // Specify pre-transfer callback to handle D/C line
    };

    //Initialize the SPI bus
    ret = spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO);
    ESP_ERROR_CHECK(ret);
    //Attach the LCD to the SPI bus
    ret = spi_bus_add_device(LCD_HOST, &devcfg, &spi);
    ESP_ERROR_CHECK(ret);
}

SPI初始化完毕后就可以进行LCD面板(控制器)的初始化了。通过常规的GPIO初始化代码初始化LCD的辅助控制引脚(RST/DC),然后按照厂商提供的初始化命令序列发送指定的数据/指令给LCD后,LCD即处于就绪状态。

void lcd_init(spi_device_handle_t spi) {
    int cmd = 0;
    const lcd_init_cmd_t *lcd_init_cmds;

    //Initialize non-SPI GPIOs
    gpio_set_direction(LCD_PIN_NUM_DC, GPIO_MODE_OUTPUT);
    gpio_set_direction(LCD_PIN_NUM_RST, GPIO_MODE_OUTPUT);

    //Reset the display
    gpio_set_level(LCD_PIN_NUM_RST, 0);
    vTaskDelay(100 / portTICK_RATE_MS);
    gpio_set_level(LCD_PIN_NUM_RST, 1);
    vTaskDelay(100 / portTICK_RATE_MS);

    lcd_init_cmds = st_init_cmds;

    //Send all the commands
    while (lcd_init_cmds[cmd].databytes != 0xff) {
        lcd_cmd(spi, lcd_init_cmds[cmd].cmd);
        lcd_data(spi, lcd_init_cmds[cmd].data, lcd_init_cmds[cmd].databytes & 0x1F);
        if (lcd_init_cmds[cmd].databytes & 0x80) {
            vTaskDelay(100 / portTICK_RATE_MS);
        }
        cmd++;
    }
}

TCS3200传感器数据的读取

TCS3200无需进行高级初始化,它的工作原理是将选择的通道的光的强度转化为频率随强度变化的方波。对于DFRobot的TS3200模块而言,它有6个控制IO,包括2个通道选择控制引脚S2/S3,2个输出比例因子选择引脚S0/S1,一个输出使能控制引脚OE,和一个照明用LED控制引脚LED。这些IO都只需要进行简单的GPIO常规初始化即可。

static void bsp_gpio_init(){
    gpio_config_t sConfig = { 0 };
    sConfig.intr_type = GPIO_INTR_DISABLE;
    sConfig.mode = GPIO_MODE_OUTPUT;
    sConfig.pin_bit_mask = 1 << TCS3200_PIN_NUM_S0;
    sConfig.pull_down_en = 0;
    sConfig.pull_up_en = 0;
    gpio_config(&sConfig);

    sConfig.intr_type = GPIO_INTR_DISABLE;
    sConfig.mode = GPIO_MODE_OUTPUT;
    sConfig.pin_bit_mask = 1 << TCS3200_PIN_NUM_S1;
    sConfig.pull_down_en = 0;
    sConfig.pull_up_en = 0;
    gpio_config(&sConfig);

    sConfig.intr_type = GPIO_INTR_DISABLE;
    sConfig.mode = GPIO_MODE_OUTPUT;
    sConfig.pin_bit_mask = 1 << TCS3200_PIN_NUM_S2;
    sConfig.pull_down_en = 0;
    sConfig.pull_up_en = 0;
    gpio_config(&sConfig);

    sConfig.intr_type = GPIO_INTR_DISABLE;
    sConfig.mode = GPIO_MODE_OUTPUT;
    sConfig.pin_bit_mask = 1 << TCS3200_PIN_NUM_S3;
    sConfig.pull_down_en = 0;
    sConfig.pull_up_en = 0;
    gpio_config(&sConfig);

    sConfig.intr_type = GPIO_INTR_DISABLE;
    sConfig.mode = GPIO_MODE_OUTPUT;
    sConfig.pin_bit_mask = 1 << TCS3200_PIN_NUM_OE;
    sConfig.pull_down_en = 0;
    sConfig.pull_up_en = 0;
    gpio_config(&sConfig);

    sConfig.intr_type = GPIO_INTR_DISABLE;
    sConfig.mode = GPIO_MODE_OUTPUT;
    sConfig.pin_bit_mask = 1 << TCS3200_PIN_NUM_LED;
    sConfig.pull_down_en = 0;
    sConfig.pull_up_en = 0;
    gpio_config(&sConfig);

    gpio_set_level(TCS3200_PIN_NUM_S0,  1);
    gpio_set_level(TCS3200_PIN_NUM_S1,  1);
    gpio_set_level(TCS3200_PIN_NUM_S2,  1);
    gpio_set_level(TCS3200_PIN_NUM_S3,  0);
    gpio_set_level(TCS3200_PIN_NUM_OE,  1);
    gpio_set_level(TCS3200_PIN_NUM_LED, 0);

    sConfig.intr_type = GPIO_INTR_DISABLE;
    sConfig.mode = GPIO_MODE_INPUT;
    sConfig.pin_bit_mask = 1 << KEY_PIN_NUM;
    sConfig.pull_down_en = 0;
    sConfig.pull_up_en = 1;
    gpio_config(&sConfig);
}

TCS3200的输出信号是一个方波,我们需要测量这个方波的频率才能得到测量的结果。对方波信号频率的测量,我们通过使用ESP32内置的PCNT外设来实现。PCNT(脉冲计数器 )模块通常用于对一段时间内的脉冲计数进而计算得到周期信号的频率以及对正交信号进行解码获得速度和方向信息。

PCNT测量脉冲数的原理是统计输入信号的上升沿和/或下降沿的数量,可检测边沿信号及电平信号。因为我们只需要测量方波的频率,所以只检测边沿信号就足够了。我们将计数模式设置为上升沿递增计数,然后统计1ms内的脉冲数量即可得到单位为kHz的频率。这个地方我们不需要过于准确的测量结果(因为LCD的色深只有16bit,也就是每个通道最只有多6bit也就是64阶的信号强度数据)。

PCNT还有一个控制信号,也就是用来控制边沿信号的计数模式的信号,我们用不到,只做常规初始化即可。

ESP32集成了多个脉冲计数单元,本项目中我们选用PCNT_UNIT_0。

以下为PCNT初始化代码

static void bsp_pcnt_init(int unit)
{
    /* Prepare configuration for the PCNT unit */
    pcnt_config_t pcnt_config = {
        // Set PCNT input signal and control GPIOs
        .pulse_gpio_num = PCNT_INPUT_SIG_IO,
        .ctrl_gpio_num = PCNT_INPUT_CTRL_IO,
        .channel = PCNT_CHANNEL_0,
        .unit = unit,
        // What to do on the positive / negative edge of pulse input?
        .pos_mode = PCNT_COUNT_INC,      // Count up on the positive edge
        .neg_mode = PCNT_COUNT_DIS,      // Keep the counter value on the negative edge
        // What to do when control input is low or high?
        .lctrl_mode = PCNT_MODE_KEEP,    // Reverse counting direction if low
        .hctrl_mode = PCNT_MODE_KEEP,    // Keep the primary counter mode if high
        // Set the maximum and minimum limit values to watch
        .counter_h_lim = 32767,
        .counter_l_lim = -32768,
    };
    /* Initialize PCNT unit */
    pcnt_unit_config(&pcnt_config);

    /* Initialize PCNT's counter */
    pcnt_counter_pause(unit);
    pcnt_counter_clear(unit);

    /* Everything is set up, now go to counting */
    pcnt_counter_resume(unit);
}

从TCS3002读取数据时,需要先选择一种颜色过滤器,此时它只允许一个特定的颜色通道,并防止其他颜色的干扰。例如,当选择红色滤波器,则只有红色的入射光可以被测量,蓝色和绿色将被阻止。此时我们就可以得到红光强度。同理,选择其他过滤器我们可以得到蓝色或绿色光。

TCS3002有四种颜色通道过滤器:红,蓝,绿和无光,通过控制引脚S2、S3的不同组合可以选择不同的滤波器,具体的组合为:

L L 红色
L H 蓝色
H L
H H 绿色

TCS3200输出的方波典型输出频率范围为2Hz~500kHz,可通过两个可编程引脚来选择100%、20%或2%的输出比例因子。如下所示:

L L 关断电源
L H 2%
H L 20%
H H 100%

对于测量得到的频率,我们通过一个简单的map函数映射到RGB颜色空间:

int16_t mapColor(int16_t value){
    if(value < tscValueMin){
        return 0;
    }
    if(value > tscValueMax){
        return 255;
    }

    return (value - tscValueMin) *  255 / (tscValueMax - tscValueMin);
}

TCS3200只测量光的颜色,对于不发光的物体的颜色就无法进行测量了。此时可通过借助模块上自带的4颗白光LED照射物体并测量反射光的方式来测量物体的表面颜色。因为需要在投射光和反射光的测量模式下进行切换,所以此处使用FireBeetle-E上自带的用户按键来作为LED的切换开关。

最终,我们通过以下代码实现对颜色的读取和对测量模式的切换(LED的控制)

void tcs3200_task(void *arg)
{
    int16_t r, g, b;

    // set scale
    gpio_set_level(TCS3200_PIN_NUM_S0,  1);
    gpio_set_level(TCS3200_PIN_NUM_S1,  1);
    tscValueMax = 130;
    tscValueMin = 2;

    // turn led off
    isLedOn = false;
    gpio_set_level(TCS3200_PIN_NUM_LED,  0);

    while(1){
        gpio_set_level(TCS3200_PIN_NUM_OE,  1);

        // RED
        gpio_set_level(TCS3200_PIN_NUM_S2,  0);
        gpio_set_level(TCS3200_PIN_NUM_S3,  0);

        pcnt_counter_clear(PCNT_UNIT);
        gpio_set_level(TCS3200_PIN_NUM_OE,  0);
        vTaskDelay(1 / portTICK_RATE_MS);
        gpio_set_level(TCS3200_PIN_NUM_OE,  1);

        pcnt_get_counter_value(PCNT_UNIT, &r);

        // GREEN
        gpio_set_level(TCS3200_PIN_NUM_S2,  1);
        gpio_set_level(TCS3200_PIN_NUM_S3,  1);

        pcnt_counter_clear(PCNT_UNIT);
        gpio_set_level(TCS3200_PIN_NUM_OE,  0);
        vTaskDelay(1 / portTICK_RATE_MS);
        gpio_set_level(TCS3200_PIN_NUM_OE,  1);

        pcnt_get_counter_value(PCNT_UNIT, &g);

        // BLUE
        gpio_set_level(TCS3200_PIN_NUM_S2,  0);
        gpio_set_level(TCS3200_PIN_NUM_S3,  1);

        pcnt_counter_clear(PCNT_UNIT);
        gpio_set_level(TCS3200_PIN_NUM_OE,  0);
        vTaskDelay(1 / portTICK_RATE_MS);
        gpio_set_level(TCS3200_PIN_NUM_OE,  1);

        pcnt_get_counter_value(PCNT_UNIT, &b);

        tscRed = mapColor(r), tscGreen = mapColor(g), tscBlue = mapColor(b);

        if(!gpio_get_level(KEY_PIN_NUM)){
            vTaskDelay(10 / portTICK_RATE_MS);
            if(!gpio_get_level(KEY_PIN_NUM)){
                continue;
            }
            while(!gpio_get_level(KEY_PIN_NUM)){
                vTaskDelay(1 / portTICK_RATE_MS);
            }

            if(isLedOn){
                isLedOn = false;
                gpio_set_level(TCS3200_PIN_NUM_LED,  0);
            }
            else{
                isLedOn = true;
                gpio_set_level(TCS3200_PIN_NUM_LED,  1);
            }

        }

        vTaskDelay(10 / portTICK_RATE_MS);
    }
}

LVGL的配置

本项目使用的LVGL的版本为开发时的最新版本8.3.5-dev。以下是一些关键的配置信息:

LV_COLOR_DEPTH 16
LV_COLOR_16_SWAP 1
LV_MEM_SIZE 48U * 1024U
LV_DISP_DEF_REFR_PERIOD 16
LV_USE_PERF_MONITOR 1

通过以下代码,为LVGL注册显示驱动并分配显示缓存:

void lv_port_disp_init(void) {

    static lv_disp_drv_t disp_drv;

    /*-----------------------------
     * Create a buffer for drawing
     *----------------------------*/

    static lv_disp_draw_buf_t dispBuffer;
    static lv_color_t buffer1[LV_SINGLE_BUF_SIZE_IN_PX];
    lv_color_t *buf2_1 = buffer1;
    lv_disp_draw_buf_init(&dispBuffer, buf2_1, NULL, LV_SINGLE_BUF_SIZE_IN_PX);

    /*-----------------------------------
     * Register the display in LVGL
     *----------------------------------*/

    /*Basic initialization*/
    lv_disp_drv_init(&disp_drv);

    /*Set up the functions to access to your display*/

    /*Set the resolution of the display*/
    disp_drv.hor_res = LV_LCD_HW_WIDTH;  // 320
    disp_drv.ver_res = LV_LCD_HW_HEIGHT; // 240

    /*Used to copy the buffer's content to the display*/
    disp_drv.flush_cb = disp_flush;
    // disp_drv.clean_dcache_cb = ex_disp_clean_dcache;

    /*Set a display buffer*/
    disp_drv.draw_buf = &dispBuffer;

    /*Required for Example 3)*/
    //disp_drv.full_refresh = 1

    /*Finally register the driver*/
    lv_disp_drv_register(&disp_drv);
}

LVGL的性能优化

对于LVGL性能的优化,这里比较重要的是对LVGL的这个宏进行定义

#define LV_ATTRIBUTE_FAST_MEM IRAM_ATTR

因为默认的代码从Flash中运行,在超过32KB的缓存大小后(这对LVGL来说很正常),运行速度将直线下降。此配置的原理是将LVGL的性能敏感代码通过显式设定,放置到ESP32的内部的I-RAM中,从而加快运行效率,提高了LVGL的性能。

优化后,LVGL的跑分可以达到86FPS,这对于SPI总线接口的LCD来说已经是一个非常高的帧率了。

FhRPOOgpOWLPpzV-WWmnXnsDYv80

LVGL界面的设计

因为界面并不复杂,所以使用纯手动编写代码进行布局,没有用到设计器。最终界面设计效果如图

Fn-fDMDFrWgnSvtm05PuO7yGs_sZ

通过4个lv_Obj和4个lv_label显示各个通道的颜色值以及最终的颜色值。

默认界面是透射光的测量界面(某个通道没有颜色时是黑色),如果打开了LED选择测量反射光,则界面的颜色和呈现模式也会对应的发生变化(某个通道没有颜色时是白色)

Fp9by2qhDheMMlNKs9rTm681yfAf

具体界面的设计代码如下

static void main_frame(){
    lv_obj_t *scr = lv_scr_act();
    lv_obj_set_style_bg_color(scr, lv_color_white(), LV_PART_MAIN);

    redIndView = lv_obj_create(scr);
    lv_obj_set_style_arc_rounded(redIndView, 0, LV_PART_MAIN);
    lv_obj_set_style_bg_color(redIndView, lv_color_black(), LV_PART_MAIN);
    lv_obj_set_size(redIndView, 104, 58);
    lv_obj_set_pos(redIndView, 8, 20);

    redLabel = lv_label_create(scr);
    lv_obj_set_pos(redLabel, 12, 4);
    lv_label_set_text(redLabel, "Red");

    greenIndView = lv_obj_create(scr);
    lv_obj_set_style_arc_rounded(greenIndView, 0, LV_PART_MAIN);
    lv_obj_set_style_bg_color(greenIndView, lv_color_black(), LV_PART_MAIN);
    lv_obj_set_size(greenIndView, 104, 58);
    lv_obj_set_pos(greenIndView, 8, 97);

    greenLabel = lv_label_create(scr);
    lv_obj_set_pos(greenLabel, 12, 81);
    lv_label_set_text(greenLabel, "Green");

    blueIndView = lv_obj_create(scr);
    lv_obj_set_style_arc_rounded(blueIndView, 0, LV_PART_MAIN);
    lv_obj_set_style_bg_color(blueIndView, lv_color_black(), LV_PART_MAIN);
    lv_obj_set_size(blueIndView, 104, 58);
    lv_obj_set_pos(blueIndView, 8, 174);

    blueLabel = lv_label_create(scr);
    lv_obj_set_pos(blueLabel, 12, 158);
    lv_label_set_text(blueLabel, "Blue");

    colorIndView = lv_obj_create(scr);
    lv_obj_set_style_arc_rounded(colorIndView, 0, LV_PART_MAIN);
    lv_obj_set_style_bg_color(colorIndView, lv_color_black(), LV_PART_MAIN);
    lv_obj_set_size(colorIndView, 186, 212);
    lv_obj_set_pos(colorIndView, 126, 20);

    colorLabel = lv_label_create(scr);
    lv_obj_set_pos(colorLabel, 130, 4);
    lv_label_set_text(colorLabel, "Color");

    redValueLabel = lv_label_create(redIndView);
    lv_label_set_text(redValueLabel, "0");
    lv_obj_set_content_width(redValueLabel, 50);
    lv_obj_set_style_text_color(redValueLabel, lv_color_white(), LV_PART_MAIN);
    lv_obj_set_style_text_align(redValueLabel, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
    lv_obj_align(redValueLabel, LV_ALIGN_CENTER, 0, 0);

    greenValueLabel = lv_label_create(greenIndView);
    lv_label_set_text(greenValueLabel, "0");
    lv_obj_set_content_width(greenValueLabel, 50);
    lv_obj_set_style_text_color(greenValueLabel, lv_color_white(), LV_PART_MAIN);
    lv_obj_set_style_text_align(greenValueLabel, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
    lv_obj_align(greenValueLabel, LV_ALIGN_CENTER, 0, 0);

    blueValueLabel = lv_label_create(blueIndView);
    lv_label_set_text(blueValueLabel, "0");
    lv_obj_set_content_width(blueValueLabel, 50);
    lv_obj_set_style_text_color(blueValueLabel, lv_color_white(), LV_PART_MAIN);
    lv_obj_set_style_text_align(blueValueLabel, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
    lv_obj_align(blueValueLabel, LV_ALIGN_CENTER, 0, 0);

    colorValueLabel = lv_label_create(colorIndView);
    lv_label_set_text(colorValueLabel, "#000000");
    lv_obj_set_content_width(colorValueLabel, 120);
    lv_obj_set_style_text_color(colorValueLabel, lv_color_white(), LV_PART_MAIN);
    lv_obj_set_style_text_align(colorValueLabel, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN);
    lv_obj_align(colorValueLabel, LV_ALIGN_CENTER, 0, 0);

    lv_timer_create(refresh_task_timer, 50, NULL);
}

界面的刷新使用LVGL自带的timer组件配合task来实现,在task中检查当前TCS3200的工作模式并更新组件的颜色。

void refresh_task_timer(lv_timer_t *timer) {
    if (isLedOn) {
        lv_obj_set_style_bg_color(lv_scr_act(), lv_color_black(), LV_PART_MAIN);

        lv_obj_set_style_text_color(redLabel, lv_color_white(), LV_PART_MAIN);
        lv_obj_set_style_text_color(greenLabel, lv_color_white(), LV_PART_MAIN);
        lv_obj_set_style_text_color(blueLabel, lv_color_white(), LV_PART_MAIN);
        lv_obj_set_style_text_color(colorLabel, lv_color_white(), LV_PART_MAIN);

        lv_obj_set_style_text_color(redValueLabel, lv_color_black(), LV_PART_MAIN);
        lv_obj_set_style_text_color(greenValueLabel, lv_color_black(), LV_PART_MAIN);
        lv_obj_set_style_text_color(blueValueLabel, lv_color_black(), LV_PART_MAIN);

        lv_obj_set_style_bg_color(redIndView, lv_color_make(0xFF, 0xFF - tscRed, 0xFF - tscRed), LV_PART_MAIN);
        lv_obj_set_style_bg_color(greenIndView, lv_color_make(0xFF - tscGreen, 0xFF, 0xFF - tscGreen), LV_PART_MAIN);
        lv_obj_set_style_bg_color(blueIndView, lv_color_make(0xFF - tscBlue, 0xFF - tscBlue, 0xFF), LV_PART_MAIN);
        lv_obj_set_style_bg_color(colorIndView, lv_color_make(tscRed, tscGreen, tscBlue), LV_PART_MAIN);

        lv_obj_set_style_text_color(colorValueLabel, lv_color_make(0xFF - tscRed, 0xFF - tscGreen, 0xFF - tscBlue),
                LV_PART_MAIN);
    }
    else {
        lv_obj_set_style_bg_color(lv_scr_act(), lv_color_white(), LV_PART_MAIN);

        lv_obj_set_style_text_color(redLabel, lv_color_black(), LV_PART_MAIN);
        lv_obj_set_style_text_color(greenLabel, lv_color_black(), LV_PART_MAIN);
        lv_obj_set_style_text_color(blueLabel, lv_color_black(), LV_PART_MAIN);
        lv_obj_set_style_text_color(colorLabel, lv_color_black(), LV_PART_MAIN);

        lv_obj_set_style_text_color(redValueLabel, lv_color_white(), LV_PART_MAIN);
        lv_obj_set_style_text_color(greenValueLabel, lv_color_white(), LV_PART_MAIN);
        lv_obj_set_style_text_color(blueValueLabel, lv_color_white(), LV_PART_MAIN);

        lv_obj_set_style_bg_color(redIndView, lv_color_make(tscRed, 0, 0), LV_PART_MAIN);
        lv_obj_set_style_bg_color(greenIndView, lv_color_make(0, tscGreen, 0), LV_PART_MAIN);
        lv_obj_set_style_bg_color(blueIndView, lv_color_make(0, 0, tscBlue), LV_PART_MAIN);
        lv_obj_set_style_bg_color(colorIndView, lv_color_make(tscRed, tscGreen, tscBlue), LV_PART_MAIN);

        lv_obj_set_style_text_color(colorValueLabel, lv_color_make(0xFF - tscRed, 0xFF - tscGreen, 0xFF - tscBlue),
                LV_PART_MAIN);
    }

    lv_label_set_text_fmt(redValueLabel, "%d", tscRed);
    lv_label_set_text_fmt(greenValueLabel, "%d", tscGreen);
    lv_label_set_text_fmt(blueValueLabel, "%d", tscBlue);
    lv_label_set_text_fmt(colorValueLabel, "#%02X%02X%02X", tscRed, tscGreen, tscBlue);
}

至此,所有的功能都已经实现

结语

Funpack活动为我们这些喜欢折腾各类技术的电子爱好者提供了一个非常好的平台,本期活动的板子和任务也非常有趣,希望这个活动能长期做下去,并且越办越好。

附件下载
LVGL-ColorPicker-sub-v1.0.zip.001
代码第一部分(共三部分,需要一起解压缩)
LVGL-ColorPicker-sub-v1.0.zip.002
代码第二部分(共三部分需要一起解压缩)
LVGL-ColorPicker-sub-v1.0.zip.003
代码第三部分(共三部分需要一起解压缩)
团队介绍
个人开发者
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号