NeoPixel
NeoPixel
This is a small module developped to control WS2812B (4 pins) NeoPixel LED modules. The module uses simple pin bitbanging in order to shift data into the LED strip.
Used datasheet was this one http://www.pocketmt.com/downloads/WS2812B.pdf
The datasheet claims that the High pulse needs to be two times larger than low pulse (0.80µs vs 0.40µs) for one (1), and the reverse for zero (0). From my personal testing, the values need not to be as exact as on the datasheet. Worse, on different strips coming from different manufacturer (chineese strips on ebay), sometimes code didn't even worked. So I implemented the bitbanging code on a powerful ARM chip with fast IO capabilities (bitbanging up to 72MHz). I tried to check the limits that makes the LEDs work or not.
From my personal experience, those conditions need to be true:
- The pulse need to be 2 vs 1 and 1 vs 2 for One and Zero bit to be correct. Be careful, even if it works for single LED, you need to check that your code works for multiple LEDs also (data propagation).
- The High pulse for One need absolutely to be wider than 0.38µs in order to work for all LED strips I tested.
- The Low pulse for One need to be at least as large as the high pulse (two times is not 100% mandatory), if it doesn't, you'll have problem making "long" LED strips to work.
- After powering up your circuit, you need to give about 2 seconds to the LED strips before starting communication with them. If you communicate with them "before" this timing, you can write ANY value for RGB, but not the (0,0,0) which is equivalent to shutdown (the LEDs keeps a bit lighting).
Be careful, the following program and module have been tuned for a PIC18F25K22 running at 64MHz with internal Oscillator. If you want to change PIC frequency (in order to use another PIC chip for example), you need to re-tune the neopixel module procedure Inline Sub PulseWidth() This procedure must provide approximatively 0.40µs timing (it's better to have something a bit beyond 0.40 than below it).
I have done my tests using this module, but a lot of friends reported that it works for them on rings and other LED strips modules
PS. The DataPin PIC pin need to be pulled up with a 4K7 resistor.
Main Features of Swordfish NeoPixel module :
1- Handle single RGB NeoPixel LED or multiple NeoPixel RGB LED Strips
For any comment, or feedback (or bug report), please post to Swordfish Forum.
IMPORTANT Options
- option LED_COUNT = nnValue // where nnValue is the number of the NeoPixel strip RGB LEDs, default to 8
- option DATA_PIN = PORTB.0 // Data Pin used to communicate with the LED strip
Example Code NeoPixel module handling a strop of 8 NeoPixel LEDs
{ ************************************************************************* * Name : neopixeltest.BAS * * Author : Ahmed Lazreg ([email protected]) * * http://www.pocketmt.com * * http://www.microcodeproject.com * * Notice : Copyright (c) 20017 Pocket MicroTechnics * * : All Rights Reserved * * Date : 18/01/2017 * * Version : 1.0 * * Desc. : Test program for NeoPixel module * * : PIC18F25K22 running at 64MHz with internal OSC * ************************************************************************* } Device = 18F25K22 'Automatically brings in device file 18F25K22.bas Clock = 64 '64MHz Config FOSC = INTIO7 'tells the chip to use its internal oscillator //dim dataPin as LATB.0 Include "SetDigitalIO.bas" include "spi.bas" #option LED_COUNT = 8 #option DATA_PIN = PORTB.0 include "neopixel.bas" dim i as byte Dim gPixels(8) as TColor Const gConstPixels(LED_COUNT * 3 ) as Byte = ( // G , R , B 0 , 0 , 255, 0 , 255, 0 , 255, 0 , 0 , 0 , 0 , 255, 0 , 255, 0 , 255, 0 , 0 , 0 , 0 , 255, 255, 255, 255 ) OSCCON = %01111100 'Tells the chip how fast to run (amongst other things), see below OSCTUNE.6 = 1 SetAllDigital() TRISB = $00 LATB = 0 delayMs(2000) //if you use only a single RGB LED, you can directly write to it // WriteRGBPixel(255, 255, 255) while true NeoPixel.ClearStrip() delayMs(1000) for i = 0 to 7 NeoPixel.Pixels(i) = NeoPixel.MakeColor(255, 0, 0) next NeoPixel.UpdateStrip() delayMs(1000) for i = 0 to 7 NeoPixel.Pixels(i) = NeoPixel.MakeColor(0, 255, 0) next NeoPixel.UpdateStrip() delayMs(1000) for i = 0 to 7 NeoPixel.Pixels(i) = NeoPixel.MakeColor(0, 0, 255) next NeoPixel.UpdateStrip() delayMs(1000) for i = 0 to 7 gPixels(i).R = 255 gPixels(i).G = 255 gPixels(i).B = 255 next NeoPixel.WriteRGBPixels(gPixels) // copy new pixels NeoPixel.UpdateStrip() delayMs(1000) NeoPixel.ClearStrip() delayMs(1000) WriteRGBPixels(gConstPixels) NeoPixel.UpdateStrip() delayMs(1000) wend
Module Code
{ ************************************************************************* * Name : NeoPixel.BAS * * Author : Ahmed Lazreg ([email protected]) * * http://www.pocketmt.com * * http://www.microcodeproject.com * * Notice : Copyright (c) 20017 Pocket MicroTechnics * * : All Rights Reserved * * Date : 15/01/2017 * * Version : 0.1 * * Modified: * * Notes : This module handles NeoPixels LED strips * ************************************************************************* } Module NeoPixel #option LED_COUNT = 8 #if LED_COUNT <= 0 #error LED_COUNT, "Invalid option. LED_COUNT must be greater than zero" #endif #option DATA_PIN = PORTB.0 #if IsOption(DATA_PIN) And Not IsValidPortPin(DATA_PIN) #error DATA_PIN, "Invalid option. DATA_PIN must be a valid port pin." #endif Dim DataPin As DATA_PIN.DATA_PIN@ // PixelStrip data-pin Public Const PIXELS_COUNT = LED_COUNT Public Structure TColor G as Byte R as Byte B as Byte End Structure Public Dim Pixels(PIXELS_COUNT) as TColor Public Function MakeColor(R As Byte, G As Byte, B As Byte) as TColor result.R = R result.G = G result.B = B End Function Inline sub NOP() asm NOP end asm end sub // this is tuned for 64MHz internal osc of PIC18F25K22 internal oscillator Inline Sub PulseWidth() NOP() NOP() NOP() NOP() End Sub Inline Sub One() DataPin = 1 PulseWidth() PulseWidth() DataPin = 0 PulseWidth() End Sub Inline Sub Zero() DataPin = 1 PulseWidth() DataPin = 0 PulseWidth() PulseWidth() End Sub Inline Sub ResetCom() DelayUs(50) End Sub Inline Sub WriteNeoByte(pB as Byte) ' do NOT replace me with a loop !!! If (pB.7) = 1 Then One() Else Zero() EndIf If (pB.6) = 1 Then One() Else Zero() EndIf If (pB.5) = 1 Then One() Else Zero() EndIf If (pB.4) = 1 Then One() Else Zero() EndIf If (pB.3) = 1 Then One() Else Zero() EndIf If (pB.2) = 1 Then One() Else Zero() EndIf If (pB.1) = 1 Then One() Else Zero() EndIf If (pB.0) = 1 Then One() Else Zero() EndIf End Sub Public Sub WriteRGBPixel(Green As Byte, Red As Byte, Blue As Byte ) WriteNeoByte(Green) WriteNeoByte(Red) WriteNeoByte(Blue) End Sub Public Sub WriteRGBPixel(pColor as TColor ) WriteNeoByte(pColor.G) WriteNeoByte(pColor.R) WriteNeoByte(pColor.B) End Sub Public Sub WriteRGBPixels(ByRef pPixels() As TColor) Dim i As Byte For i = 0 To Bound(pPixels) Pixels(i) = pPixels(i) Next End Sub // Const Arrays of Structures cannot be defined in Swordfish BASIC // User can define array of bytes arranged in G,R,B order Public Sub WriteRGBPixels(ByRefConst pPixelsBytesGRB() As Byte) Dim i As Byte For i = 0 To Bound(pPixelsBytesGRB) Step 3 Pixels(i).G = pPixelsBytesGRB(i) Pixels(i).R = pPixelsBytesGRB(i+1) Pixels(i).B = pPixelsBytesGRB(i+2) Next End Sub // puts pixels content on hardware strip Public Sub UpdateStrip() Dim i As Byte For i = 0 To PIXELS_COUNT-1 WriteRGBPixel(Pixels(i)) Next ResetCom() End Sub // Writes Zero everywhere on hardware strip. Dos not alter the module pixels array Public Sub ClearStrip() Dim i As Byte For i = 0 To PIXELS_COUNT-1 WriteRGBPixel(0, 0, 0) Next ResetCom() End Sub Sub Initialize() Output(DataPin) DataPin = 0 End Sub Initialize() End Module