A friend asked me to create a program that would drive five independent LEDs with PWM, so their duty cycle values could be changed for differing illumination, so he could test an MSQEQ7 device running on another development board as a form of Sound-To-Light unit, as used in discos. However, this triggered my curiousity, and he kindly sent me an MSGEQ7 board so I could write interfacing code using the Positron8 compiler for a PIC device.
Before we look at the code for the Sound-To-Light unit, the MSQEQ7 is an old, but interesting device, and it contains seven band-pass filters for frequencies; 63Hz, 160Hz, 400Hz, 1KHz, 2.5KHz, 6.25KHz, and 16KHz, and outputs their peaks as voltages so they can be read via an ADC.
A block diagram of the MSQEQ7 device's internals is shown below:
The MSQEQ7 operates by setting its 'DC Out' pin to the voltage passed by the filters from the audio presented on its 'In' pin, and each filter is selected by toggling the 'Strobe' pin. So it is not a complex device to interface with, but does need some care with the timings of the toggling, and making sure the MSQEQ7 does not loose sync with its Strobe and internal filter band, as it sometimes does.
Once I had written the procedures to interface with the MSQEQ7, I thought I would have a go at creating a program on a single PIC18F device that simulates a Sound-To-Light unit, so that different coloured lights would illuminate with respect to the seperate frequencies of the audio it sees. Just like a disco light unit. However, I did not want to power mains bulbs for the lights because that would require Triacs and high voltages, and standard LEDs would be too dim. So I had another excuse to use the excellent WS2812B RGB LED chips, because they are very bright, and come in many lengths with lots of them on a flexible strip, and are interfaced with using a single pin!
So below is the complete code listing written in Positron8 BASIC, on a PIC18F26K40 device using its internal oscillator at 64MHz, that interfaces with an MSQEQ7 device, and illuminates 30 WS2812B chips, so that each of the seven frequency bands of the audio input to it, will illuminate several LEDs at different colours for the particular band:
'
' /\\\\\\\\\
' /\\\///////\\\
' \/\\\ \/\\\ /\\\ /\\\
' \/\\\\\\\\\\\/ /\\\\\ /\\\\\\\\\\ /\\\\\\\\ /\\\\\\\\\\\ /\\\\\\\\\\\ /\\\\\\\\\
' \/\\\//////\\\ /\\\///\\\ \/\\\////// /\\\/////\\\ \////\\\//// \////\\\//// \////////\\\
' \/\\\ \//\\\ /\\\ \//\\\ \/\\\\\\\\\\ /\\\\\\\\\\\ \/\\\ \/\\\ /\\\\\\\\\\
' \/\\\ \//\\\ \//\\\ /\\\ \////////\\\ \//\\/////// \/\\\ /\\ \/\\\ /\\ /\\\/////\\\
' \/\\\ \//\\\ \///\\\\\/ /\\\\\\\\\\ \//\\\\\\\\\\ \//\\\\\ \//\\\\\ \//\\\\\\\\/\\
' \/// \/// \///// \////////// \////////// \///// \///// \////////\//
' Let's find out together what makes a PIC Tick!
'
' Read an MSGEQ7, multi Pass-Band filter, device and illuminate WS2812B devices in differing colours
' and intensities for each of the 7 bands.
' The frequency bands within the MSGEQ7 device are: 63Hz, 160Hz, 400Hz, 1KHz, 2.5KHz, 6.25KHz, and 16KHz.
' This simulates a Sound-To-Light unit used in discos.
'
' This program initialises the peripherals for PIC18FxxK40 devices, but initialisations can be changed for different 18F devices.
'
' Written by Les Johnson for the Positron8 BASIC Compiler (03-01-2025).
'
' The WS2812B devices connect to pin PORTB.0
' Depending on the amount of WS2812B devices attached, dictates how many of them will be used as a light for a frequency band.
' This is calculated automatically by the program,
' but 30 WS2812B devices is recommended as a minimum. The maximum is 65535 WS2812B devices.
'
' The MSGEQ7 Strobe pin connects to PORTC.0
' The MSGEQ7 RST pin connects to PORTC.1
' The MSGEQ7 DC pin connects to PORTA.0
'
' Version 2 (04-01-2025).
' Loads the arrays for the WS2812B devices, then when all bulb colours filled, update the WS2812B chips for faster operation
'
Device = 18F26K40 ' Tell the compiler what device to compile for
Declare Xtal = 64 ' Tell the compiler what frequency the device is operating at (in MHz)
Declare Auto_Heap_Arrays = On ' Make all arrays "Heap" types, so they always get placed after standard variables
Declare Auto_Variable_Bank_Cross = On ' Make sure all multi-byte variables remain within a single RAM bank
'
' Setups for the WS2812B RGB devices
'
$define WS2812B_Pin PORTB.0 ' The pin used for the WS2812B devices
$define WS2812B_Amount 30 ' The maximum amount of WS2812B devices to control (0 to X)
'
' Set the pins used for the MSGEQ7 device
'
$define MSGEQ7_Strobe_Pin PORTC.0 ' Connects to the MSGEQ7 device's Strobe pin
$define MSGEQ7_RST_Pin PORTC.1 ' Connects to the MSGEQ7 device's Reset pin
$define MSGEQ7_DC_Pin PORTA.0 ' Connects to the MSGEQ7 device's Out pin
$define cANA0 0 ' Pin number for ANA0 (PORTA.0)
'
' Create some meta-macros for Timer0 on a PIC18F26K40 device
'
$define Timer0_Flag() PIR0bits_TMR0IF ' Timer0 Flag
$define Timer0_FlagClear() Timer0_Flag() = 0 ' Clear the Timer0 interrupt flag
$define Timer0_Start() T0CON0bits_T0EN = 1 ' Start Timer0
$define Timer0_Stop() T0CON0bits_T0EN = 0 ' Stop Timer0
'
' Create global variables for the MSGEQ7 device
'
Dim MSGEQ7_bBandValue0 As Byte ' \
Dim MSGEQ7_bBandValue1 As Byte ' |
Dim MSGEQ7_bBandValue2 As Byte ' |
Dim MSGEQ7_bBandValue3 As Byte ' | Holds the 7 frequency bands ADC value read from the MSGEQ7 device
Dim MSGEQ7_bBandValue4 As Byte ' |
Dim MSGEQ7_bBandValue5 As Byte ' |
Dim MSGEQ7_bBandValue6 As Byte ' /
Dim MSGEQ7_bBandValues[7] As Byte At MSGEQ7_bBandValue0 ' Alias to hold 7 frequency bands ADC value read from the MSGEQ7 device
'
' Create variables and constants for the WS2812B devices
'
Dim WS2812B_lColour As Long ' Used to hold the 24-bit colour in some procedures
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 WS2812B_bRedArray[WS2812B_Amount] As Byte Heap ' Holds the red value for a particular WS2812B device
Dim WS2812B_bGreenArray[WS2812B_Amount] As Byte Heap ' Holds the green value for a particular WS2812B device
Dim WS2812B_bBlueArray[WS2812B_Amount] As Byte Heap ' Holds the blue value for a particular WS2812B device
'
' WS2812B pulse timing interface constants for a microcontroller operating at 64MHz
'
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)
Symbol cBulb_Amount = (WS2812B_Amount / 7) ' The amount of WS2812B devices used per coloured bulb
Symbol cLight1_Pos = 0 ' The start WS2812B position of light 1
Symbol cLight2_Pos = cLight1_Pos + cBulb_Amount ' The start WS2812B position of light 2
Symbol cLight3_Pos = cLight2_Pos + cBulb_Amount ' The start WS2812B position of light 3
Symbol cLight4_Pos = cLight3_Pos + cBulb_Amount ' The start WS2812B position of light 4
Symbol cLight5_Pos = cLight4_Pos + cBulb_Amount ' The start WS2812B position of light 5
Symbol cLight6_Pos = cLight5_Pos + cBulb_Amount ' The start WS2812B position of light 6
Symbol cLight7_Pos = cLight6_Pos + cBulb_Amount ' The start WS2812B position of light 7
Dim wTimer0_SFR As TMR0L.Word ' Combine SFRs TMR0L and TMR0H into a 16-bit SFR
'---------------------------------------------------------------------------------------------
' Send a 0 (approx 0.35us) to the WS2812B device
' Input : None
' Output : None
' Notes : Uses microcontroller instruction cycles for the small delay
'
$define WS2812B_SendZero() '
PinSet WS2812B_Pin '
DelayCS cWS2812B_Zero '
PinClear WS2812B_Pin
'---------------------------------------------------------------------------------------------
' Send a 1 (approx 0.9us) to the WS2812B device
' Input : None
' Output : None
' Notes : Uses microcontroller instruction cycles for the small delay
'
$define WS2812B_SendOne() '
PinSet WS2812B_Pin '
DelayCS cWS2812B_One '
PinClear WS2812B_Pin
'---------------------------------------------------------------------------------------------
' Send a reset (60us) to the WS2812B device
' Input : None
' Output : None
' Notes : None
'
$define WS2812B_Finish() '
PinClear WS2812B_Pin '
DelayUS 60
'------------------------------------------------------------------------------------------------
' The main program starts here
' Interface with an MSGEQ7 device and illuminate WS2812B RGB chips for the seven frequency bands.
'
Setup() ' Setup the program and any peripherals
Do ' Create a loop
MSGEQ7_ReadBands() ' Read the MSGEQ7 device
Light_1(MSGEQ7_bBandValue0) ' Load the light colour for band 0 (63Hz)
Light_2(MSGEQ7_bBandValue1) ' Load the light colour for band 1 (160Hz)
Light_3(MSGEQ7_bBandValue2) ' Load the light colour for band 2 (400Hz)
Light_4(MSGEQ7_bBandValue3) ' Load the light colour for band 3 (1KHz)
Light_5(MSGEQ7_bBandValue4) ' Load the light colour for band 4 (2.5KHz)
Light_6(MSGEQ7_bBandValue5) ' Load the light colour for band 5 (6.25KHz)
Light_7(MSGEQ7_bBandValue6) ' Load the light colour for band 6 (16KHz)
WS2812B_Update() ' Illuminate the strip of WS2812B devices with the colours
Loop ' Loop forever
'------------------------------------------------------------------------------------------------
' Setup the program and any peripherals
' Input : None
' Output : None
' Notes : None
'
Proc Setup()
WS2812B_Setup() ' Setup the interface to the WS2812B RGB controller chips
WS2812B_Clear(0) ' Clear the line of WS2812B devices so all LEDs are off
ADC_Setup() ' Setup the ADC
Timer0_Init_3_Seconds() ' Setup Timer0 for an approx 3 seconds overflow
MSGEQ7_Init() ' Setup the MSGEQ7 device
EndProc
'------------------------------------------------------------------------------------------------
' MSGEQ7 procedures
'------------------------------------------------------------------------------------------------
' Read the MSGEQ7 DC out values from the 7 band-pass filters
' Input : None
' Output : MSGEQ7_bBandValue0 to MSGEQ7_bBandValue6 hold the 7 band ADC values read
' MSGEQ7_bBandValue0 holds the ADC reading for 63Hz
' MSGEQ7_bBandValue1 holds the ADC reading for 160Hz
' MSGEQ7_bBandValue2 holds the ADC reading for 400Hz
' MSGEQ7_bBandValue3 holds the ADC reading for 1KHz
' MSGEQ7_bBandValue4 holds the ADC reading for 2.5KHz
' MSGEQ7_bBandValue5 holds the ADC reading for 6.25KHz
' MSGEQ7_bBandValue6 holds the ADC reading for 16KHz
' Notes : Uses a Timer0 overflow to reset the MSGEQ7 every few seconds, so it does not become out of sync
'
Proc MSGEQ7_ReadBands()
Dim bBand As Byte ' Holds the band to read (0 to 6)
Dim bADC_Value As Byte ' Holds the 8-bit ADC reading
If Timer0_Flag() = 1 Then ' Has a Timer0 overflow occured?
Timer0_Reset_3_Seconds() ' Yes. So reset it
MSGEQ7_Reset() ' Reset the MSGEQ7 device
EndIf
For bBand = 0 To 6 ' Create a loop for the amount of bands to read
PinSet MSGEQ7_Strobe_Pin ' \
DelayUS 40 ' | Toggle the Strobe pin for the next frequency band reading
PinClear MSGEQ7_Strobe_Pin ' /
DelayUS 10 ' A small delay to allow the MSGEQ7 to settle
bADC_Value = ADC_Read8(cANA0) ' Read a band from the MSGEQ7 device via the ADC
If bADC_Value <= 60 Then ' \
bADC_Value = 0 ' | Remove any MSGEQ7 noise from the ADC reading
EndIf ' /
MSGEQ7_bBandValues[bBand] = bADC_Value ' Fill the array with the band ADC value
Next
EndProc
'------------------------------------------------------------------------------------------------
' Initialise the MSGEQ7 device
' Input : None
' Output : None
' Notes : None
'
Proc MSGEQ7_Init()
PinLow MSGEQ7_Strobe_Pin ' Make the MSGEQ7 Strobe pin an output low
PinLow MSGEQ7_RST_Pin ' Make the MSGEQ7 RST pin an output low
PinInput MSGEQ7_DC_Pin ' Make the MSGEQ7 DC pin an input
Clear MSGEQ7_bBandValues ' Clear the frequency bands array
MSGEQ7_Reset() ' Reset the MSGEQ7 device
EndProc
'------------------------------------------------------------------------------------------------
' Reset the MSGEQ7 device
' Input : None
' Output : None
' Notes : None
'
Proc MSGEQ7_Reset()
PinClear MSGEQ7_Strobe_Pin ' Pull the MSGEQ7 Strobe pin low
PinSet MSGEQ7_RST_Pin ' Set the MSGEQ7 RST pin high
DelayUS 1 ' A small delay
PinClear MSGEQ7_RST_Pin ' Pull the MSGEQ7 RST pin low
DelayUS 72 ' A small delay before returning
EndProc
'------------------------------------------------------------------------------------------------
' ADC procedures
'------------------------------------------------------------------------------------------------
' Setup the ADC peripheral on a PIC18F26K40 device
' Input : None
' Output : None
' Notes : Default for 8-bit operation, with -Vref as VSS and +Vref as VDD
'
Proc ADC_Setup()
ADLTHL = %00000000
ADLTHH = %00000000
ADUTHL = %00000000
ADUTHH = %00000000
ADSTPTL = %00000000
ADSTPTH = %00000000
ADRPT = %00000000
ADPCH = %00000000
ADCAP = %00000000
ADCON0 = %00000000 ' Left justify for 8-bit operation. ADC is disabled
ADCON1 = %00000000
ADCON2 = %00000000 ' Set for Basic mode
ADCON3 = %00000000
ADSTAT = %00000000
ADREF = %00000000 ' -Vref is VSS, +Vref is VDD
ADACT = %00000000
ADCLK = %00000111 ' FOSC/32 (FOSC / (2 * (n + 1)))
ADACQ = %00000000
EndProc
'------------------------------------------------------------------------------------------------
' Get an 8-bit reading from the ADC on a PIC18F26K40 device
' Input : pChan holds the ADC channel to read
' Output : Returns the 8-bit ADC value. Also held in SFR ADRESH
' Notes : Sets the ADFM bit to left justified 8-bit operation
'
Proc ADC_Read8(pChan As WREG), ADRESH
ADPCH = pChan ' Load the channel into the relevant SFR
ADCON0bits_ADFM = 0 ' Set the ADFM bit for left justified
ADCON0bits_ADON = 1 ' Enable the ADC
DelayUS 50 ' A delay before sampling
ADCON0bits_GO_DONE = 1 ' \
Repeat : Until ADCON0bits_GO_DONE = 0 ' / Start a sample and wait for it to finish
EndProc
'-----------------------------------------------------------------------------------------
' WS2812B procedures
'---------------------------------------------------------------------------------------------
' Interface to a single WS2812B RGB controller chip (with gamma control)
' 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 to the WS2812B MSB first (Most Significant Bit).
' : 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)
Dim bBitIndex As Byte Access ' Used to access each bit in the WS2812B interface
'
' Create a Gamma correction table to make the LEDs more linear in illumonation
'
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}
pRed = CRead8 Gamma8[pRed] ' \
pGreen = CRead8 Gamma8[pGreen] ' | Add Gamma correction to the LEDs
pBlue = CRead8 Gamma8[pBlue] ' /
For bBitIndex = 23 DownTo 0 ' Create a loop for the 24-bits of data to send to a WS2812B
Rol pBlue ' WS2812B_dRGB.Byte0 \
Rol pRed ' WS2812B_dRGB.Byte1 | Rotate the 24 colour bits
Rol pGreen ' WS2812B_dRGB.Byte2 /
If STATUSbits_C = 1 Then ' Is the bit set?
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
'---------------------------------------------------------------------------------------------
' Fill all of a WS2812B strip with a colour
' Input : pColour holds the 24-bit colour value
' Output : None
' Notes : Red is pColour.Byte0
' Green is pColour.Byte1
' Blue is pColour.Byte2
'
Proc WS2812B_Clear(pColour As WS2812B_lColour)
Global Dim WS2812B_wChipNumber As Word Shared ' The WS2812B to access on a line of them
WS2812B_wChipNumber = 0 ' \ Create a loop for the amount of WS2812B devices to alter
Repeat ' /
WS2812B_RGB(pColour.Byte0, pColour.Byte1, pColour.Byte2)
Inc WS2812B_wChipNumber ' \
Until WS2812B_wChipNumber >= WS2812B_Amount ' / Loop for the amount of devices
WS2812B_Finish() ' Bring the pin low long enough to reset the WS2812B devices
EndProc
'---------------------------------------------------------------------------------------------
' Fill all or some of the Red, Green and Blue arrays with a colour
' Input : pFirst holds the first element to fill (0 to 65535)
' : pAmount holds the number of elements to fill (0 to 65535)
' : pColour holds the 24-Bit colour value
' Output : None
' Notes : None
'
Proc WS2812B_ShadFill(pFirst As Word, pAmount As Word, pColour As WS2812B_lColour)
Global Dim WS2812B_wChipNumber As Word Shared ' The WS2812B to access on a line of them
Symbol cIndexAmount = (WS2812B_Amount - 1)
If pFirst >= WS2812B_Amount Then ExitProc ' Do nothing if the first LED is past end of strip
pAmount = pFirst + pAmount ' Calculate the last WS2812B chip
If pAmount > cIndexAmount Then ' Make sure that the loop will not go past the last pixel
pAmount = cIndexAmount
EndIf
WS2812B_wChipNumber = 0 ' \ Create a loop for the amount of WS2812B devices to alter
Repeat ' /
If WS2812B_wChipNumber >= pFirst Then ' Is it at the starting chip we want to alter?
WS2812B_bRedArray[WS2812B_wChipNumber] = pColour.Byte0 ' Red
WS2812B_bGreenArray[WS2812B_wChipNumber] = pColour.Byte1 ' Green
WS2812B_bBlueArray[WS2812B_wChipNumber] = pColour.Byte2 ' Blue
EndIf
Inc WS2812B_wChipNumber ' \
Until WS2812B_wChipNumber >= pAmount ' / Loop for the amount of devices specified in pAmount
EndProc
'---------------------------------------------------------------------------------------------
' Read the red, green and blue arrays and send the contents to WS2812B devices
' Input : None
' Output : None
' Notes : None
'
Proc WS2812B_Update()
Global Dim WS2812B_wChipNumber As Word Shared ' The WS2812B to access on a line of them
WS2812B_wChipNumber = 0 ' \ Create a loop for the amount of WS2812B devices to alter
Repeat ' /
WS2812B_RGB(WS2812B_bRedArray[WS2812B_wChipNumber], WS2812B_bGreenArray[WS2812B_wChipNumber], WS2812B_bBlueArray[WS2812B_wChipNumber])
Inc WS2812B_wChipNumber ' \
Until WS2812B_wChipNumber >= WS2812B_Amount ' / Loop for the amount of WS2812B devices to control
WS2812B_Finish() ' Bring the pin low long enough to reset the WS2812B devices
EndProc
'---------------------------------------------------------------------------------------------
' Setup the interface with WS2812B RGB controller chips
' Input : None
' Output : None
' Notes : None
'
Proc WS2812B_Setup()
PinLow WS2812B_Pin ' Make the pin that connects to the WS2812B chip low
DelayMS 10 ' Reset the WS2812B
Clear WS2812B_bRedArray ' \
Clear WS2812B_bGreenArray ' | Clear the arrays holding the WS2812B colours
Clear WS2812B_bBlueArray ' /
EndProc
'-----------------------------------------------------------------------------------------
' Setup to illuminate a set of WS2812B devices to act as Light 1
' Input : pLUX holds the luminance of the WS2812B devices
' Output : None
' Notes : Changing the values of lLight_Colour will dictate the colour of the RGB LEDs.
'
Proc Light_1(pLUX As Byte)
Global Dim lLight_Colour As Long Shared
lLight_Colour.Byte2 = pLUX ' Red
lLight_Colour.Byte1 = 0 ' Green
lLight_Colour.Byte0 = 0 ' Blue
WS2812B_ShadFill(cLight1_Pos, cBulb_Amount, lLight_Colour)
EndProc
'-----------------------------------------------------------------------------------------
' Setup to illuminate the second set of WS2812B devices to act as Light 2
' Input : pLUX holds the luminance of the WS2812B devices
' Output : None
' Notes : Changing the values of lLight_Colour will dictate the colour of the RGB LEDs.
'
Proc Light_2(pLUX As Byte)
Global Dim lLight_Colour As Long Shared
lLight_Colour.Byte2 = pLUX ' Red
lLight_Colour.Byte1 = 0 ' Green
lLight_Colour.Byte0 = pLUX ' Blue
WS2812B_ShadFill(cLight2_Pos, cBulb_Amount, lLight_Colour)
EndProc
'-----------------------------------------------------------------------------------------
' Setup to illuminate a set of WS2812B devices to act as Light 3
' Input : pLUX holds the luminance of the WS2812B devices
' Output : None
' Notes : Changing the values of lLight_Colour will dictate the colour of the RGB LEDs.
'
Proc Light_3(pLUX As Byte)
Global Dim lLight_Colour As Long Shared
lLight_Colour.Byte2 = 0 ' Red
lLight_Colour.Byte1 = pLUX ' Green
lLight_Colour.Byte0 = 0 ' Blue
WS2812B_ShadFill(cLight3_Pos, cBulb_Amount, lLight_Colour)
EndProc
'-----------------------------------------------------------------------------------------
' Setup to illuminate a set of WS2812B devices to act as Light 4
' Input : pLUX holds the luminance of the WS2812B devices
' Output : None
' Notes : Changing the values of lLight_Colour will dictate the colour of the RGB LEDs.
'
Proc Light_4(pLUX As Byte)
Global Dim lLight_Colour As Long Shared
lLight_Colour.Byte2 = 0 ' Red
lLight_Colour.Byte1 = pLUX ' Green
lLight_Colour.Byte0 = pLUX ' Blue
WS2812B_ShadFill(cLight4_Pos, cBulb_Amount, lLight_Colour)
EndProc
'-----------------------------------------------------------------------------------------
' Setup to illuminate a set of WS2812B devices to act as Light 5
' Input : pLUX holds the luminance of the WS2812B devices
' Output : None
' Notes : Changing the values of lLight_Colour will dictate the colour of the RGB LEDs.
'
Proc Light_5(pLUX As Byte)
Global Dim lLight_Colour As Long Shared
lLight_Colour.Byte2 = 0 ' Red
lLight_Colour.Byte1 = 0 ' Green
lLight_Colour.Byte0 = pLUX ' Blue
WS2812B_ShadFill(cLight5_Pos, cBulb_Amount, lLight_Colour)
EndProc
'-----------------------------------------------------------------------------------------
' Setup to illuminate a set of WS2812B devices to act as Light 6
' Input : pLUX holds the luminance of the WS2812B devices
' Output : None
' Notes : Changing the values of lLight_Colour will dictate the colour of the RGB LEDs.
'
Proc Light_6(pLUX As Byte)
Global Dim lLight_Colour As Long Shared
lLight_Colour.Byte2 = pLUX ' Red
lLight_Colour.Byte1 = pLUX ' Green
lLight_Colour.Byte0 = 0 ' Blue
WS2812B_ShadFill(cLight6_Pos, cBulb_Amount, lLight_Colour)
EndProc
'-----------------------------------------------------------------------------------------
' Setup to illuminate a set of WS2812B devices to act as Light 7
' Input : pLUX holds the luminance of the WS2812B devices
' Output : None
' Notes : Changing the values of lLight_Colour will dictate the colour of the RGB LEDs.
'
Proc Light_7(pLUX As Byte)
Global Dim lLight_Colour As Long Shared
lLight_Colour.Byte2 = pLUX ' Red
lLight_Colour.Byte1 = pLUX ' Green
lLight_Colour.Byte0 = pLUX ' Blue
WS2812B_ShadFill(cLight7_Pos, cBulb_Amount, lLight_Colour)
EndProc
'-----------------------------------------------------------------------------------------
' Timer0 procedures
'-----------------------------------------------------------------------------------------
' Initialise Timer0 for a 3 seconds overflow
' Input : None
' Output : None
' Notes : Timer0 configured for 16-bit
' : For use with a PIC18FxxK40 device running at 64MHz
'
Proc Timer0_Init_3_Seconds()
T0CON1 = %01011010 ' T0CS FOSC/4. Prescaler is 1:1024. Not synchronised
wTimer0_SFR = $48E5 ' Load Timer0 for an overflow after 3 seconds
Timer0_FlagClear() ' Clear Interrupt flag before enabling the interrupt
T0CON0 = %10010000 ' Postscaler is 1:1. Timer0 enabled. 16-bit mode
EndProc
'-----------------------------------------------------------------------------------------
' Reset Timer0 for a 3 second overflow
' Input : None
' Output : None
' Notes : None
'
Proc Timer0_Reset_3_Seconds()
wTimer0_SFR = $48E5 ' Load Timer0 for an overflow after 3 seconds
Timer0_FlagClear() ' Clear Interrupt flag before enabling the interrupt
EndProc
'------------------------------------------------------------------------------------------------
' Setup the fuses to use the internal oscillator at 64MHz on a PIC18F26K40. With RA6 and RA7 as general purpose I/O
'
Config_Start
RSTOSC = HFINTOSC_64MHZ ' HFINTOSC with HFFRQ = 64MHz and CDIV = 1:1
FEXTOSC = Off ' External Oscillator not enabled
WDTE = Off ' WDT disabled
CLKOUTEN = Off ' CLKOUT function is disabled
CSWEN = On ' Writing to NOSC and NDIV is allowed
FCMEN = Off ' Fail-Safe Clock Monitor disabled
MCLRE = EXTMCLR ' If LVP = 0, MCLR pin is MCLR. If LVP = 1, RE3 pin function is MCLR
PWRTE = On ' Power up timer enabled
LPBOREN = off ' ULPBOR disabled
BOREN = On ' Brown-out turned on
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 ' Low Voltage programming disabled
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 reference clock is the 31.2kHz HFINTOSC output
WRT0 = Off ' Block 0 (000800-001FFF) not write-protected
WRT1 = Off ' Block 1 (002000-003FFF) not write-protected
WRTC = Off ' Configuration registers (300000-30000Bh) not write-protected
WRTB = Off ' Boot Block (000000-0007FF) 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-001FFF) not protected from table reads executed in other blocks
EBTR1 = Off ' Block 1 (002000-003FFF) not protected from table reads executed in other blocks
EBTRB = Off ' Boot Block (000000-0007FF) not protected from table reads executed in other blocks
Config_End
A block circuit diagram for the above source code to operate with is shown below. The PIC device uses its internal oscilator at 64MHz, so it does not require an external crystal or resonator:
The MSGEQ7 PCB and the microphone pre-amplifier PCB are from DFRobots, and are shown below:
I made a couple of videos of the 30 WS2812B LEDs moving to some music using the firmware, and they are shown below:
The source code for a PIC18F26K40 device is downloadable from here: MSGEQ7_WS2812B_Sound-To-Light - Source.zip.