褪色Arduino RGB LED从一种颜色到另一种颜色?

时间:2013-04-04 06:27:46

标签: c++ colors arduino rgb led

我目前设法让我的LED循环显示我选择的八种颜色。一切都正常,除了我想要更自然的感觉,并希望淡化/从一种颜色过渡到下一种颜色,而不是让它们只是互相替换。

到目前为止,这是我的代码:

int redPin = 11;
int greenPin = 10;
int bluePin = 9;

void setup()
{
    pinMode(redPin, OUTPUT);
    pinMode(greenPin, OUTPUT);
    pinMode(bluePin, OUTPUT);
}

void loop()
{
    setColor(250, 105, 0);   // Yellow
    delay(1000);

    setColor(250, 40, 0);    // Orange
    delay(1000);

    setColor(255, 0, 0);     // Red
    delay(1000);

    setColor(10, 10, 255);   // Blue
    delay(1000);

    setColor(255, 0, 100);   // Pink
    delay(1000);

    setColor(200, 0, 255);   // Purple
    delay(1000);

    setColor(0, 255, 0);     // Green
    delay(1000);

    setColor(255, 255, 255); // White
    delay(1000);
}

void setColor(int red, int green, int blue)
{
    analogWrite(redPin, 255-red);
    analogWrite(greenPin, 255-green);
    analogWrite(bluePin, 255-blue);
}

7 个答案:

答案 0 :(得分:13)

其他答案省略了关于这个主题的事实是人类对光强度的感知是logarithmic, not linearanalogWrite()例程设置输出引脚的PWM占空比,并且是线性的。因此,通过采用最小占空比(比如0)和最大占空比(比如,为了便于数学,这是10)并将其分成相等的块,您将控制强度< em>线性,不会产生令人满意的结果。

您需要做的是将指数强度设置为强度。假设您的最大强度为255。您可以通过将强度视为将某些数字增加到的幂来生成此结果。在我们的例子中,鉴于我们正在处理类似二进制的计算机,两个权限是方便的。所以,

2^0 =1
2^8=256

所以我们可以有8个强度等级。实际上,请注意,现在没有完全关闭(1而不是0)并且我们的最大值超出范围(256而不是255)。所以我们将公式修改为

output = 2 ^ intensity - 1

或代码

int output = 1<<intensity - 1;

对于从08(包括)的强度等级,这会产生0到255的值,因此我们实际上得到了9个强度等级。如果你想要更平滑的过渡(即更高强度级别),并且仍然使用对数强度,你将需要浮点数学。

如果您将这种计算强度的方法应用于每个通道(R,G,B),那么您的感知将与您的代码所说的一致。


作为如何在各种颜色之间平滑过渡的争论,答案取决于您想要如何导航色彩空间。最简单的方法是将你的色彩空间想象成一个三角形,用R,G和B作为椎体:

enter image description here

接下来的问题是如何导航这个三角形:你可以沿着两边,从R到G,再到B.这样你永远不会看到白色(所有通道完全打开)或“黑色”(全部完全关闭) )。您可以将您的色彩空间视为六边形,带有额外的紫色(R + B),黄色(G + B)和棕色(R + G)颜色,并且还可以导航周边(再次,没有白色或黑色)。有很多褪色的可能性,因为有很多方法可以在这些方面进行导航,以及我们可能会想到的其他数据。

当我构建这样的淡入淡出程序时,我喜欢的颜色空间和遍历如下:将每个通道视为二进制位,所以现在你有三个(R,G和B)。如果您认为每种颜色都完全打开了这些通道的某些组合,则会获得7种颜色(不包括黑色,但包括白色)。取这些颜色中的第一种,从黑色渐变为黑色,然后转到下一种颜色。这里有一些类似的代码:

int targetColor = 1;
int nIntensity = 0;
int nDirection = 1;         // When direction is 1 we fade towards the color (fade IN)
                            // when 0 we fade towards black (fade OUT)
#define MAX_INTENSITY 8
#define MIN_INTENSITY 0
#define MAX_TARGETCOLOR 7

void loop() {
    for (;;) {

        // Update the intensity value
        if (nDirection) {
            // Direction is positive, fading towards the color
            if (++nIntensity >= MAX_INTENSITY) {
                // Maximum intensity reached
                nIntensity = MAX_INTENSITY;  // Just in case
                nDirection = 0;             // Now going to fade OUT
            } // else : nothing to do
        } else {
            if (--nIntensity <= MIN_INTENSITY) {
                nIntensity = MIN_INTENSITY; // Just in case
                // When we get back to black, find the next target color
                if (++targetColor>MAX_TARGETCOLOR) 
                    targetColor=1;          // We'll skip fading in and out of black
                nDirection = 1;             // Now going to fade IN
            } // else: nothing to do
        }

        // Compute the colors
        int colors[3];
        for (int i=0;i<3;i++) {
            // If the corresponding bit in targetColor is set, it's part of the target color
            colors[i] = (targetColor & (1<<i)) ? (1<<nIntensity) -1 : 0;
        }

        // Set the color
        setColor(colors[0], colors[1], colors[2]);

        // Wait
        delay(100);     
    }
}

答案 1 :(得分:7)

确实可以在不同颜色之间淡化。我在Arduino书籍和Web代码中通常也缺少的是,可以在Arduino IDE中编写C ++类。因此,我将展示一个使用C ++类在颜色之间淡化的示例。

应该解决的问题是应该对analogWrite执行哪些引脚,因为并非所有引脚都能够进行脉冲宽度调制(PWM)。在Arduino器件上,支持PWM的引脚用波形符号表示&#39;〜#39;。 Arduino UNO具有数字引脚〜3,~5,~6,~9,~10和~11。大多数Arduino都将这些引脚用于PWM,但请检查您的设备。您可以通过将导通开启1ms并持续1 ms来在常规数字引脚上创建PWM,这样可以模拟LED上的50%功率。或者将它打开3毫秒和1毫秒,这模仿了75%的功率。

为了淡化LED,您必须降低/增加PWM值并稍等一下。你不得不等待一段时间,因为否则arduino会尝试每秒褪色/调暗LED数千次并且你不会看到褪色效果,尽管它可能存在。因此,您正在寻找一种方法来逐步减少/增加三个LED的第二个参数analogWrite( );有关更详尽的解释,请参阅Arduino Cookbook的第7章。无论如何,这本书对于Arduino粉丝来说都是一本很好的读物!

所以我调整了OP中的代码以包含一个&#39; rgb_color&#39;类或多或少只是红色,绿色和蓝色值的容器。但更重要的是推子类。当构造一个推子的实例时,正确的引脚应分别在构造函数中为红色,绿色和蓝色。比推子包含一个成员函数void fade( const rgb_color& const rgb_color&),它将在进出颜色之间进行淡入淡出。默认情况下,该功能将从输入颜色到输出颜色采取256步10ms。 (请注意,由于整数除法,这并不意味着每个步骤的第1/256,但在理论上你没有注意到它。)

/*
 * LedBrightness sketch
 * controls the brightness of LEDs on "analog" (PWM) output ports.
 */

class rgb_color {

  private:
    int my_r;
    int my_g;
    int my_b;
  public:
    rgb_color (int red, int green, int blue)
      :
        my_r(red),
        my_g(green),
        my_b(blue)
    {
    }

    int r() const {return my_r;}
    int b() const {return my_b;}
    int g() const {return my_g;}
};

/*instances of fader can fade between two colors*/
class fader {

  private:
    int r_pin;
    int g_pin;
    int b_pin;

  public:
    /* construct the fader for the pins to manipulate.
     * make sure these are pins that support Pulse
     * width modulation (PWM), these are the digital pins
     * denoted with a tilde(~) common are ~3, ~5, ~6, ~9, ~10 
     * and ~11 but check this on your type of arduino. 
     */ 
    fader( int red_pin, int green_pin, int blue_pin)
      :
        r_pin(red_pin),
        g_pin(green_pin),
        b_pin(blue_pin)
    {
    }

    /*fade from rgb_in to rgb_out*/
    void fade( const rgb_color& in,
               const rgb_color& out,
               unsigned n_steps = 256,  //default take 256 steps
               unsigned time    = 10)   //wait 10 ms per step
    {
      int red_diff   = out.r() - in.r();
      int green_diff = out.g() - in.g();
      int blue_diff  = out.b() - in.b();
      for ( unsigned i = 0; i < n_steps; ++i){
        /* output is the color that is actually written to the pins
         * and output nicely fades from in to out.
         */
        rgb_color output ( in.r() + i * red_diff / n_steps,
                           in.g() + i * green_diff / n_steps,
                           in.b() + i * blue_diff/ n_steps);
        /*put the analog pins to the proper output.*/
        analogWrite( r_pin, output.r() );
        analogWrite( g_pin, output.g() );
        analogWrite( b_pin, output.b() );
        delay(time);
      }
    }

};

void setup()
{
  //pins driven by analogWrite do not need to be declared as outputs
}

void loop()
{
  fader f (3, 5, 6); //note OP uses 9 10 and 11
  /*colors*/
  rgb_color yellow( 250, 105,   0 );
  rgb_color orange( 250,  40,   0 );
  rgb_color red   ( 255,   0,   0 );
  rgb_color blue  (  10,  10, 255 );
  rgb_color pink  ( 255,   0, 100 );
  rgb_color purple( 200,   0, 255 );
  rgb_color green (   0, 255,   0 );
  rgb_color white ( 255, 255, 255 );

  /*fade colors*/
  f.fade( white, yellow);
  f.fade( yellow, orange);
  f.fade( orange, red);
  f.fade( red, blue);
  f.fade( blue, pink);
  f.fade( pink, purple);
  f.fade( purple, green);
  f.fade( green, white);
}

答案 2 :(得分:4)

如果你想在颜色之间淡入淡出,可以在颜色空间中工作,这样可以轻松完成,然后在最后转换回RGB。

例如,在HSL colour space中工作,保持S和L不变(比如完全饱和和明亮的颜色)然后在圆圈周围“淡化”H - 你将从红色变为绿色,蓝色和背面变红了。转换回RGB,然后将这些值用于LED驱动器。我将此技术用于"mood lamp" app,其他code for the colour space conversion可以在SO上找到。

答案 3 :(得分:3)

这可能就是你要找的东西。每当我们想要在光谱上移动颜色并以圆形和平滑运动的方式传输颜色时,我们真正做的是在HSI / HSV(色调,饱和度,强度/值)颜色空间中使用HUE来移动光。

如果你想这个数字:

enter image description here

我们将附加一个0-360的值作为色调,因为色调有360度的颜色。 饱和度值为0.00 - 1.00,强度值/值

值为0.00 -1.00

这是我在MEGA 2560上的电路: enter image description here

以下是此代码运行的视频:

<iframe width="560" height="315" src="https://www.youtube.com/embed/gGG-GndSKi0" frameborder="0" allowfullscreen></iframe>

所以让我们构建一个函数,我们可以在循环函数中传递色调值和一个for循环来调用该值360次以移动整个彩虹色。

//Define the pins we will use with our rgb led
int redPin = 9;
int greenPin = 10;
int bluePin = 11;

//define that we are using common anode leds
#define COMMON_ANODE

void setup()
{
  pinMode(redPin, OUTPUT);
  pinMode(greenPin, OUTPUT);
  pinMode(bluePin, OUTPUT);
}

int rgb[3];
//Arduino has no prebuilt function for hsi to rgb so we make one:
void hsi_to_rgb(float H, float S, float I) {
  int r, g, b;
  if (H > 360) {
    H = H - 360;
  }
  // Serial.println("H: "+String(H));
  H = fmod(H, 360); // cycle H around to 0-360 degrees
  H = 3.14159 * 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 (H < 2.09439) {
    r = 255 * I / 3 * (1 + S * cos(H) / cos(1.047196667 - H));
    g = 255 * I / 3 * (1 + S * (1 - cos(H) / cos(1.047196667 - H)));
    b = 255 * I / 3 * (1 - S);
  } else if (H < 4.188787) {
    H = H - 2.09439;
    g = 255 * I / 3 * (1 + S * cos(H) / cos(1.047196667 - H));
    b = 255 * I / 3 * (1 + S * (1 - cos(H) / cos(1.047196667 - H)));
    r = 255 * I / 3 * (1 - S);
  } else {
    H = H - 4.188787;
    b = 255 * I / 3 * (1 + S * cos(H) / cos(1.047196667 - H));
    r = 255 * I / 3 * (1 + S * (1 - cos(H) / cos(1.047196667 - H)));
    g = 255 * I / 3 * (1 - S);
  }
  rgb[0] = r;
  rgb[1] = g;
  rgb[2] = b;

}
void setColor(int red, int green, int blue)
{
  #ifdef COMMON_ANODE
    red = 255 - red;
    green = 255 - green;
    blue = 255 - blue;
  #endif
  analogWrite(redPin, red);
  analogWrite(greenPin, green);
  analogWrite(bluePin, blue);
}


///here we have our main loop and the for loop to shift color
void loop()
{
//the for loop counts to 360 and because its in our control loop it will run forever
// We will use int i to increment the actual desired color 
for (int i=0; i<=360;i++){
  hsi_to_rgb(i,1,1);
  setColor(rgb[0],rgb[1],rgb[2]);
  //Changing the delay() value in milliseconds will change how fast the
  //the light moves over the hue values 
  delay(5);
  }


}

答案 4 :(得分:1)

您可以使用颜色的结构来简化代码。

struct Color
{
    unsigned char r;
    unsigned char g;
    unsigned char b;
};

然后,很容易有淡化功能

// the old color will be modified, hence it is not given as reference
void fade(Color old, const Color& newColor)
{
    // get the direction of increment first (count up or down)
    // each of the inc_x will be either 1 or -1
    char inc_r = (newColor.r - old.r)/abs(newColor.r-old.r); // note, that the right hand side will be sign extended to int according to the standard.
    char inc_g = (newColor.g - old.g)/abs(newColor.g-old.g);
    char inc_b = (newColor.g - old.g)/abs(newColor.g-old.g);

    fadeOneColor(old.r, newColor.r, inc_r, old);
    fadeOneColor(old.g, newColor.g, inc_g, old);
    fadeOneColor(old.b, newColor.b, inc_b, old);
}

void fadeOneColor( unsigned char& col_old, 
                   const unsigned char& col_new, 
                   const char inc, 
                   Color& col)
{
    while(col_old != col_new)
    {
        col_old += inc;
        SetColor(col); 
        delay(20);
    }        
}

答案 5 :(得分:1)

我想提供一个更用户友好的答案,以帮助理解它的工作原理。

在下面的示例中,我使用的是共阳极 RGB LED。


然而,在我的项目中:要将颜色设置为 RGB LED,我通过 HW Serial 发送一个字符串。

命令示例: RGB000255000

此字符串形式的命令分为 4 个部分,每个部分 3 个字符。 使用上面的命令示例:

  • "RGB" : 过滤命令的执行位置。
  • "000" : 第二个 3 个字符代表红色值。
  • "255" : 第三个 3 个字符代表绿色值。
  • “000”:第 4 个 3 个字符代表蓝色值。

结果:这将在您的 LED 上输出绿色。


请参阅以下代码:

// Set your LED Pins.
const int rPin = 9;
const int gPin = 10;
const int bPin = 11;

// Set the variables that will assign a value to each Color Pin.
int rVal = 255;
int gVal = 255;
int bVal = 255;

// Fade Red Pin (In / Out).
void FadeRed(int red)
{
  // When Red Value on Red Pin is Inferior to the New Value: Fade In.
  if (rVal < red)
  {
    // Fade Out.
    for (int r = rVal; r <= red; r++)
    {
      // Set the Variable and Pin values.
      rVal = r;
      analogWrite(rPin, rVal);

      // Delay Slighlty (Synchronously). For Asynchronous Delay; you may try using "millis".
      delay(6);
    } 
  }

  // When Red Value on Red Pin is Superior to the New Value: Fade Out.
  else
  { 
    for (int r = rVal; r >= red; r--)
    {
      rVal = r;
      analogWrite(rPin, rVal);
      delay(6); 
    }
  }
}

// Fade Green Pin (In / Out).
void FadeGreen(int green)
{
  if (gVal < green)
  {
    for (int g = gVal; g <= green; g++)
    {
      gVal = g;
      analogWrite(gPin, gVal);
      delay(6);
    }
  }
  
  else
  { 
    for (int g = gVal; g >= green; g--)
    { 
      gVal = g;
      analogWrite(gPin, gVal);
      delay(6);
    }
  }
}

// Fade Blue Pin (In / Out).
void FadeBlue(int blue)
{
  if (bVal < blue)
  {
    for (int b = bVal; b <= blue; b++)
    {
      bVal = b;
      delay(6);
      analogWrite(bPin, b);
    }
  }
  
  else
  {
    for (int b = bVal; b >= blue; b--)
    {
      bVal = b;
      delay(6);
      analogWrite(bPin, b);
    }
  }
}

void FadeColor(int red, int green, int blue)
{
    // Debug Only.
    Serial.println("\n[+] Received Values");
    Serial.println(red);
    Serial.println(green);
    Serial.println(blue);

    // Code.
    FadeRed(red);
    FadeGreen(green);
    FadeBlue(blue);

    // Debug Only.
    Serial.println("\n[+] Pin Values \n");
    Serial.println(rVal);
    Serial.println(gVal);
    Serial.println(bVal);
}

/* Set RGB LED Color According to String Value. (i.e: RGB000000000) */
void SetColor(String color)
{  
  // Retrieve the New Color from String.
  /* Split a String : Start Position; End Position */
  String red = color.substring(3, 6);   /* Get the 1st 3 Characters Corresponding to RED   */
  String green = color.substring(6, 9); /* Get the 2nd 3 Characters Corresponding to GREEN */
  String blue = color.substring(9, 12); /* Get the Last 3 Characters Corresponding to BLUE */

  int r = atoi(red.c_str());
  int g = atoi(green.c_str());
  int b = atoi(blue.c_str());

  int redVal = 255 - r;
  int grnVal = 255 - g;
  int bluVal = 255 - b;
  
  FadeColor(redVal, grnVal, bluVal);
}


void setup()
{  
  pinMode(rPin, OUTPUT);
  pinMode(gPin, OUTPUT);
  pinMode(bPin, OUTPUT);

  pinMode(rPin, HIGH);
  pinMode(gPin, HIGH);
  pinMode(bPin, HIGH);

  analogWrite(rPin, rVal);
  analogWrite(gPin, gVal);
  analogWrite(bPin, bVal);
}

答案 6 :(得分:0)

这里&#39;存储在uint32_t中的两个RGB值之间的快速线性淡入淡出0x00RRGGBB,就像许多可寻址的RGB像素带(如NeoPixel)中使用的那样(受一些代码的启发)在NeoPixel Arduino库中。)

它没有考虑色彩空间,但在实践中仍然看起来很漂亮。

uint32_t fadeColor(uint32_t const x, uint32_t const y, uint8_t const fade)
{
  // boundary cases don't work with bitwise stuff below
  if (fade == 0)
  {
    return x;
  }
  else if (fade == 255)
  {
    return y;
  }

  uint16_t const invFadeMod = (255 - fade) + 1;
  uint16_t const fadeMod = fade + 1;
  // overflows below to give right result in significant byte
  uint8_t const xx[3] // r g b
  {
    static_cast<uint8_t>((uint8_t(x >> 16) * invFadeMod) >> 8),
    static_cast<uint8_t>((uint8_t(x >> 8) * invFadeMod) >> 8),
    static_cast<uint8_t>((uint8_t(x >> 0) * invFadeMod) >> 8),
  };
  uint8_t const yy[3] // r g b
  {
    static_cast<uint8_t>((uint8_t(y >> 16) * fadeMod) >> 8),
    static_cast<uint8_t>((uint8_t(y >> 8)* fadeMod) >> 8),
    static_cast<uint8_t>((uint8_t(y >> 0)* fadeMod) >> 8),
  };
  return ((uint32_t)(xx[0] + yy[0]) << 16) | ((uint32_t)(xx[1] + yy[1]) <<  8) | (xx[2] + yy[2]);
}