Neopixels WS2812B

Coding and general discussion relating to user created compiler modules

Moderators: David Barker, Jerry Messina

Post Reply
User avatar
Coccoliso
Posts: 152
Joined: Mon Feb 17, 2014 10:34 am

Neopixels WS2812B

Post by Coccoliso » Sat Jan 14, 2017 4:54 pm

Hi at all,

has anyone tried to use WS2812B?

it seems that they not need special care..
but the problem is the generation of T0L / T0H - T1L / T1H pulses of 0.85us / 0,4us - 0,45us / 0,8us and then it seems to me that I have to write some NOP in assembler to get out alive ( without an oscilloscope is a problem )

there is some good news about it? :cry:

User avatar
David Barker
Swordfish Developer
Posts: 1214
Joined: Tue Oct 03, 2006 7:01 pm
Location: Saltburn by the Sea, UK
Contact:

Re: Neopixels WS2812B

Post by David Barker » Sun Jan 15, 2017 12:24 pm

I've not had the chance to play around with these myself but have heard very good reports. As you point out, timing is critical to their operation.

I would also be interested to hear if anyone has used this LED device...

User avatar
octal
Registered User
Registered User
Posts: 586
Joined: Thu Jan 11, 2007 12:49 pm
Location: Paris IDF
Contact:

Re: Neopixels WS2812B

Post by octal » Mon Jan 16, 2017 8:51 am

I have already played with them and I can say that the datasheet is almost all false (no joking). :(
I'll post my code (tested on PIC18F25K22) once at home with detailed explanation about my findings and experience with them.

User avatar
Coccoliso
Posts: 152
Joined: Mon Feb 17, 2014 10:34 am

Re: Neopixels WS2812B

Post by Coccoliso » Mon Jan 16, 2017 1:26 pm

Hi Octal,

LED strips comes at the weekend and I have yet to buy a power supply with a dozen amps ..
they are still forced to wait.
If it has at least the routine for the creation of 1 and 0 (perhaps 40MHz) let me know here.
Then we can ask David or Jerry if it can engage with the setting for different clock frequencies
(with one of those beautiful formulas on the clock calculations we know well).

See you soon.

User avatar
Coccoliso
Posts: 152
Joined: Mon Feb 17, 2014 10:34 am

Re: Neopixels WS2812B

Post by Coccoliso » Wed Jan 18, 2017 6:51 pm

Hi at all,
GOOD NEWS !
:D

With this SUB

Code: Select all

{
****************************************************************************
* Name    : SendPixel (PRIVATE)                                    
* Purpose : NRZ pulse generator                                    
*           This sub was written by MichelJasmin in this forum  
*           http://www.picbasic.co.uk/forum/                          
*                  showthread.php?t=19408&p=128453#post128453
****************************************************************************
} 
Public Sub SendPixel(NeoPixValue As Byte)
    If (NeoPixValue And %10000000 ) = 0 Then
        '400ns pulse required
        NEOPIN = 1
        Asm
            nop
            nop
            nop
        End Asm
        NEOPIN = 0

    Else
        '800ns pulse required
        NEOPIN = 1
        Asm
            nop
            nop
            nop
            nop
            nop
            nop
            nop
        End Asm
        NEOPIN = 0
    EndIf


    If (NeoPixValue And %01000000 ) = 0 Then
        '400ns pulse required
        NEOPIN = 1
        Asm
            nop
            nop
            nop
        End Asm
        NEOPIN = 0

    Else
        '800ns pulse required
        NEOPIN = 1
        Asm
            nop
            nop
            nop
            nop
            nop
            nop
            nop
        End Asm
        NEOPIN = 0
    EndIf


    If (NeoPixValue And %00100000 ) = 0 Then
        '400ns pulse required
        NEOPIN = 1
        Asm
            nop
            nop
            nop
        End Asm
        NEOPIN = 0

    Else
        '800ns pulse required
        NEOPIN = 1
        Asm
            nop
            nop
            nop
            nop
            nop
            nop
            nop
        End Asm
        NEOPIN = 0
    EndIf

    If (NeoPixValue And %00010000 ) = 0 Then
        '400ns pulse required
        NEOPIN = 1
        Asm
            nop
            nop
            nop
        End Asm
        NEOPIN = 0

    Else
        '800ns pulse required
        NEOPIN = 1
        Asm
            nop
            nop
            nop
            nop
            nop
            nop
            nop
        End Asm
        NEOPIN = 0
    EndIf

    If (NeoPixValue And %00001000 ) = 0 Then
        '400ns pulse required
        NEOPIN = 1
        Asm
            nop
            nop
            nop
        End Asm
        NEOPIN = 0

    Else
        '800ns pulse required
        NEOPIN = 1
        Asm
            nop
            nop
            nop
            nop
            nop
            nop
            nop
        End Asm
        NEOPIN = 0
    EndIf

    If (NeoPixValue And %00000100 ) = 0 Then
        '400ns pulse required
        NEOPIN = 1
        Asm
            nop
            nop
            nop
        End Asm
        NEOPIN = 0

    Else
        '800ns pulse required
        NEOPIN = 1
        Asm
            nop
            nop
            nop
            nop
            nop
            nop
            nop
        End Asm
        NEOPIN = 0
    EndIf

    If (NeoPixValue And %00000010 ) = 0 Then
        '400ns pulse required
        NEOPIN = 1
        Asm
            nop
            nop
            nop
        End Asm
        NEOPIN = 0

    Else
        '800ns pulse required
        NEOPIN = 1
        Asm
            nop
            nop
            nop
            nop
            nop
            nop
            nop
        End Asm
        NEOPIN = 0
    EndIf

    If (NeoPixValue And %00000001 ) = 0 Then
        '400ns pulse required
        NEOPIN = 1
        Asm
            nop
            nop
            nop
        End Asm
        NEOPIN = 0

    Else
        '800ns pulse required
        NEOPIN = 1
        Asm
            nop
            nop
            nop
            nop
            nop
            nop
            nop
        End Asm
        NEOPIN = 0
    EndIf
End Sub
called from this SUB

Code: Select all

{
****************************************************************************
* Name    : SendBuffer (PUBLIC)                                              
* Purpose : GRB buffer content serializer                                   
****************************************************************************
} 
Public Sub SendBuffer()
    Dim i As Word
    For i = 0 To (NEONUM - 1)
        SendPixel(NeoBufG(i))
        SendPixel(NeoBufR(i))
        SendPixel(NeoBufB(i))
    Next
End Sub
.. all works fine at 40Mhz of clock.

Is everything to the maximum.
Is not possible:
- use an array of RGB structures instead of 3 arrays of bytes
- using a loop in SendPixel instead of test NeoPixValue value at every single bit
every code variation at this code delays the pulse logic and nothing works any more.

User avatar
octal
Registered User
Registered User
Posts: 586
Joined: Thu Jan 11, 2007 12:49 pm
Location: Paris IDF
Contact:

Re: Neopixels WS2812B

Post by octal » Wed Jan 18, 2017 11:48 pm

Sorry Coccoliso, I'll post my module and functions right I go home. I'm very sorry. :oops:

here is my module

http://www.sfcompiler.co.uk/wiki/pmwiki ... r.NeoPixel

This module uses bitbanging.
A better approach that I didn't got time to implement is to map RGB data to a bit pattern and send the bit pattern directly via SPI hardware module. This makes the module independent of PIC speed, and these days most PIC18F can have the Timer2 as a source (if I remember) so you can tune the module to conform as close as possible to the requested chronograms.
If it was for me, I would never do a controller for such LEDs with PIC18Fxx. I would switch to dsPIC/PIC24 or PIC32. With those PICs you have DMA, so you can simply program your DMA to transfer data from pattern memory to any SPI out pin, and you have only to tune the speed of your DMA tranfers to get your timing correct. The nice thing with DMA is that you can still continue to do other things like calculating color effects and push modifications live to your buffer, while the DMA keeps refreshing your strip at the refresh rate you want. It also makes your code insensitive to interrupts :wink:

User avatar
Coccoliso
Posts: 152
Joined: Mon Feb 17, 2014 10:34 am

Re: Neopixels WS2812B

Post by Coccoliso » Thu Jan 19, 2017 12:20 pm

Hi Octal,

now I modify it to make it work at 40 MHz.

OK for DMA and PIC24/32.
Arduino and Netduino also use the SPI for the generation of pulses.

Thanks for this source and I think that may be of interest to many.

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: Neopixels WS2812B

Post by Jerry Messina » Thu Jan 19, 2017 2:18 pm

Just FYI... there are a few PIC18 devices that have DMA channels for SPI (like the J50 and J94 series).

I don't know how timing sensitive the pixels are but the SPI clock freq selections aren't as flexible as you might like. I've played around with trying to do this type of thing before and when you're trying to get really fast clocks (in the MHz range) you only end up with a few choices. At the high end the jumps are fairly large so you can't fine tune things very well. The selections can vary depending on the device, so they're not all the same.

There might be some setting that works with the pixels but I've never found one that matches up well with the WS2812B datasheet values without playing around with the main system clock. From what I've seen many people say the datasheet doesn't tell the whole story. That matches up with some of octal's findings too.

Also, even if you use the DMA peripheral be aware that there's a gap of a few clock periods between bytes so it won't do back-to-back transmissions.

User avatar
octal
Registered User
Registered User
Posts: 586
Joined: Thu Jan 11, 2007 12:49 pm
Location: Paris IDF
Contact:

Re: Neopixels WS2812B

Post by octal » Thu Jan 19, 2017 2:22 pm

I have quickly checked the datasheet of the PIC18F25K22, and the SPI module can have any of these freq:
In
Master mode, the SPI clock rate (bit rate) is user
programmable to be one of the following:
• FOSC/4 (or TCY)
• FOSC/16 (or 4 * TCY)
• FOSC/64 (or 16 * TCY)
• Timer2 output/2
• FOSC/(4 * (SSPxADD + 1))
I think that by adjusting the FOSC and SSPxADD conviniently, you may be able to easily have the needed timings. be careful, if I remember, on of my colleagues have had some problems using SSPxADD == 0.

I won't be at home until next sunday, so I can't do any tests until the mid of next week.

User avatar
Coccoliso
Posts: 152
Joined: Mon Feb 17, 2014 10:34 am

Re: Neopixels WS2812B

Post by Coccoliso » Thu Jan 19, 2017 5:21 pm

Hi Octal,

your code works perfectly :wink:
and for a 40MHz frequency I just changed the number of calls to the NOP

You send 4 NOP (1x4) or 8 NOP (2x4) for 64MHz, then for 40MHz the numbers are 3 NOP or 7 NOP

Code: Select all

{
****************************************************************************
* Name    : One (Private Inline)                                           *
* Purpose :                                                                *
****************************************************************************
} 
Private Inline Sub One()
    NEOPIN = 1
#if (_Clock = 64)
    NOP()
    NOP()
    NOP()
    NOP()
    NOP()
    NOP()
    NOP()
    NOP()
#elseif (_Clock = 40)
    NOP()
    NOP()
    NOP()
    NOP()
    NOP()
    NOP()
    NOP()
#endif
    NEOPIN = 0
#if (_Clock = 64)
    NOP()
    NOP()
    NOP()
    NOP()
#elseif (_Clock = 40)
    NOP()
    NOP()
    NOP()
#endif
End Sub

{
****************************************************************************
* Name    : Zero (Private Inline)                                          *
* Purpose :                                                                *
****************************************************************************
} 
Private Inline Sub Zero()
    NEOPIN = 1
#if (_Clock = 64)
    NOP()
    NOP()
    NOP()
    NOP()
#elseif (_Clock = 40)
    NOP()
    NOP()
    NOP()
#endif
    NEOPIN = 0
#if (_Clock = 64)
    NOP()
    NOP()
    NOP()
    NOP()
    NOP()
    NOP()
    NOP()
    NOP()
#elseif (_Clock = 40)
    NOP()
    NOP()
    NOP()
    NOP()
    NOP()
    NOP()
    NOP()
#endif
End Sub
.. and this thing I tried it and it works up to a length of 3M :D
and I can say that your code works up to that length!

Just what I need

THANKS!

User avatar
octal
Registered User
Registered User
Posts: 586
Joined: Thu Jan 11, 2007 12:49 pm
Location: Paris IDF
Contact:

Re: Neopixels WS2812B

Post by octal » Thu Jan 19, 2017 8:06 pm

Thanks for your feedback. I know that a lot of friends are using it for a while and it works.

As for the clock dependency, it's a pitty that we don't have the preprocessor of MPASM in Swordfish ASM blocs. We could have calculated the ammount of needed NOPs depending on the oscillator frequency and used a repeat loop (in the MPASM preprocessor) to generate the needed NOPs count.

I don't know, actual compiler does all calculations to generate the amount of wasted cycles for DelayMs() and DelayUs() functions.
But now, since there are a lot of PICs with PLL and frequencies up to 64MHz, I think that it can be a good opprotunity to ask David to add a Delay10Ns() function to Swordfish basic compiler itself (function that accept a parameter which is tens of nano seconds, an indirect way to make delays with 0.xx µ-second without having to use floats).

Post Reply