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.

Parents
  • Hi Einar,

    Sorry, please disregard 34 mV and 2~6 mV, it's wrong, it should be 2.79 V for High and -15.9 mV (or 0 V) for Low. This is the data signal created on WS2812B's Data out.

    I connected the strip directly to the board's VDD to power it on, P0.11 for Data, and ground. I did not use any level converter (I know that I'm suggested to supply 5v and use a level converter, but 3.3 V is able to turn the LED on so, I'm trying it).

    When there is no data being transferred through the WS2812b, the voltage of the DOUT is around -0.8 mV ~ -78.92 mV. At this state, if I call the board to read any data, by using

    i2s_config_init(DIN2_PIN, 17);

    where DIN2_PIN is defined by

    #define DIN2_PIN 23

    , followed by

    i2s_start();
    nrf_delay_us(I2S_DETECTION);
    
    for (i = 1; i < I2S_BUFFER_SIZE; i++) {
    	if (I2SRxBuffer[i] != 0 || I2SRxBuffer[i] != -1) {
    		printf("isi: %x\r\n", I2SRxBuffer[i]);
    		printf("as: %d\r\n", I2SRxBuffer[i]);
    		if (I2SRxBuffer[i] > 0x11111110) {
    			lengthz++;
    		}
    	}
    }
    
    i2s_stop();

    , I2SRxBuffer[i] will always print ffffffff.

  • Hi,

    OK. The logical levels for the nRF are supposed to be above 0.7 * Vdd for high and below 0.3 * Vdd (but > 0 V) for low. However, I would not expect that the logical levels are OK for the nRF, and the low negative voltage for logical '0' should be ok. But I would not do it like this in an actual end product.

    I do not have any idea if the voltages are acceptable to the WS2812b, though. It is out of spec after all (according to the datasheet it should be between +3.5 and +5.3). Can you show us logic analyzer plots of the data out of the WS2812b? Is it all FF's there as well? If so then I would try using a level shifter and supplying the WS2812b with a voltage within spec before digging any further.

    By the way, are you basing your endeavors on this blog post?

  • Hi,

    That is right, the data I shifted in is the same on both input and output (it's actually 00010000 00000000 01010000).

    Ah.. okay... I did not remember about timing, especially when both are not connected directly.

    During this time (receiving data time), the nRF does not have to do anything else. Therefore, I think it may be safe to do GPIO reading for several ms, right? That being said, is 1.78 V safe for nRF52's GPIO Input? (Yes, as I checked the documentation). And what about the current? my multimeter seems like did not read any current from the Data Out of WS2812b (or there should not be?)

    As well, will nRF52 be able to detect the voltage change, between 0.x mV and 1.8 V on the GPIO output? I was thinking to reserve a time for nRF52 to count (in us (microsecond)), then once it detected signal change, read the second one (remember both time). Then, analyze it and decide if it's a 1 or 0 and so on. Do you think this will be a good idea?

    Thank you.

  • Hi,

    Regarding current the inputs have a high impedance, so you will see very little current on the data line. That is correct and expected.

    I assume you want to know if the nRF can detect voltage change on a GPIO input? That is a good idea. You can combine a comparator and timer with PPI and measure the time between the edges of the data signal by triggering timer capture registers from the comparator. That will be handled mostly in HW, but you would need to generate a interrupt for every bit and read the timer registers before the start of the next bit, so you will still have to handle the interrupts in a timely manner.

  • Hi,

    Alright, I will try that. Or do you have other idea you would like to suggest?

    And is there any other things that I may need to notice?

    I noticed from the Documentation Library that I will need to use the Analog Pins (AIN0 ~ AIN7). Therefore, I suppose Digital Pins will not be able to do it? Assuming I'm using a comparator

    Thank you

  • Hi,

    Yes, the comparator (and the other analog peripherals) can only be used with the 8 analog pins (see pinout in Product spec). Nothing else comes to mind at this point.

  • Hi,

    Oh, alright.

    By the way, I got a suggestion: keep using i2s to read the data. While the clock may not be synchronized, we create a Clock Phase Locking, setting an interrupt on the other board; which when triggered, will start the i2s over there and hopefully will be able to read and understand the data. What would you say about this?

Reply Children
  • 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