WS2812 with spi on nrf9160 disrupted by an high signal before data on the spi MOSI

Heey,

I'm currently trying to implement a w2812 on my custom nrf9160 board. For this I want to use my SPi bus to instruct the ws2812 leds. Because I've already implemented something similar for a different project I've copied the code to my current project. But for some reason the first LED always seems to be off. 

After doing some research I've noted that the MOSI starts high before the clock starts. The ws2812 is a one wire protocol, meaning that the MOSI starting high will influence the first bit sent to the ws2812.

Until now I haven't found any settings regarding the MOSI starting off as high. So I'm wondering if there is any possibility to force the MOSI to start low before the first bit.


These are my SPI settings:

struct spi_config spi_flash_cfg = { .slave = 3,
                                        .operation = SPI_OP_MODE_MASTER | SPI_TRANSFER_MSB | SPI_LINES_SINGLE | (8 << SPI_WORD_SIZE_SHIFT),
                                        .frequency = 10000000 }; /*10MHz is used for ws2812*/

These are the board file settings regarding SPI

spi_flash: &spi3 {
	status = "okay";
	compatible = "nordic,nrf-spim";
	cs-gpios = <&gpio0 2 GPIO_ACTIVE_LOW>,
			   <&gpio0 26 GPIO_ACTIVE_LOW>,
			   <&gpio0 7 GPIO_ACTIVE_LOW>;
	pinctrl-0 = <&SPI_FLASH_default>;
	pinctrl-1 = <&SPI_FLASH_sleep>;
	pinctrl-names = "default", "sleep";

	sgt1001: spi-dev-sgt1001@0 {
		reg = <0>;
	};

	mx25r64: mx25r6435f@1 {
		status = "okay";
		compatible = "jedec,spi-nor";
		reg = <1>;
		spi-max-frequency = <8000000>;
		jedec-id = [ c2 28 17  ];
		size = <0x04000000>;
		mxicy,mx25r-power-mode = "high-performance";
		
		partitions{
			compatible = "fixed-partitions";
			#address-cells = <1>;
			#size-cells = <1>;

			slot1_partition: partition@0 {
				label = "image-1";
				reg = <0x00000000 0xdf000>;
			};
		};
	};

	ws2812: spi-dev-ws2812@2 {
		/* SPI */
		reg = <2>;
	};
};

For this test I have defined the other SPI devices, but there are currently not used.

I do know that there is a zephyr implementation of the ws2812, but I would prefer using my own implementation. I also haven't found how the zephyr implemenation circumvented this high signal on the MOSI before sending data.

Thanks in advance

  • Hi,

    The nRF9160 SPIM typically keeps MOSI at the level of the last transmitted bit while the peripheral is enabled. If that final bit is high, MOSI will idle high before the next transfer, and the WS2812 interprets this as the start of a new bit. This shifts the entire data stream and causes the first LED to appear off. To ensure the WS2812 always sees a clean start, the data line must be low before each new frame. You can achieve this in two practical ways:

    1. Ensure your encoded SPI pattern always ends with MOSI = 0 (for example by appending extra “0” bits or a dummy all zero LED at the end)
    2. ⁠Alternatively, after each SPI transfer, disable SPIM and configure the MOSI pin as a GPIO output driven low for at least the WS2812 reset time you require, then re‑enable SPIM before the next frame.

    Either approach should ensures the line starts low for the next update.

    Best Regards,
    Syed Maysum

  • Heey Syed,

    TLDR,
    The transmission needs to be:
    { 0 , "WS2812 Data", 0}

    I've got it working with your advice.

    For anyone else trying to do this, your answer is partially true, when the MOSI ends high, the MOSI is high while the peripheral is enabled. Sending a "0 at the end of the transmission ensures that the MOSI is low after the transmission. It does not ensure that the MOSI is low before the first bit. When the first bit of the MOSI data is high, the MOSI line is pulled high before the clock starts. If both the first and the last byte are "0", the MOSI line starts low before the clock starts.

    Thanks for your advice.

Related