Closed Linzm99 closed 3 years ago
Not strange at all. Why do you think tinyNeoPixel exists? Because the Adafruit version didn't work for most tinyAVR parts...
You're running at 8 or 12 MHz I presume?
The Adafruit library (at least the timing critical portion of it) was written to follow the datasheet timing specifications very closely (more closely than was necessary, it turns out).
The Adafruit library also was written to allow the pin assignment to be set at runtime
Those two decisions combine to have consequences for the library, which was one of the reasons for tinyNeoPixel (The others were that the original wastes space supporting 400kHz pixels, which I've never seen for sale, and only supports 8, 12, and 16 MHz, while this core supports all kinds of dumbass speeds, mostly because of a few "squeaky wheels" who wanted UART crystal speeds. Also the original lets you set the length of the string at runtime, which adds like 1k of bloat because it pulls in malloc and free. tinyNeoPixel_Static also eliminates the dynamic size setting, while "tinyNeoPixel" leaves that in for compatibility. Both versions drop support for the apparently-discontinued half-speed pixels)
Because it was written to very closely follow the datasheet timing specs, for parts running at 12 MHz or less, they couldn't do that by using the normal ST ("STore") instruction to write to the PORTx register - ST takes 2 clocks. There is a faster instruction for certain registers, including the ones that control the pins, the OUT instruction. However, while ST writes to the location specified by the X, Y or Z pointer, and thus can be determined at runtime, OUT must have the destination address encoded in the opcode (there are only 64 addresses permitted for OUT, the so-called "I/O space" - all the pin control registers are in the lower half of that, which also works with the sbi, cbi, sbis and sbic instructions. The high I/O space just gets in and out (hence the name), single clock load/store instructions). That is, to use OUT, the assembly code is specific to the port that the output is being generated on.
That wouldn't be a problem, except that the port that the output was to be generated on wasn't known at compile time, because of that wacky feature of supporting changing the pin at runtime....
So that in turn meant that for every port, there was an extra copy of the assembly routine.
That wastes flash, so they only did PORTB, PORTC, PORTD and PORTF, conditionally compiling them only where that port is present. And they only check which port the pin is on if there is more than one supported port on the device - otherwise they assume it's a tiny85 and that any pin must be on PORTB (like a great many libraries in Arduino-land, they wrote it with a small number of parts in mind and anything else is luck of the draw whether it works). So if no other port is present, it will output on PORTB. I'd have expected PB0-2 to be used when you ask for PA0-2, PB3 is reset so not usable and thus PA3-7 wouldn't work at all. The documentation for the Adafruit library does specify that only pins on PORTB and PORTD can be used.
Note that PORTB is the only port that's always present on any classic AVR (there is a single part that doesn't have a "PORTB" - the ancient, special purpose ATtiny28, which while it has a "port B" there's no "PORTB" because port B is input only). I've always said that Atmel's process of assigning pins involved a dartboard and a blindfold, but everything having a PORTB is one of the few things that was consistent. Since Microchip took over, there is far less randomness in pin assignments -
I recently realized that it in fact IS possible to do 12 and 8 MHz with ST on a classic AVR (modern AVRs always can because they have better timing for ST), and hence have it work on any port and not require the tools submenu! The price of that was that the LOW periods of the last two bits were 1 clock longer than the target. That's not a problem though - You can have a LOW that is up to several dozen clocks "too long" before it causes any problems, which they either didn't realize, or had decided not to take advantage of (I wager they hadn't seen a way to meet the timing constraints with ST and work on all pins without duplicating the routine. The author may well have been inexperienced with assembly, or at least inline assembly, as they made a few errors in the constraints too - it works reliably, but only thanks to the calling conventions and the way classes are kryptonite to the optimizer). That submenuless version is going to go into the next version of ATTinyCore - its also a few bytes smaller at sensible speeds.
Ah that actually makes sence. I was expecting that somehow just the pinlayout was falsly defined in the normal neopixle library but now everything makes sence. Thank you
Hi, I just tried to use the normal Adafruit neopixel lib with the tiny841 and for some reason everytime only pin 2(digitalpin 0) gets the data signal for the neopixel nomatter what pin i feed into the neopixel function. Any idea why that is happening? Edit: Its working with tinyneopixel, but its still strange why it doesnt work with the normal lib