Can't get my NeoPixels to turn on

Discussion about programs, libraries and tools that work with MicroPython. Mostly these are provided by a third party.
Target audience: All users and developers of MicroPython.
User avatar
jimmo
Posts: 1700
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Can't get my NeoPixels to turn on

Post by jimmo » Sat Nov 23, 2019 4:29 am

I can see that the driver is cpu frequency aware -- https://github.com/micropython/micropyt ... neopixel.c -- but I have also seen reports that the timing isn't perfect, and perhaps the timing inaccuracies are worse at other frequencies. (The ws2812 is very sensitive to timing).

There are two concurrent efforts underway to improve this on ESP32 -- first a common neopixel driver for all ports, and second an RMT implementation for ESP32 that uses hardware functionality to generate extremely precise waveform timing.

OutoftheBOTS_
Posts: 783
Joined: Mon Nov 20, 2017 10:18 am

Re: Can't get my NeoPixels to turn on

Post by OutoftheBOTS_ » Sat Nov 23, 2019 8:34 am

jimmo wrote:
Sat Nov 23, 2019 4:29 am
I can see that the driver is cpu frequency aware -- https://github.com/micropython/micropyt ... neopixel.c -- but I have also seen reports that the timing isn't perfect, and perhaps the timing inaccuracies are worse at other frequencies. (The ws2812 is very sensitive to timing).

There are two concurrent efforts underway to improve this on ESP32 -- first a common neopixel driver for all ports, and second an RMT implementation for ESP32 that uses hardware functionality to generate extremely precise waveform timing.
I have some questions for you jimmo as you seem to be so good at answering questions.

I have never wrote my own driver for neopixels but have wrote my own driver using SPI for APA102. I have read the data sheet for many of the ws2812 clones and understand how they work and the precise timings required for this asynchronous serial communication. I am not sure of just how SPI is used to create these timing but can see why RTM on ESP32 works so well(pretty much exacly what RTM is designed for)

Now here is my question. I understand how the length of the timing of both the high and low will govern whether a 1 or 0 is sent. Iam wondering if the same method I use for generating stepper motor pulses might be able to be used for generating the pulses for the NeoPixels. i.e you use a timer and when the count reaches the ARR it fires an interrupt that toggles the pin the sets the new value for the ARR based upon what timing is needed for next toggle of the pin. I realize this will use some CPU but it maybe possible to get better timings???

User avatar
jimmo
Posts: 1700
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Can't get my NeoPixels to turn on

Post by jimmo » Sat Nov 23, 2019 11:29 am

OutoftheBOTS_ wrote:
Sat Nov 23, 2019 8:34 am
I have some questions for you jimmo as you seem to be so good at answering questions.
Thanks :)
OutoftheBOTS_ wrote:
Sat Nov 23, 2019 8:34 am
I have never wrote my own driver for neopixels but have wrote my own driver using SPI for APA102. I have read the data sheet for many of the ws2812 clones and understand how they work and the precise timings required for this asynchronous serial communication. I am not sure of just how SPI is used to create these timing but can see why RTM on ESP32 works so well(pretty much exacly what RTM is designed for)
Yeah, as you say, the key here is that WS2812 are asynchronous serial. I _much_ prefer working with APA102 for this exact reason.

The way you use SPI for asynchronous serial is you treat MOSI as a waveform generator and choose your baud rate precisely such that the pulse width is exactly a multiple of the bit time. (Ignore the CLK and MISO pins). So as a simple example, if you transmit [0b11110011 0b11001111] repeated at 60kbit then you'll get a square wave at 10Hz with a 66% duty cycle. The neat thing here is you can generate any waveform you like (at the timing precision of the SPI baud rate). The disadvantage of course is that it uses _lots_ of RAM.

You can do the same in reverse -- use MOSI as a high speed logic sampler, and by oversampling you can then reconstruct your actual signal.
OutoftheBOTS_ wrote:
Sat Nov 23, 2019 8:34 am
Now here is my question. I understand how the length of the timing of both the high and low will govern whether a 1 or 0 is sent. Iam wondering if the same method I use for generating stepper motor pulses might be able to be used for generating the pulses for the NeoPixels. i.e you use a timer and when the count reaches the ARR it fires an interrupt that toggles the pin the sets the new value for the ARR based upon what timing is needed for next toggle of the pin. I realize this will use some CPU but it maybe possible to get better timings???
Yes, absolutely, and this sort of approach (using the timer hardware) is used in a bunch of different "soft" asynchronous serial drivers. For example, the Arduino SoftwareSerial.h has used the same approach for a long time with the AVR equivalent of ARR. It's a great way to reduce the jitter -- the counter reset triggers the pin toggle in hardware, then the interrupt has the entire bit time to figure out the new reset value, but as long as your interrupt always runs in less than one bit time, you get no jitter.

I think with NeoPixels though, especially on more powerful micros, the timing isn't _that_ difficult to achieve using just a simple busy loop. i.e. see https://github.com/micropython/micropython/pull/5318 which claims to work on the whole range of supported STM32 parts.

I don't know much about ESP32 and whether the timer hardware has equivalent functionality (I guess this is why RMT exists). But from https://github.com/micropython/micropython/pull/5117 it would seem that it's possible to get the timing working well without any hardware support. I'm looking forward to seeing that PR updated to match #5318. And then longer term updated to use RMT instead :)

OutoftheBOTS_
Posts: 783
Joined: Mon Nov 20, 2017 10:18 am

Re: Can't get my NeoPixels to turn on

Post by OutoftheBOTS_ » Sat Nov 23, 2019 6:23 pm

Here is some timing of some of the different types of Neopixels timings that I have discovered there is still a couple of other not mentioned here.

WS2812
T0H : 0.35us , T0L : 0.8us
T1H : 0.7us , T1L : 0.6us

WS2812B
T0H : 0.4us , T0L : 0.85us
T1H : 0.8us , T1L : 0.45us

WS2811
T0H : 0.5us , T0L : 2.0us
T1H : 1.2us , T1L : 1.3us

WS2813
T0H : 0.37us , T0L : 0.2us
T1H : 0.87us , T1L : 0.2us

WS2815
T0H : 0.3us , T0L : 1.1us
T1H : 1.1us , T1L : 0.3us

SK6812
T0H : 0.3us , T0L : 0.9us
T1H : 0.6us , T1L : 0.6us

OutoftheBOTS_
Posts: 783
Joined: Mon Nov 20, 2017 10:18 am

Re: Can't get my NeoPixels to turn on

Post by OutoftheBOTS_ » Sat Nov 23, 2019 6:30 pm

jimmo wrote:
Sat Nov 23, 2019 11:29 am

Yes, absolutely, and this sort of approach (using the timer hardware) is used in a bunch of different "soft" asynchronous serial drivers. For example, the Arduino SoftwareSerial.h has used the same approach for a long time with the AVR equivalent of ARR. It's a great way to reduce the jitter -- the counter reset triggers the pin toggle in hardware, then the interrupt has the entire bit time to figure out the new reset value, but as long as your interrupt always runs in less than one bit time, you get no jitter.

I don't know much about ESP32 and whether the timer hardware has equivalent functionality (I guess this is why RMT exists). But from https://github.com/micropython/micropython/pull/5117 it would seem that it's possible to get the timing working well without any hardware support. I'm looking forward to seeing that PR updated to match #5318. And then longer term updated to use RMT instead :)
I certainly see the RMT as best option for ESP32

For STM32 I think timers can be used quite well with much less use of RAM than SPI. I think that maybe the output can go out as a hardware PWM and an interrupt fires when it reaches the CCR (capture compare register) then the interrupt updates both the CCR and ARR because there is a flag that can be set in 1 of the control registers that means the new values of these registers gets stored in a buffer and only gets updated when the timer reaches the ARR and resets. This would mean that you are loading the next value when the CCR is reached but it isn't updated till after the ARR is reached and would have the timing accuracy of a timer but would only need the RAM used to store the binary data.

If I can find some time I might have a play with this on my STM32F4

I think it is going to be pushing the limits of the speed of the interrupts because the baud rate of these Neopixels isn't that much less than 1MHz. I did play with my STMF4 to see how fast it timer interrupts could go and I could get the interrupt to toggle a pin at 3MHz when doing nothing else but toggling the pin.

OutoftheBOTS_
Posts: 783
Joined: Mon Nov 20, 2017 10:18 am

Re: Can't get my NeoPixels to turn on

Post by OutoftheBOTS_ » Sun Apr 26, 2020 9:39 am

jimmo wrote:
Sat Nov 23, 2019 11:29 am
OutoftheBOTS_ wrote:
Sat Nov 23, 2019 8:34 am
Now here is my question. I understand how the length of the timing of both the high and low will govern whether a 1 or 0 is sent. Iam wondering if the same method I use for generating stepper motor pulses might be able to be used for generating the pulses for the NeoPixels. i.e you use a timer and when the count reaches the ARR it fires an interrupt that toggles the pin the sets the new value for the ARR based upon what timing is needed for next toggle of the pin. I realize this will use some CPU but it maybe possible to get better timings???
Yes, absolutely, and this sort of approach (using the timer hardware) is used in a bunch of different "soft" asynchronous serial drivers. For example, the Arduino SoftwareSerial.h has used the same approach for a long time with the AVR equivalent of ARR. It's a great way to reduce the jitter -- the counter reset triggers the pin toggle in hardware, then the interrupt has the entire bit time to figure out the new reset value, but as long as your interrupt always runs in less than one bit time, you get no jitter.
I have neopixels driving very well using timers and interrupts. It was much easier than I though. The timer creates both the freq and the pulse width timing then fires an interrupt to set the next freq and pulse with for the next bit till all the bits are sent. It produces perfect timings on my scope and is very easy to adjust timing to suit any neopixel IC timings. see the code here that produced this result https://github.com/OutOfTheBots/STM32_NEOpixels_timer
20200426_192105.jpg
20200426_192105.jpg (62.16 KiB) Viewed 407 times

User avatar
jimmo
Posts: 1700
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Can't get my NeoPixels to turn on

Post by jimmo » Tue Apr 28, 2020 2:59 am

OutoftheBOTS_ wrote:
Sun Apr 26, 2020 9:39 am
I have neopixels driving very well using timers and interrupts.
That's cool! Neopixel support for STM32 is a long outstanding item for MicroPython. But I think the OP was using ESP32?

Also FYI you may be interested in https://github.com/micropython/micropython/pull/5318 (which adds an STM32 driver just using instruction counting), and https://github.com/jimmo/micropython/co ... eopixel-f7 which modifies that to use the cycle counter on F4 and higher.

User avatar
jimmo
Posts: 1700
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Can't get my NeoPixels to turn on

Post by jimmo » Tue Apr 28, 2020 3:01 am

misaalanshori wrote:
Sat Nov 23, 2019 2:06 am
Could it be caused by the cpu frequency? My boot file have machine.freq(240000000) on it and could that be the cause?
Yes, this would almost certainly explain it.

FYI there is work in progress to update the Neopixel driver on ESP32 to be more reliable and work on all clock frequencies.

OutoftheBOTS_
Posts: 783
Joined: Mon Nov 20, 2017 10:18 am

Re: Can't get my NeoPixels to turn on

Post by OutoftheBOTS_ » Tue Apr 28, 2020 5:55 am

A quick google of micropython RMT neopixel yeilded this https://gist.github.com/nevercast/9c485 ... 4a22062795

It declares the timing at the top as constants but could easily be modified to accept timing parameters to be passed to it.

OutoftheBOTS_
Posts: 783
Joined: Mon Nov 20, 2017 10:18 am

Re: Can't get my NeoPixels to turn on

Post by OutoftheBOTS_ » Tue Apr 28, 2020 6:12 am

jimmo wrote:
Tue Apr 28, 2020 2:59 am
OutoftheBOTS_ wrote:
Sun Apr 26, 2020 9:39 am
I have neopixels driving very well using timers and interrupts.
That's cool! Neopixel support for STM32 is a long outstanding item for MicroPython. But I think the OP was using ESP32?

Also FYI you may be interested in https://github.com/micropython/micropython/pull/5318 (which adds an STM32 driver just using instruction counting), and https://github.com/jimmo/micropython/co ... eopixel-f7 which modifies that to use the cycle counter on F4 and higher.
From the googling that I have done this seems to be the most common method for creating neopixle timings. basically work out how many NOPs are needed to get correct timings. Although it gets the job done it isn't very flexible to be able change the timings or running on different CPU freq MCUs. You can see mine uses a timer to create PWM on a pin but an interrupt changes the duty and freq at the end of each cycle to update the next cycle to be what ever the timing are needed for next cycle. The advantage is that it is very easy to adapt from different MCU's and different timing LEDs. See this in my video from 9:50 to 10:50 https://www.youtube.com/watch?v=4NSNO7NJv5c&t=2s

This could very easily be wrapped up in a python class for the user to use like this

Code: Select all

WS2812B = const((0.4, 0.8, 0.85, 0.45))

timer = 4
channel = 1
timings = WS2812B

my_neopixel = NeoPixel(timer, channel, timings)
Also have you seen the method used by Adafruit neopixel driver fir ardunio STM https://github.com/adafruit/Adafruit_Ne ... .cpp#L1902 it uses the SysTick to create the timing something like they use SPI but instead of using DMA they use a SysTick interrupt

Post Reply