Using color spaces in ATTiny13a for WS2811


    And again I welcome, Habr!

    My new idea is about using color spaces in microcontrollers.
    The fact that my news will not seem to someone like that, I will not be surprised at all.
    However, I propose a method and its implementation, similar to which I have not seen.

    A little introduction, how it all began. Most recently, the year ended and I inherited from it about 300 ws2811 LEDs. For some reason, my hands never reached them, but after the celebration of the New Year, I noticed with my relatives that my aunt had a small souvenir Christmas tree made of glass, which she loves. But this time she did not sparkle at all and did not sparkle for a very banal reason ... the batteries were dead. Remembering about his set of LEDs, he promised to make a small garland for this Christmas tree in order to eliminate these shortcomings once and for all and make another person close to me even a little happier.

    Development revolves in the head, gradually accelerating, sometimes turning into a whirlwind of thoughts, but until these days it has not budged. It stopped that all those ideas that related to the effects program and their switching seemed very cumbersome and terribly uncomfortable in my head when I implemented them in the RGB color space, and besides, I couldn’t decide which controller to use because of this. But thanks to my little experience as a designer, I also have knowledge of other spaces, which prompted me to think about HSL and HSV. There was no point in inventing a new one, it was completely necessary to implement it, just search ... but I still could not find it. No, of course I found many interesting solutions, and many of them were yours, presented right here on Habré, and for which I am infinitely grateful to all of you, you have given food to the mind.

    As a result, it was decided to dwell on HSL, excluding one S (Saturation) component from it, leaving it constant (since this is an extra parameter in the CDM implementation), and the controller decided to use ATMega8U.

    Having sketched the code and tested the firmware, I realized that something was missing. And there wasn’t enough for me a little discrepancy with the real.
    Every hunter wants to know ... And again I return to this question, and again I realize that there is none ...
    There is no orange color in nature, anyway, it's just a shade.

    This was the root and core of my idea, thanks to which I completely rewrote the code and came up with a method for using a compact description of the color space, and this is no longer HSL, HSV and not RGB, because I expanded the range of orange and moved it to a separate spatial sector.

    Now only in essence

    The space can be represented as the surface of the cylinder if it is closed, with the basic colors located at angles offset by 60 degrees, between which is filled with gradient transitions from one basic color to another:


    Where is the height of the cylinder, which is also a component of the gradient of shades .
    The same space is represented by a rectangle with the sides “A x B”, where “A” is the angle defining the base color and its hue, and “B” is the brightness, if the space is not closed or turned out of the cylinder.

    For the task, I decided to use a square with a side of 256 x 256, thereby keeping within the byte type, where for the angle, the values ​​vary within 0..255 (bytes), and the brightness: -127..127 (signed byte), thanks this got the opportunity to use 8 basic colors and 32 gradations of shades for each.

    The space is described as an array with RGB - components of the basic colors, and gradients of shades are calculated on the fly.
    Description example:

    {0,128,0}, {64,128,0}, {128,128,0}, {128,0,0}, {128,0,128}, {0,0,128}, {0,128,128}, {0,128,0}

    a sequence of basic colors is described here, let's just call it a rainbow, where the order of the components is changed according to the ws2811 (GRB) datasheet, and the last one is red, it closes the space, it looks like this:


    Now, if we take some color index, for example 183, then you can make it display in the RGB space this way:
    Base color index = 183/32 = 5 (blue, or {0,0,128})
    Hue offset = 183% 32 = 23
    Now we calculate the difference between the components of the resulting base color ({0,0,128 }) and the following {0,128,128} to calculate the increment (n call it the delta):
    dG = 0 - 0 = 0,   dR = 128 - 0 = 128,   dB = 128 - 128 = 0;

    Since there are 32 gradations between the colors, it is necessary to split the difference of the components:
    dG= 0 / 32 = 0,   dR= 128 / 32 = 4,   dB= 0 / 32 = 0;

    Now you need to multiply the resulting delta by the hue offset and add to the components of the current base color ({0,0,128}):
    G = 0 + 0 * 23 = 0,   R = 0 + 4 * 23 = 92,     B = 128 + 0 * 23 = 128;

    We got {0.92,128}, to which you can now add brightness, for example 50: {50,142,178} - the desired color.

    As you can see from the example, there is nothing complicated. If the delta takes a negative value, then the hue shift during multiplication gives a negative complement, which in total will give a difference with the final component, this will happen when the gradient component is in decline.

    And yes, the brightness is applied not proportionally, but linearly, which gives a certain error, but within the framework of the described problem this is not scary. It is also necessary to monitor the lower threshold so as not to cause overflow at negative brightness.

    Thus, the resulting space makes it possible to use 256 * 256 = 65536 shades.

    The entire method described has been optimized, all multiplications and divisions have been eliminated, and reduced to simplified quick math, it can be easily rewritten to assembler (I personally am absolutely satisfied with the speed: 200 ns between component calculations for 7 LEDs), if optimization is needed to reduce the size code.
    As a result, the initial decision to use ATMega8u was canceled in favor of tinky, since the received firmware took less than half a kilobyte.

    No, I'm not a goon, I'm just lazy.

    So, the code below performs initialization, and contains the function of outputting data to the ws2811 line: And this is the code of the main loop and the function of converting the space index into color components with an example of use:



    And here is a video demonstrating the work (though the colors are far from reality):

    There is no sense in describing the comments in the text; the method itself has already been described above. If you have questions, ask.

    Well, actually, that’s all I wanted to tell.

    ... not a joke!


    Now let's imagine for a second how this method can be used. How do you like, for example, the space shown in this picture:

    That's right! You can describe different colors and gradients between them. And who at the beginning of the description decided that the method does not allow using shades of gray - is wrong.

    In conclusion - the most delicious

    An array that describes space in memory takes 32 bytes (due to alignment), which is stored in program memory. If you create several such descriptions of spaces, and switch between them on the go, switching the pointer to the array, you can select the current space for each new LED in the queue, and also allows you to expand the number of gradations between the shades. And if you slightly modify the code, and increase the number of steps in the component description to 8, then 32 * 8 will give 256 (in my code 3 steps), then use seven arrays (0..255 red, yellow, green, blue, blue , purple, white) give a combination of 16m shades "simultaneously on the screen"! When using 224 bytes of firmware for storage. And if you sweat a little more and rewrite the code a little, then you can meet 96 bytes with the same result.

    Plus, thanks to the optimization and the resulting processing speed, you can use the so-called dithering method ((eng. Dither from Old English didderen - to tremble) With which you can achieve even more shades (just think: 512 * 512 * 512 = 134.217. 728) .This also allows you to create closures of spaces on each other in order to eliminate or make invisible transition effects. In general, everything proposed can be modified as you like, to your taste and color, but can be used as is, and I’m pleased I guarantee you!

    02/26/2015: I offer to download the source code for free use

    02/27/2015: An updated version of the code v1.2 is available.
    Changes are as follows:
    1. The base color is encoded with bits of one byte: 0bxxGGRRBB, the array is reduced to 8 bytes.
    2. The conversion function has been changed in accordance with paragraph 1., The code has become a bit larger (20-30 bytes)
    In the future I plan more changes, who are interested - do not miss!

    02/28/2015: Last update of the code to version v2.0
    1. Added work with subspaces, introduced 8x8 space (separate selection of 65,536 shades for subspaces with switching)

    2. Added an example of choosing a color subspace (in the demo, switching to the next subspace when reaching the lower brightness threshold)
    3. The code size has grown to 626 bytes (64 bytes - the space itself + 16 bytes to the pointers, total - 80 bytes, the rest is an example code: 188 bytes). Thus, more than 500 bytes of additional code are available for organizing the effects program.
    4. Slightly optimized code.
    5. Profiling was not performed.

    RESULTS: 5 free tinky legs, allow you to use several separate channels for independently displaying color effects on ws2811 lines, and implement control through hardware input or in automatic mode. The implemented buffer (14 LEDs) allows you to use a line of LEDs that is a multiple of this number, and transfer data to the next section by repeating the buffer transfer.

    There will be no more updates in this direction, we will consider the algorithm complete, although the ideas seem inexhaustible. I suggest that you independently develop developments in this direction.
    For example: you can implement a smooth change of color subspaces (teleportation), change the brightness control algorithm to the correct one, enter the color saturation parameter, and much more, which is missing here.
    Good luck to all the delights and accomplishments, with the upcoming spring!

    I look forward to your comments, criticisms, questions and tips.
    Thank you for your attention, see you soon!

    Use in commercial projects, resale of source code, use for profit and any mercenary purposes is prohibited. The source code is distributed free of charge as is, in case of use on other sites or in other sources, the author’s indication and notification of placement is mandatory.

    Also popular now: