drivers: Added WS281x RGB LED driver for ATmega platform#12693
drivers: Added WS281x RGB LED driver for ATmega platform#12693benpicco merged 4 commits intoRIOT-OS:masterfrom
Conversation
|
Tried it on the What I especially like about this solution is that it leaves the door open for e.g. that DMA based I2S implementation on esp8266/esp32, the ABC backend or other hardware specific implementations. I'd suggest to call the driver @herjulf I also tried it on the When I use the SDA pin ( (Also works with |
|
BTW: The timing of those LEDs in not so bad, it's even possible to drive them using SPI. But that would be just another backend for this driver. |
I actually used this already. (The Arduino lib referred to in the link unrolls the loop manually and did everything in assembly to strictly comply with the timings.) But having confirmed that the low phase could be pretty long as long it is significantly shorter than the 50µs end of sequence signal, I might add a I'll update the name as suggested, but I cannot work on this during work time, so it has to wait a bit 😉 |
|
@benpicco Yes the exposed AD ports has voltage dividers and and a small capacitor to reduce AD noise. Also an diode to protect from reverse voltage. Board has do handle ESD & immunity etc for CE. So you did the right thing taking one unprotected pin. The screw terminals was no success they were removed in the 2.4 rev. |
Moved macros and static inline helper functions needed to access ATmega GPIOs to cpu/atmega_common/include/atmega_gpio.h in order to reuse them for the platform specific low level part of the Neopixel driver.
613aefb to
7154413
Compare
|
OK. Doing the rename was to much mess to handle via fixup commits. I squash therefore and updated the commit messages to the new name. |
|
@benpicco: I now added low level functions (in addition to the current API) to allow users to use small buffers for huge LED chains. The high level API is a syntactic sugar |
Can you provide an example of it's use in |
Sure, but I don't habe enough LEDs (in a row) to actually see the test result. But I guess you have? |
Done. With my chain of 10 LEDs, I can see part of the second rainbow being displayed. I'm optimistic that people with long enough chains will be able to see 100 rainbows. May I squash? |
|
Hm, after applying 767f044 I only get |
|
Can you add |
|
I don't get any more output than that, even with |
|
Strange. It also doesn't work for me when I build with Ubuntu's old toolchain using |
|
@benpicco: Does it work before the change to |
|
Let me extend the doc a bit to point out how backends are selected. |
tests/driver_ws281x/Makefile
Outdated
|
|
||
| FEATURES_REQUIRED := arch_avr8 # Currently only a backend for AVR available | ||
| USEMODULE += ws281x # <-- Main platform independent driver | ||
| USEMODULE += ws281x_atmega # <-- Backend used to communicate with the LEDs |
There was a problem hiding this comment.
Can we resolve this in Makefile.dep?
There was a problem hiding this comment.
I was more thinking of
ifneq (,$(filter ws281x,$(USEMODULE)))
ifneq (,$(filter arch_avr8,$(FEATURES_PROVIDED)))
USEMODULE += ws281x_atmega
else
$(error No ws281x backend available for the current platform)
endif
endifThere was a problem hiding this comment.
Travis says that modules must never inspect FEASTURES_PROVIDED. I dropped this to comply
There was a problem hiding this comment.
Does it also complain when you put this into drivers/Makefile.dep?
There was a problem hiding this comment.
Hm that is silly.
How about ifneq (,$(filter atmega_common,$(USEMODULE)))?
There was a problem hiding this comment.
This was also my first idea. But then I figured this is actually the same, but less elegant and readable. And being honest, it would be only to weasel around the check.
I'm not sure what the check wants to prevent. Maybe it wants to catch cases were FEATURES_REQUIRED should have been used, but the contributor was unaware and added a manual check of FEATURES_PROVIDED instead?
But until I know the actual reason, I thought it is better to back off. If my guess is correct, that message would be a false positive in the given case and the check could be improved. Or maybe there is a good reason why my first approach isn't a good idea that I'm just not aware of.
There was a problem hiding this comment.
When reading up on #10179 it seems like the original intention was that modules should rely on FEATURES_USED instead of FEATURES_PROVIDED, because just because a feature (e.g. SPI) is available doesn't mean that it's being used.
Adding the arch to FEATURES_PROVIDED happened later, but the arch is never 'used'.
So using ifneq (,$(filter atmega_common,$(USEMODULE))) would not be against the spirit of that check, but it would make using the driver much more user friendly.
I want to add an SDL backend for native so creating animations becomes easier to test - I just want to change the BOARD for that, not the Makefile.
|
That's the travis problem I have with avr-rss2 PR as well. |
Do you mean
That's normal. Once the PR is ACKed the author must squash the fixup commits. This check prevents accidental merging of un-squashed commits. |
|
@maribu looks good now! |
0876fdf to
845df85
Compare
benpicco
left a comment
There was a problem hiding this comment.
Code looks good and works well on arduino-mega2560.
drivers/include/ws281x.h
Outdated
| static inline void ws281x_end_transmission(void) | ||
| { | ||
| xtimer_usleep(WS281X_T_END_US); | ||
| } |
There was a problem hiding this comment.
I'm afraid I'll have to change this to
#ifdef MODULE_WS281X_ATMEGA
static inline void ws281x_end_transmission(ws281x_t *dev)
{
(void) dev;
xtimer_usleep(WS281X_T_END_US);
}
#else
void ws281x_end_transmission(ws281x_t *dev);
#endifwhen adding another backend.
drivers/ws281x/ws281x.c
Outdated
| /* Default buffer used in ws281x_params.h. Will be optimized out if unused */ | ||
| uint8_t ws281x_buf[WS281X_PARAM_NUMOF * WS281X_BYTES_PER_DEVICE]; | ||
|
|
||
| int ws281x_init(ws281x_t *dev, const ws281x_params_t *params) |
There was a problem hiding this comment.
I just noticed that a backend-specific ws281x_init() will be necessary.
There was a problem hiding this comment.
But I guess this should work for most backends. E.g. the GPIO ABC backend will also use this. And I guess for other slow platforms (e.g. MSP430) manual bitbanging with CPU cycle counting will also be the easiest path. I simple marked the symbol as weak to allow other backends (e.g. the SPI one) to provide their own.
| if (gpio_init(dev->params.pin, GPIO_OUT)) { | ||
| return -EIO; | ||
| } |
There was a problem hiding this comment.
better move this to atmega.c into some ws281x_init_backend(ws281x_t *dev) function.
|
Hm turns out SDL is not as straightforward as I thought with all that syscall magic But who needs graphical output anyway? #include <stdio.h>
#include "ws281x.h"
void ws281x_write_buffer(ws281x_t *dev, const void *buf, size_t size)
{
(void) dev;
const uint8_t *src = buf;
for (unsigned i = 0; i < size; ++i) {
int r = src[WS281X_BYTES_PER_DEVICE * i + WS281X_OFFSET_R];
int g = src[WS281X_BYTES_PER_DEVICE * i + WS281X_OFFSET_G];
int b = src[WS281X_BYTES_PER_DEVICE * i + WS281X_OFFSET_B];
printf("\033[48;2;%d;%d;%dm ", r, g, b);
}
}
void ws281x_end_transmission(ws281x_t *dev)
{
(void) dev;
puts("\033[0m");
} |
|
@benpicco: I adapted the |
|
Yes that will work! You can squash - feel free to remove that superfluous |
Added driver for the WS2812/SK6812 RGB LEDs often sold as NeoPixels, which due to their integrated RGB controller can be chained to arbitrary length and controlled with a single GPIO.
49f2f52 to
8d0a9ea
Compare
|
Squashed and both Murdock and Travis didn't find anything to complain :-) |
|
@miri64: Can you confirm I addressed your comments? |
|
@miri64: Ping? |
| port_addr += ATMEGA_GPIO_BASE_PORT_A; | ||
| port_addr += ATMEGA_GPIO_OFFSET_PIN_PORT; | ||
|
|
||
| #if defined (PORTG) |
There was a problem hiding this comment.
@maribu When I changed the ATmega implementation for the new GPIO API, I wondered if this should be:
- #if defined (PORTG)
+ #if defined (PORTH)The additional offset is only required for ports starting from H. That is, a MCU which has port G but not port H such as ATmega128 doesn't require it.
There was a problem hiding this comment.
Yes, indeed. (To my defense, while git blame will show me as author, I only moved the code to a different file without touching or authoring it.)
There was a problem hiding this comment.
Yeah I know, you just exposed it from cpu/atmega_common/periph/gpio.c.


Contribution description
This PR provides a driver to use the WS2812/SK6812 RGB LEDs (a.k.a. NeoPixels).
This PR contains a platform independent and a platform specific code, as sadly the protocol to access the LEDs is a custom single wire protocol with to rigid timing requirements to allow using
xtimerandperiph_gpio. In this PR the platform specific part is only provided for ATmega based platforms, but the infrastructure to add support for more platforms is in place.Testing procedure
Hook up chain of one or more WS2812 or SK6812 LEDs to an ATmega clocked at either 8 MHz or 16 MHz and run the test application. See the documentation on limitations of the 8 MHz version.
Issues/PRs references
PR #12020 will need to be rebased on top of this.