├── .gitattributes ├── .gitignore ├── README.md ├── SimpleNeoPixelTimingTester └── SimpleNeoPixelTimingTester.ino ├── SimpleNeopixelDemo └── SimpleNeopixelDemo.ino └── licence.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ################# 2 | ## Eclipse 3 | ################# 4 | 5 | *.pydevproject 6 | .project 7 | .metadata 8 | bin/ 9 | tmp/ 10 | *.tmp 11 | *.bak 12 | *.swp 13 | *~.nib 14 | local.properties 15 | .classpath 16 | .settings/ 17 | .loadpath 18 | 19 | # External tool builders 20 | .externalToolBuilders/ 21 | 22 | # Locally stored "Eclipse launch configurations" 23 | *.launch 24 | 25 | # CDT-specific 26 | .cproject 27 | 28 | # PDT-specific 29 | .buildpath 30 | 31 | 32 | ################# 33 | ## Visual Studio 34 | ################# 35 | 36 | ## Ignore Visual Studio temporary files, build results, and 37 | ## files generated by popular Visual Studio add-ons. 38 | 39 | # User-specific files 40 | *.suo 41 | *.user 42 | *.sln.docstates 43 | 44 | # Build results 45 | 46 | [Dd]ebug/ 47 | [Rr]elease/ 48 | x64/ 49 | build/ 50 | [Bb]in/ 51 | [Oo]bj/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | *_i.c 58 | *_p.c 59 | *.ilk 60 | *.meta 61 | *.obj 62 | *.pch 63 | *.pdb 64 | *.pgc 65 | *.pgd 66 | *.rsp 67 | *.sbr 68 | *.tlb 69 | *.tli 70 | *.tlh 71 | *.tmp 72 | *.tmp_proj 73 | *.log 74 | *.vspscc 75 | *.vssscc 76 | .builds 77 | *.pidb 78 | *.log 79 | *.scc 80 | 81 | # Visual C++ cache files 82 | ipch/ 83 | *.aps 84 | *.ncb 85 | *.opensdf 86 | *.sdf 87 | *.cachefile 88 | 89 | # Visual Studio profiler 90 | *.psess 91 | *.vsp 92 | *.vspx 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | 101 | # TeamCity is a build add-in 102 | _TeamCity* 103 | 104 | # DotCover is a Code Coverage Tool 105 | *.dotCover 106 | 107 | # NCrunch 108 | *.ncrunch* 109 | .*crunch*.local.xml 110 | 111 | # Installshield output folder 112 | [Ee]xpress/ 113 | 114 | # DocProject is a documentation generator add-in 115 | DocProject/buildhelp/ 116 | DocProject/Help/*.HxT 117 | DocProject/Help/*.HxC 118 | DocProject/Help/*.hhc 119 | DocProject/Help/*.hhk 120 | DocProject/Help/*.hhp 121 | DocProject/Help/Html2 122 | DocProject/Help/html 123 | 124 | # Click-Once directory 125 | publish/ 126 | 127 | # Publish Web Output 128 | *.Publish.xml 129 | *.pubxml 130 | 131 | # NuGet Packages Directory 132 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 133 | #packages/ 134 | 135 | # Windows Azure Build Output 136 | csx 137 | *.build.csdef 138 | 139 | # Windows Store app package directory 140 | AppPackages/ 141 | 142 | # Others 143 | sql/ 144 | *.Cache 145 | ClientBin/ 146 | [Ss]tyle[Cc]op.* 147 | ~$* 148 | *~ 149 | *.dbmdl 150 | *.[Pp]ublish.xml 151 | *.pfx 152 | *.publishsettings 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | App_Data/*.mdf 166 | App_Data/*.ldf 167 | 168 | ############# 169 | ## Windows detritus 170 | ############# 171 | 172 | # Windows image file caches 173 | Thumbs.db 174 | ehthumbs.db 175 | 176 | # Folder config file 177 | Desktop.ini 178 | 179 | # Recycle Bin used on file shares 180 | $RECYCLE.BIN/ 181 | 182 | # Mac crap 183 | .DS_Store 184 | 185 | 186 | ############# 187 | ## Python 188 | ############# 189 | 190 | *.py[co] 191 | 192 | # Packages 193 | *.egg 194 | *.egg-info 195 | dist/ 196 | build/ 197 | eggs/ 198 | parts/ 199 | var/ 200 | sdist/ 201 | develop-eggs/ 202 | .installed.cfg 203 | 204 | # Installer logs 205 | pip-log.txt 206 | 207 | # Unit test / coverage reports 208 | .coverage 209 | .tox 210 | 211 | #Translations 212 | *.mo 213 | 214 | #Mr Developer 215 | .mr.developer.cfg 216 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimpleNeoPixelDemo 2 | 3 | A demonstration showing how easy it can be to drive WS2812 NeoPixels. 4 | 5 | The code sends bits and bytes directly to the LED strip without allocating memory for them, 6 | which allows to **control hundreds of pixels** with e.g. a small ATtiny85. 7 | The [Adafruit NeoPixel](https://github.com/adafruit/Adafruit_NeoPixel) library allocates 4 bytes 8 | per LED, and an ATtiny85 can only control a bit more than 50 pixels. 9 | 10 | So why use the Adafruit NeoPixel library at all? There are good reasons for both. 11 | 12 | * Use SimpleNeoPixel if you want to control loads of pixel and your code is *fast*, 13 | and you are not afraid of manually adjusting some timings in the library, if required. 14 | * Use Adafruit NeoPixel as an out-of-the-box solution to control as many pixels 15 | as fit into the microcontroller’s dynamic memory, without worrying about low-level performance. 16 | 17 | *Fast* means less than 6 µs per pixel. This allows for some arithmetic operations, 18 | but a sine is already far too slow. 19 | 20 | More in-depth info at: 21 | [NeoPixels Revealed: How to (not need to) generate precisely timed signals](http://wp.josh.com/2014/05/13/ws2812-neopixels-are-not-so-finicky-once-you-get-to-know-them/) 22 | -------------------------------------------------------------------------------- /SimpleNeoPixelTimingTester/SimpleNeoPixelTimingTester.ino: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | This is an example of how simple driving a Neopixel can be 4 | This code is optimized for understandability and changability rather than raw speed 5 | More info at http://wp.josh.com/2014/05/11/ws2812-neopixels-made-easy/ 6 | */ 7 | 8 | // Change this to be at least as long as your pixel string (too long will work fine, just be a little slower) 9 | 10 | #define PIXELS 144 // Number of pixels in the string 11 | 12 | // These values depend on which pin your string is connected to and what board you are using 13 | // More info on how to find these at http://www.arduino.cc/en/Reference/PortManipulation 14 | 15 | // These values are for digital pin 8 on an Arduino Yun or digital pin 12 on a DueMilinove 16 | // Note that you could also include the DigitalWriteFast header file to not need to to this lookup. 17 | 18 | #define PIXEL_PORT PORTB // Port of the pin the pixels are connected to 19 | #define PIXEL_DDR DDRB // Port of the pin the pixels are connected to 20 | #define PIXEL_BIT 4 // Bit of the pin the pixels are connected to 21 | 22 | // These are the timing constraints taken mostly from the WS2812 datasheet 23 | 24 | #define T1H 700 // Width of a 1 bit in ns 25 | #define T1L 600 // Width of a 1 bit in ns 26 | 27 | #define T0H 350 // Width of a 0 bit in ns 28 | #define T0L 800 // Width of a 0 bit in ns 29 | 30 | #define RES 6000 // Width of the low gap between bits to cause a frame to latch 31 | 32 | // Here are some convience defines for using nanoseconds specs to generate actual CPU delays 33 | 34 | #define NS_PER_SEC (1000000000L) // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives 35 | 36 | #define CYCLES_PER_SEC (F_CPU) 37 | 38 | #define NS_PER_CYCLE ( NS_PER_SEC / CYCLES_PER_SEC ) 39 | 40 | #define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE ) 41 | 42 | #define DELAY_CYCLES(n) ( ((n)>0) ? __builtin_avr_delay_cycles( n ) : __builtin_avr_delay_cycles( 0 ) ) // Make sure we never have a delay less than zero 43 | 44 | 45 | // Actually send a bit to the string. We turn off optimizations to make sure the compile does 46 | // not reorder things and make it so the delay happens in the wrong place. 47 | 48 | void sendBit(bool) __attribute__ ((optimize(0))); 49 | 50 | void sendBit( bool bitVal ) { 51 | 52 | if ( bitVal ) { 53 | 54 | bitSet( PIXEL_PORT , PIXEL_BIT ); 55 | 56 | DELAY_CYCLES( NS_TO_CYCLES( T1H ) - 2 ); // 1-bit width less overhead for the actual bit setting 57 | // Note that this delay could be longer and everything would still work 58 | bitClear( PIXEL_PORT , PIXEL_BIT ); 59 | 60 | DELAY_CYCLES( NS_TO_CYCLES( T1L ) - 10 ); // 1-bit gap less the overhead of the loop 61 | 62 | } else { 63 | 64 | bitSet( PIXEL_PORT , PIXEL_BIT ); 65 | 66 | DELAY_CYCLES( NS_TO_CYCLES( T0H ) - 2 ); // 0-bit width less overhead 67 | // ************************************************************************** 68 | // This line is really the only tight goldilocks timing in the whole program! 69 | // ************************************************************************** 70 | bitClear( PIXEL_PORT , PIXEL_BIT ); 71 | 72 | DELAY_CYCLES( NS_TO_CYCLES( T0L ) - 10 ); // 0-bit gap less overhead of the loop 73 | 74 | } 75 | 76 | // Note that the inter-bit gap can be as long as you want as long as it doesn't exceed the 5us reset timeout (which is A long time) 77 | // Here I have been generous and not tried to squeeze the gap tight but instead erred on the side of lots of extra time. 78 | // This has thenice side effect of avoid glitches on very long strings becuase 79 | 80 | 81 | } 82 | 83 | 84 | 85 | void sendByteFast( unsigned char byte ) { 86 | 87 | 88 | sendBit( bitRead( byte , 7 ) ); // Neopixel wants bit in highest-to-lowest order 89 | // so send highest bit (bit #7 in an 8-bit byte since they start at 0) 90 | byte <<= 1; // and then shift left so bit 6 moves into 7, 5 moves into 6, etc 91 | 92 | sendBit( bitRead( byte , 7 ) ); // Neopixel wants bit in highest-to-lowest order 93 | // so send highest bit (bit #7 in an 8-bit byte since they start at 0) 94 | byte <<= 1; // and then shift left so bit 6 moves into 7, 5 moves into 6, etc 95 | sendBit( bitRead( byte , 7 ) ); // Neopixel wants bit in highest-to-lowest order 96 | // so send highest bit (bit #7 in an 8-bit byte since they start at 0) 97 | byte <<= 1; // and then shift left so bit 6 moves into 7, 5 moves into 6, etc 98 | sendBit( bitRead( byte , 7 ) ); // Neopixel wants bit in highest-to-lowest order 99 | // so send highest bit (bit #7 in an 8-bit byte since they start at 0) 100 | byte <<= 1; // and then shift left so bit 6 moves into 7, 5 moves into 6, etc 101 | sendBit( bitRead( byte , 7 ) ); // Neopixel wants bit in highest-to-lowest order 102 | // so send highest bit (bit #7 in an 8-bit byte since they start at 0) 103 | byte <<= 1; // and then shift left so bit 6 moves into 7, 5 moves into 6, etc 104 | sendBit( bitRead( byte , 7 ) ); // Neopixel wants bit in highest-to-lowest order 105 | // so send highest bit (bit #7 in an 8-bit byte since they start at 0) 106 | byte <<= 1; // and then shift left so bit 6 moves into 7, 5 moves into 6, etc 107 | sendBit( bitRead( byte , 7 ) ); // Neopixel wants bit in highest-to-lowest order 108 | // so send highest bit (bit #7 in an 8-bit byte since they start at 0) 109 | byte <<= 1; // and then shift left so bit 6 moves into 7, 5 moves into 6, etc 110 | sendBit( bitRead( byte , 7 ) ); // Neopixel wants bit in highest-to-lowest order 111 | // so send highest bit (bit #7 in an 8-bit byte since they start at 0) 112 | byte <<= 1; // and then shift left so bit 6 moves into 7, 5 moves into 6, etc 113 | 114 | 115 | } 116 | 117 | 118 | 119 | void sendByte( unsigned char byte ) { 120 | 121 | for( unsigned char bit = 0 ; bit < 8 ; bit++ ) { 122 | 123 | sendBit( bitRead( byte , 7 ) ); // Neopixel wants bit in highest-to-lowest order 124 | // so send highest bit (bit #7 in an 8-bit byte since they start at 0) 125 | byte <<= 1; // and then shift left so bit 6 moves into 7, 5 moves into 6, etc 126 | 127 | } 128 | } 129 | 130 | /* 131 | 132 | The following three functions are the public API: 133 | 134 | ledSetup() - set up the pin that is connected to the string. Call once at the begining of the program. 135 | sendPixel( r g , b ) - send a single pixel to the string. Call this once for each pixel in a frame. 136 | show() - show the recently sent pixel on the LEDs . Call once per frame. 137 | 138 | */ 139 | 140 | 141 | // Set the specified pin up as digital out 142 | 143 | void ledsetup() { 144 | 145 | bitSet( PIXEL_DDR , PIXEL_BIT ); 146 | 147 | } 148 | 149 | void sendPixel( unsigned char r, unsigned char g , unsigned char b ) { 150 | 151 | sendByte(g); // Neopixel wants colors in green then red then blue order 152 | sendByte(r); 153 | sendByte(b); 154 | 155 | } 156 | 157 | 158 | // Just wait long enough without sending any bots to cause the pixels to latch and display the last sent frame 159 | 160 | void show() { 161 | DELAY_CYCLES( NS_TO_CYCLES(RES) ); 162 | } 163 | 164 | 165 | /* 166 | 167 | That is the whole API. What follows are some demo functions rewriten from the AdaFruit strandtest code... 168 | 169 | https://github.com/adafruit/Adafruit_NeoPixel/blob/master/examples/strandtest/strandtest.ino 170 | 171 | Note that we always turn off interrupts while we are sending pixels becuase an interupt 172 | could happen just when we were in the middle of somehting time sensitive. 173 | 174 | If we wanted to minimize the time interrupts were off, we could instead 175 | could get away with only turning off interrupts just for the very brief moment 176 | when we are actually sending a 0 bit (~1us), as long as we were sure that the total time 177 | taken by any interrupts + the time in our pixel generation code never exceeded the reset time (5us). 178 | 179 | */ 180 | 181 | 182 | // Display a single color on the whole string 183 | 184 | void showColor( unsigned char r , unsigned char g , unsigned char b ) { 185 | 186 | cli(); 187 | for( int p=0; pg->b where 269 | // hue of 0 = Full red 270 | // hue of 128 = 1/2 red and 1/2 green 271 | // hue of 256 = Full Green 272 | // hue of 384 = 1/2 green and 1/2 blue 273 | // ... 274 | 275 | unsigned int firstPixelHue = 0; // Color for the first pixel in the string 276 | 277 | for(unsigned int j=0; j=(3*256)) { // Normalize back down incase we incremented and overflowed 286 | currentPixelHue -= (3*256); 287 | } 288 | 289 | unsigned char phase = currentPixelHue >> 8; 290 | unsigned char step = currentPixelHue & 0xff; 291 | 292 | switch (phase) { 293 | 294 | case 0: 295 | sendPixel( ~step , step , 0 ); 296 | break; 297 | 298 | case 1: 299 | sendPixel( 0 , ~step , step ); 300 | break; 301 | 302 | case 2: 303 | sendPixel( step ,0 , ~step ); 304 | break; 305 | 306 | } 307 | 308 | currentPixelHue+=pixelAdvance; 309 | 310 | 311 | } 312 | 313 | sei(); 314 | 315 | show(); 316 | 317 | firstPixelHue += frameAdvance; 318 | 319 | } 320 | } 321 | 322 | 323 | // I added this one just to demonstrate how quickly you can flash the string. 324 | // Flashes get faster and faster until *boom* and fade to black. 325 | 326 | void detonate( unsigned char r , unsigned char g , unsigned char b , unsigned int startdelayms) { 327 | while (startdelayms) { 328 | 329 | showColor( r , g , b ); // Flash the color 330 | showColor( 0 , 0 , 0 ); 331 | 332 | delay( startdelayms ); 333 | 334 | startdelayms = ( startdelayms * 4 ) / 5 ; // delay between flashes is halved each time until zero 335 | 336 | } 337 | 338 | // Then we fade to black.... 339 | 340 | for( int fade=256; fade>0; fade-- ) { 341 | 342 | showColor( (r * fade) / 256 ,(g*fade) /256 , (b*fade)/256 ); 343 | 344 | } 345 | 346 | showColor( 0 , 0 , 0 ); 347 | 348 | 349 | } 350 | 351 | void inline trigger() { 352 | PORTD |= _BV(1); 353 | PORTD &= ~_BV(1); 354 | } 355 | 356 | 357 | void setup() { 358 | 359 | ledsetup(); 360 | 361 | pinMode( 2 , OUTPUT ); 362 | } 363 | 364 | 365 | void loop() { 366 | showColor(0,0,0); 367 | return; 368 | showColor(0xff,0xff,0xff); 369 | showColor(0,0,0); 370 | showColor(0xff,0xff,0xff); 371 | showColor(0,0,0); 372 | showColor(0xff,0xff,0xff); 373 | showColor(0,0,0); 374 | showColor(0xff,0xff,0xff); 375 | return; 376 | 377 | 378 | cli(); 379 | 380 | // sendPixel(0x0,0x0,0x0); 381 | sendPixel(0xff,0xff,0xff); 382 | sendPixel(0xff,0xff,0xff); 383 | 384 | trigger(); 385 | sendPixel(0xff,0xff,0xff); 386 | 387 | sendPixel(0x0,0x0,0x0); 388 | sendPixel(0x0,0x0,0x0); 389 | 390 | sei(); 391 | 392 | 393 | show(); 394 | return; 395 | 396 | 397 | const unsigned char o = 8; 398 | const unsigned char d = 4; 399 | 400 | bitSet( PIXEL_PORT , PIXEL_BIT ); 401 | 402 | DELAY_CYCLES( o ); // 1-bit width less overhead for the actual bit setting 403 | // Note that this delay could be longer and everything would still work 404 | bitClear( PIXEL_PORT , PIXEL_BIT ); 405 | 406 | DELAY_CYCLES( d ); // 1-bit width less overhead for the actual bit setting 407 | 408 | bitSet( PIXEL_PORT , PIXEL_BIT ); 409 | 410 | DELAY_CYCLES( o ); // 1-bit width less overhead for the actual bit setting 411 | // Note that this delay could be longer and everything would still work 412 | bitClear( PIXEL_PORT , PIXEL_BIT ); 413 | 414 | DELAY_CYCLES( d ); // 1-bit width less overhead for the actual bit setting 415 | bitSet( PIXEL_PORT , PIXEL_BIT ); 416 | 417 | DELAY_CYCLES( o ); // 1-bit width less overhead for the actual bit setting 418 | // Note that this delay could be longer and everything would still work 419 | bitClear( PIXEL_PORT , PIXEL_BIT ); 420 | 421 | DELAY_CYCLES( d ); // 1-bit width less overhead for the actual bit setting 422 | bitSet( PIXEL_PORT , PIXEL_BIT ); 423 | 424 | DELAY_CYCLES( o ); // 1-bit width less overhead for the actual bit setting 425 | // Note that this delay could be longer and everything would still work 426 | bitClear( PIXEL_PORT , PIXEL_BIT ); 427 | 428 | DELAY_CYCLES( d ); // 1-bit width less overhead for the actual bit setting 429 | bitSet( PIXEL_PORT , PIXEL_BIT ); 430 | 431 | DELAY_CYCLES( o ); // 1-bit width less overhead for the actual bit setting 432 | // Note that this delay could be longer and everything would still work 433 | bitClear( PIXEL_PORT , PIXEL_BIT ); 434 | 435 | DELAY_CYCLES( d ); // 1-bit width less overhead for the actual bit setting 436 | bitSet( PIXEL_PORT , PIXEL_BIT ); 437 | 438 | DELAY_CYCLES( o ); // 1-bit width less overhead for the actual bit setting 439 | // Note that this delay could be longer and everything would still work 440 | bitClear( PIXEL_PORT , PIXEL_BIT ); 441 | 442 | DELAY_CYCLES( d ); // 1-bit width less overhead for the actual bit setting 443 | bitSet( PIXEL_PORT , PIXEL_BIT ); 444 | 445 | DELAY_CYCLES( o ); // 1-bit width less overhead for the actual bit setting 446 | // Note that this delay could be longer and everything would still work 447 | bitClear( PIXEL_PORT , PIXEL_BIT ); 448 | 449 | DELAY_CYCLES( d ); // 1-bit width less overhead for the actual bit setting 450 | bitSet( PIXEL_PORT , PIXEL_BIT ); 451 | 452 | DELAY_CYCLES( o ); // 1-bit width less overhead for the actual bit setting 453 | // Note that this delay could be longer and everything would still work 454 | bitClear( PIXEL_PORT , PIXEL_BIT ); 455 | 456 | DELAY_CYCLES( d ); // 1-bit width less overhead for the actual bit setting 457 | bitSet( PIXEL_PORT , PIXEL_BIT ); 458 | 459 | DELAY_CYCLES( o ); // 1-bit width less overhead for the actual bit setting 460 | // Note that this delay could be longer and everything would still work 461 | bitClear( PIXEL_PORT , PIXEL_BIT ); 462 | 463 | DELAY_CYCLES( d ); // 1-bit width less overhead for the actual bit setting 464 | 465 | sendPixel(0xff,0xff,0xff); 466 | 467 | sendPixel(0xff,00,0xff); 468 | show(); 469 | delay(1000); 470 | return; 471 | 472 | // Some example procedures showing how to display to the pixels: 473 | colorWipe(255, 0, 0, 0); // Red 474 | colorWipe(0, 255, 0, 0); // Green 475 | colorWipe(0, 0, 255, 0); // Blue 476 | 477 | // Send a theater pixel chase in... 478 | theaterChase(127, 127, 127, 0); // White 479 | theaterChase(127, 0, 0, 0); // Red 480 | theaterChase( 0, 0, 127, 0); // Blue 481 | 482 | rainbowCycle(1000 , 20 , 5 ); 483 | detonate( 255 , 255 , 255 , 1000); 484 | 485 | return; 486 | 487 | } 488 | 489 | 490 | 491 | 492 | 493 | -------------------------------------------------------------------------------- /SimpleNeopixelDemo/SimpleNeopixelDemo.ino: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | This is an example of how simple driving a Neopixel can be 4 | This code is optimized for understandability and changability rather than raw speed 5 | More info at http://wp.josh.com/2014/05/11/ws2812-neopixels-made-easy/ 6 | */ 7 | 8 | // Change this to be at least as long as your pixel string (too long will work fine, just be a little slower) 9 | 10 | #define PIXELS 96*11 // Number of pixels in the string 11 | 12 | // These values depend on which pin your string is connected to and what board you are using 13 | // More info on how to find these at http://www.arduino.cc/en/Reference/PortManipulation 14 | 15 | // These values are for the pin that connects to the Data Input pin on the LED strip. They correspond to... 16 | 17 | // Arduino Yun: Digital Pin 8 18 | // DueMilinove/UNO: Digital Pin 12 19 | // Arduino MeagL PWM Pin 4 20 | 21 | // You'll need to look up the port/bit combination for other boards. 22 | 23 | // Note that you could also include the DigitalWriteFast header file to not need to to this lookup. 24 | 25 | #define PIXEL_PORT PORTB // Port of the pin the pixels are connected to 26 | #define PIXEL_DDR DDRB // Port of the pin the pixels are connected to 27 | #define PIXEL_BIT 4 // Bit of the pin the pixels are connected to 28 | 29 | // These are the timing constraints taken mostly from the WS2812 datasheets 30 | // These are chosen to be conservative and avoid problems rather than for maximum throughput 31 | 32 | #define T1H 900 // Width of a 1 bit in ns 33 | #define T1L 600 // Width of a 1 bit in ns 34 | 35 | #define T0H 400 // Width of a 0 bit in ns 36 | #define T0L 900 // Width of a 0 bit in ns 37 | 38 | // Older pixels would reliabily reset with a 6us delay, but some newer (>2019) ones 39 | // need 250us. This is set to the longer delay here to make sure things work, but if 40 | // you want to get maximum refresh speed, you can try decreasing this until your pixels 41 | // start to flicker. 42 | 43 | #define RES 250000 // Width of the low gap between bits to cause a frame to latch 44 | 45 | // Here are some convience defines for using nanoseconds specs to generate actual CPU delays 46 | 47 | #define NS_PER_SEC (1000000000L) // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives 48 | 49 | #define CYCLES_PER_SEC (F_CPU) 50 | 51 | #define NS_PER_CYCLE ( NS_PER_SEC / CYCLES_PER_SEC ) 52 | 53 | #define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE ) 54 | 55 | // Actually send a bit to the string. We must to drop to asm to enusre that the complier does 56 | // not reorder things and make it so the delay happens in the wrong place. 57 | 58 | inline void sendBit( bool bitVal ) { 59 | 60 | if ( bitVal ) { // 0 bit 61 | 62 | asm volatile ( 63 | "sbi %[port], %[bit] \n\t" // Set the output bit 64 | ".rept %[onCycles] \n\t" // Execute NOPs to delay exactly the specified number of cycles 65 | "nop \n\t" 66 | ".endr \n\t" 67 | "cbi %[port], %[bit] \n\t" // Clear the output bit 68 | ".rept %[offCycles] \n\t" // Execute NOPs to delay exactly the specified number of cycles 69 | "nop \n\t" 70 | ".endr \n\t" 71 | :: 72 | [port] "I" (_SFR_IO_ADDR(PIXEL_PORT)), 73 | [bit] "I" (PIXEL_BIT), 74 | [onCycles] "I" (NS_TO_CYCLES(T1H) - 2), // 1-bit width less overhead for the actual bit setting, note that this delay could be longer and everything would still work 75 | [offCycles] "I" (NS_TO_CYCLES(T1L) - 2) // Minimum interbit delay. Note that we probably don't need this at all since the loop overhead will be enough, but here for correctness 76 | 77 | ); 78 | 79 | } else { // 1 bit 80 | 81 | // ************************************************************************** 82 | // This line is really the only tight goldilocks timing in the whole program! 83 | // ************************************************************************** 84 | 85 | 86 | asm volatile ( 87 | "sbi %[port], %[bit] \n\t" // Set the output bit 88 | ".rept %[onCycles] \n\t" // Now timing actually matters. The 0-bit must be long enough to be detected but not too long or it will be a 1-bit 89 | "nop \n\t" // Execute NOPs to delay exactly the specified number of cycles 90 | ".endr \n\t" 91 | "cbi %[port], %[bit] \n\t" // Clear the output bit 92 | ".rept %[offCycles] \n\t" // Execute NOPs to delay exactly the specified number of cycles 93 | "nop \n\t" 94 | ".endr \n\t" 95 | :: 96 | [port] "I" (_SFR_IO_ADDR(PIXEL_PORT)), 97 | [bit] "I" (PIXEL_BIT), 98 | [onCycles] "I" (NS_TO_CYCLES(T0H) - 2), 99 | [offCycles] "I" (NS_TO_CYCLES(T0L) - 2) 100 | 101 | ); 102 | 103 | } 104 | 105 | // Note that the inter-bit gap can be as long as you want as long as it doesn't exceed the 5us reset timeout (which is A long time) 106 | // Here I have been generous and not tried to squeeze the gap tight but instead erred on the side of lots of extra time. 107 | // This has thenice side effect of avoid glitches on very long strings becuase 108 | 109 | 110 | } 111 | 112 | 113 | inline void sendByte( unsigned char byte ) { 114 | 115 | for( unsigned char bit = 0 ; bit < 8 ; bit++ ) { 116 | 117 | sendBit( bitRead( byte , 7 ) ); // Neopixel wants bit in highest-to-lowest order 118 | // so send highest bit (bit #7 in an 8-bit byte since they start at 0) 119 | byte <<= 1; // and then shift left so bit 6 moves into 7, 5 moves into 6, etc 120 | 121 | } 122 | } 123 | 124 | /* 125 | 126 | The following three functions are the public API: 127 | 128 | ledSetup() - set up the pin that is connected to the string. Call once at the begining of the program. 129 | sendPixel( r g , b ) - send a single pixel to the string. Call this once for each pixel in a frame. 130 | show() - show the recently sent pixel on the LEDs . Call once per frame. 131 | 132 | */ 133 | 134 | 135 | // Set the specified pin up as digital out 136 | 137 | void ledsetup() { 138 | 139 | bitSet( PIXEL_DDR , PIXEL_BIT ); 140 | 141 | } 142 | 143 | inline void sendPixel( unsigned char r, unsigned char g , unsigned char b ) { 144 | 145 | sendByte(g); // Neopixel wants colors in green then red then blue order 146 | sendByte(r); 147 | sendByte(b); 148 | 149 | } 150 | 151 | 152 | // Just wait long enough without sending any bots to cause the pixels to latch and display the last sent frame 153 | 154 | void show() { 155 | _delay_us( (RES / 1000UL) + 1); // Round up since the delay must be _at_least_ this long (too short might not work, too long not a problem) 156 | } 157 | 158 | 159 | /* 160 | 161 | That is the whole API. What follows are some demo functions rewriten from the AdaFruit strandtest code... 162 | 163 | https://github.com/adafruit/Adafruit_NeoPixel/blob/master/examples/strandtest/strandtest.ino 164 | 165 | Note that we always turn off interrupts while we are sending pixels becuase an interupt 166 | could happen just when we were in the middle of somehting time sensitive. 167 | 168 | If we wanted to minimize the time interrupts were off, we could instead 169 | could get away with only turning off interrupts just for the very brief moment 170 | when we are actually sending a 0 bit (~1us), as long as we were sure that the total time 171 | taken by any interrupts + the time in our pixel generation code never exceeded the reset time (5us). 172 | 173 | */ 174 | 175 | 176 | // Display a single color on the whole string 177 | 178 | void showColor( unsigned char r , unsigned char g , unsigned char b ) { 179 | 180 | cli(); 181 | for( int p=0; pg->b where 263 | // hue of 0 = Full red 264 | // hue of 128 = 1/2 red and 1/2 green 265 | // hue of 256 = Full Green 266 | // hue of 384 = 1/2 green and 1/2 blue 267 | // ... 268 | 269 | unsigned int firstPixelHue = 0; // Color for the first pixel in the string 270 | 271 | for(unsigned int j=0; j=(3*256)) { // Normalize back down incase we incremented and overflowed 280 | currentPixelHue -= (3*256); 281 | } 282 | 283 | unsigned char phase = currentPixelHue >> 8; 284 | unsigned char step = currentPixelHue & 0xff; 285 | 286 | switch (phase) { 287 | 288 | case 0: 289 | sendPixel( ~step , step , 0 ); 290 | break; 291 | 292 | case 1: 293 | sendPixel( 0 , ~step , step ); 294 | break; 295 | 296 | case 2: 297 | sendPixel( step ,0 , ~step ); 298 | break; 299 | 300 | } 301 | 302 | currentPixelHue+=pixelAdvance; 303 | 304 | 305 | } 306 | 307 | sei(); 308 | 309 | show(); 310 | 311 | firstPixelHue += frameAdvance; 312 | 313 | } 314 | } 315 | 316 | 317 | // I added this one just to demonstrate how quickly you can flash the string. 318 | // Flashes get faster and faster until *boom* and fade to black. 319 | 320 | void detonate( unsigned char r , unsigned char g , unsigned char b , unsigned int startdelayms) { 321 | while (startdelayms) { 322 | 323 | showColor( r , g , b ); // Flash the color 324 | showColor( 0 , 0 , 0 ); 325 | 326 | delay( startdelayms ); 327 | 328 | startdelayms = ( startdelayms * 4 ) / 5 ; // delay between flashes is halved each time until zero 329 | 330 | } 331 | 332 | // Then we fade to black.... 333 | 334 | for( int fade=256; fade>0; fade-- ) { 335 | 336 | showColor( (r * fade) / 256 ,(g*fade) /256 , (b*fade)/256 ); 337 | 338 | } 339 | 340 | showColor( 0 , 0 , 0 ); 341 | 342 | 343 | } 344 | 345 | void setup() { 346 | 347 | ledsetup(); 348 | 349 | } 350 | 351 | 352 | void loop() { 353 | 354 | // Some example procedures showing how to display to the pixels: 355 | colorWipe(255, 0, 0, 0); // Red 356 | colorWipe(0, 255, 0, 0); // Green 357 | colorWipe(0, 0, 255, 0); // Blue 358 | 359 | // Send a theater pixel chase in... 360 | theaterChase(127, 127, 127, 0); // White 361 | theaterChase(127, 0, 0, 0); // Red 362 | theaterChase( 0, 0, 127, 0); // Blue 363 | 364 | rainbowCycle(1000 , 20 , 5 ); 365 | detonate( 255 , 255 , 255 , 1000); 366 | 367 | return; 368 | 369 | } 370 | 371 | 372 | 373 | 374 | 375 | -------------------------------------------------------------------------------- /licence.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Josh Levine 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | --------------------------------------------------------------------------------