News:

;) This forum is the property of Proton software developers

Main Menu

Controlling a WS2812B RGB LED Matrix Board

Started by top204, Jun 03, 2022, 02:25 PM

Previous topic - Next topic

top204

Below is a link to a page I have just created on my google web site to show how to control a WS2812B RGB LED matrix board. So the 16x16 matrix shown in the page acts like a low resolution colour OLED display, all controlled from a single pin of the microcontroller. The web site below has a download link that has the WS2812B and WS2812B matrix library sources and a few demo programs. As well as an Isis simulator file to see the demos working in simulation.

Controlling a WS2812B RGB LED Matrix Board

I was sent the board a few years ago by my good friend Tony (the toy designer) to see if I could create a library for his wonderful "Silent Running" display with his "truly excellent" robot he has built. I got so carried away with the excellent matrix board's show of colours, I added text and graphics capabilities to the library, and a few demos.

For those users who are interested in seeing the WS2812B matrix library's code, it is listed below:
$ifndef _WS2812B_MATRIX_
$define _WS2812B_MATRIX_
'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' WS2812B RGB LED Matrix interface
' Because of the very tight pulse requirements of the WS2812B device, this code is for a PIC18F device operating at a higher oscillation frequency.
' The frequencies supported with this library are 12MHz, 16MHz, 32MHz, 40MHz, 48MHz and 64MHz
'
' The library uses the 24-bit Long variable for the RGB colours to save memory and allow the code to run faster.
'
' The library requires three $defines placed before it is loaded into the main program:
'
' $define WS2812B_Pin       Tells the library which pins the WS2812B strip is connected too
' $define Matrix_X_Size     Tells the library the amount of X axis pixels on the matrix display
' $define Matrix_Y_Size     Tells the library the amount of Y axis pixels on the matrix display
'
' Written by Les Johnson for the Positron8 BASIC Compiler.
'
' Set defaults for the library if the $defines are not added to the main program listing
'
$ifndef Matrix_Pin
    $define Matrix_Pin PORTB.0                          ' The default pin that the WS2812B chips are connected too
    $SendWarning "$define Matrix_Pin not issued in the program. Using the default pin of PORTB.0"
$endif

$ifndef Matrix_X_Size
    $SendWarning "$define Matrix_X_Size not issued in the main program. Using default of 16"
    $define Matrix_X_Size 16                            ' The default amount of the X LEDs on the matrix display
$endif

$ifndef Matrix_Y_Size
    $SendWarning"$define Matrix_Y_Size not issued in the main program. Using the default of 16"
    $define Matrix_Y_Size 16                            ' The default amount of the Y LEDs on the matrix display
$endif

$define cMatrix_X_Max $eval (Matrix_X_Size - 1)
$define cMatrix_Y_Max $eval (Matrix_Y_Size - 1)
'
' The amount of WS2812B chips on the strip
'
$define cWS2812B_IndexAmount $eval ((Matrix_X_Size * Matrix_Y_Size) - 1)
$define cWS2812B_Amount $eval (Matrix_X_Size * Matrix_Y_Size)
'
' Create variables
'
    Dim WS2812B_bBitIndex As Byte Access                        ' Used to access each bit in the WS2812B interface
    Dim WS2812B_lRGB      As Long 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_lRGB.Byte2                    ' Alias the green byte
    Dim WS2812B_bRed   As WS2812B_lRGB.Byte1                    ' Alias the red byte
    Dim WS2812B_bBlue  As WS2812B_lRGB.Byte0                    ' Alias the blue byte

    Dim Matrix_wPixelPos    As Word Access                      ' Holds the position within the display of a pixel's colour values
    Dim Matrix_bPixelPos    As Matrix_wPixelPos.Byte0           ' Holds the position within the display of a pixel's colour values
    Dim Matrix_lColour      As Long Access                      ' Used as an alias to hold the colour value for some procedures
    Dim Matrix_lPenColour   As Long                             ' Holds the font's pen colour
    Dim Matrix_lPaperColour As Long                             ' Holds the font's paper colour
    Dim Matrix_lDisplay[cWS2812B_Amount] As Long Heap           ' Holds the matrix display's colour map for each pixel
'
' Alias some SFRs as 16-bit variables
'
    Dim Matrix_wTblPtr As TBLPTRL.Word
    Dim Matrix_wProd   As PRODL.Word
    Dim Matrix_wFSR0   As FSR0L.Word
    Dim Matrix_wFSR1   As FSR1L.Word
    Dim Matrix_wIndex  As PRODL.Word
'
' WS2812B pulse timing constants for 12MHz, 16MHz, 32MHz, 40MHz, 48MHz and 64MHz oscillators
'
$if _xtal = 64                                      ' Are we using a 64MHz oscillator?
    Symbol cWS2812B_Zero = 6                        ' Amount of cycles for a zero delay (approx 350ns)
    Symbol cWS2812B_One  = 16                       ' Amount of cycles for a one delay (approx 900ns)
$elseif _xtal = 48                                  ' Are we using a 48MHz oscillator?
    Symbol cWS2812B_Zero = 4                        ' Amount of cycles for a zero delay (approx 350ns)
    Symbol cWS2812B_One  = 10                       ' Amount of cycles for a one delay (approx 900ns)
$elseif _xtal = 40                                  ' Are we using a 40MHz oscillator?
    Symbol cWS2812B_Zero = 3                        ' Amount of cycles for a zero delay (approx 350ns)
    Symbol cWS2812B_One  = 9                        ' Amount of cycles for a one delay (approx 900ns)
$elseif _xtal = 32                                  ' Are we using a 32MHz oscillator?
    Symbol cWS2812B_Zero = 2                        ' Amount of cycles for a zero delay (approx 350ns)
    Symbol cWS2812B_One  = 7                        ' Amount of cycles for a one delay (approx 900ns)
$elseif _xtal = 16                                  ' Are we using a 32MHz oscillator?
    Symbol cWS2812B_Zero = 0                        ' Amount of cycles for a zero delay (approx 350ns)
    Symbol cWS2812B_One  = 2                        ' Amount of cycles for a one delay (approx 900ns)
$elseif _xtal = 12                                  ' Are we using a 12MHz oscillator?
    Symbol cWS2812B_Zero = 0                        ' Amount of cycles for a zero delay (approx 350ns)
    Symbol 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
    Symbol cWS2812B_ResetUs = 60                    ' Amount of microseconds for a reset delay
'
' Flash memory table to Map the 16x16 matrix display's X and Y pixels to the lines of WS2812B chips
' This table will need to be changed to match any different matrix X and Y sizes
'
    Dim Matrix_cMap As Flash8 = {15,  14,  13,  12,  11,  10,  9,   8,   7,   6,   5,   4,   3,   2,   1,   0,   ' Line 0
                                 16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,  ' Line 1
                                 47,  46,  45,  44,  43,  42,  41,  40,  39,  38,  37,  36,  35,  34,  33,  32,  ' Line 2
                                 48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,  ' Line 3
                                 79,  78,  77,  76,  75,  74,  73,  72,  71,  70,  69,  68,  67,  66,  65,  64,  ' Line 4 
                                 80,  81,  82,  83,  84,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,  ' Line 5 
                                 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99,  98,  97,  96,  ' Line 6 
                                 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, ' Line 7
                                 143, 142, 141, 140, 139, 138, 137, 136, 135, 134, 133, 132, 131, 130, 129, 128, ' Line 8
                                 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, ' Line 9
                                 175, 174, 173, 172, 171, 170, 169, 168, 167, 166, 165, 164, 163, 162, 161, 160, ' Line 10
                                 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, ' Line 11
                                 207, 206, 205, 204, 203, 202, 201, 200, 199, 198, 197, 196, 195, 194, 193, 192, ' Line 12
                                 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, ' Line 13
                                 239, 238, 237, 236, 235, 234, 233, 232, 231, 230, 229, 228, 227, 226, 225, 224, ' Line 14
                                 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255} ' Line 15

'---------------------------------------------------------------------------------------------
' Send a 0 (approx 0.35us) to the WS2812B device
'
$define WS2812B_SendZero() '
    PinSet Matrix_Pin      '
    DelayCS cWS2812B_Zero  '
    PinClear Matrix_Pin

'---------------------------------------------------------------------------------------------
' Send a 1 (approx 0.9us) to the WS2812B device
'
$define WS2812B_SendOne() '
    PinSet Matrix_Pin     '
    DelayCS cWS2812B_One  '
    PinClear Matrix_Pin

'---------------------------------------------------------------------------------------------
' Send a reset (60us) to the WS2812B device
'
$define WS2812B_Finish() '
    PinClear Matrix_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 DownTo 0                 ' Create a loop for the 24-bits of data to send to the WS2812B
        Rol pBlue                                       ' WS2812B_lRGB.Byte0 \
        Rol pRed                                        ' WS2812B_lRGB.Byte1 | Rotate the 24 colour bits
        Rol pGreen                                      ' WS2812B_lRGB.Byte2 /
        If STATUSbits_C = 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
    Next
EndProc

'---------------------------------------------------------------------------------------------
' Update the matrix LEDs from the memory mapped display array
' Input     : None
' Output    : None
' Notes     : None
'
Proc Matrix_Update()
    Dim dColour As Dword

    Matrix_wPixelPos = 0                                                ' \ Create a loop for the amount of WS2812B devices on the line
    Repeat                                                              ' /
        dColour = Matrix_lDisplay[Matrix_wPixelPos]                     ' Read a value from the display array
        WS2812B_RGB(dColour.Byte2, dColour.Byte1, dColour.Byte0)        ' Transfer it to a WS2812B chip
        Inc Matrix_wPixelPos
    Until Matrix_wPixelPos >= cWS2812B_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

'---------------------------------------------------------------------------------------------
' Change a pixel's colour inside the maxrix screen array
' Input     : pXpos holds the X position of the pixel (0 to 15)
'           : pYpos holds the Y position of the Pixel (0 to 15)
'           : pColour holds the RGB colour for a pixel
'           : The colour is: pColour.Byte2 = Red
'           :                pColour.Byte1 = Green
'           :                pColour.Byte0 = Blue
' Output    : None
' Notes     : The matrix line of WS2812B devices scans from right to left, then left to right
'           : So a flash memory table of the pixel X/Y positions on the matrix display need to be looked up
'
Proc Matrix_ShadowPlot(pXpos As SByte, pYpos As SByte, pColour As Matrix_lColour)
'
' Display Boundary checks
'
    If pYpos < 0 Then ExitProc
    If pYpos >= Matrix_Y_Size Then ExitProc
    If pXpos < 0 Then ExitProc
    If pXpos >= Matrix_X_Size Then ExitProc
'
' Optimisation of:
' Matrix_bPixelPos = (pYpos * Matrix_Y_Size) + pXpos
'
    pYpos = pYpos * Matrix_Y_Size
    Matrix_bPixelPos = pYpos + pXpos
    Matrix_bPixelPos = CRead8 Matrix_cMap[Matrix_bPixelPos]             ' Find the location of the pixel's X/Y position in the line of WS2812B devices
    Matrix_lDisplay[Matrix_bPixelPos] = pColour
EndProc

'---------------------------------------------------------------------------------------------
' Change a pixel's colour on the matrix display
' Input     : pXpos holds the X position of the pixel (0 to 15)
'           : pYpos holds the Y position of the Pixel (0 to 15)
'           : pColour holds the RGB colour for a pixel
'           : pColour holds the colour of the circle
'           : The colour is: pColour.Byte2 = Red
'           :                pColour.Byte1 = Green
'           :                pColour.Byte0 = Blue
' Output    : None
' Notes     : The matrix line of WS2812B devices scans from right to left, then left to right
'           : So a flash memory table of the pixel X/Y positions on the matrix display need to be looked up
'
Proc Matrix_Plot(pXpos As SByte, pYpos As SByte, pColour As Matrix_lColour)
'
' Display Boundary checks
'
    If pYpos < 0 Then ExitProc
    If pYpos >= Matrix_Y_Size Then ExitProc
    If pXpos < 0 Then ExitProc
    If pXpos >= Matrix_X_Size Then ExitProc
'
' Optimisation of:
' Matrix_bPixelPos = (pYpos * Matrix_Y_Size) + pXpos
'
    pYpos = pYpos * Matrix_Y_Size
    Matrix_bPixelPos = pYpos + pXpos
    Matrix_bPixelPos = CRead8 Matrix_cMap[Matrix_bPixelPos]                           ' Find the location of the pixel's X/Y position in the line of WS2812B devices
    Matrix_lDisplay[Matrix_bPixelPos] = pColour
    Matrix_Update()                                                     ' Update the matrix display from the display array
EndProc

'---------------------------------------------------------------------------------------------
' Read a pixel's colour from the WS2812B matrix display
' Input     : pXpos holds the X position of the pixel (0 to 15)
'           : pYpos holds the Y position of the Pixel (0 to 15)
' Output    : Returns the RGB colour of a Pixel
'           : The colour is: Result.Byte2 = Red
'           :                Result.Byte1 = Green
'           :                Result.Byte0 = Blue
' Notes     : None
'
Proc Matrix_Pixel(pXpos As SByte, pYpos As SByte), Dword
'
' Display Boundary checks
'
    If pYpos < 0 Then ExitProc
    If pYpos >= Matrix_Y_Size Then ExitProc
    If pXpos < 0 Then ExitProc
    If pXpos >= Matrix_X_Size Then ExitProc
'
' Optimisation of:
' Matrix_bPixelPos = (pYpos * Matrix_Y_Size) + pXpos
'
    pYpos = pYpos * Matrix_Y_Size
    Matrix_bPixelPos = pYpos + pXpos
    Matrix_bPixelPos = CRead8 Matrix_cMap[Matrix_bPixelPos]                            ' Find the location of the pixel's X/Y position in the line of WS2812B devices
    Result = Matrix_lDisplay[Matrix_bPixelPos]
EndProc

'-------------------------------------------------------------------------------------------------
' Draw a line from pStartXpos, pStartYpos to pEndXpos, pEndYpos
' Input     : pStartXpos = Starting X position of the Line
'           : pStartYpos = Starting Y position of the Line
'           : pEndXpos = Ending X position of the Line
'           : pEndYpos = Ending Y position of the Line
'           : pColour holds the colour of the line
'           : The colour is RGB: pColour.Byte2 = Red
'           :                    pColour.Byte1 = Green
'           :                    pColour.Byte0 = Blue
' Output    : None
' Notes     : Draws a line to the buffer array, then updates the matrix with the array's contents.
'
Proc Matrix_Line(pStartXpos As Byte, pStartYpos As Byte, pEndXpos As Byte, pEndYpos As Byte, pColour As Matrix_lColour)
    Dim sbDeltaX As SByte
    Dim sbDeltaY As SByte
    Dim sbStepX  As SByte
    Dim sbStepY  As SByte
    Dim sbDeviation As SByte

    sbDeltaY = pEndYpos - pStartYpos                                ' Find the initial Y direction
    sbDeltaX = pEndXpos - pStartXpos                                ' Find the initial X direction

    sbStepY = 1                                                     ' Default to a positive step for Ypos
    If sbDeltaY < 0 Then                                            ' Check if negative
        sbDeltaY = -sbDeltaY                                        ' Negate sbDeltaY
        sbStepY = -1                                                ' And indicate a negative step
    EndIf
    sbStepX = 1                                                     ' Default to a positive step for Xpos
    If sbDeltaX < 0 Then                                            ' Check if negative
        sbDeltaX = -sbDeltaX                                        ' Negate sbDeltaX
        sbStepX = -1                                                ' And indicate a negative step
    EndIf
    Matrix_ShadowPlot(pStartXpos, pStartYpos, pColour)              ' The first pixel plot is a special case

    If sbDeltaX > sbDeltaY Then
        sbDeviation = sbDeltaY - sbDeltaX
        While pStartXpos <> pEndXpos
            If sbDeviation >= 0 Then                                ' Check if negative
                pStartYpos = pStartYpos + sbStepY                   ' Move on the Y axis directed by sbStepY
                sbDeviation = sbDeviation - sbDeltaX
            EndIf
            pStartXpos = pStartXpos + sbStepX                       ' Move on the X axis directed by sbStepX
            sbDeviation = sbDeviation + sbDeltaY
            Matrix_ShadowPlot(pStartXpos, pStartYpos, pColour)
        Wend
        GoTo Update                                                 ' Return prematurely
    EndIf
    sbDeviation = sbDeltaX - sbDeltaY
    While pStartYpos <> pEndYpos
        If sbDeviation >= 0 Then                                    ' Check if negative
            pStartXpos = pStartXpos + sbStepX                       ' Move on the X axis directed by sbStepX
            sbDeviation = sbDeviation - sbDeltaY
        EndIf
        pStartYpos = pStartYpos + sbStepY                           ' Move on the Y axis directed by sbStepY
        sbDeviation = sbDeviation + sbDeltaX
        Matrix_ShadowPlot(pStartXpos, pStartYpos, pColour)
    Wend
Update:
    Matrix_Update()                                                 ' Update the matrix display from the display array
EndProc

'-------------------------------------------------------------------------------------------------
' Draw a horizontal line from pStartXpos, pStartYpos to pEndXpos, pEndYpos
' Input     : pStartXpos = Starting X position of the Line
'           : pStartYpos = Starting Y position of the Line
'           : pEndXpos = Ending X position of the Line
'           : pColour holds the colour of the line
'           : The colour is RGB: pColour.Byte2 = Red
'           :                    pColour.Byte1 = Green
'           :                    pColour.Byte0 = Blue
' Output    : None
' Notes     : Draws a line to the buffer array, then updates the matrix with the array's contents.
'
Proc Matrix_HorizLine(pStartXpos As Byte, pStartYpos As Byte, pEndXpos As Byte, pColour As Matrix_lColour)
    Repeat
        Matrix_ShadowPlot(pStartXpos, pStartYpos, pColour)
        Inc pStartXpos
    Until pStartXpos >= pEndXpos
    Matrix_Update()                                                     ' Update the matrix display from the display array
EndProc

'-------------------------------------------------------------------------------------------------
' Draw a Circle on the Matrix display
' Input     : pXpos holds the starting X position
'           : pYpos holds the starting Y position
'           : pRadius holds the radius of the circle
'           : pColour holds the colour of the circle
'           : The colour is RGB: pColour.Byte2 = Red
'           :                    pColour.Byte1 = Green
'           :                    pColour.Byte0 = Blue
' Output    : None
' Notes     : Draws a circle to the buffer array, then updates the matrix with the array's contents.
'
Proc Matrix_Circle(pXpos As Byte, pYpos As Byte, pRadius As Byte, pColour As Matrix_lColour)
    Dim bDD As Byte
    Dim bXX As Byte
    Dim bYY As Byte
    Dim bTR As Byte
    Dim bXpos_S As Byte

    Dim bQuadA As Byte
    Dim bQuadB As Byte
    Dim bQuadC As Byte
    Dim bQuadD As Byte

    bXpos_S = pXpos                                                 ' Transfer pXpos into its working variable
    If pRadius = 0 Then ExitProc                                    ' Trap a radius of 0

    bDD = pYpos - bXpos_S
    bXX = 0
    bYY = pRadius
    bTR = 3 - (2 * pRadius)
    While bXX <= bYY
        bQuadA = bXpos_S + bXX
        bQuadB = pYpos + bYY

        bQuadC = bXpos_S - bXX
        bQuadD = pYpos - bYY
        GoSub PlotIt                                                ' Plot 4 locations simultaneously
        bQuadA = pYpos + bYY
        bQuadA = bQuadA - bDD

        bQuadB = pYpos + bXX
        bQuadC = pYpos - bYY

        bQuadC = bQuadC - bDD
        bQuadD = pYpos - bXX
        GoSub PlotIt                                                ' Plot 4 locations simultaneously
        If bTR.7 = 1 Then
            bTR = bTR + 6
            bTR = bTR + (bXX * 4)
        Else
            bTR = bTR + 10
            bTR = bTR + ((bXX - bYY) * 4)
            Dec bYY
        EndIf
        Inc bXX
    Wend
    Matrix_Update()                                                 ' Update the matrix display from the display buffer array
    ExitProc
'
' Plot 4 locations simultaneously
'
PlotIt:
    Matrix_ShadowPlot(bQuadA, bQuadB, pColour)
    Matrix_ShadowPlot(bQuadA, bQuadD, pColour)
    Matrix_ShadowPlot(bQuadC, bQuadB, pColour)
    Matrix_ShadowPlot(bQuadC, bQuadD, pColour)
EndProc

'---------------------------------------------------------------------------------------------
' Clear the Matrix display
' Input     : pColour holds the colour of the display when cleared
'           : The colour is RGB: pColour.Byte2 = Red
'           :                    pColour.Byte1 = Green
'           :                    pColour.Byte0 = Blue
' Output    : None
' Notes     : None
'
Proc Matrix_Cls(pColour As Matrix_lColour)
    For Matrix_wPixelPos = Bound(Matrix_lDisplay) DownTo 0
        Matrix_lDisplay[Matrix_wPixelPos] = pColour             ' Set all the elements of Matrix_lDisplay to the colour chosen
    Next
    Matrix_Update()                                             ' Transfer the pixels from the screen buffer array to the WS2812B LED chips
EndProc

'-------------------------------------------------------------------------------------
' Copy a character glyph from the font data table to the LED matrix, with its upper left at the given coordinate
' Input     : pX holds the X position of the character
'             pY holds the Y position of the character
'             pCh holds the character to display
' Output    : None
' Notes     : Uses Matrix_ShadowPlot to draw each dot of the character, then updates the matrix with the array's contents.
'
Proc Matrix_DisplayChar(pX As Byte, pY As Byte, pCh As Byte)
    Dim bRow As Byte
    Dim bCol As Byte
    Dim wOffset As Word
    Dim FontBits As Byte
'
' Move character definition, pixel by pixel, onto the display
' Fonts are defined as one byte per column
'
' Characters's Address =  pCh * 6
' pCh holds the byte to display
' Add the offset for the address of font: wOffset + font label's address
' This is the code that requires altering for different sized fonts
'
    bCol = 0
    Repeat                                                      ' Create a loop for all the bytes in the character's font
        wOffset = pCh * 6                                       ' 6 bytes per font
        wOffset = wOffset + bCol                                ' Add the offset to the character's value
        FontBits = CRead8 Font_Table[wOffset]                   ' Read the byte from the font table
        '
        ' Send the value in FontBits to the Display
        '
        bRow = 7
        Repeat
            If FontBits.6 = 1 Then
                Matrix_lColour = Matrix_lPenColour
            Else
                Matrix_lColour = Matrix_lPaperColour
            EndIf
            Matrix_ShadowPlot(pX + bCol, pY + bRow, Matrix_lColour)
            FontBits = FontBits << 1
            Dec bRow
        Until bRow = 0
        Inc bCol                                                ' Next byte in the font
    Until bCol >= 5                                             ' Have we done 5 writes yet ?
    Matrix_Update()                                             ' Transfer the pixels from the screen buffer array to the WS2812B LED chips
EndProc

'-------------------------------------------------------------------------------------
' Display a string of characters from a String variable on the matrix
' Input     : pX holds the X position of the character
'             pY holds the Y position of the character
'             SFR pair FSR1L\H hold the address of the string to display
' Output    : None
' Notes     : None
'
Proc Matrix_DisplayString(pX As Byte, pY As Byte, ByRef pStr As Matrix_wFSR1)
    Dim bIndex As Byte
    Dim bChar As Byte

    bIndex = 0                                              ' Reset a counter
    Repeat                                                  ' Form a loop
        bChar = POSTINC1                                    ' Transfer the element of the string into WREG and increment position
        If bChar = 0 Then Break
        Matrix_DisplayChar(pX, pY, bChar)                   ' Display the character held in WREG
        pX = pX + 6                                         ' Move 6 pixels to the right (width of each character)
        Inc bIndex                                          ' Increment the counter
    Until bIndex = 0                                        ' Until 256 iterations have occured
EndProc

'-------------------------------------------------------------------------------------
' Display a string of characters from a character string on the matrix
' Input     : pX holds the X position of the character
'             pY holds the Y position of the character
'             pStr holds the address of the character block to display
' Output    : None
' Notes     : None
'
Proc Matrix_DisplayText(pX As Byte, pY As Byte, BycRef pStr As Matrix_wTblPtr)
    Dim bIndex As Byte
    Dim bChar As Byte
    Dim Save_wTBLPTR As Word

    bIndex = 0                                              ' Reset a counter
    Repeat                                                  ' Form a loop
        Tblrd*+
        bChar = TABLAT                                      ' Transfer the element of the string into WREG and increment position
        If bChar = 0 Then Break
        Save_wTBLPTR = Matrix_wTblPtr
        Matrix_DisplayChar(pX, pY, bChar)                   ' Display the character held in WREG
        Matrix_wTblPtr = Save_wTBLPTR
        pX = pX + 6                                         ' Move 6 pixels to the right (width of each character)
        Inc bIndex                                          ' Increment the counter
    Until bIndex = 0                                        ' Until 256 iterations have occured
EndProc

'---------------------------------------------------------------------------------------------
' Set the pen colour for the font characters
' Input     : pColour holds the colour of the display when cleared
'           : The colour is RGB: pColour.Byte2 = Red
'           :                    pColour.Byte1 = Green
'           :                    pColour.Byte0 = Blue
' Output    : None
' Notes     :
$define Matrix_PenColour(pColour) Matrix_lPenColour = pColour

'---------------------------------------------------------------------------------------------
' Set the paper colour for the font characters
' Input     : pColour holds the colour of the display when cleared
'           : The colour is RGB: pColour.Byte2 = Red
'           :                    pColour.Byte1 = Green
'           :                    pColour.Byte0 = Blue
' Output    : None
' Notes     : None
'
$define Matrix_PaperColour(pColour) Matrix_lPaperColour = pColour

'---------------------------------------------------------------------------------------------
' Setup the interface with WS2812B RGB controller chips
' Input     : None
' Output    : None
' Notes     : None
'
Proc Matrix_Setup()
    PinLow Matrix_Pin                           ' Make the pin that connects to the WS2812B chip a low output
    DelayMS 10                                  ' And reset the WS2812B
    Clear Matrix_lDisplay                       ' Set all the elements of Matrix_lDisplay to 0
EndProc

$endif  ' _WS2812B_MATRIX_