Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

Help understanding some C# for LED strip


Best Answer Corey Kosak , 01 January 2014 - 09:21 PM

I think I can demystify some of this for you.

 

First (as another poster pointed out), this is integer division. In most languages, integer division always rounds down. For example, in real life 129/64 = 2.015625 but a computer doing integer division would round it down to exactly 2.

 

The question is, what can we do when we want "round up" behavior rather than "round down"? The standard trick is

 

replace

   numerator / denominator

with

  (numerator + (denominator-1)) / denominator

 

It has to be written this way because we want to add 1 except when the numerator is evenly divisible by the denominator.

 

Consider these examples in real life (using a calculator):

  • (127/64) = [font="helvetica, arial, sans-serif;"]1.984375, which rounds up to 2[/font]
  • (128/64) = 2 (exactly)
  • (129/64) = [font="helvetica, arial, sans-serif;"]2.015625, which rounds up to 3[/font]

Now let's imagine that we are only allowed to round down. Then:

  • [font="helvetica, arial, sans-serif;"](127+63)/64 = 2.96875, which rounds down to 2[/font]
  • [font="helvetica, arial, sans-serif;"](128+63)/64 = 2.984375, which rounds down to 2[/font]
  • [font="helvetica, arial, sans-serif;"](129+63)/64 = exactly 3, which rounds down to 3[/font]

[font="helvetica, arial, sans-serif;"]You can see that we get the same output (2, 2, 3) as we did in the "round up" case, but we were able to solve our problem using only "round down" operations.[/font]

 

You can try it on other cases and see that the substitution trick always gets you the same answer but you only need to ever round down, not up.

 

Now, as for why you want to round up. As you point out, your code fragment looks a little strange because it's all constants and you could have computed it yourself. Normally however this kind of thing appears in programs where one value (say, numLEDs) is a variable, not a constant. If you replace (most) of the occurrences of "32" with "numLEDs" in this program, you can see how this could be convenient.

 

Now as for your other questions:

  • The reason the high bit (0x80) is set on all the color bytes has to do with the protocol, which simply requires it
  • The reason line 13 exists is because the author is trying to write defensive code. The colors array occurs in groups of three. Line 18 sets the red component of each triple (which occur in any given triple at offset 0 of the triple). Line 17 sets the green component (which occurs at offset 1). But you'll notice that the blue component is not set inside the loop. Line 13 exists to make sure that any unset values have the default value of 0x80 (again, the protocol requires that the high bit be asserted in all of these bytes). This is probably overkill. If it were me, I'd probably delete line 13 and insert a new line in the inner for loop, namely:
    [color=rgb(0,0,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);]colors[/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);][[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);]i [/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);]*[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);] [/color][color=rgb(0,102,102);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);]3[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);] [/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);]+[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);] [/color][font="monospace;font-size:13px;background-color:rgb(250,250,250);"]2[/font][color=rgb(102,102,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);]][/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);] [/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);]=[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);] [/color][color=rgb(0,102,102);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);]0x80; // put this before line 17[/color]

I'm not sure what source code you adapted this from, but the lengthy comments on top of https://github.com/a...ter/LPD8806.cpp pretty much explain why this is so, almost. (almost!)

 

In particular, there's this paragraph:

[color=rgb(153,153,136);font-style:italic;]Curiously, zero bytes can only travel one meter (32[/color]
[color=rgb(153,153,136);font-style:italic;]LEDs) down the line before needing backup; the next meter requires an[/color]
[color=rgb(153,153,136);font-style:italic;]extra zero byte, and so forth. [/color]

 

If the constants in the array involved "31 and 32" rather then "63 and 64", this would match the above explanation perfectly. In other words, given the number of LEDs we have, we need to compute how many "meters" of strip we have. As a result of the above explanation, I would have expected to see a line like this:

 

 

[color=rgb(0,0,136);font-family:monospace;font-size:13px;]var[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;] zeros [/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;]=[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;] [/color][color=rgb(0,0,136);font-family:monospace;font-size:13px;]new[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;] [/color][color=rgb(0,0,136);font-family:monospace;font-size:13px;]byte[/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;][[/color][color=rgb(0,102,102);font-family:monospace;font-size:13px;]3[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;] [/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;]*[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;] [/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;](([/color][font="monospace;font-size:13px;"]numLEDs[/font][color=rgb(0,0,0);font-family:monospace;font-size:13px;] [/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;]+[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;] [/color][font="monospace;font-size:13px;"]31[/font][color=rgb(102,102,0);font-family:monospace;font-size:13px;])[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;] [/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;]/[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;] [/color][font="monospace;font-size:13px;"]32[/font][color=rgb(102,102,0);font-family:monospace;font-size:13px;])];[/color]

 

I can't explain why it's 63 and 64 rather than 31 and 32, but I hope the rest of this explanation helps.

 

Go to the full post


  • Please log in to reply
6 replies to this topic

#1 Mark Anderson

Mark Anderson

    Member

  • Members
  • PipPip
  • 25 posts
  • LocationChciago

Posted 01 January 2014 - 02:29 PM

Hi Guys

 

I bought one of these: http://www.adafruit.com/products/306 and can drive it from netduino using the modified example code on the adafruit site:

using Microsoft.SPOT.Hardware;        public static void LightStripSpi()        {            var spi = new SPI(new SPI.Configuration(Cpu.Pin.GPIO_NONE,                false, 0, 0, false, true, 10000, SPI.SPI_module.SPI1));            var colors = new byte[3 * 32];            var zeros = new byte[3 * ((32 + 63) / 64)];             while (true)            {                // all pixels off                for (int i = 0; i < colors.Length; ++i) colors[i] = (byte)(0x80 | 0);                // a progressive yellow/red blend                for (byte i = 0; i < 32; ++i)                {                    colors[i * 3 + 1] = 0x80 | 32;                    colors[i * 3 + 0] = (byte)(0x80 | (32 - i));                    spi.Write(colors);                    spi.Write(zeros);                    Thread.Sleep(1000 / 32); // march at 32 pixels per second                }            }        }

I can't for the life of me figure out what line 8 is doing:

 

var zeros = new byte[3 * ((32 + 63) / 64)];

 

I know it's creating a byte array. I assume the values of 63 and 64 are related to the no of LED's (32). Pretty poor example really: would be much clearer if no of LED's was defined as a variable or const.

 

Anyway why not just use: 

 

new byte[3 * 32]

 

I've never used byte arrays before, but it looks like byte[3 * 32] creates an array of 96 bytes (which makes sense: 1 byte per color * 3 * 32 LEDs) and it looks like byte[3 * ((32 + 63) / 64) creates a byte array of 4.453125 bytes - clearly this can't be what it's doing. Can anyone enlighten me?

 

It also seems that the zeros array never gets initialized to anything, so I'm surprised spi.Write(zeros) actually does clear the LEDs.

 

Is there a reason why line 13 isn't just:

 

[color=rgb(0,0,0);font-family:monospace;font-size:13px;]spi[/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;].[/color][color=rgb(102,0,102);font-family:monospace;font-size:13px;]Write[/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;]([/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;]zeros[/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;]);[/color]

 

Also, I'm not sure what line 17 does:

 

colors[i * 3 + 1] = 0x80 | 32;

 

I know it's writing a value to the i-th array member, but not sure why the color is OR'd with the color value. I guess it's just setting the MSB to 1, but not sure why.

 

TIA

 

Mark

 

 



#2 jacobgladish

jacobgladish

    New Member

  • Members
  • Pip
  • 6 posts

Posted 01 January 2014 - 06:54 PM

[color=rgb(40,40,40);font-family:helvetica, arial, sans-serif;]<quote>var zeros = new byte[3 * ((32 + 63) / 64)];</quote>[/color]

 

evaluate what's in parenthesis first and use integer math.

 

32 + 63 = 95

95 / 64 = 1

1 * 3 = 3

 

 

[color=rgb(40,40,40);font-family:helvetica, arial, sans-serif;]var zeros = new byte[3];[/color]

 

[color=rgb(40,40,40);font-family:helvetica, arial, sans-serif;]why all the extra math? That's usually done to communicate that it's not an arbitrary number that the programmer has selected, but some function of other stuff. I have no idea why +63. [/color]

 

As fars as the zeros not being set, the c# compiler always initializes everything to zero. Numerics are 0 or 0.0, bools are false, and references are null. It's the same as this

 

byte[] zeros = new byte[] { 0, 0, 0 };



#3 vader7071

vader7071

    Advanced Member

  • Members
  • PipPipPip
  • 132 posts
  • LocationDothan, AL

Posted 01 January 2014 - 08:16 PM

Just thinking out loud, could the reason for the "+63" be to make sure no matter what, the value always comes up as 1?  unless the "32" is zero, then your value at the end will always be 1 or larger (as long as the "32" value is less than 65).

I may be WAAAAAAY off base.

 

But then looking back at the code, 32+63 is hard coded, not a variable.  Why not just place

var zeros = new byte[3]
[font="tahoma, geneva, sans-serif;"]? [/font]
You would get the same result.  

#4 Corey Kosak

Corey Kosak

    Advanced Member

  • Members
  • PipPipPip
  • 276 posts
  • LocationHoboken, NJ

Posted 01 January 2014 - 09:21 PM   Best Answer

I think I can demystify some of this for you.

 

First (as another poster pointed out), this is integer division. In most languages, integer division always rounds down. For example, in real life 129/64 = 2.015625 but a computer doing integer division would round it down to exactly 2.

 

The question is, what can we do when we want "round up" behavior rather than "round down"? The standard trick is

 

replace

   numerator / denominator

with

  (numerator + (denominator-1)) / denominator

 

It has to be written this way because we want to add 1 except when the numerator is evenly divisible by the denominator.

 

Consider these examples in real life (using a calculator):

  • (127/64) = [font="helvetica, arial, sans-serif;"]1.984375, which rounds up to 2[/font]
  • (128/64) = 2 (exactly)
  • (129/64) = [font="helvetica, arial, sans-serif;"]2.015625, which rounds up to 3[/font]

Now let's imagine that we are only allowed to round down. Then:

  • [font="helvetica, arial, sans-serif;"](127+63)/64 = 2.96875, which rounds down to 2[/font]
  • [font="helvetica, arial, sans-serif;"](128+63)/64 = 2.984375, which rounds down to 2[/font]
  • [font="helvetica, arial, sans-serif;"](129+63)/64 = exactly 3, which rounds down to 3[/font]

[font="helvetica, arial, sans-serif;"]You can see that we get the same output (2, 2, 3) as we did in the "round up" case, but we were able to solve our problem using only "round down" operations.[/font]

 

You can try it on other cases and see that the substitution trick always gets you the same answer but you only need to ever round down, not up.

 

Now, as for why you want to round up. As you point out, your code fragment looks a little strange because it's all constants and you could have computed it yourself. Normally however this kind of thing appears in programs where one value (say, numLEDs) is a variable, not a constant. If you replace (most) of the occurrences of "32" with "numLEDs" in this program, you can see how this could be convenient.

 

Now as for your other questions:

  • The reason the high bit (0x80) is set on all the color bytes has to do with the protocol, which simply requires it
  • The reason line 13 exists is because the author is trying to write defensive code. The colors array occurs in groups of three. Line 18 sets the red component of each triple (which occur in any given triple at offset 0 of the triple). Line 17 sets the green component (which occurs at offset 1). But you'll notice that the blue component is not set inside the loop. Line 13 exists to make sure that any unset values have the default value of 0x80 (again, the protocol requires that the high bit be asserted in all of these bytes). This is probably overkill. If it were me, I'd probably delete line 13 and insert a new line in the inner for loop, namely:
    [color=rgb(0,0,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);]colors[/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);][[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);]i [/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);]*[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);] [/color][color=rgb(0,102,102);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);]3[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);] [/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);]+[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);] [/color][font="monospace;font-size:13px;background-color:rgb(250,250,250);"]2[/font][color=rgb(102,102,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);]][/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);] [/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);]=[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);] [/color][color=rgb(0,102,102);font-family:monospace;font-size:13px;background-color:rgb(250,250,250);]0x80; // put this before line 17[/color]

I'm not sure what source code you adapted this from, but the lengthy comments on top of https://github.com/a...ter/LPD8806.cpp pretty much explain why this is so, almost. (almost!)

 

In particular, there's this paragraph:

[color=rgb(153,153,136);font-style:italic;]Curiously, zero bytes can only travel one meter (32[/color]
[color=rgb(153,153,136);font-style:italic;]LEDs) down the line before needing backup; the next meter requires an[/color]
[color=rgb(153,153,136);font-style:italic;]extra zero byte, and so forth. [/color]

 

If the constants in the array involved "31 and 32" rather then "63 and 64", this would match the above explanation perfectly. In other words, given the number of LEDs we have, we need to compute how many "meters" of strip we have. As a result of the above explanation, I would have expected to see a line like this:

 

 

[color=rgb(0,0,136);font-family:monospace;font-size:13px;]var[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;] zeros [/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;]=[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;] [/color][color=rgb(0,0,136);font-family:monospace;font-size:13px;]new[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;] [/color][color=rgb(0,0,136);font-family:monospace;font-size:13px;]byte[/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;][[/color][color=rgb(0,102,102);font-family:monospace;font-size:13px;]3[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;] [/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;]*[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;] [/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;](([/color][font="monospace;font-size:13px;"]numLEDs[/font][color=rgb(0,0,0);font-family:monospace;font-size:13px;] [/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;]+[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;] [/color][font="monospace;font-size:13px;"]31[/font][color=rgb(102,102,0);font-family:monospace;font-size:13px;])[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;] [/color][color=rgb(102,102,0);font-family:monospace;font-size:13px;]/[/color][color=rgb(0,0,0);font-family:monospace;font-size:13px;] [/color][font="monospace;font-size:13px;"]32[/font][color=rgb(102,102,0);font-family:monospace;font-size:13px;])];[/color]

 

I can't explain why it's 63 and 64 rather than 31 and 32, but I hope the rest of this explanation helps.

 


  • Mark Anderson likes this

#5 Mark Anderson

Mark Anderson

    Member

  • Members
  • PipPip
  • 25 posts
  • LocationChciago

Posted 01 January 2014 - 10:49 PM

Thanks a lot guys.

 

I got sample netduino code from adafruit. All it had was what's in original post.

 

I'd also scrapped the whole = 63 / 64 and used byte[3 * numLEDs] and it worked fine. (Would have made example much clearer in first place.)



#6 Corey Kosak

Corey Kosak

    Advanced Member

  • Members
  • PipPipPip
  • 276 posts
  • LocationHoboken, NJ

Posted 01 January 2014 - 11:42 PM

Care to post the exact link where you got the code?



#7 Mark Anderson

Mark Anderson

    Member

  • Members
  • PipPip
  • 25 posts
  • LocationChciago

Posted 02 January 2014 - 02:03 PM

Here's the link

 

http://learn.adafrui...-led-strip/code

 

It does have info for the arduino and the libs, but I read this

 

"[color=rgb(82,82,82);font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;]Driving these strips from Netduino (or other .Net Micro Framwork boards like FEZ Panda) is very convenient and doesn't require a code library if SPI ports are used."[/color]

 

and assumed that the way you drove it from Netduino was totally different (i.e. not SPI). Guess I misunderstood

 

Regards

 

Mark






0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.