Thursday, February 4, 2021

How to control RGB leds | RGB fader library for AVR ATmega328P

RGB leds are fun and because they can be used in many projects I have decided to make a library to easily crossfade the colors of one or multiple RGB leds.

To see this library used in a real project, check out this video Digital Clock With RGB Night Lamp & Spherical Shelf.

How to control RGB leds | Crossfading RGB leds | Library for AVR ATmega328P

Crossfading an RGB led in the RGB colorspace

With 8 bits we have 256 values from 0 to 255 that represents the duty cycle - how long a led will be on then off in a period. For example setting the RED led to 255 and GREEN and BLUE to 0 will result in RED color. Or RED 255, GREEN 0 and BLUE 255 will show a purple color. All leds on (255 value) will result in a white lite. So this is how a certain color can be produced but how to cycle through all the possible combinations?

Crossfading an RGB led in the RGB colorspace 1

First the red color is set at 255 and green and blue to 0. Then the red will be decremented and the green will be incremented. When the red will be 0 and green 255 we change the fading up and fading down colors.

Crossfading an RGB led in the RGB colorspace 2

Now the green color is at 255 and red got to 0. In step 2 the green is decremented and the blue color is incremented until the green reaches 0 and blue 255.

Crossfading an RGB led in the RGB colorspace 3

Now the green color is at 0 and blue at 255 so we decrement blue and increment red and after that we start at step 1. In total we have 768 of color combinations (3 * 256).

The following is the full code. For generating the PWM on the RGB pins you can use one of my two libraries for generating PWM:

- Software PWM:

- Binary Code Modulation (BCM):

BCM has the advantage of taking very low processing power compared to soft PWM and it's only drawback is that it has a short blink at the cross-over between 127 and 128 values.

Cross-fading the RGB led without the library

#include <avr/io.h>
#include "softwarePWM.h"

#define RGB_MAX_COLOR_VALUE		255

enum RGB{

int main(void){
	// RGB related
	int16_t RGB_values[] = {255, 0, 0}; // Red, Green, Blue (must be int16_t or int)
	uint8_t RGB_fading_up_color = GREEN;
	uint8_t RGB_fading_down_color = RED;
	const uint8_t RGB_fade_step = 1;

		softwarePWM_Set(0, RGB_values[RED]);
softwarePWM_Set(1, RGB_values[GREEN]);
softwarePWM_Set(2, RGB_values[BLUE]); RGB_values[RGB_fading_up_color] += RGB_fade_step; RGB_values[RGB_fading_down_color] -= RGB_fade_step; // Reached top of fading up color, change to the next one if(RGB_values[RGB_fading_up_color] > RGB_MAX_COLOR_VALUE){ RGB_values[RGB_fading_up_color] = RGB_MAX_COLOR_VALUE; RGB_fading_up_color++; if(RGB_fading_up_color > BLUE) RGB_fading_up_color = RED; } // Reached bottom of fading down color, change to the next one if(RGB_values[RGB_fading_down_color] < 0){ RGB_values[RGB_fading_down_color] = 0; RGB_fading_down_color++; if(RGB_fading_down_color > BLUE) RGB_fading_down_color = RED; } _delay_ms(10); } return 0; }

RGB fader library - crossfading in RGB colorspace

Instead of having all that code in the main loop let's use a library instead. First include it:

include "rgbFader.h"

By default the code is for a common cathode RGB led and if you are using a common anode one you have to change this define to TRUE:


Then create an object for every RGB led. In this example we have 2 RGB leds that requires a total of 6 pins. "RGBFader" is the name of the "class" and "rgb_led1" can be any name.

RGBFader rgb_led1;
RGBFader rgb_led2;

Next initialize each object:

rgbFader_init(&rgb_led1, 1);
rgbFader_init(&rgb_led2, 5);

First parameter is the address of the object hence the & symbol and the second is the fade step. With a fade step of 5 we have less colors and the cross-fading will be faster.

The above code must not be in the loop only the following code must be in the main loop:

softwarePWM_Set(0, rgb_led1.RGB_values[RGB_RED_IDX]);
softwarePWM_Set(1, rgb_led1.RGB_values[RGB_GREEN_IDX]);
softwarePWM_Set(2, rgb_led1.RGB_values[RGB_BLUE_IDX]);
softwarePWM_Set(3, rgb_led2.RGB_values[RGB_RED_IDX]);
softwarePWM_Set(4, rgb_led2.RGB_values[RGB_GREEN_IDX]);
softwarePWM_Set(5, rgb_led2.RGB_values[RGB_BLUE_IDX]);
/*softwarePWM_Set(0, softwarePWM_LineartoLog(rgb_led1.RGB_values[RGB_RED_IDX]));
softwarePWM_Set(1, softwarePWM_LineartoLog(rgb_led1.RGB_values[RGB_GREEN_IDX]));
softwarePWM_Set(2, softwarePWM_LineartoLog(rgb_led1.RGB_values[RGB_BLUE_IDX]));*/

RGB_values[] holds the values for each color for each object and is a member of RGBFader structure. rgbFader() function is the main function that holds the code for crossfading the RGB colors and updates the RGB_values[] array with the new values for each object (led). Like with the init function it takes the address of the led object.

The delay can be changed depending on how fast you want to change the colors and can even be set by a flag in a timer interrupt instead.

The commented code is an alternative to the above code and is using a logarithmic brightness. Try both methods and see which ones do you like. In my opinion the logarithmic brightness produces better colors. Try increasing the minimum RGB color when using the logarithmic function

self->RGB_min_color_value = 0;

But what if you want to set a specific color?

rgbFader_setColor(RGBFader *self, uint8_t color_index)
First parameter is the address of the led object and the second one is the index of the color inside the color array. The array can be accessed by index or by the name of a defined color. In this way the color can be changed using a button and incrementing an index.

rgbFader array with predefined RGB colors
rgbFader array with predefined RGB colors


This define must be true for the color array to be included.

Cycling through all the predefined colors

// Cycle through colors
for(uint8_t i = 0; i < RGBFader_NrOfColors; i++){
	rgbFader_setColor(&rgb_led1, i);
	softwarePWM_Set(0, softwarePWM_LineartoLog(rgb_led1.RGB_values[RGB_RED_IDX]));
	softwarePWM_Set(1, softwarePWM_LineartoLog(rgb_led1.RGB_values[RGB_GREEN_IDX]));
	softwarePWM_Set(2, softwarePWM_LineartoLog(rgb_led1.RGB_values[RGB_BLUE_IDX]));

Crossfading an RGB led in HSL color space

With the help of the HSL to RGB library provided at this link cycling the RGB colors is much easier and also the saturation and brightness can be changed with just one function.

#include <avr/io.h> 
#include <util/delay.h>
#include "softwarePWM.h"
#include "RGBvsHSL.h"

int main(void){
	uint8_t saturation = 0;
	uint8_t lightness = 0;
	uint8_t rgb[3] = {0};

		for(uint16_t i = 0; i < 361; i++){
			HSLtoRGB(i, saturation, lightness, rgb);
			// 0, 1 and 2 are channel numbers where led pins are located
			softwarePWM_Set(0, rgb[0]);
			softwarePWM_Set(1, rgb[1]);
			softwarePWM_Set(2, rgb[2]);
			// How fast to change the colors
	return 0;




No comments:

Post a Comment