News:

Let's find out together what makes a PIC Tick!

Main Menu

Neopixel LEDs

Started by glenlivet, Jun 08, 2021, 02:57 AM

Previous topic - Next topic

glenlivet

Has anyone ever gotten Neopixel LEDs (i.e. the SK6812) to work in Proton/Positron?
The Project: I have a bunch of white LED solar post lights on the deck that have died. Yea, I can just buy a gob of the original controller chips off Ebay for dirt cheap but that's too easy. I want to redo the guts to randomly cycle through colors. So, when they come on the cpu will generate a random number for intensity and  cycle time for each LED, that way on startup none of the lights would start at or cycle though any color scheme the same way.
 I have a prototype PCB up-n-runnin, sort of. The data timing on these things is crazy! It all appears to work except for the way I'm assembling the 24-bit data string and marching the bits in- most likely timing issues in the unipolar NRZ protocol. My code looks like a pile of spaghetti. I'm curious to see how others have handled the problem.   

It's a great way to turn a $12.97 solar post light into a $25.00 solar post light...
That's the beauty of being a nerd, you don't need a reason. I'm definitely not "beating the man" on this one!

top204

#1
The RGB LEDs are very easy to interface with using the Positron Compilers because they create such fast and compact code.

I've interfaced with quite a few of the RGB LEDs, but not the SK6812, which is probably similar to the WS2812 or WS2812B devices. Each of the earlier, async, RGB LED interfaces had different timings required for 1, 0 and end, so the code for one will not work correctly with another. The better RGB LEDs chips are the newer types with an SPI interface, because timings do not matter so much and they actually operate a lot faster internally, such as the APA102 type. I created a library for the APA102 devices and they are excellenent and also have an internal 19KHz PWM for their LED control, so do not interfere with audio as the lower frequency RGB LED units sometimes do without a lot of decoupling being added.

However, the WS2812B units do work excellently, and are now extremely inexpensive. I would highly recommend getting a strip of WS2812B LEDs and having a go with them.

Here is a library include file I created for the WS2812B RGB LED a few years ago. As can be seen, no assembler code is required for the fast timings because the compiler produces such compact assembler code itself. :-)

$ifndef  _WS2812B_INC_
$define _WS2812B_INC_
'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' WS2812B RGB LED interface
' Because of the very tight pulse requirements of the WS2812B device, this code is for a PIC device operating at higher oscillation frequencies
' The frequencies supported with this library are 12MHz, 16MHz, 32MHz, 40MHz, 48MHz and 64MHz
'
' Written by Les Johnson for the Positron8 BASIC compiler.
'
' Create variables
'
    Dim WS2812B_bIndex    As Byte Access            ' Used to access the amount of WM2812B devices on the line
    Dim WS2812B_bBitIndex As Byte Access            ' Used to access each bit in the WS2812B interface
    Dim WS2812B_dRGB      As Dword Access           ' Used to hold the Green, Red, and Blue bytes
'
' The colour of each LED is encoded as three LED brightness values, which must be sent in GRB (Green-Red-Blue) order.
'
    Dim WS2812B_bGreen As WS2812B_dRGB.Byte2        ' Alias the green byte
    Dim WS2812B_bRed   As WS2812B_dRGB.Byte1        ' Alias the red byte
    Dim WS2812B_bBlue  As WS2812B_dRGB.Byte0        ' Alias the blue byte

    Dim WS2812B_bChipNumber As Byte                 ' The WS2812B to access on a line of them
    Dim WS2812B_pGreen As Byte                      ' The Green value for the WS2812B to access on a line of them
    Dim WS2812B_pRed   As Byte                      ' The Red value for the WS2812B to access on a line of them
    Dim WS2812B_pBlue  As Byte                      ' The Blue value for the WS2812B to access on a line of them
'
' The amount of WS2812B chips on the strip
'
$ifndef WS2812B_Amount
    $error "$define WS2812B_Amount not issued in the main program"
$else
    $define cWS2812B_IndexAmount $eval (WS2812B_Amount - 1)
$endif

$if WS2812B_Amount <> 1                             ' Is there only 1 WS2812B on the line?
    Dim WS2812B_Red[WS2812B_Amount]   As Byte Heap  ' Holds the red value for a particular WS2812B device
    Dim WS2812B_Green[WS2812B_Amount] As Byte Heap  ' Holds the green value for a particular WS2812B device
    Dim WS2812B_Blue[WS2812B_Amount]  As Byte Heap  ' Holds the blue value for a particular WS2812B device
$endif
'
' WS2812B pulse timing constants for 12MHz, 16MHz, 32MHz, 40MHz, 48MHz and 64MHz oscillators
'
$if _xtal = 64                                      ' Are we using a 64MHz oscillator?
    $define cWS2812B_Zero  6                        ' Amount of cycles for a zero delay (approx 350ns)
    $define cWS2812B_One   16                       ' Amount of cycles for a one delay (approx 900ns)
$elseif _xtal = 48                                  ' Are we using a 48MHz oscillator?
    $define cWS2812B_Zero  4                        ' Amount of cycles for a zero delay (approx 350ns)
    $define cWS2812B_One   10                       ' Amount of cycles for a one delay (approx 900ns)
$elseif _xtal = 40                                  ' Are we using a 40MHz oscillator?
    $define cWS2812B_Zero  3                        ' Amount of cycles for a zero delay (approx 350ns)
    $define cWS2812B_One   9                        ' Amount of cycles for a one delay (approx 900ns)
$elseif _xtal = 32                                  ' Are we using a 32MHz oscillator?
    $define cWS2812B_Zero  2                        ' Amount of cycles for a zero delay (approx 350ns)
    $define cWS2812B_One   7                        ' Amount of cycles for a one delay (approx 900ns)
$elseif _xtal = 16                                  ' Are we using a 32MHz oscillator?
    $define cWS2812B_Zero  0                        ' Amount of cycles for a zero delay (approx 350ns)
    $define cWS2812B_One   2                        ' Amount of cycles for a one delay (approx 900ns)
$elseif _xtal = 12                                  ' Are we using a 12MHz oscillator?
    $define cWS2812B_Zero  0                        ' Amount of cycles for a zero delay (approx 350ns)
    $define cWS2812B_One   1                        ' Amount of cycles for a one delay (approx 900ns)
$else
    $error "Only 12MHz, 16MHz, 32MHZ, 40MHz, 48MHz and 64MHz oscillators suitable for this code"
$endif
$define cWS2812B_ResetUs 60                         ' Amount of microseconds for a reset delay
'
' The pin that the WS2812B chips are connected too
'
$ifndef WS2812B_Pin
    $define WS2812B_Pin PORTB.0
    $SendWarning "$define WS2812B_Pin not issued in the program. Using the default pin of PORTB.0"
$endif

'---------------------------------------------------------------------------------------------
' Send a 0 (approx 0.35us) to the WS2812B device
'
$define WS2812B_SendZero() '
    Set WS2812B_Pin        '
    DelayCS cWS2812B_Zero  '
    Clear WS2812B_Pin

'---------------------------------------------------------------------------------------------
' Send a 1 (approx 0.9us) to the WS2812B device
'
$define WS2812B_SendOne() '
    Set WS2812B_Pin       '
    DelayCS cWS2812B_One  '
    Clear WS2812B_Pin

'---------------------------------------------------------------------------------------------
' Send a reset (50us) to the WS2812B device
'
$define WS2812B_Finish() '
    Clear WS2812B_Pin    '
    DelayUS cWS2812B_ResetUs

'---------------------------------------------------------------------------------------------
' Interface to a single WS2812B RGB controller chip
' Input     : pRed holds the red value (0 to 255)
'           : pGreen holds the green value (0 to 255)
'           : pBlue holds the blue value (0 to 255)
' Output    : None
' Notes     : Sends the 24-bits MSB.
'           : A zero bit is a high pulse for approx 350ns
'           : A one bit is a high pulse for approx 900ns
'
Proc WS2812B_RGB(pRed As WS2812B_bRed, pGreen As WS2812B_bGreen, pBlue As WS2812B_bBlue)
    For WS2812B_bBitIndex = 23 To 0 Step -1     ' Create a loop for the 24-bits of data to send to the WS2812B
        If WS2812B_dRGB.23 = 1 Then             ' Is the bit a 1?
            WS2812B_SendOne()                   ' Yes. So send a 1 bit to the WS2812B chip
        Else                                    ' Otherwise... We have a zero bit. So...
            WS2812B_SendZero()                  ' Send a 0 bit to the WS2812B chip
        EndIf
        Rol pBlue                               ' WS2812B_dRGB.Byte0 \
        Rol pRed                                ' WS2812B_dRGB.Byte1  | Rotate the 24 colour bits
        Rol pGreen                              ' WS2812B_dRGB.Byte2 /
    Next
EndProc

'---------------------------------------------------------------------------------------------
' Illuminate a WS2812B based upon its position in a string of them
' Input     : pChipNumber holds the WS2812B in the line to alter (0 to 255)
'           : pRed holds the red value (0 To 255)
'           : pGreen holds the green value (0 to 255)
'           : pBlue holds the blue value (0 to 255)
' Output    : None
' Notes     : None
'
Proc WS2812B_Colour(pChipNumber As WS2812B_bChipNumber, pRed As WS2812B_pRed, pGreen As WS2812B_pGreen, pBlue As WS2812B_pBlue)

$if WS2812B_Amount = 1                                                  ' Is there only 1 WS2812B on the line?
    WS2812B_RGB(pRed, pGreen, pBlue)                                    ' Yes. So access a single WS2812B
    WS2812B_Finish()                                                    ' Bring the pin low for approx 50us to reset the WS2812B
$else
    If pChipNumber > cWS2812B_IndexAmount Then ExitProc
    WS2812B_bIndex = 0                                                  ' \ Create a loop for the amount of WS2812B devices on the line
    Repeat                                                              ' /
        If WS2812B_bIndex = pChipNumber Then                            ' Is it the chip we want to alter?
            WS2812B_Red[WS2812B_bIndex]   = pRed                        ' \
            WS2812B_Green[WS2812B_bIndex] = pGreen                      ' | Yes. So save the colour in the colour array
            WS2812B_Blue[WS2812B_bIndex]  = pBlue                       ' /
        EndIf
        WS2812B_RGB(WS2812B_Red[WS2812B_bIndex], WS2812B_Green[WS2812B_bIndex], WS2812B_Blue[WS2812B_bIndex])
        Inc WS2812B_bIndex
    Until WS2812B_bIndex >= WS2812B_Amount                              ' Loop for all the devices attached to the line
    WS2812B_Finish()                                                    ' Bring the pin low for approx 50us to reset the WS2812B devices
EndProc

'---------------------------------------------------------------------------------------------
' Setup the interface with WS2812B RGB controller chips
' Input     : None
' Output    : None
' Notes     : None
'
Proc WS2812B_Setup()
    Low WS2812B_Pin                 ' Make the pin that connects to the WS2812B chip a low output
    DelayMS 10                      ' And reset the WS2812B
$if WS2812B_Amount <> 1             ' Is there only 1 WS2812B on the line?
    Clear WS2812B_Red               ' \
    Clear WS2812B_Green             ' | No. So clear the arrays holding the WS2812B colours
    Clear WS2812B_Blue              ' /
$endif
EndProc

'---------------------------------------------------------------------------------------------
'
_WS2812B_Main:
    WS2812B_Setup()     ' Setup the interface to the WS2812B RGB controller chip

$endif  ' _WS2812B_INC_

The term "Neopixel" is not what the devices are actually called. That is the AdaFruit company trying to make people think that "they" created them alone, and they have succeeded in doing so, which always amazes and frustrates me. :-) If only I had been with a company such as Adafruit for all of those "wasted" years at Cro#n*i#l, I'd have been able to create a lot more and had a bit of money behind me for all the work I did. :-(

top204

#2
Here's a program I created, quite a few years ago, for a WS2812B strip that immitates the red lights scanning at the front of the KIT car from "Night Rider", using the library above:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' WS2812B RGB Interface to scan a line of LEDs with illumination of the lagging LEDs decreasing gradually.
' Much like the front scanner on the KIT car of the TV program Night Rider.
'
' Written by Les Johnson for the Positron8 BASIC Compiler.
'
    Device = 18F26K40
    Declare Xtal = 64

$define WS2812B_Pin PORTB.0                         ' The pin used for the WS2812B chips
$define WS2812B_Amount 16                           ' The amount of WS2812B chips to control
$define WS2812B_AmountCnt $eval (WS2812B_Amount - 1)

$define cRedBar   0
$define cGreenBar 1
$define cBlueBar  2

    Include "WS2812B.inc"                           ' Load the WS2812B library into the program

    Dim bChipNumber As Byte
    Dim IllumArray[WS2812B_Amount] As Byte Heap

'--------------------------------------------------------------------
' Set the PIC18F26K40 microcontroller to 64MHz operation using its internal oscillator
' Input     : None
' Output    : None
' Notes     : Waits for the oscillator to become stable
'
Proc Oscillator_64MHz()
    OSCCON1 = %00000000
    OSCCON3 = %00000000
    OSCEN   = %00000000
    OSCFRQ  = %00001000     ' Choose 64MHz
    OSCTUNE = %00000000
    Repeat : Until OSCSTATbits_HFOR = 1
EndProc

'---------------------------------------------------------------------------------------------
' Gradually dim a specific WS2812B chip
' Input     : pIndex holds the WS2818B to examine for the dimming
'           : pColour chooses cRed, cGreen or cBlue for the colour of the scanning bar
' Output    : None
' Notes     : None
'
Proc IlluminateBar(pIndex As Byte, pColour As Byte)
    Dim bChip As Byte
    Dim bColStore As Byte

    For bChip = WS2812B_AmountCnt To 0 Step -1                  ' Scan all the WS2812B devices available
        If pIndex = bChip Then                                  ' Are we at the specific chip?
            IllumArray[bChip] = 255                             ' Yes. So illuminate the LED fully
        Else                                                    ' Otherwise...
            IllumArray[bChip] = IllumArray[bChip] / 3           ' Divide its value to dim the LED gradually
        EndIf
        bColStore = IllumArray[bChip]

        If pColour = cRedBar Then                               ' Do we want a red scanning bar?
            WS2812B_Colour(bChip, bColStore, 0, 0)              ' Yes. So illuminate the Red LED of the WS2812B chip
        ElseIf pColour = cGreenBar Then                         ' Do we want a green scanning bar?
            WS2812B_Colour(bChip, 0, bColStore, 0)              ' Yes. So illuminate the Green LED of the WS2812B chip
        Else                                                    ' Otherwise... A blue scanning bar
            WS2812B_Colour(bChip, 0, 0, bColStore)              ' So illuminate the Blue LED of the WS2812B chip
        EndIf
    Next
EndProc

'---------------------------------------------------------------------------------------------
' The main program starts here
' It moves a bar of LEDs from left to right, then right to left
' with the LEDs behind the fully illuminated LED gradually dimming in brightness
'
Main:
    Oscillator_64MHz()                                      ' Set the device to operate at 64MHz using the internal oscillator

    Clear IllumArray                                        ' Set all the elements of IllumArray to 0
    Do
        '
        ' Move the LED bar right to left
        '
        For bChipNumber = 0 To WS2812B_AmountCnt            ' Create a loop for the amount of LEDs available
            IlluminateBar(bChipNumber, cRedBar)             ' Light up the bar of LEDs
            DelayMS 60                                      ' Add a delay to see things moving, otherwise the compiler is too fast and it is a blur
        Next
        '
        ' Move the LED bar left to right
        '
        For bChipNumber = WS2812B_AmountCnt To 0 Step -1    ' Create a loop for the amount of LEDs available
            IlluminateBar(bChipNumber, cRedBar)             ' Light up the bar of LEDs
            DelayMS 60                                      ' Add a delay to see things moving, otherwise the compiler is too fast and it is a blur
        Next
    Loop

'---------------------------------------------------------------------------------------------
' Setup the fuses to use the internal oscillator on a PIC18F26K40.
'
Config_Start
    RSTOSC   = HFINTOSC_1MHZ            ' HFINTOSC with HFFRQ = 4 MHz and CDIV = 4:1
    FEXTOSC  = Off                      ' Oscillator not enabled
    MCLRE    = EXTMCLR                  ' If LVP = 0, MCLR pin is MCLR. If LVP = 1, RE3 pin function is MCLR
    WDTE     = Off                      ' WDT enabled regardless of sleep
    CLKOUTEN = Off                      ' CLKOUT function is disabled
    CSWEN    = On                       ' Writing to NOSC and NDIV is allowed
    FCMEN    = Off                      ' Fail-Safe Clock Monitor disabled
    PWRTE    = On                       ' Power up timer enabled
    LPBOREN  = Off                      ' LPBOREN disabled
    BOREN    = Off                      ' Brown-out turned off
    BORV     = VBOR_245                 ' Brown-out Reset Voltage (VBOR) set to 2.45V
    ZCD      = Off                      ' ZCD disabled. ZCD can be enabled by setting the ZCDSEN bit of ZCDCON
    PPS1WAY  = Off                      ' PPSLOCK bit can be set and cleared repeatedly (subject to the unlock sequence)
    STVREN   = Off                      ' Stack full/underflow will not cause Reset
    Debug    = Off                      ' Background debugger disabled
    XINST    = Off                      ' Extended Instruction Set and Indexed Addressing Mode disabled
    SCANE    = Off                      ' Scanner module is Not available for use. SCANMD bit is ignored
    LVP      = Off                      ' HV On MCLR/VPP must be used for programming
    WDTCPS   = WDTCPS_15                ' Watchdog Divider ratio 1:1048576 (32 seconds)
    WDTCWS   = WDTCWS_7                 ' Window always open (100%). Software control. Keyed access not required
    WDTCCS   = LFINTOSC                 ' WDT input clock selector->WDT reference clock is the 31.2kHz HFINTOSC output
    WRT0     = Off                      ' Block 0 (000800-001FFFh) not write-protected
    WRT1     = Off                      ' Block 1 (002000-003FFFh) not write-protected
    WRTC     = On                       ' Configuration registers (300000-30000Bh) write-protected
    WRTB     = Off                      ' Boot Block (000000-0007FFh) not write-protected
    WRTD     = Off                      ' Data EEPROM not write-protected
    Cp       = Off                      ' UserNVM code protection disabled
    CPD      = Off                      ' DataNVM code protection disabled
    EBTR0    = Off                      ' Block 0 (000800-001FFFh) not protected from table reads executed in other blocks
    EBTR1    = Off                      ' Block 1 (002000-003FFFh) not protected from table reads executed in other blocks
    EBTRB    = Off                      ' Boot Block (000000-0007FFh) not protected from table reads executed in other blocks
Config_End

For quite a complex process of dimming the lagging LEDs as they scan, the code above looks remarkable simple. That's thanks to the flexability of the Positron compilers. :-)

glenlivet

#3
Les,
 Thanks, I ordered up some of the APA102 devices for the try. My design is quite simple, I'm using the original YX8018 chip that was in the post light to charge the battery from the solar cell during the day. After dusk when the solar cell output goes down the chip turns on a built-in boost converter power supply to run the LED but I'm using that signal to turn on a more powerful boost converter to drive the LED's and wake up the CPU.
 I wasn't aware of the Adafruit Neo-pixel thing. But I get your drift, I too spent too many years (15 total) at a place that only wanted to abuse me. On my own since 1996. I haven't set the world on fire but I haven't starved either and I've definitely NEVER looked back! Regarding the C-hill thing, the only direction is forward. "If you gaze long enough into an abyss, the abyss will gaze back into you." - a little something I once read in a book by this literary smart guy named Friedrich Nietzsce... 
Or in the venerable words of the great Groucho Marx, "I started with nothing and worked my way into complete poverty."

See_Mos

#4
I just bought a couple of 8 LED Neopixel clones for £3 each, from Ebay, to have a play with then find that Les has already done the hard work.

Thanks Les.

atomix

#5
I am also grateful to him. And here are some code examples from me.

See_Mos

Hi atomix,

The color pick is brilliant but on my Windows 7 PC with 48cm screen I cannot see the full color panel.

I can see what it does but I have not figured out how to use it with your INC files yet.  Do you have a simple demo file please.

top204

#7
Whenever I have used the RGB LED chips, I have had to implement a simple gamma correction for them, otherwise, the low and high luminence values make no change to the LED brightness' It also makes the colours on a computer screen match more with the colours produced on the LEDs.

Below is a flash memory table I have used for the Gamma correction in some programs:

'
' Create a Gamma correction table for the RGB LEDs
'
    Dim Gamma8 As Flash8 = {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
                            0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,
                            1,  1,  1,  1,  1,  1,  1,  1,  1,  2,  2,  2,  2,  2,  2,  2,
                            2,  3,  3,  3,  3,  3,  3,  3,  4,  4,  4,  4,  4,  5,  5,  5,
                            5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  9,  9,  9, 10,
                            10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
                            17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
                            25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
                            37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
                            51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
                            69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
                            90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 109, 110, 112, 114,
                            115, 117, 119, 120, 122, 124, 126, 127, 129, 131, 133, 135, 137, 138, 140, 142,
                            144, 146, 148, 150, 152, 154, 156, 158, 160, 162, 164, 167, 169, 171, 173, 175,
                            177, 180, 182, 184, 186, 189, 191, 193, 196, 198, 200, 203, 205, 208, 210, 213,
                            215, 218, 220, 223, 225, 228, 231, 233, 236, 239, 241, 244, 247, 249, 252, 255}

And below is how the "WS2812B_RGB" procedure uses the flash memory table's values:

    pRed   = CRead8 Gamma8[pRed]                ' \
    pGreen = CRead8 Gamma8[pGreen]              ' | Add Gamma correction to the LEDs
    pBlue  = CRead8 Gamma8[pBlue]               ' /

Other gamma tables can be created for the required liminance of the LEDs, and even seperate tables for the Red, Green and Blue LEDs, but the same, simple, principle of reading and adding them applies. I have used antilog gamma, linearising, tables in the past, but I got so bored with checking the linear nature of the LEDs with every table change, I just used the simple table data above. :-)

The gamma correction data can also be held in a RAM array, and I have used the program below a few times, in the past, to create new gamma data and see what are the better choices for the pGamma and pSteps parameters values.

The program below can easily be changed to create the data for a flash memory table once a user is happy with the linearised LED, by outputting the table values to a serial terminal, then copying the table values to the actual progam in a Dim As Flash8 directive. It could also be changed to implement the WS2812B library above instead of the PWM for the LEDs, because when I wrote it approx 10 to 12 years ago, the RGB LED chips did not exist. :-) It has been changed from the original code, to now use a procedure.

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Linearise a PWM driven LED
'
' Written for the Positron8 BASIC compiler by Les Johnson
'
    Include "Amicus18.inc"                  ' Set the compiler to use a PIC18F25K20 device operating at 64MHz
    Include "Amicus18_Hpwm8.inc"            ' Load the 8-bit PWM routines into the program

    Dim bIndex    As Byte
    Dim bLinValue As Byte

$define cTableSize  255

    Dim bLinTable[cTableSize] As Byte Heap

'--------------------------------------------------------------------
' Create a data array to linearise an LED's brightness for 8-bit PWM
' Input     : pGamma holds the Gamma correction value. Normally between 0.5 to 1.0
'           : pSteps holds the total number of PWM steps in the table
'           : cTableSize holds the size of the array table
' Output    : Array bLinTable is filled with the values
' Notes     : Leo Bodnar's antilogarithmic gamma correction algorithm
'           : Equivalent to: wDcyValue = pSteps ^ (( (wIndex + 1) / cTableSize) ^ pGamma) + 0.3
'
Proc Create_LineariseTable(pGamma As Float, pSteps As Word)
    Dim fTemp     As Float
    Dim wDcyValue As Word
    Dim wIndex    As Word

    Clear bLinTable
    For wIndex = 1 To Bound(bLinTable)
        fTemp = Pow((wIndex / cTableSize), pGamma)
        wDcyValue = Pow(pSteps, fTemp)
        bLinTable[wIndex] = wDcyValue
    Next
'
' Draw the curve on a serial terminal for visualisation
'
(*
    HRSOutLn "' Gamma Value = ", Dec2 pGamma
    For wIndex = 0 To Bound(bLinTable)
        bLinValue = bLinTable[wIndex]
        Hrsout Dec3 bLinValue, "| "
        If bLinValue <= 1 Then
            HRSOutLn "*"
        Else
            HRSOutLn Rep " "\(bLinValue / 4), "*"
        EndIf
    Next
*)
EndProc

'--------------------------------------------------------------------
' Increase and decrease the brightness of two LEDs on CCP1 and CCP2 pins.
' One LED using the linearisation calculations and one using the index as the duty cycle to see the difference
'
Main:
    Create_LineariseTable(0.85, 255)                ' Create the linearising array table
    Analog12_Open()                                 ' Open PWM 1 and 2

    Do
        '
        ' Illuminate the LEDs gradually
        '
        For bIndex = 0 To Bound(bLinTable)
            bLinValue = bLinTable[bIndex]           ' Read the duty cycle value from the array
            Analog1_Write(bLinValue)                ' Illuminate the LED on PWM 1 using the table value
            Analog2_Write(bIndex)                   ' Illuminate the LED on PWM 2 using the standard duty value
            DelayMS 10
        Next
        '
        ' Extinguish the LEDs gradually
        '
        For bIndex = Bound(bLinTable) To 0 Step -1
            bLinValue = bLinTable[bIndex]           ' Read the duty cycle value from the array
            Analog1_Write(bLinValue)                ' Illuminate the LED on PWM 1 using the table value
            Analog2_Write(bIndex)                   ' Illuminate the LED on PWM 2 using the standard duty value
            DelayMS 10
        Next
    Loop

atomix

#8
Thanks Les. I added your gamma table to my library. Also i fixed example (added how to use) and program. Everything should be fine now

See_Mos

Thank you Les and atomix

Color.exe now works as expected on my old PC.  I will have a look at the rest later.