将 Arduino RGB LED 从一种颜色褪色到另一种颜色?
Posted
技术标签:
【中文标题】将 Arduino RGB LED 从一种颜色褪色到另一种颜色?【英文标题】:Fading Arduino RGB LED from one color to the other? 【发布时间】:2013-03-26 02:25:36 【问题描述】:我目前已经设法让我的 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);
【问题讨论】:
LED 的阴极是否连接到 AVR 的端口?或者是什么解释了AnalogWrite()
中的255 - x
?
你好!我使用的是阳极(+)RGB LED,这就是为什么需要“255 -”。
@userXXX 好的,我明白了,谢谢。
您需要的是 delta sigma 调制。您可以使用 SW IO 生成它,也可以使用具有它的微控制器,例如 Infineon XMC1000(BCCU)。 infineon.ro/cms/en/product/microcontrollers/…
【参考方案1】:
关于这个主题的其他答案忽略了一个事实,即人类对光强度的感知是logarithmic, not linear。 analogWrite()
例程设置输出引脚的PWM 占空比,并且是线性的。因此,通过取最小占空比(比如0
)和最大占空比(比如,为了简单的数学,这是10
)并将其分成相等的块,您将控制强度线性 这不会给出令人满意的结果。
您需要做的是以指数方式设置强度。假设您的最大强度是255
。您可以通过将您的强度视为提高某个数字的力量来生成此结果。在我们的例子中,鉴于我们正在处理喜欢二进制的计算机,所以 2 的幂是方便的。所以,
2^0 =1
2^8=256
所以我们可以有 8 个强度级别。实际上,请注意,最小值现在没有完全关闭(它是1
而不是0
)并且我们的最大值超出了范围(256
不是255
)。所以我们修改公式为
output = 2 ^ intensity - 1
或者在代码中
int output = 1<<intensity - 1;
对于从 0
到 8
(含)的强度级别,这会产生从 0 到 255 的值,因此我们实际上得到了九个强度级别。如果您想要更平滑的过渡(即更多级别的强度),并且仍然使用对数强度,您将需要浮点数学。
如果您将这种计算强度的方法应用于每个通道(R、G、B),那么您的感知将与您的代码所说的一致。
至于如何在各种颜色之间平滑过渡,答案取决于您希望如何导航颜色空间。最简单的做法是将色彩空间想象成一个三角形,以 R、G 和 B 为顶点:
那么问题是如何导航这个三角形:你可以沿着边走,从 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);
【讨论】:
这并没有真正尝试回答“颜色之间的褪色”(恕我直言)的问题 @MartinThompson:添加了一些代码来实际执行此操作。我想这就是你要找的。span> 谢谢,有用的代码。虽然......我仍然认为使用具有颜色代表的色彩空间更有帮助 - 您的“六边形路线”似乎是围绕 HSV 类型空间的圆圈的近似值。 @Martin Thompson 我认为一个人对如何导航色彩空间的偏好实际上取决于应用程序。我不是 100% 清楚 OP 试图完成什么,我认为这确实会影响一个人在色彩空间中的导航方式。 如果您有两种颜色可以渐变,我的经验是在 HSL 域中执行插值步骤比在 RGB 域中执行更令人愉悦(视觉上)的结果。但是,正如您所说,这确实取决于最终目标是什么......【参考方案2】:确实可以在不同颜色之间褪色。我在 Arduino 书籍和网络上的代码中通常也缺少的是,可以在 Arduino IDE 中编写 C++ 类。因此,我将展示一个使用 C++ 类在颜色之间渐变的示例。
应该解决的一个问题是应该对哪些引脚进行模拟写入,因为并非所有引脚都能够进行脉冲宽度调制 (PWM)。在 Arduino 设备上,支持 PWM 的引脚用波浪号“~”表示。 Arduino UNO 有数字引脚 ~3、~5、~6、~9、~10 和 ~11。大多数 Arduino 将这些引脚用于 PWM,但请检查您的设备以确保。您可以通过将 LED 打开 1 毫秒和 1 毫秒来在常规数字引脚上创建 PWM,这模拟了 LED 上 50% 的功率。或者在 3 ms 和 1 ms 后打开它,这模拟了 75% 的功率。
为了使 LED 变暗,您必须减少/增加 PWM 值并稍等片刻。您将不得不等待一会儿,否则 arduino 会每秒尝试使 LED 褪色/变暗数千次,而您将看不到褪色效果,尽管它可能在那里。因此,您正在寻找一种方法来逐渐减少/增加三个 LED 的第二个参数到 analogWrite( )
;有关更详尽的解释,请参见 Arduino Cookbook 的第 7 章。无论如何,这本书对于 Arduino 粉丝来说都是一本好书!
所以我修改了 OP 的代码以包含一个“rgb_color”类,它或多或少只是一个红色、绿色和蓝色值的容器。但更重要的是推子类。构造推子实例时,正确的引脚应分别位于构造函数中的红色、绿色和蓝色。推子包含一个成员函数void fade( const rgb_color& const rgb_color&)
,它将在输入和输出颜色之间进行渐变。默认情况下,该函数从输入颜色到输出颜色需要 10 毫秒的 256 步。 (请注意,由于整数除法,这并不意味着每一步都是 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);
【讨论】:
【参考方案3】:这可能是您正在寻找的。每当我们想要在光谱上移动颜色并以圆形和平滑的运动方式转换颜色时,我们真正要做的是在 HSI/HSV(色相、饱和度、强度/值)颜色空间中使用 HUE 来移动光。
如果你愿意,就拿这个图:
我们将为色调附加一个 0-360 的值,因为色调具有 360 度的颜色。 0.00 - 1.00 的值表示饱和度,0.00 -1.00 的值表示强度/值
这是我在 MEGA 2560 上的电路:
这是这段代码运行的视频:
<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】:如果您想在颜色之间渐变,请在一个颜色空间中工作,使其变得容易,然后在最后转换回 RGB。
例如,在HSL colour space 中工作,保持 S 和 L 不变(比如完全饱和且明亮的颜色),然后围绕圆圈“淡化”H - 你将从红色变为绿色、蓝色并返回红色.转换回 RGB,然后将这些值用于您的 LED 驱动器。我将这种技术用于"mood lamp" app,其他code for the colour space conversion 可以在 SO 上找到。
【讨论】:
【参考方案5】:您可以通过为颜色使用结构来简化代码。
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);
【讨论】:
这真的回答了“褪色”的问题吗? @ogni42:使用示例?【参考方案6】:我想提供一个更用户友好的答案,以帮助理解它的工作原理。
在下面的示例中,我使用的是共阳极 RGB LED。
然而,在我的项目中:要将颜色设置为 RGB LED,我通过硬件串行发送一个字符串。
命令示例: 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);
【讨论】:
【参考方案7】:这是存储在uint32_t
和0x00RRGGBB
中的两个 RGB 值之间的快速线性渐变,用于许多可寻址 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]);
【讨论】:
以上是关于将 Arduino RGB LED 从一种颜色褪色到另一种颜色?的主要内容,如果未能解决你的问题,请参考以下文章
text Arduino Multiplex RGB Led演示