~WS2812 crashes: hw_is_claimed() failed

I’m using a Pico 2040W for testing - it’ll be ported to a Pimoroni Plasma 2350W - and my control web page allows the user to set how many LEDs in a string, and what GPIO to use for data. The first time through, everything works Ok: “new WS2812()” works, and I can see a bit pattern on the scope coming from the pin. The second time, I delete the current WS2812 object to reassign the pointer a new WS2812, but I get the following:

(gdb) back
#0  \_exit (status=status@entry=1) at /Users/aaronm/bin/dev/raspi-pico/pico-sdk/src/rp2_common/pico_clib_interface/newlib_interface.c:45
#1  0x100042d4 in \__assert_func (file=file@entry=0x1001cad4 “/Users/aaronm/bin/dev/raspi-pico/pico-sdk/src/common/hardware_claim/claim.c”, line=line@entry=51,
func=func@entry=0x10022790 <**func**.0> “hw_claim_clear”, failedexpr=failedexpr@entry=0x1001cab4 “hw_is_claimed(bits, bit_index)”)
at /Users/aaronm/bin/dev/raspi-pico/pico-sdk/src/rp2_common/pico_clib_interface/newlib_interface.c:173
#2  0x10000f2a in hw_claim_clear (bits=bits@entry=0x200010c4  “\\020”, bit_index=0) at /Users/aaronm/bin/dev/raspi-pico/pico-sdk/src/common/hardware_claim/claim.c:51
#3  0x10012870 in pio_sm_unclaim (pio=, sm=) at /Users/aaronm/bin/dev/raspi-pico/pico-sdk/src/rp2_common/hardware_pio/pio.c:45
#4  0x1000034c in plasma::WS2812::\~WS2812 (this=0x2002dfe0, \__in_chrg=) at /Users/aaronm/bin/dev/raspi-pico/pimoroni-pico/drivers/plasma/ws2812.hpp:78
#5  0x10000464 in core0_mainTask (pvParameters=) at /Users/aaronm/bin/dev/raspi-pico/my-projects/20251203-kiddos-ws2812-leds/my-ws2812/main.cpp:158
#6  0x100180d8 in xPortPendSVHandler () at /Users/aaronm/bin/dev/raspi-pico/RPI-pico-FreeRTOS/picoFreeRTOS-Kernel/FreeRTOS-Kernel/portable/ThirdParty/GCC/RP2040/port.c:613

Here’s the relevant code:

if ( ledStrip1 != NULL ) {
//ledStrip1->start();
delete ledStrip1;
vTaskDelay( 500 );
}
ledStrip1 = new WS2812( ledCt, pio0, 0, pin, WS2812::DEFAULT_SERIAL_FREQ, false, WS2812::COLOR_ORDER::RGB );
ledStrip1->start();

I see in the class definition for WS2812, there’s already a problem with pio_sm_unclaim() if using Python:

            ~WS2812() {
                stop();
                clear();
                update(true);
                dma_channel_unclaim(dma_channel);
                pio_sm_set_enabled(pio, sm, false);
                pio_remove_program(pio, &ws2812_program, pio_program_offset);
#ifndef MICROPY_BUILD_TYPE
                // pio_sm_unclaim seems to hardfault in MicroPython
                pio_sm_unclaim(pio, sm);
#endif
                if(managed_buffer) {
                    // Only delete buffers we have allocated ourselves.
                    delete[] buffer;
                }
            }

Any suggestions to fix or get around this? Being able to redefine an LED string on the fly is kind of important in this application.

Thanks!

For giggles, I commented out the pio_sm_unclaim() line, reloaded, and tested, and it works - I can redefine the number of LEDs and which pin to use multiple times - but I don’t know what possible unintended consequences I’m looking at by not unclaiming the pio_sm.

Have you tried multiple times? The number of SMs is limited, so not unclaiming them would eventually lead to resources not being available. If multiple iterations work, I would not be worried.

You might also want to look at https://github.com/adafruit/Adafruit_NeoPXL8/pull/11/changes

I looked at the code you linked, and it looks the same as the Pimoroni code, except for a minor change in the order of function calls. So I changed Pimoroni’s to match Adafruit’s, and it still failed. For now, I guess I can live with the commented out pio_sm_unclaim(), since I don’t expect frequent deleting and creating new LED strings.

~WS2812() {
                stop();
                clear();
                update(true);
                pio_sm_set_enabled(pio, sm, false);
                pio_remove_program(pio, &ws2812_program, pio_program_offset);
#ifndef MICROPY_BUILD_TYPE
                // pio_sm_unclaim seems to hardfault in MicroPython
                pio_sm_unclaim(pio, sm);
#endif
                dma_channel_unclaim(dma_channel);  /* afm: moved to after pio_sm_unclaim */
                if(managed_buffer) {
                    // Only delete buffers we have allocated ourselves.
                    delete[] buffer;
                }
            }

Still fails in the same place:

(gdb) back
#0  _exit (status=status@entry=1) at /Users/aaronm/bin/dev/raspi-pico/pico-sdk/src/rp2_common/pico_clib_interface/newlib_interface.c:45
#1  0x100042dc in __assert_func (file=file@entry=0x1001cad4 "/Users/aaronm/bin/dev/raspi-pico/pico-sdk/src/common/hardware_claim/claim.c", line=line@entry=51,
    func=func@entry=0x10022790 <__func__.0> "hw_claim_clear", failedexpr=failedexpr@entry=0x1001cab4 "hw_is_claimed(bits, bit_index)")
    at /Users/aaronm/bin/dev/raspi-pico/pico-sdk/src/rp2_common/pico_clib_interface/newlib_interface.c:173
#2  0x10000f32 in hw_claim_clear (bits=bits@entry=0x200010c4 <claimed> "\020", bit_index=0) at /Users/aaronm/bin/dev/raspi-pico/pico-sdk/src/common/hardware_claim/claim.c:51
#3  0x10012878 in pio_sm_unclaim (pio=<optimized out>, sm=<optimized out>) at /Users/aaronm/bin/dev/raspi-pico/pico-sdk/src/rp2_common/hardware_pio/pio.c:45
#4  0x10000346 in plasma::WS2812::~WS2812 (this=0x2002dfe0, __in_chrg=<optimized out>) at /Users/aaronm/bin/dev/raspi-pico/pimoroni-pico/drivers/plasma/ws2812.hpp:77
#5  0x10000464 in core0_mainTask (pvParameters=<optimized out>) at /Users/aaronm/bin/dev/raspi-pico/my-projects/20251203-kiddos-ws2812-leds/my-ws2812/main.cpp:157
#6  0x100180d8 in xPortPendSVHandler () at /Users/aaronm/bin/dev/raspi-pico/RPI-pico-FreeRTOS/picoFreeRTOS-Kernel/FreeRTOS-Kernel/portable/ThirdParty/GCC/RP2040/port.c:613
Backtrace stopped: previous frame identical to this frame (corrupt stack?)

I think I found it. In the constructor for WS2812, it doesn’t call pio_sm_claim() anywhere - and a cscope scan, as well as a breakpoint on the pio_sm_claim() function, bear it out - so when it calls pio_sm_unclaim(), it fails. I added pio_sm_claim() in the constructor, and creating and deleting objects works as expected, now, with no adjustment to the WS2812 destructor, though commenting out pio_sm_unclaim() probably would’ve worked, too.

I’m rather curious how it worked, before, unless I’m the first one to create and delete WS2812 objects on the fly. Shrug.

    pio_sm_init(pio, sm, pio_program_offset, &c);
    /* afm */ pio_sm_claim( pio, sm );
    pio_sm_set_enabled(pio, sm, true);

This is in ws2812.cpp; my changes are around line 25; highlighted by the “afm”.

I think the pio_sm_claim() is only useful if you have multiple independent places in your code that use (or try to use) the same state-machine (“Use of this method by libraries detects accidental configurations that would fail in unpredictable ways.”).

1 Like

I’m not knowledgeable enough to say one way or the other, but it’s very clear that you can’t call unclaim without a preceding claim, so either one has to be added, or the other removed. Unless you create an object only once and never delete it, which seems to work fine.