Skip to content

Instantly share code, notes, and snippets.

@jpraus
Created November 25, 2019 18:49
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save jpraus/07995fcb9248f39292aa7d68cc030bf6 to your computer and use it in GitHub Desktop.
Save jpraus/07995fcb9248f39292aa7d68cc030bf6 to your computer and use it in GitHub Desktop.
/*
This is an example of how simple driving a Neopixel can be
This code is optimized for understandability and changability rather than raw speed
More info at http://wp.josh.com/2014/05/11/ws2812-neopixels-made-easy/
*/
// Change this to be at least as long as your pixel string (too long will work fine, just be a little slower)
#define PIXELS 46 // Number of pixels in the string
#define BRIGHTNESS 60 // 0-255
byte ledBrightness[PIXELS];
// #define OUTER_LEDS_SIZE 20
// byte outerLeds[] = {0, 1, 2, 3, 10, 18, 19, 20, 21, 22, 23, 24, 30, 35, 40, 41, 42, 43, 44, 45};
// #define MIDDLE_LEDS_SIZE 16
// byte middleLeds[] = {4, 5, 6, 9, 11, 15, 16, 17, 25, 26, 29, 31, 36, 37, 38, 39};
// #define INNER_LEDS_SIZE 10
// byte innerLeds[] = {7, 8, 12, 13, 14, 27, 28, 32, 33, 34};
#define MODE_STILL 0
#define MODE_STILL_SHELL 1
byte mode = 0;
// These values depend on which pin your string is connected to and what board you are using
// More info on how to find these at http://www.arduino.cc/en/Reference/PortManipulation
// These values are for the pin that connects to the Data Input pin on the LED strip. They correspond to...
// Arduino Yun: Digital Pin 8
// DueMilinove/UNO: Digital Pin 12
// Arduino MeagL PWM Pin 4
// You'll need to look up the port/bit combination for other boards.
// Note that you could also include the DigitalWriteFast header file to not need to to this lookup.
#define PIXEL_PORT PORTB // Port of the pin the pixels are connected to
#define PIXEL_DDR DDRB // Port of the pin the pixels are connected to
#define PIXEL_BIT 3 // Bit of the pin the pixels are connected to
// These are the timing constraints taken mostly from the WS2812 datasheets
// These are chosen to be conservative and avoid problems rather than for maximum throughput
#define T1H 900 // Width of a 1 bit in ns
#define T1L 600 // Width of a 1 bit in ns
#define T0H 400 // Width of a 0 bit in ns
#define T0L 900 // Width of a 0 bit in ns
// The reset gap can be 6000 ns, but depending on the LED strip it may have to be increased
// to values like 600000 ns. If it is too small, the pixels will show nothing most of the time.
#define RES 6000 // Width of the low gap between bits to cause a frame to latch
// Here are some convience defines for using nanoseconds specs to generate actual CPU delays
#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
#define CYCLES_PER_SEC (F_CPU)
#define NS_PER_CYCLE ( NS_PER_SEC / CYCLES_PER_SEC )
#define NS_TO_CYCLES(n) ( (n) / NS_PER_CYCLE )
// Actually send a bit to the string. We must to drop to asm to enusre that the complier does
// not reorder things and make it so the delay happens in the wrong place.
inline void sendBit( bool bitVal ) {
if ( bitVal ) { // 0 bit
asm volatile (
"sbi %[port], %[bit] \n\t" // Set the output bit
".rept %[onCycles] \n\t" // Execute NOPs to delay exactly the specified number of cycles
"nop \n\t"
".endr \n\t"
"cbi %[port], %[bit] \n\t" // Clear the output bit
".rept %[offCycles] \n\t" // Execute NOPs to delay exactly the specified number of cycles
"nop \n\t"
".endr \n\t"
::
[port] "I" (_SFR_IO_ADDR(PIXEL_PORT)),
[bit] "I" (PIXEL_BIT),
[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
[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
);
} else { // 1 bit
// **************************************************************************
// This line is really the only tight goldilocks timing in the whole program!
// **************************************************************************
asm volatile (
"sbi %[port], %[bit] \n\t" // Set the output bit
".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
"nop \n\t" // Execute NOPs to delay exactly the specified number of cycles
".endr \n\t"
"cbi %[port], %[bit] \n\t" // Clear the output bit
".rept %[offCycles] \n\t" // Execute NOPs to delay exactly the specified number of cycles
"nop \n\t"
".endr \n\t"
::
[port] "I" (_SFR_IO_ADDR(PIXEL_PORT)),
[bit] "I" (PIXEL_BIT),
[onCycles] "I" (NS_TO_CYCLES(T0H) - 2),
[offCycles] "I" (NS_TO_CYCLES(T0L) - 2)
);
}
// 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)
// Here I have been generous and not tried to squeeze the gap tight but instead erred on the side of lots of extra time.
// This has thenice side effect of avoid glitches on very long strings becuase
}
inline void sendByte(unsigned char byte) {
for( unsigned char bit = 0 ; bit < 8 ; bit++ ) {
sendBit( bitRead( byte , 7 ) ); // Neopixel wants bit in highest-to-lowest order
// so send highest bit (bit #7 in an 8-bit byte since they start at 0)
byte <<= 1; // and then shift left so bit 6 moves into 7, 5 moves into 6, etc
}
}
/*
The following three functions are the public API:
ledSetup() - set up the pin that is connected to the string. Call once at the begining of the program.
sendPixel( r g , b ) - send a single pixel to the string. Call this once for each pixel in a frame.
show() - show the recently sent pixel on the LEDs . Call once per frame.
*/
// Set the specified pin up as digital out
void ledsetup() {
bitSet(PIXEL_DDR, PIXEL_BIT);
}
inline void sendPixel(unsigned char r, unsigned char g, unsigned char b) {
sendByte(g); // Neopixel wants colors in green then red then blue order
sendByte(r);
sendByte(b);
}
// Just wait long enough without sending any bots to cause the pixels to latch and display the last sent frame
void show() {
_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)
}
// This is my custom code
void showColor() {
for (byte i = 0; i < PIXELS; i++) {
ledBrightness[i] = BRIGHTNESS;
}
render();
}
void showShell() {
for (byte i = 0; i < PIXELS; i++) {
ledBrightness[i] = 0;
}
ledBrightness[0] = BRIGHTNESS;
ledBrightness[1] = BRIGHTNESS;
ledBrightness[2] = BRIGHTNESS;
ledBrightness[3] = BRIGHTNESS;
ledBrightness[10] = BRIGHTNESS;
ledBrightness[18] = BRIGHTNESS;
ledBrightness[19] = BRIGHTNESS;
ledBrightness[20] = BRIGHTNESS;
ledBrightness[21] = BRIGHTNESS;
ledBrightness[22] = BRIGHTNESS;
ledBrightness[23] = BRIGHTNESS;
ledBrightness[24] = BRIGHTNESS;
ledBrightness[30] = BRIGHTNESS;
ledBrightness[35] = BRIGHTNESS;
ledBrightness[40] = BRIGHTNESS;
ledBrightness[41] = BRIGHTNESS;
ledBrightness[42] = BRIGHTNESS;
ledBrightness[43] = BRIGHTNESS;
ledBrightness[44] = BRIGHTNESS;
ledBrightness[45] = BRIGHTNESS;
render();
}
void render() {
cli();
for(byte p = 0; p < PIXELS; p ++) {
sendPixel(ledBrightness[p], 0, 0);
}
sei();
show();
}
void setup() {
ledsetup();
randomSeed(analogRead(A2));
mode = random(0, 2);
}
void loop() {
switch (mode) {
case MODE_STILL:
showColor();
break;
case MODE_STILL_SHELL:
showShell();
break;
}
delay(10000);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment