安信可ESP-12K模组学习心得 ④ 在智能家居灯具行业,如何理解PWM平滑调光?带你实现在安信可 ESP-12K模组上;(附带DEMO)

一、冷暖光的PWM关系;

     我们在买灯具时候,一般看我们的灯支持怎么样的调节,比如只是单色,冷暖色,还是RGB七彩控制的。

    一般地,家庭也就单色和冷暖光为多,而 RGB 是那些舞台音乐场景为多。所以,智能家居的灯具涉及,以单色和冷暖光为多,以 冷暖光为例,那些灯具的色温是怎么定义的? PWM 又是如何输出这样的效果呢? 这里,我一一为大家阐述:


2.1、色温的定义

    任何一种属性都有单位,比如温度有摄氏度,而色温的单位是 卡尔文 ,简称 K,下面一图说明了:

    色温数值越大,看到的效果越冷白;
    
误解:色温越大,就越暖光;
在这里插入图片描述

    而我们常买的灯具又是如何产生这样的效果呢? 聪明的开发者,采用冷白灯珠和暖光灯具各自产生的亮度不一样,就有了以上的效果!下面是我平时开发中常用的2钟灯珠类型,3.3v即可点亮;

在这里插入图片描述


2.2、PWM与指定的色温亮度的计算

     既然有2个灯珠同时在亮,各自的亮度不同来调节色温亮度,那么我们好奇的是如何通过 pwm 产生这样的关系呢?

     很多人也许会这样说:色温大小就是暖色灯珠的亮度明暗,而亮度大小就是冷白灯珠的亮度明暗,这样的说话是错误的!

     对于 PWM 和 色温亮度的计算的公式,博主接触到很多公式,这里以乐鑫开源的为例,如下:

  • color_temperature :用户预想的灯具的色温数值,范围[0,100]
  • brightness :用户预想的灯具的亮度数值,范围[0,100]
  • 计算过程如下,得到的cold_tmpPWM冷灯珠的占空比,warm_tmpPWM冷灯珠的占空比:
    uint8_t warm_tmp = color_temperature * brightness / 100;
    uint8_t cold_tmp = (100 - color_temperature) * brightness / 100;

     看了上面的计算步骤,是否很简单?我们可以假想一下:

  • 效果1 中性光
         入参 color_temperature = 50 ,brightness = 100 ;
         代入公式后得到:warm_tmp:50 ,cold_tmp :50 ;

  • 效果2 最冷光
         入参 color_temperature = 100 ,brightness = 100 ;
         代入公式后得到:warm_tmp:100 ,cold_tmp :0;

  • 效果3 最暖光
         入参 color_temperature = 0,brightness = 100 ;
         代入公式后得到:warm_tmp:0,cold_tmp :100 ;

效果预想的各路PWM输出以上公式算出来的PWM输出对比结果
中性光2路一样亮度的输出warm_tmp:50 ,cold_tmp :50符合
最冷光效果冷灯珠100亮度,暖灯珠0亮度warm_tmp:100 ,cold_tmp :0反了
最暖光效果冷灯珠 0亮度,暖灯珠100亮度warm_tmp:0,cold_tmp :100反了

     以上步骤,为啥结论是反了?因为上上图可以看到:色温数值越大表示越冷效果,意味着色温越大,其暖灯珠的PWM高电平占空比越小! 那么我们如何纠正这个错误呢?

在这里插入图片描述

     这不用我来说吧?把入参的 color_temperature 修改为即可:

color_temperature = 100 - color_temperature;

二、 RGB/HSV

    这个应该是最简单的啦!博主读书的时候,经常接触这样的模型转换,下面给大家贴贴转换代码把?

HSV模型转RGB

static esp_err_t light_driver_hsv2rgb(uint16_t hue, uint8_t saturation, uint8_t value,
                                      uint8_t *red, uint8_t *green, uint8_t *blue)
{
    uint16_t hi = (hue / 60) % 6;
    uint16_t F = 100 * hue / 60 - 100 * hi;
    uint16_t P = value * (100 - saturation) / 100;
    uint16_t Q = value * (10000 - F * saturation) / 10000;
    uint16_t T = value * (10000 - saturation * (100 - F)) / 10000;

    switch (hi)
    {
    case 0:
        *red = value;
        *green = T;
        *blue = P;
        break;
    case 1:
        *red = Q;
        *green = value;
        *blue = P;
        break;
    case 2:
        *red = P;
        *green = value;
        *blue = T;
        break;
    case 3:
        *red = P;
        *green = Q;
        *blue = value;
        break;
    case 4:
        *red = T;
        *green = P;
        *blue = value;
        break;
    case 5:
        *red = value;
        *green = P;
        *blue = Q;
        break;
    default:
        return ESP_FAIL;
    }
    *red = *red * 255 / 100;
    *green = *green * 255 / 100;
    *blue = *blue * 255 / 100;
    return ESP_OK;
}

RGB模型转 HSV

static void light_driver_rgb2hsv(uint16_t red, uint16_t green, uint16_t blue,
                                 uint16_t *h, uint8_t *s, uint8_t *v)
{
    double hue, saturation, value;
    double m_max = MAX(red, MAX(green, blue));
    double m_min = MIN(red, MIN(green, blue));
    double m_delta = m_max - m_min;

    value = m_max / 255.0;
    if (m_delta == 0)
    {
        hue = 0;
        saturation = 0;
    }
    else
    {
        saturation = m_delta / m_max;
        if (red == m_max)
        {
            hue = (green - blue) / m_delta;
        }
        else if (green == m_max)
        {
            hue = 2 + (blue - red) / m_delta;
        }
        else
        {
            hue = 4 + (red - green) / m_delta;
        }
        hue = hue * 60;
        if (hue < 0)
        {
            hue = hue + 360;
        }
    }
    *h = (int)(hue + 0.5);
    *s = (int)(saturation * 100 + 0.5);
    *v = (int)(value * 100 + 0.5);
}

三、函数说明

  • light_driver_set_hue(uint16_t hue); //设置HSV模型的H

  • light_driver_set_saturation(uint8_t saturation); //设置HSV模型的S

  • light_driver_set_value(uint8_t value); //设置HSV模型的V

  • light_driver_set_color_temperature(uint8_t color_temperature); //设置色温

  • light_driver_set_brightness(uint8_t brightness); //设置俩亮度

  • light_driver_set_hsv(uint16_t hue, uint8_t saturation, uint8_t value); //设置HSV模型

  • light_driver_set_ctb(uint8_t color_temperature, uint8_t brightness); //设置色温亮度

  • light_driver_set_switch(bool status); //设置 开关

四、其他