Frank Vannieuwkerke / HSI2RGBW_PWM

Dependents:   KL25Z_HSI2RGBW_PWM KL25Z_HSI2RGBW_PWM_local KL25Z_FFT_Demo FFT_BUENA ... more

hsi2rgbw_pwm.cpp

Committer:
frankvnk
Date:
2014-02-02
Revision:
4:a16b9c09561e
Parent:
3:dda6914d713f

File content as of revision 4:a16b9c09561e:

/**************************************************************************************************************
 *****                                                                                                    *****
 *****  Name: hsi2rgbw.cpp                                                                                *****
 *****  Date: 22/12/2013                                                                                  *****
 *****  Auth: Frank Vannieuwkerke                                                                         *****
 *****  Func: library for converting HSI color space values to RGBW                                       *****
 *****                                                                                                    *****
 *****  Code ported from http://saikoled.com - Copyright 2013, Brian Neltner                              *****
 *****  http://blog.saikoled.com/post/44677718712/how-to-convert-from-hsi-to-rgb-white                    *****
 *****  http://blog.saikoled.com/post/43693602826/why-every-led-light-should-be-using-hsi-colorspace      *****
 *****  https://github.com/saikoLED/MyKi/blob/master/myki_16_bit_random_fade/myki_16_bit_random_fade.ino  *****
 *****  https://github.com/saikoLED/MyKi/blob/master/myki_16_bit_fade/myki_16_bit_fade.ino                *****
 *****                                                                                                    *****
 *****  This program is free software: you can redistribute it and/or modify                              *****
 *****  it under the terms of the GNU General Public License as published by                              *****
 *****  the Free Software Foundation, version 3 of the License.                                           *****
 *****                                                                                                    *****
 *****  This program is distributed in the hope that it will be useful,                                   *****
 *****  but WITHOUT ANY WARRANTY; without even the implied warranty of                                    *****
 *****  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                     *****
 *****  GNU General Public License for more details.                                                      *****
 *****                                                                                                    *****
 *****  A copy of the GNU General Public License can be found at                                          *****
 *****  http://www.gnu.org/licenses/gpl.html                                                              *****
 **************************************************************************************************************/

#include "hsi2rgbw_pwm.h"

#define PI 3.14159265

PwmOut *_red;
PwmOut *_green;
PwmOut *_blue;
PwmOut *_white;

hsi2rgbw_pwm::hsi2rgbw_pwm(PinName pred, PinName pgreen, PinName pblue, PinName pwhite)
{
    parabol = 1;
    use_invpwm = 0;
    use_rgbw = RGBW;
    if(pred != NC) _red = new PwmOut (pred);
    if(pgreen != NC) _green = new PwmOut (pgreen);
    if(pblue != NC) _blue = new PwmOut (pblue);
    if(pwhite != NC) _white = new PwmOut (pwhite);
    if((pred != NC) && (pgreen != NC) && (pblue != NC) && (pwhite == NC))
        use_rgbw = RGB;
    if((pred == NC) && (pgreen == NC) && (pblue == NC) && (pwhite == NC))
    {
        use_pwm = 0;
    }
    else
    {
        use_pwm = 1;
        //Setup PWM channels - default period = 4 ms (250Hz) 
        _red->period_ms(4);
        _green->period_ms(4);
        _blue->period_ms(4);
        if(pwhite != NC)
            _white->period_ms(4);

        // Initial RGB values.
        _red->write(0.0f);
        _green->write(0.0f);
        _blue->write(0.0f);
        if(pwhite != NC)
            _white->write(0.0f);
    }
}

void hsi2rgbw_pwm::hsi2rgbw(float H, float S, float I, float* rgbw) {
    float _rgbw[4]; 
    if(rgbw == NULL)
        rgbw = _rgbw;
    float cos_h, Srgb;
    H = fmod(H,360); // cycle H around to 0-360 degrees
    H = PI*H/(float)180; // Convert to radians.
    S = S>0?(S<1?S:1):0; // clamp S and I to interval [0,1]
    I = I>0?(I<1?I:1):0;
    if(use_rgbw == RGBW)
        Srgb = 1;
    else
    {
        Srgb = S;
        S = 1;
    }
    // This section is modified by the addition of white so that it assumes 
    // fully saturated colors, and then scales with white to lower saturation.
    //
    // Next, scale appropriately the pure color by mixing with the white channel.
    // Saturation is defined as "the ratio of colorfulness to brightness" so we will
    // do this by a simple ratio wherein the color values are scaled down by (1-S)
    // while the white LED is placed at S.

    // This will maintain constant brightness because in HSI, R+B+G = I. Thus, 
    // S*(R+B+G) = S*I. If we add to this (1-S)*I, where I is the total intensity,
    // the sum intensity stays constant while the ratio of colorfulness to brightness
    // goes down by S linearly relative to total Intensity, which is constant.

    if(H < 2.09439) {
        cos_h = cos(H) / cos(1.047196667-H);
        rgbw[0] = S*I/3*(1+Srgb*cos_h);
        rgbw[1] = S*I/3*(1+Srgb*(1-cos_h));
        if(use_rgbw == RGBW)
        {
            rgbw[2] = 0;
            rgbw[3] = (1-S)*I;
        }
        else
            rgbw[2] = I/3*(1-Srgb);
    } else if(H < 4.188787) {
        H = H - 2.09439;
        cos_h = cos(H) / cos(1.047196667-H);
        rgbw[1] = S*I/3*(1+Srgb*cos_h);
        rgbw[2] = S*I/3*(1+Srgb*(1-cos_h));
        if(use_rgbw == RGBW)
        {
            rgbw[0] = 0;
            rgbw[3] = (1-S)*I;
        }
        else
            rgbw[0] = I/3*(1-Srgb);
    } else {
        H = H - 4.188787;
        cos_h = cos(H) / cos(1.047196667-H);
        rgbw[2] = S*I/3*(1+Srgb*cos_h);
        rgbw[0] = S*I/3*(1+Srgb*(1-cos_h));
        if(use_rgbw == RGBW)
        {
            rgbw[1] = 0;
            rgbw[3] = (1-S)*I;
        }
        else
            rgbw[1] = I/3*(1-Srgb);
    }

    if(use_invpwm)
    {
        rgbw[0] = (1.0f - rgbw[0]);
        rgbw[1] = (1.0f - rgbw[1]);
        rgbw[2] = (1.0f - rgbw[2]);
        if(use_rgbw == RGBW)
            rgbw[3] = (1.0f - rgbw[3]);
    }

    // parabolic mapping.
    if(parabol) {
        rgbw[0] *= rgbw[0];     // RED
        rgbw[1] *= rgbw[1];     // GREEN
        rgbw[2] *= rgbw[2];     // BLUE
        if(use_rgbw == RGBW)
            rgbw[3] *= rgbw[3]; // WHITE
    }

    if(use_pwm)
    {
        _red->write(rgbw[0]);
        _green->write(rgbw[1]);
        _blue->write(rgbw[2]);
        if(use_rgbw == RGBW)
            _white->write(rgbw[3]);
    }
}

void hsi2rgbw_pwm::period(uint32_t per)
{
    if(use_pwm)
    {
        _red->period_us(per);
        _green->period_us(per);
        _blue->period_us(per);
        if(use_rgbw == RGBW)
            _white->period_us(per);
    }
}
    
void hsi2rgbw_pwm::pwm(float* rgbw)
{
    if(use_pwm)
    {
        if(!use_invpwm)
        {
            _red->write(rgbw[0]);
            _green->write(rgbw[1]);
            _blue->write(rgbw[2]);
            if(use_rgbw == RGBW)
                _white->write(rgbw[3]);
        }
        else
        {
            _red->write(1.0f - rgbw[0]);
            _green->write(1.0f - rgbw[1]);
            _blue->write(1.0f - rgbw[2]);
            if(use_rgbw == RGBW)
                _white->write(1.0f - rgbw[3]);
        }
    }
}

void hsi2rgbw_pwm::parabolic(bool para)
{
    parabol = para;
}

void hsi2rgbw_pwm::colorMode(bool como)
{
    use_rgbw = como;
}

void hsi2rgbw_pwm::invertpwm(bool invpwm)
{
    use_invpwm = invpwm;
}