From 1cddfcb0e22b1e1321f92bb551fbace9bdb70c43 Mon Sep 17 00:00:00 2001 From: Mark Kriegsman Date: Fri, 18 Apr 2014 11:07:23 -0400 Subject: Adding basic XY Matrix example --- examples/XYMatrix/XYMatrix.ino | 197 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 examples/XYMatrix/XYMatrix.ino diff --git a/examples/XYMatrix/XYMatrix.ino b/examples/XYMatrix/XYMatrix.ino new file mode 100644 index 00000000..ff0c5d14 --- /dev/null +++ b/examples/XYMatrix/XYMatrix.ino @@ -0,0 +1,197 @@ +#include + +#define LED_PIN 3 + +#define COLOR_ORDER GRB +#define CHIPSET WS2811 + +#define BRIGHTNESS 64 + +// Helper functions for an two-dimensional XY matrix of pixels. +// Simple 2-D demo code is included as well. +// +// XY(x,y) takes x and y coordinates and returns an LED index number, +// for use like this: leds[ XY(x,y) ] == CRGB::Red; +// No error checking is performed on the ranges of x and y. +// +// XYsafe(x,y) takes x and y coordinates and returns an LED index number, +// for use like this: leds[ XY(x,y) ] == CRGB::Red; +// Error checking IS performed on the ranges of x and y, and an +// index of "-1" is returned. Special instructions below +// explain how to use this without having to do your own error +// checking every time you use this function. +// This is a slightly more advanced technique, and +// it REQUIRES SPECIAL ADDITIONAL setup, described below. + + +// Params for width and height +const uint8_t kMatrixWidth = 16; +const uint8_t kMatrixHeight = 16; + +// Param for different pixel layouts +const bool kMatrixSerpentineLayout = true; +// Set 'kMatrixSerpentineLayout' to false if your pixels are +// laid out all running the same way, like this: +// +// 0 > 1 > 2 > 3 > 4 +// | +// .----<----<----<----' +// | +// 5 > 6 > 7 > 8 > 9 +// | +// .----<----<----<----' +// | +// 10 > 11 > 12 > 13 > 14 +// | +// .----<----<----<----' +// | +// 15 > 16 > 17 > 18 > 19 +// +// Set 'kMatrixSerpentineLayout' to true if your pixels are +// laid out back-and-forth, like this: +// +// 0 > 1 > 2 > 3 > 4 +// | +// | +// 9 < 8 < 7 < 6 < 5 +// | +// | +// 10 > 11 > 12 > 13 > 14 +// | +// | +// 19 < 18 < 17 < 16 < 15 +// +// Bonus vocabulary word: anything that goes one way +// in one row, and then backwards in the next row, and so on +// is call "boustrophedon", meaning "as the ox plows." + + +// This function will return the right 'led index number' for +// a given set of X and Y coordinates on your matrix. +// IT DOES NOT CHECK THE COORDINATE BOUNDARIES. +// That's up to you. Don't pass it bogus values. +// +// Use the "XY" function like this: +// +// for( uint8_t x = 0; x < kMatrixWidth; x++) { +// for( uint8_t y = 0; y < kMatrixHeight; y++) { +// +// // Here's the x, y to 'led index' in action: +// leds[ XY( x, y) ] = CHSV( random8(), 255, 255); +// +// } +// } +// +// +uint16_t XY( uint8_t x, uint8_t y) +{ + uint16_t i; + + if( kMatrixSerpentineLayout == false) { + i = (y * kMatrixWidth) + x; + } + + if( kMatrixSerpentineLayout == true) { + if( y & 0x01) { + // Odd rows run backwards + uint8_t reverseX = (kMatrixWidth - 1) - x; + i = (y * kMatrixWidth) + reverseX; + } else { + // Even rows run forwards + i = (y * kMatrixWidth) + x; + } + } + + return i; +} + + +// Once you've gotten the basics working (AND NOT UNTIL THEN!) +// here's a helpful technique that can be tricky to set up, but +// then helps you avoid the needs for sprinkling array-bound-checking +// throughout your code. +// +// It requires a careful attention to get it set up correctly, but +// can potentially make your code smaller and faster. +// +// Suppose you have an 8 x 5 matrix of 40 LEDs. Normally, you'd +// delcare your leds array like this: +// CRGB leds[40]; +// But instead of that, declare an LED buffer with one extra pixel in +// it, "leds_plus_safety_pixel". Then declare "leds" as a pointer to +// that array, but starting with the 2nd element (id=1) of that array: +// CRGB leds_with_safety_pixel[41]; +// const CRGB* leds( leds_plus_safety_pixel + 1); +// Then you use the "leds" array as you normally would. +// Now "leds[0..N]" are aliases for "leds_plus_safety_pixel[1..(N+1)]", +// AND leds[-1] is now a legitimate and safe alias for leds_plus_safety_pixel[0]. +// leds_plus_safety_pixel[0] aka leds[-1] is now your "safety pixel". +// +// Now instead of using the XY function above, use the one below, "XYsafe". +// +// If the X and Y values are 'in bounds', this function will return an index +// into the visible led array, same as "XY" does. +// HOWEVER -- and this is the trick -- if the X or Y values +// are out of bounds, this function will return an index of -1. +// And since leds[-1] is actually just an alias for leds_plus_safety_pixel[0], +// it's a totally safe and legal place to access. And since the 'safety pixel' +// falls 'outside' the visible part of the LED array, anything you write +// there is hidden from view automatically. +// Thus, this line of code is totally safe, regardless of the actual size of +// your matrix: +// leds[ XYsafe( random8(), random8() ) ] = CHSV( random8(), 255, 255); +// +// The only catch here is that while this makes it safe to read from and +// write to 'any pixel', there's really only ONE 'safety pixel'. No matter +// what out-of-bounds coordinates you write to, you'll really be writing to +// that one safety pixel. And if you try to READ from the safety pixel, +// you'll read whatever was written there last, reglardless of what coordinates +// were supplied. + +#define NUM_LEDS (kMatrixWidth * kMatrixHeight) +CRGB leds_plus_safety_pixel[ NUM_LEDS + 1]; +CRGB* leds( leds_plus_safety_pixel + 1); + +uint16_t XYsafe( uint8_t x, uint8_t y) +{ + if( x >= kMatrixWidth) return -1; + if( y >= kMatrixHeight) return -1; + return XY(x,y); +} + + +// Demo that USES "XY" follows code below + +void loop() +{ + uint32_t ms = millis(); + int32_t yHueDelta32 = ((int32_t)cos16( ms * (27/1) ) * (350 / kMatrixWidth)); + int32_t xHueDelta32 = ((int32_t)cos16( ms * (39/1) ) * (310 / kMatrixHeight)); + DrawOneFrame( ms / 65536, yHueDelta32 / 32768, xHueDelta32 / 32768); + if( ms < 5000 ) { + FastLED.setBrightness( scale8( BRIGHTNESS, (ms * 256) / 5000)); + } else { + FastLED.setBrightness(BRIGHTNESS); + } + FastLED.show(); +} + +void DrawOneFrame( byte startHue8, int8_t yHueDelta8, int8_t xHueDelta8) +{ + byte lineStartHue = startHue8; + for( byte y = 0; y < kMatrixHeight; y++) { + lineStartHue += yHueDelta8; + byte pixelHue = lineStartHue; + for( byte x = 0; x < kMatrixWidth; x++) { + pixelHue += xHueDelta8; + leds[ XY(x, y)] = CHSV( pixelHue, 255, 255); + } + } +} + + +void setup() { + FastLED.addLeds(leds, NUM_LEDS).setCorrection(TypicalSMD5050); + FastLED.setBrightness( BRIGHTNESS ); +} + -- cgit v1.2.3