Skip to content

Commit 8e1158c

Browse files
committed
Un-mess-up tinyNeoPixel libraries
The copies on DxCore and here had diverged significantly. Also, the way that the presence/absence of a valid pin was handled here was nonsensical... t was checled for.... in places where it did not matter. But in the one place where it did, show, the same check was not performed. The null pointer would be treated as the pointer to the port struct of the pin (port struct address + 4 gives PORTx.OUT -so null pointer + 4 gives 0x0004. That's VPORTB.DIR... And because the bitmask was coming back NOT_A_PIN, in the event that it wound up getting an invalid pin iot would merrily toggle the hell out of the direction register for PORTB. Also, in some places the pin was an int8_t, in others uint8_t. Now is uint8_t everywhere in the library, consistent with conventions elsewhere. If pin is set to something that couldn't be a digital pin number. we now refuse to output signals.
1 parent 7929c10 commit 8e1158c

File tree

7 files changed

+2133
-1561
lines changed

7 files changed

+2133
-1561
lines changed

megaavr/extras/tinyNeoPixel.md

Lines changed: 92 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,66 @@
1-
## tinyNeoPixel - a compatible library for WS2812 "NeoPixel" and similar
1+
## tinyNeoPixel - a modern AVR (AVRxt) compatible library for WS2812 "NeoPixel" and similar
2+
#### for tinyAVR 0/1/2-series, megaAVR 0-series, AVR DA/DB-series, future AVR Ex-series
23

3-
The change in architecture from the classic AVR to the "modern AVR" of the tinyAVR 0-series and 1-series parts (and megaAVR 0-series parts) causes many of the existing WS2812 libraries to not work on the these parts. This library adapts the Adafruit_NeoPixel library to work with the improved architecture, and adds support for 10MHz and 20MHz clock speeds. Unlike in ATTinyCore, where slower clock speeds require a separate implementation (chosen via a menu option in that case) depending on which PORT the pin being used is on, these parts do not need this, because the indirect ST instruction executes in a single clock cycle instead of 2. Prior to version 2.0.2, this was not realized, and there were issues with real WS2812B LEDs (as opposed to SK6812 clones) in addition to a requirement for awkward port-dependant methods of calling the library.
4+
The change in architecture from the classic AVRs (which use the AVRe or AVRe+ variant of the AVR instruction set) to "modern" AVRs (which use AVRxt) causes many of the existing WS2812 libraries to not work on the new parts. This library adapts the Adafruit_NeoPixel library to work with AVRxt version; this has slightly improved execution times, among other things, and adds support for additional clock speeds: 10 and 20 MHz because of it's use on the tinyAVR 0/1/2-series and megaAVR 0-series, and faster speeds all the way up to 48 MHz (I have yet to try overclocking them quite that hard, but while rated at 24 MHz, the Dx-series seems to be able to do 32 MHz off the internal oscillator (that's without touching the calibration). Unlike the version for classic AVR (AVRe/AVRe+), one of the benefits of the improved timing is that ST (indirect store) executes in 1 clock cycle, rather than 2, so there is no need for multiple versions of the library.
45

5-
### tinyNeoPixel and tinyNeoPixel_Static
6-
There are two versions of this library provided. `tinyNeoPixel` implements the entire API that Adafruit_NeoPixel does, including setting the pin and length of the string at runtime. This provides maximum portability between code written for use with Adafruit_NeoPixel and tinyNeoPixel (only the constructor and library name need to be changed).
6+
Like the normal Adafruit_NeoPixel library, this supports WS2811/WS2812/SK6812 and all the nominally compatible single-wire individually addressable LEDs. It does not support two-wire ones like the APA102 or the countless knockoffs thereof; libraries for those do not pose special compatibility issues with AVRxt because they are not timing critical in the same way.
77

8-
`tinyNeoPixel_Static` is slightly cutdown, removing the option to change the length at runtime, and instead of using malloc() to dynamically allocate the pixel buffer, the user must declare the pixel buffer and pass it to the constructor. Additionally, it does not set the pinMode of the pin (the sketch must set this as OUTPUT). Finally, no call to begin() need be made - begin() is removed entirely. These changes reduce flash use (by eliminating malloc() and free(), and because the pixel buffer is statically declared, the memory used for it is included in count of used SRAM output when the sketch is compiled. Removal of the pinMode() call within the library, while it requires an additional line of code in the sketch, opens the option of eliminating all calls to pinMode() from a sketch (replacing with direct port manipulation) when maximum flash savings is required.
8+
### tinyNeoPixel and tinyNeoPixel_Static
9+
There are two versions of this library provided. `tinyNeoPixel` implements the entire API that Adafruit_NeoPixel does, including setting the length and type of LEDs (color order and whether it's RGB or RGBW) of the string at runtime. This provides maximum portability between code written for use with Adafruit_NeoPixel and tinyNeoPixel (only the constructor and library name need to be changed) - however, the memory used to store the "frame buffer" (the color of every pixel) is not included in the SRAM usage displayed when compiling a sketch because it is "dynamically allocated". This can be a large portion of the memory available on smaller parts (I have seen support inquiries from people trying to control a string of LEDs which would require several times the total memory of the part for this alone); trying to allocate a larger array than will fit in RAM does not generate any sort of error - it just fails to allocate anything at runtime, nothing gets sent to the light string, and no LEDs turn on. This dynamic memory allocation also requires compiling in malloc(), free(), and associated functions; on parts with small flash (ie, tinyAVR), this can be significant. Finally, dynamic memory allocation is arguably bad pratice on small embedded systems like the AVR devices.
10+
11+
`tinyNeoPixel_Static` is slightly cutdown, removing the option to change the length or type of pixels at runtime (which relies on dynamic memory allocation), and requires the user to manually declare the pixel array and pass it to the tinyNeoPixel constructor. Additionally, it does not set the pinMode of the pin (the sketch must set this as OUTPUT); this allows severely flash-constrained applications to save a small amount of flash by eliminating calls to pinMode and replacing them with writes to the `PORTx.DIR` or `VPORTx.DIR` registers. Finally, no call to begin() need be made - begin() is removed entirely. These changes reduce sketch size and provide greater visibility on the memory usage. Unless you need to change string length or type at runtime, it is recommended that `tinyNeoPixel_Static` be used.
12+
13+
14+
### Constructors
15+
The constructor is the declaration that you call outside of any function, to create the global tinyNeoPixel object.
16+
17+
18+
```
19+
#include <tinyNeoPixel_Static.h>
20+
#define NUMLEDS 100
21+
byte pixels[NUMLEDS*3];
22+
tinyNeoPixel leds = tinyNeoPixel(NUMLEDS, 5, NEO_GRB, pixels);
23+
void setup() {
24+
pinMode(5,OUTPUT);
25+
leds.setPixelColor(0,255,0,0); // first LED full RED
26+
leds.show(); // LED turns on.
27+
}
28+
```
29+
The equivilant example with the Static version uses only 1076 bytes and reports (accurately) that it uses 330 bytes of RAM.
30+
31+
32+
`tinyNeoPixel(uint16_t n, uint8_t p, neoPixelType t=NEO_GRB)` - for `tinyNeoPixel` only.
33+
```
34+
#include <tinyNeoPixel.h>
35+
#define NUMLEDS 100
36+
tinyNeoPixel leds = tinyNeoPixel(NUMLEDS, 5, NEO_GRB);
37+
void setup() {
38+
leds.begin();
39+
leds.setPixelColor(0,255,0,0); // first LED full RED
40+
leds.show(); // LED turns on.
41+
}
42+
```
43+
This non-static tinyNeoPixel example uses 2236 bytes of flash, and reports using only 40 bytes of RAM (it actually uses 340 - and if you didn't have enough free memory the call to leds.begin() would fail, the LEDs would not be enabled, bnut the rest of the sketch would continue to run, which could be confusing to debug.
44+
45+
`tinyNeoPixel()` - Empty constructor for `tinyNeoPixel` only - for when you won't even know the type of LEDs, or how many of them, until your sketch starts running. You set pin and length later with `setPin()`, `updateLength()`, and `updateType()`, which must be set before you can control any LEDs. You might have the same code running a number of lighting displays, and store the specifics in EEPROM, as shown below:
46+
```
47+
#include <tinyNeoPixel.h>
48+
#include <EEPROM.h>
49+
tinyNeoPixel leds = tinyNeoPixel();
50+
void setup() {
51+
uint16_t numleds=EEPROM.read(0)*50; // see how many 50-LED strips we're driving.
52+
leds.updateLength(numleds); //
53+
leds.updateType(EEPROM.read(1)); // and read what type of LEDs
54+
leds.setPin(5); // Maybe I don't need to get everything from the EEPROM
55+
leds.setPixelColor(0,255,0,0); // first LED full RED
56+
leds.show(); // LED turns on.
57+
}
58+
59+
```
60+
This compiles to 2256 bytes and reports 40 bytes of RAM used as well (but, it is using an additional 150 bytes times whatever number was read from tyhe EEPROM)
961

1062
### API Summary
1163

12-
13-
`tinyNeoPixel(uint16_t n, uint8_t p=6, neoPixelType t=NEO_GRB)` constructor for tinyNeoPixel - the first argument is is the number of LEDs in the string, the second is the pin, and the final argument is the color order of the LEDs in use; the library provides #defines for every possible color order for RGB and RGBW LEDs (full list below).
14-
15-
`tinyNeoPixel()` - empty constructor, set pin and length later with setPin(), updateLength(), and updateType().
16-
17-
`tinyNeoPixel(uint16_t n, uint8_t p, neoPixelType t,uint8_t *pxl);` constructor for tinyNeoPixel_Static - the final argument is a uint_8 (byte) array sized to accommodate the data to be sent to the LED. For example:
18-
19-
20-
#include <"tinyNeoPixel_Static.h">
21-
#define NUMLEDS 10
22-
byte pixels[NUMLEDS*3];
23-
tinyNeoPixel(NUMPIXELS, 5, NEO_GRB, pixels); //pin 5 is on PORT B on all parts except the 8-pin ones
24-
void setup() {
25-
pinMode(5,OUTPUT);
26-
}
27-
28-
2964
`begin()` Enable the LEDs, on tinyNeoPixel, must be called before show() - not applicable for tinyNeoPixel_Static
3065

3166
`show()` Output the contents of the pixel buffer to the LEDs
@@ -36,15 +71,21 @@ There are two versions of this library provided. `tinyNeoPixel` implements the e
3671

3772
`setPixelColor(uint16_t n, uint32_t c)` set the color of pixel `n` to color c (expressed as a uint_32 - as returned from getColor())
3873

39-
`setBrightness(uint8_t)` set the brightness for the whole string (0~255)
74+
`setPixelColor(uint16_t n, uint32_t c)` set the color of pixel `n` to color c (expressed as a uint_32 - as returned from getColor())
75+
76+
`getPixelColor(uint16_t n)` Returns the color of pin `n` as a uint_32
77+
78+
`fill(uint32_t c, uint16_t first, uint16_t count)` set `count` pixels, starting from `first` to color `c` which is a 32-bit "packed color". If `first` is unspecified, the first LED on the string is assumed. If `count` is unspecified, or if 0 is passed to it, all the LEDs from `first` to the end of the strip will be set. And if `c` is not specified, it is assumed to be 0 (off) - so `fill()` with no arguments is equivalent to `clear()`.
79+
80+
`setBrightness(uint8_t)` set the brightness for the whole string (0-255). Adjusting the brightness is implemented as multiplying each channel by the given brightness to get a uint16_t, and then taking only the high byte; once brightness has been set, this is done every time pixel(s) are set. Because this process is lossy, frequently adjusting the brightness will lead to quantization errors.
4081

41-
`clear()` clear the pixel buffer (set all colors on all LEDs to 0)
82+
`clear()` clear the pixel buffer (set all colors on all LEDs to 0).
4283

43-
`setPin(uint8_t p)` Set the pin for output. At 8 or 10MHz, this must be on the port selected from the tools -> tinyNeoPixel Port submenu. Note that in tinyNeoPixel, the old pin is set as input, and the new pin is set as OUTPUT and written LOW. This is not done on tinyNeoPixel_Static for the reasons described above.
84+
`setPin(uint8_t p)` Set the pin for output; in `tinyNeoPixel_Static`, it is your responsability to ensure that this pin is set OUTPUT. `tinyNeoPixel` copies the Adafruit behavior, and called pinMode() on it. Be aware
4485

4586
`updateLength(uint16_t n)` Set the length of the string of LEDs. Not available on tinyNeoPixel_Static.
4687

47-
`updateType(neoPixelType t)` Set the color order. Not available on tinyNeoPixel_Static.
88+
`updateType(neoPixelType_t)` Set the color order and number of colors per pixel. Not available on tinyNeoPixel_Static.
4889

4990
`getPixels()` Returns a pointer to the pixel buffer (a uint_8 array).
5091

@@ -54,24 +95,32 @@ There are two versions of this library provided. `tinyNeoPixel` implements the e
5495

5596
`numPixels()` Returns the number of LEDs in the string
5697

57-
`Color(uint8_t r, uint8_t g, uint8_t b)` Return the color `r,g,b` as a uint_32 (For RGB leds)
98+
`sine8(uint8_t angle)` Returns the sine of the angle (angle in 256's of a circle, that is, 128 = 180 degrees), from 0 to 255. Used for some animation effects, uses a lookup table kept in PROGMEM.
5899

59-
`Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w)` Return the color `r,g,b,w` as a uint_32 (For RGBW leds)
100+
`gamma8(uint8_t input_brightness)` Performs basic gamma correction for smoother color transitions, returns a gamma corrected brightness that can be passed to setPixelColor().
60101

61-
`getPixelColor(uint16_t n)` Returns the color of pin `n` as a uint_32
102+
`gamma32(uint_32 input_color)` As gamma8, only acts on and returns a 32-bit "packed" color (uint32_t).
103+
104+
`Color(uint8_t r, uint8_t g, uint8_t b)` Return the color `r,g,b` as a "packed" color, which is a uint32_t (For RGB leds)
105+
106+
`Color(uint8_t r, uint8_t g, uint8_t b, uint8_t w)` Return the color `r,g,b,w` as a uint_32 as a "packed" color, which is a uint32_t (For RGBW leds)
62107

63-
### Pixel order #defines
64-
These are the same names for the #defines used by Adafruit_NeoPixel; these are used for the third argument to tinyNeoPixel().
108+
`ColorHSV(uint16_t hue, uint8_t sat, uint8_t val)` Return the color described by the given Hue, Saturation and Value numbers as a uint32_t
109+
110+
### Pixel order constants
111+
In order to specify the order of the colors on each LED, the third argument passed to the constructor should be one of these constants; a define is provided for every possible permutation, however only a small subset of those are widespread in the wild. GRB and
65112

66113
#### For RGB LEDs
114+
```
67115
NEO_RGB
68116
NEO_RBG
69117
NEO_GRB
70118
NEO_GBR
71119
NEO_BRG
72120
NEO_BGR
73-
121+
```
74122
#### For RGBW LEDs
123+
```
75124
NEO_WRGB
76125
NEO_WRBG
77126
NEO_WGRB
@@ -96,3 +145,14 @@ These are the same names for the #defines used by Adafruit_NeoPixel; these are u
96145
NEO_BRGW
97146
NEO_BGWR
98147
NEO_BGRW
148+
```
149+
### The name
150+
While this library was initially created in order to both ensure compatibility with, and through the Static version, fit within the flash and memory constraints of, the tinyAVR line of parts, this library is entirely suitable for non-tiny devices; It offers all the functionality of the Adafruit version on the library, with the addition of the Static mode - and has been ported to the AVRxt core, and where appropriate, core-specific matters are accounted for. Frankly, dynamic allocation has no place on an 8-bit microcontroller. Since almost all of the other pixel libraries *dont* work, considering the populkarity of WS2812 LEDs, I decided it would be improve the UX with the core to distribute a known working '2812 library with a standard API along with the cores.
151+
152+
Why did I start from the Adafruit library, and not FASTLed? Because I can't make sense of that code - it also sort of exemplifies how I do not like libraries written or architected.
153+
154+
### New Adafruit additions
155+
If Adafruit has added new methods to their library, please report via an issue in one of my cores that ships with this library, and I will go pull them in!
156+
157+
### License
158+
Unlike the core itself (as noted within the library files), tinyNeoPixel is licensed under LGPL 3, not LGPL 2.1, since the Adafruit library was always LGPL3.
Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#######################################
2-
# Syntax Coloring Map For Adafruit_NeoPixel
2+
# Syntax Coloring Map For tinyNeopixel
33
#######################################
44
# Class
55
#######################################
@@ -11,19 +11,56 @@ tinyNeoPixel KEYWORD1
1111
#######################################
1212

1313
setPixelColor KEYWORD2
14+
getPixelColor KEYWORD2
1415
setPin KEYWORD2
16+
getPin KEYWORD2
17+
updateLength KEYWORD1
18+
updateType KEYWORD1
1519
setBrightness KEYWORD2
20+
getBrightness KEYWORD2
1621
numPixels KEYWORD2
17-
getPixelColor KEYWORD2
22+
getPixels KEYWORD2
23+
show KEYWORD2
24+
clear KEYWORD2
25+
fill KEYWORD2
1826
Color KEYWORD2
27+
ColorHSV KEYWORD2
28+
gamma8 KEYWORD2
29+
sine8 KEYWORD2
30+
gamma32 KEYWORD2
1931

2032
#######################################
2133
# Constants
2234
#######################################
2335

2436
NEO_GRB LITERAL1
25-
NEO_COLMASK LITERAL1
26-
NEO_KHZ800 LITERAL1
27-
NEO_SPDMASK LITERAL1
2837
NEO_RGB LITERAL1
29-
NEO_KHZ400 LITERAL1
38+
NEO_RBG LITERAL1
39+
NEO_GRB LITERAL1
40+
NEO_GBR LITERAL1
41+
NEO_BRG LITERAL1
42+
NEO_BGR LITERAL1
43+
NEO_WRGB LITERAL1
44+
NEO_WRBG LITERAL1
45+
NEO_WGRB LITERAL1
46+
NEO_WGBR LITERAL1
47+
NEO_WBRG LITERAL1
48+
NEO_WBGR LITERAL1
49+
NEO_RWGB LITERAL1
50+
NEO_RWBG LITERAL1
51+
NEO_RGWB LITERAL1
52+
NEO_RGBW LITERAL1
53+
NEO_RBWG LITERAL1
54+
NEO_RBGW LITERAL1
55+
NEO_GWRB LITERAL1
56+
NEO_GWBR LITERAL1
57+
NEO_GRWB LITERAL1
58+
NEO_GRBW LITERAL1
59+
NEO_GBWR LITERAL1
60+
NEO_GBRW LITERAL1
61+
NEO_BWRG LITERAL1
62+
NEO_BWGR LITERAL1
63+
NEO_BRWG LITERAL1
64+
NEO_BRGW LITERAL1
65+
NEO_BGWR LITERAL1
66+
NEO_BGRW LITERAL1

0 commit comments

Comments
 (0)