Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Reading WS2812b leftover data through I2S

Hello there,

I am currently trying to read the leftover WS2812b LED strip data which was sent using a single channel I2S. The data sent from the board (through I2S) looks like this:

with around 34 mV for high and around 2~6 mV on low. I decided not to send FFF, but 888 instead.

I am aiming to calculate the length of the LED strip. Each LED chip will read the first 24 bits of the data, cascade (discard) it, then pass it to the next one. Therefore, the strategy is to get the data after being cascaded by the last chip and calculate the length of it (which later will be divided by 24 to see how many are left).

The question is: How will I be able to read those data? I tried reading as usual (set a buffer for the i2s, start it, listen for it (delay), stop it, then check its buffer), but I got my buffer filled with all FFF instead. and a thing is that... when I tried to read anything from the pin, although I am not sending any data (the LED strip is not powered on, but connected to other's ground), there will be data, which should not happen. I assigned pin 22, 23, 24, 25 to be the i2s input pin (which will be assigned and unassigned when used or unused), and they are configured as follows:

nrf_gpio_cfg_sense_input(DIN1_PIN, NRF_GPIO_PIN_PULLDOWN, NRF_GPIO_PIN_SENSE_HIGH);

Thank you in advance.

  • Hi,

    I have to admit I didn't understand this suggestion. Can you elaborate?

  • Hi,

    I mean.. We generate the clock on one board, then transfer it to another board through a cable. Hopefully, the other board will be able to use that clock to read the data.

    Right now, I am testing we discussed. Otherwise, I had no idea how it should be built.

    I used the Pin change interrupt example and tested with my own setup. I know that the example uses GPIOTE, instead of PPI and COMP. In this example, it worked nice.

    I did not understand how to setup a PPI. Therefore, I decided to build a COMP driver. I set the event handler to a function, and here's the snippet of the code:

    #define DEBUG_PIN 22
    
    static void pin_init(void) {
    	nrf_gpio_cfg_output(DEBUG_PIN);
    	nrf_gpio_pin_clear(DEBUG_PIN);
    }
    
    static void comp_event_handler(nrf_comp_event_t event) {
    	nrf_gpio_pin_set(DEBUG_PIN);
    	nrf_delay_us(5);
    	nrf_gpio_pin_clear(DEBUG_PIN);
    }
    
    static void comp_init(void) {
    	uint32_t err_code = NRF_SUCCESS;
    	
    	nrf_drv_comp_config_t comp_config = NRF_DRV_COMP_DEFAULT_CONFIG(NRF_COMP_INPUT_1);
    	comp_config.isource = NRF_COMP_ISOURCE_Ien5uA;
    	// Configure threshold voltages.
    	comp_config.threshold.th_down = VOLTAGE_THRESHOLD_TO_INT(0.9, 1.8);
    	comp_config.threshold.th_up = VOLTAGE_THRESHOLD_TO_INT(1.5, 1.8);
    	err_code = nrf_drv_comp_init(&comp_config, comp_event_handler);
    	
    	APP_ERROR_CHECK(err_code);
    	nrf_drv_comp_start(0,0);
    }
    
    int main(void)
    {
        //gpio_init();
    	pin_init();
    	comp_init();
    	
        while (true)
        {
            // Do nothing.
        }
    }
    

    And here's some definitions:

    //==========================================================
    // <e> COMP_ENABLED - nrf_drv_comp - COMP peripheral driver - legacy layer
    //==========================================================
    #ifndef COMP_ENABLED
    #define COMP_ENABLED 1
    #endif
    // <o> COMP_CONFIG_REF  - Reference voltage
    
    // <0=> Internal 1.2V
    // <1=> Internal 1.8V
    // <2=> Internal 2.4V
    // <4=> VDD
    // <7=> ARef
    
    #ifndef COMP_CONFIG_REF
    #define COMP_CONFIG_REF 0
    #endif
    
    // <o> COMP_CONFIG_MAIN_MODE  - Main mode
    
    // <0=> Single ended
    // <1=> Differential
    
    #ifndef COMP_CONFIG_MAIN_MODE
    #define COMP_CONFIG_MAIN_MODE 0
    #endif
    
    // <o> COMP_CONFIG_SPEED_MODE  - Speed mode
    
    // <0=> Low power
    // <1=> Normal
    // <2=> High speed
    
    #ifndef COMP_CONFIG_SPEED_MODE
    #define COMP_CONFIG_SPEED_MODE 2
    #endif
    
    // <o> COMP_CONFIG_HYST  - Hystheresis
    
    // <0=> No
    // <1=> 50mV
    
    #ifndef COMP_CONFIG_HYST
    #define COMP_CONFIG_HYST 0
    #endif
    
    // <o> COMP_CONFIG_ISOURCE  - Current Source
    
    // <0=> Off
    // <1=> 2.5 uA
    // <2=> 5 uA
    // <3=> 10 uA
    
    #ifndef COMP_CONFIG_ISOURCE
    #define COMP_CONFIG_ISOURCE 0
    #endif
    
    // <o> COMP_CONFIG_INPUT  - Analog input
    
    // <0=> 0
    // <1=> 1
    // <2=> 2
    // <3=> 3
    // <4=> 4
    // <5=> 5
    // <6=> 6
    // <7=> 7
    
    #ifndef COMP_CONFIG_INPUT
    #define COMP_CONFIG_INPUT 1
    #endif
    
    // <o> COMP_CONFIG_IRQ_PRIORITY  - Interrupt priority
    
    
    // <i> Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice
    // <0=> 0 (highest)
    // <1=> 1
    // <2=> 2
    // <3=> 3
    // <4=> 4
    // <5=> 5
    // <6=> 6
    // <7=> 7
    
    #ifndef COMP_CONFIG_IRQ_PRIORITY
    #define COMP_CONFIG_IRQ_PRIORITY 6
    #endif
    
    // </e>

    Is there anything wrong in it?

    Thank you

  • Hi,

    The WS2812b  does not even have a clock input. An even if it would pass mostly unchanged through on the data signal (which it may not), what is the delay through the series of WS2812b devices? Even if you could get it to work I would not feel comfortable that you would be able to make a reliable implementation that way.

    Regarding the code snippets I don't know what to look for. I see you generate a COMP interrupt at two levels. That seems OK. I you want to go this approach instead of using PPI, then the remaining work is to use a timer of some sort to get the duration of the pulse like we discussed earlier. You can probably do this accurately enough in SW like you do here, but that would mean that any higher priority interrupts could delay the interrupt processing and corrupt your time measurements. If this is all you do at that specific point in time, then it should be OK.

  • Hi,

    Hmm, okay. I've never counted the delay, to be honest. It's in ms.

    Is my COMP generating interrupt at two different levels? I actually would like it to send me an interrupt when it crosses 1.5 V. Currently, I didn't know if I'm able to make it, but the debug pin is not being toggled. And I could not turn nRF_Log on. I referred other examples, but it's still not working...

    In the handler, I changed it to:

    static void comp_event_handler(nrf_comp_event_t event) {
    	if (event == NRF_COMP_EVENT_CROSS) {
    		nrf_gpio_pin_set(DEBUG_PIN);
    		nrf_delay_us(5);
    		nrf_gpio_pin_clear(DEBUG_PIN);
    	}
    }

  • It doesn't really matter how large the delay is. The point is that the I2S clock will not have any meaningful relationship with the output data from the last WS2812b . The data transmission is asynchronous and must be treated as such. So the COMP approach clearly seems most sensible.

    COMP interrupts are enabled by a call to nrf_drv_comp_start(), but in your case you set toe interrupt mask (first parameter) to 0, which means that you don't actually enable any interrupts (this value is used to set the INTENSET register).

Related