Skip to content

esp32: RMT timing glitches #5623

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
carstenblt opened this issue Feb 8, 2020 · 10 comments
Open

esp32: RMT timing glitches #5623

carstenblt opened this issue Feb 8, 2020 · 10 comments

Comments

@carstenblt
Copy link

carstenblt commented Feb 8, 2020

I am getting semi-random glitches in the RMT output. I am trying to use it to drive Neopixels and I get wrong pulse lengths sometimes. The glitches occur less frequently when I have longer delays between writing the pulses and if I write less data. I say semi-randomly because when I am running an animation for the Neopixels it has a high tendency to glitch at the same time and the same LEDs seem to be glitching when repeating the run.

Here are two captures at the position that the glitch first occurs. The first 300 us are fine, then I start getting pulses that are longer than expected. In total it adds up to 70 us more (from 560 us).

Bad frame:
Bildschirmfoto 2020-02-08 um 14 45 45

Good frame:
Bildschirmfoto 2020-02-08 um 14 45 55

Interestingly I also see the power go up at the end. Right now I have a level shifter attached to the output, because my first guess was that this was responsible for the glitches.
Bildschirmfoto 2020-02-08 um 14 53 03

Some people here are reporting similar problems: https://esp32.com/viewtopic.php?f=2&t=3980&start=10
Disabling power management did however not resolve the it.

@carstenblt
Copy link
Author

carstenblt commented Feb 9, 2020

Increasing the RMT memory (config.mem_block_num) makes it happen a lot less often. So I guess the problem is related to rewriting the buffer to RMT memory, which has to happen when the RMT reaches the last pulse in its memory.

Unfortunately it still isn't completely gone for more than 70 pixels (= 3360 pulses) even when using the full 16 kbit.

First, it would be great if one could configure the RMT memory dynamically. It should be documented that all channels share the same memory region, so if you use 8 blocks you can only use one channel. If you use 3 blocks for channel 0 you can use channels 0, 3-7. Maybe @mattytrentini can add it, otherwise I can make a PR.

As for the general problem, maybe somebody has an idea what is keeping the interrupt from rewriting the memory in a timely manner. I tried setting a higher priority for the interrupt handler (ESP_INTR_FLAG_LEVEL3) but it did not make a difference.

Edit: Haven't gotten any glitches yet when setting CPU frequency to 240 MHz.

@carstenblt
Copy link
Author

I couldn't get rid of the glitches in any way.

I ended up "porting" the FastLED driver to C, which implements its own interrupt handlers to handle refilling of the RMT buffer while calculating the Neopixel pulses on the fly. Haven't had any glitches so far. Downside to that approach is that you cannot use the other RMT channels using the standard implementation (the one the Micropython RMT interface uses). The upside is it's blazingly fast and uses very little RAM. You can do animations with 200 Hz refresh rate if you get the rendering done in time.

@lagmoellertim
Copy link

I couldn't get rid of the glitches in any way.

I ended up "porting" the FastLED driver to C, which implements its own interrupt handlers to handle refilling of the RMT buffer while calculating the Neopixel pulses on the fly. Haven't had any glitches so far. Downside to that approach is that you cannot use the other RMT channels using the standard implementation (the one the Micropython RMT interface uses). The upside is it's blazingly fast and uses very little RAM. You can do animations with 200 Hz refresh rate if you get the rendering done in time.

Is there a chance you could share your fork of micropython with the FastLED RMT driver? I am having the same timing problems caused by interrupts/wifi and your work would probably help others too.

@carstenblt
Copy link
Author

I know... I would absolutely share it but the code is in bad condition. I don't know C. I am surprised I got it to work. Also, the original driver had the ability to support several stripes in parallel and I left most of that in. But I didn't implement it fully, so right now there is also a lot of unused/unnecessary code.

It needs a lot of work and I don't have a lot of time to (or even know how to in many cases) make it pretty. If you still want it, I'll upload it ;) Of course it would be awesome if someone could bring it to a state so that it's maintainable and has a chance to end up in the mainline code base. But that would also be a political decision because of the conflict with the RMT driver.

@lagmoellertim
Copy link

It would be great if you could upload it! I could take a look at it, and as there are many other active members developers on this project, I think someone can make a decent pull request on your code base.

@carstenblt
Copy link
Author

Here you go: https://github.com/carstenblt/micropython

You need to:

  • add SRC_MOD = modneopixel_rmt.c neopixel_rmt.c to your GNUmakefile
  • enable the driver in your board's mpconfigboard.hwith #define NEOPIXEL_RMT_ENABLED (1)

I added a neopixel2.py to the modules. It's very similar to the original one. Basically the driver just provides a new write function. The biggest difference is that I put the RGB -> RBG reordering into the C code, as I do a lot of stuff directly on the bytearray data and didn't want to get confused.

@carstenblt
Copy link
Author

I had another glitch yesterday, I don't know what caused it and it was the first after several hours of running (well, the first that I noticed). I'm paranoid now and adjusted it to use the memory of all 8 channels, which should further reduce any possible glitches. That way the ISR has a couple of 100 us to catch up, even if it is delayed.

Removing the ability for parallel strips would simplify the code even further. If this has a chance to make it into micropython code base I would put in some effort to try to make a decent pull request. What speaks against it is the exclusive blocking of the RMT hardware. But from my understanding it will be the only decent way for a good neopixel driver, as precalculating pulses is a memory hog and is still buggy. @dpgeorge

@itdaniher
Copy link

itdaniher commented Aug 11, 2020

@carstenblt had good luck with implementing deinit() and exposing it from neopixel2, allowing neopixel_rmt to be cleaned up allowing other RMT drivers to be loaded and (probably) successfully used. I'd love to see your patchset upstreamed! You can find my deinit routine here - I'm working off of v3.1.5 of pycopy,.

@carstenblt
Copy link
Author

I'll see if I can work on this some more. To start I would probably simplify this a lot and throw out all the multi-strip code. Truth is, it's probably not going to happen in the next few months...

@jimmo
Copy link
Member

jimmo commented Nov 11, 2021

I attempted to update the built-in neopixel module to use RMT directly (via the IDF's RMT translation driver that automatically handles the pulse generation from a byte stream). If anyone who was seeing glitches is willing to test it that would be very helpful. #7985

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants