Skip to content
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

rp2040 -- neopixels not working properly #4135

Closed
jerryneedell opened this issue Feb 5, 2021 · 13 comments · Fixed by #4155
Closed

rp2040 -- neopixels not working properly #4135

jerryneedell opened this issue Feb 5, 2021 · 13 comments · Fixed by #4155
Labels
bug rp2040 Raspberry Pi RP2040
Milestone

Comments

@jerryneedell
Copy link
Collaborator

jerryneedell commented Feb 5, 2021

I am having trouble controlling neopixels on a Pico when using the neopixel.py libray
Here is an example

Adafruit CircuitPython 6.2.0-beta.1-80-gadaf43d6d on 2021-02-04; Raspberry Pi Pico with rp2040
>>> 
>>> import board,neopixel
>>> pix = neopixel.NeoPixel(board.GP27,8)
>>> pix[0]= 0xff   --- OK  -- pixle 0 is red
>>> pix[0]= 0xff00  --- neopixel 0 turns off
>>> pix[1]= 0xff00  --- neopixel 0 is blue ???
>>> 
```

I have tried it with a few different neopixel strings -- same behavior

@jerryneedell
Copy link
Collaborator Author

I ran some test when I set up the PIO writes manually, so not using neopixel,py ot neopixel_write and I can reproduce the same behavior when trying to set individual pixels.

It is odd -- some programs using .fill seem to be working and the "color wheel" seems t work OK but when I try manually poking a pixel or manually setting fill, I get these odd responses.

@jerryneedell jerryneedell changed the title rp2040 -- neopixels mot working properly with neopixel.py rp2040 -- neopixels not working properly Feb 5, 2021
@tannewt tannewt added bug rp2040 Raspberry Pi RP2040 labels Feb 5, 2021
@tannewt tannewt added this to the 6.x.x - Bug Fixes milestone Feb 5, 2021
@tannewt
Copy link
Member

tannewt commented Feb 5, 2021

Can you capture the pulse train with a logic analyzer and verify the 1/3 and 2/3 duty cycle?

@andywarburton
Copy link

andywarburton commented Feb 5, 2021

Just to add some support to this case, my Pico just arrived today and I immediately hooked it up to an "official" Adafruit neopixel ring (16 pixels). With the example code supplied and minimal modification, I get a random selection of colours all over the ring and a number of points not illuminated (I had the same issue with a simple strip and a cheap 8 pixel ring from Aliexpress). I also tried using GP1 instead of GP0 just to be sure it wasn't pin weirdness.

My code:

import time
import board
import neopixel

# Update this to match the number of NeoPixel LEDs connected to your board.
num_pixels = 16

pixels = neopixel.NeoPixel(board.GP1, num_pixels)
pixels.brightness = 0.5

while True:
    pixels.fill((255, 0, 0))
    time.sleep(5)

Obviously, I would expect to see an all red ring, but instead I get a lot of what feels like random noise. I've put an image on imgur so you can see the behaviour. I'm not super technical (a dirty hacker at best) but please let me know if there is anything I can do to help debug.

@jerryneedell
Copy link
Collaborator Author

Can you capture the pulse train with a logic analyzer and verify the 1/3 and 2/3 duty cycle?

I'll try to do this tomorrow.

@andywarburton
Copy link

Quick update from me, on a whim, I tried unplugging the Pico from my Mac (2020 16" MBP) and plugging it directly into a power strip on my desk that has USB ports and it started working as intended. I don't know if this is helpful, but perhaps @jerryneedell you could try as well and see if it starts working for you?

@ladyada
Copy link
Member

ladyada commented Feb 6, 2021

could be the VBUS voltage is too high. try powering the pixels from 3.3V

@jerryneedell
Copy link
Collaborator Author

could be the VBUS voltage is too high. try powering the pixels from 3.3V

I've tried 3V -- same result.

@jerryneedell
Copy link
Collaborator Author

jerryneedell commented Feb 6, 2021

I did some tests with a logic analyzer. In general, the timing looks ok for the 1/3 2/3 duty cycle BUT there appear to be "gaps" on some of the writes. This may be causing the odd behavior
the upper trace is the digital signal, lower (blue) is the analog -- the apparent voltage difference is , I think, just aliasing, but makes it easy to distinguish 0xff from 00. (The voltages levels are normal on an oscilloscope - that is they are all of the same level)
The first screenshot was dong a .fill(0xff)

fill_0xff

The second screenshot was a capture while running the "color wheel" -- the gap is not present on every write.

gap

I see the gaps very often when I am writing single values -- When running something with lots of writes, they are less frequent and the string appears to behave "better"

If I run the same tests on a metro_esp32s2, I don't see the gaps and the neopixels behave normally

@gamblor21
Copy link
Member

I don't have a full answer yet but looked into this a bit further. I used neopixel_write to write raw to the string (skipping the neopixel library). What I noticed is on the RP2040 the first byte of the array being sent to the string wasn't always sent correctly, resulting in the rest of the pixels being incorrect. E.g. If I sent 0xFF, 0x00, 0x00 (GRB order so a green pixel) there are times I would not see the first pixel light up (or just flash quickly on/off). If I sent 0x00, 0xFF, 0x00 (so a red pixel) the first pixel would sometimes light up green. Meaning that first byte of the bytearray had been lost.

Using the full library it seems to occur on the first display after a "while" (not sure how long). So I may fill a string with blue pixels and the first couple showed red - meaning the green first byte was lost. The last pixel is showing a mix of Red/Blue (maybe an artifact of being a byte short).

Another thing I am unsure on is the CP PIO program uses different timings then the PIO used in the RP2040 example code. May be unrelated and may not matter at all.

But it does seem somehow the first byte to the neopixel string is being "lost". I'm just not sure how yet.

@gamblor21
Copy link
Member

I think I have it solved. I noticed the problem only occurred when the transfer was smaller then the amount to use DMA for in StateMachine.c (32 bytes). In the non-DMA transfer part of the code after each byte is written "RUN_BACKGROUND_TASKS" is ran. It seems that most times after writing the first byte there were background tasks to be ran, delaying filling the TX buffer and throwing off the timing. That would explain the random pauses seen.

One solution is to run RUN_BACKGROUND_TASKS before the loop writing bytes starts to clear up any pending tasks. That solution does leave the small possibility of a delay if a task pops up while writing the other bytes (up to the max size of 31). It is only about 40 microseconds of time.

The other option is to remove running background tasks while small transfers in the state machine are required. Many may run at frequencies higher then the 800kHz of neopixels so I can see that throwing off the timing very easily.

I will submit a PR with the first change. Should the tasks be ran in that time period can be a bigger topic.

@jerryneedell
Copy link
Collaborator Author

Ah -- that makes sense -- so when working with any string > 10 pixels and setting them all, the problem will not occur -- This explains why it was working so well with the 16 pixel ring but I started having issues with the 7 pixel jewel, and when testing writes to single pixels.

I would remain concerned if there is a known likelihood of failure during the 40 microsecond window. It will happen at the lest opportune time ;-)

@jerryneedell
Copy link
Collaborator Author

jerryneedell commented Feb 7, 2021

I tried this on a Pico, unfortunately it is not resolving the problems -- still getting some odd behavior especially for the single writes.

@gamblor21
Copy link
Member

I am still seeing an issue so there is another timing issue as well. I tried the following to ensure DMA is being used:

b = bytearray(36) #12 pixels b[35] = 0xff for i in range(1,36): neopixel_write(pin,b) time.sleep(0.15)

While this runs I notice the last blue pixel will dim at times (missing the LSB I'd assume) and the first pixel lights up bright green (getting those missing bits on the high side). If I alter the delay in the sleep it will not happen.

I do not have a scope so I'm just about at the end of what I can try without being able to easily see the output on the wire. Something is delaying a write near causing the neopixels to think they are seeing a new set of values.

jepler added a commit to jepler/circuitpython that referenced this issue Feb 8, 2021
@jerryneedell noticed that this problem affected strips short enough
to not use the DMA peripheral, thanks for the hot tip!

Instead of checking for background tasks after every byte transfer,
try up to 32 transfers before attending to background tasks.

This fixes the problem I was seeing on my 5-pixel circuit.

Closes adafruit#4135.
@dhalbert dhalbert modified the milestones: 6.x.x - Bug Fixes, 6.2.0 Feb 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug rp2040 Raspberry Pi RP2040
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants