This page details the design choices and implementation of all the software used in the completion of this project.
We decided to create two copies of the Framework4PIC32, one for the Hub PIC32 called "controlSoftwareFrameworkHub" and another for the players called "controlSoftwareFrameworkP". This helped with organization of the events and services that each PIC32 is concerned about. Throughout the following sections the system that the section is referring to will be stated explicitly. These sections include the state diagrams and pseudo-code for the players and hub sub-systems and any shared libraries. All the detailed code can be found in the GitHub repository found in this link.
Note: Our individually addressable (neopixel) LED library (WS2812.h and WS2812.c) is now a published library and has been used by multiple other groups for their projects to implement neopixels as user interfaces and decoration!
This is a Finite State Machine (FSM) running in the Hub's PIC32.
It guides the gameplay by responding to the GameStart, TargetHit and PlayerActive signals with corresponding actions like starting timers, controlling outputs and posting events to other services.
This is a Service running in the Hub's PIC32.
It responds to the ES_DISPENSE_SERVO event posted by the HubFSM to run a sequence that will dispense the gears to the winner of the game once the game is over.
This is a Service running in the Hub's PIC32.
It dictates what is displayed by the LED matrix displays by sending the corresponding instructions to the TypeWriter FSM.
This is a Finite State Machine (FSM) running in the Hub's PIC32.
It updates the LED matrix displays via SPI.
This is a Service running in the Players' PIC32.
It is responsible for responding to player inputs and toggling the PlayerActive (Hub In Player Out) to it's corresponding logic (high-z or low)
This is a Service running in the Players' PIC32.
It responds to the player joystick input to control the speed at which the turret is moving.
This is a FSM running in the Players' PIC32.
It is responsible for responding to player inputs shooting events and performing all related animations (laser on, laser LED, ammo neopixels).
This is a FSM running in the Players' PIC32.
It responds to the different game stages by sending corresponding sound effects (start music, shoot sound, etc.)
/*** DEFINES: HUB PINS and Events ***/
/* Define Pins for inputs (GameStart, PlayerActive, TargetHit[1-10]) */
/* Use array or similar data structure to hold TargetHit Pins so it’s easier to code one event checker instead of 10 */
/* Use array or similar data structure to hold TargetHitEvents so it’s easier to code one event checker instead of 10 */
/* Description: Start signal Event Checker, checks if the start sensor changed state
posts the new state to the Hub(we use ES_START_LOW to start the game)*/
Function CheckStartInputEvent
set static variable LastStartInputState 1(assumed)
set ReturnValue to false
set CurrentStartInputState to pin the pin value read (read once)
if CurrentStartInputState is not equal to LastStartInputState
set ReturnValue to true
create new event to be posted
if CurrentStartInputState is 0
set event type to ES_START_LOW
end if
post event to HubFSM to notify of the change of the start input
set LastStartInputState to CurrentStartInputState
end if
return ReturnValue
/* Description: Player input signal Event Checker, checks if the player active line changed state to know if the player is active
posts the new state to the HubFSM (we use ES_PLAYER_ACTIVE_LOW to know that the players are active) */
Function CheckPlayerInputEvent
set static variable LastPlayerInputState to 0(assumed)
set ReturnValue to false
set CurrentPlayerInputState to pin the pin value read (read once)
if CurrentPlayerInputState is not equal to LastPlayerInputState
set ReturnValue to true
create new event to be posted
if CurrentPlayerInputState is 0
set event type to ES_PLAYER_ACTIVE_LOW
end if
post event to HubFSM to notify of the activity of the players
set LastPlayerInputState to CurrentPlayerInputState
end if
return ReturnValue
/* Description: Target Hit Event Checker, checks if any of the Targets got hit
posts the corresponding event to the HubFSM (i.e. we use ES_TARGET5_P1_HIT to indicate that player 1 hit target 5) */
Function CheckTargetHitEvent
set static variable LastTargetHitState to 0
set ReturnValue to false
set CurrentTargetHitState 0
set a variable portVal to the port where the targets are read (PORTB)
read CurrentTargetHitStates by looping over them
for each target from 1 to 10 (1 to 5 x 2 players)
if corresponding bit for this target in portVal is 1 (HIGH=UNHIT)
set corresponding bit of CurrentTargetHitState to 1
end if
set ChangedTargetState to CurrentTargetHitState XOR LastTargetHitState (to indicate change)
if ChangedTargetState is not 0
set ReturnValue to true
//loop over targets to see which changed and post corresponding event
for each target from 1 to 10 (1 to 5 x 2 players)
if corresponding bit in ChangedTargetState is 1 (changed)
if corresponding bit in CurrentTargetHitState is 0 (high->low=hit)
create new event to be posted
set event type to corresponding TargetHitEvent (i.e. ES_TARGET5_P1_HIT)
post event to HubFSM
end if
end if
set LastPlayerInputState to CurrentPlayerInputState
end if
return ReturnValue
Module-level variables: MyPriority, CurrentState, NextState, scoreP1, scoreP2, GameStartTime, GameCurrentTime
Defined States: InitHubState, Waiting4GameStart, GameStartAnimation, GameRunning, GameOver
Function InitHubFSM(Priority)
set MyPriority to Priority
set CurrentState to InitHubState
Post ES_INIT event to this queue
Return true if the event posted successfully, false otherwise
Function PostHubFSM(ThisEvent)
Post event to this queue
Function RunHubFSM(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
set NextState to CurrentState
switch (CurrentState):
case InitHubState:
if ThisEvent.EventType is ES_INIT
Initialize the Hub hardware, initialize all the input and output pins
Make the edge neopixel lights to initial color (orange)
Make the target neopixel lights to initial color (green)
Post ES_WELCOME_SCREEN event to Printer Service
set NextState to Waiting4GameStart
end if
case Waiting4GameStart:
if ThisEvent.EventType is ES_START_LOW:
set GameOn signal to players’ PICs low
Post ES_START_ANIMATION_SCREEN event to Printer Service
set NextState to GameStartAnimation
end if
case GameStartAnimation:
if ThisEvent.EventType is ES_START_ANIMATION_DONE:
Post ES_GAMERUNNING_SCREEN event to Printer Service
Start GAMETIME_TIMER for 60 seconds
Start IDLETIME_TIMER for 20 seconds
Set scores to 0
Make the edge neopixel lights off
Make the target neopixel lights off
set GameStartTime to ES_Timer_GetTime()
set NextState to GameRunning
end if
case GameRunning:
switch (ThisEvent.EventType)
/* For all the ES_TARGET#_P#_HIT events do the following: */
case ES_TARGET1_P1_HIT:
case ES_TARGET2_P1_HIT:
case ES_TARGET3_P1_HIT:
case ES_TARGET4_P1_HIT:
case ES_TARGET5_P1_HIT:
case ES_TARGET1_P2_HIT:
case ES_TARGET2_P2_HIT:
case ES_TARGET3_P2_HIT:
case ES_TARGET4_P2_HIT:
case ES_TARGET5_P2_HIT:
//(could be condensed in one function)
Update the score values
Update the targets’ “captured” value
Make the edge neopixel lights match the overall score
Make the target neopixel lights match the “captured” values of their targets
set NextState to GameRunning
case ES_PLAYER_ACTIVE_LOW: //If event is event one
Restart IDLETIME_TIMER for 20 seconds
case ES_TIMEOUT:
if (ThisEvent.EventParam is GAMETIME_TIMER)
set GameOn signal to players’ PICs high
Make the edge neopixel lights match the winner’s color
Post ES_GAMEOVER_SCREEN event to Printer Service
Post ES_DISPENSE_SERVO event to Dispense Servo Service
Start HUBGAMEOVERTIME_TIMER for 10 seconds
set NextState to GameOver
end if
if (ThisEvent.EventParam is IDLETIME_TIMER)
set GameOn signal to players’ PICs high
Make the edge neopixel lights match the winner’s color
Post ES_GAMEOVER_SCREEN event to Printer Service
Post ES_DISPENSE_SERVO event to Dispense Servo Service
Start HUBGAMEOVERTIME_TIMER for 10 seconds
set NextState to GameOver
end if
case ES_WIPEOUT:
set GameCurrentTime to ES_Timer_GetTime();
if ((GameCurrentTime - GameStartTime) > 30 seconds)
set GameOn signal to players’ PICs high
Make the edge neopixel lights match the winner’s color
Post ES_GAMEOVER_SCREEN event to Printer Service
Post ES_DISPENSE_SERVO event to Dispense Servo Service
Start HUBGAMEOVERTIME_TIMER for 10 seconds
set NextState to GameOver
end if
// end switch on ThisEvent.EventType
case GameOver:
if (ThisEvent.EventType is ES_TIMEOUT)
if (ThisEvent.EventParam == HUBGAMEOVERTIME_TIMER)
Make the edge neopixel lights to initial color (orange)
Make the target neopixel lights to initial color (green)
Post ES_WELCOME_SCREEN event to Printer Service
set NextState to Waiting4GameStart
end if
end if
// end switch on Current State
set CurrentState to NextState
return ReturnEvent
Module-level variables: CurrentAngle, CurrentSpeed, lastAngle
Function InitDispenseServoService(Priority)
set MyPriority to Priority
Initialize and setup PWM for the servo
Start GEAR_SERVO_TIMER for 100 ms
Post ES_INIT event to this queue
Return true if the event posted successfully, false otherwise.
Function PostDispenseServoService(ThisEvent)
Post event to this queue
Function RunDispenseServoService(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
switch (ThisEvent.EventType)
case ES_INIT:
set CurrentAngle to initial angle (center = 90deg)
case ES_DISPENSE_SERVO:
set local variable winner to ThisEvent.EventParam
if (winner is Player1)
Post ES_P1_WINS event to DispenseServoService
else if (winner is Player2)
Post ES_P2_WINS event to DispenseServoService
end if
case ES_P1_WINS:
Start DISPENSE_SERVO_TIMER for 1000 ms
Set CurrentAngle to 0 (to dispense a gear to player 1)
case ES_P2_WINS:
Start DISPENSE_SERVO_TIMER for 1000 ms
Set CurrentAngle to 1800 (to dispense a gear to player 2)
case ES_TIMEOUT:
if (ThisEvent.EventParam is GEAR_SERVO_TIMER)
Restart GEAR_SERVO_TIMER for 100 ms
set the PWM to the appropriate value using the current angle and the PWMOperate_SetPulseWidthOnChannel function
end if
if (ThisEvent.EventParam is DISPENSE_SERVO_TIMER)
set CurrentAngle to the center position (90 degrees)
end if
return ReturnEvent
Module-level variables: MyPriority, CurrentState, MyPriority, DeferralQueue[10 + 1]
Defined States: InitPState, Waiting4Key, Writing
Function InitTypewriterFSM(Priority)
set MyPriority to Priority
set CurrentState to InitPState
Initialize deferral queue
Post ES_INIT event to this queue
Return true if the event posted successfully, false otherwise
Function PostTypewriterFSM(ThisEvent)
Post event to this queue
Function RunTypewriterFSM(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
switch (CurrentState)
case InitPState:
if (ThisEvent.EventType is ES_INIT) {
Initialize all SPI settings/instructions/parameters
Set CurrentState to Waiting4Key
end if
case Waiting4Key:
switch (ThisEvent.EventType)
case ES_NEW_KEY:
Scroll buffer
Add character to buffer
Post ES_MD_ROW_UPDATE to this FSM
if (DM_TakeDisplayUpdateStep() is false (still updating)) { // start display update if needed
Post ES_MD_ROW_UPDATE to this FSM
set CurrentState to Writing
else
set CurrentState to Waiting4Key
end if
case ES_NEW_KEY_SILENT:
Scroll buffer
Add character to buffer
default: // if no ES_NEW_KEY events and we are in the waiting stage, we can handle deferral queue
Call ES_RecallEvents(MyPriority, DeferralQueue)) to recall events
end switch on ThisEvent.EventType
}
case Writing: // currently updating rows
{
switch (ThisEvent.EventType)
case ES_NEW_KEY: // new character during ongoing update, defer it
case ES_MD_ROW_UPDATED: // continue updating display rows
if (DM_TakeDisplayUpdateStep() is false (still updating)) { // start display update if needed
Post ES_MD_ROW_UPDATE to this FSM
set CurrentState to Writing
else
set CurrentState to Waiting4Key
end if
// recall any deferred characters now that display is free
if (DM_TakeDisplayUpdateStep() is true)
end switch on ThisEvent.EventType
return ReturnEvent
/*----------------------------- Module Defines ----------------------------*/
/* Define the messages */
#define WELCOME_MESSAGE "Welcome! "
#define GAMESTARTING_MESSAGE "3... 2... 1... Go! "
#define GAMERUNNING_MESSAGE "Game running... "
#define GAMEOVER_MESSAGE "Game Over "
#define PLAYER1WINS "PLAYER 1 WINS! "
#define PLAYER2WINS "PLAYER 2 WINS! "
#define TIEGAME "NO ONE WINS :| "
Module-level variables: MyPriority, CurrentState, currentCharIndex, TimeRemaining, LastScore
Defined States: InitPrinterState, WelcomeScreen, GameStartingScreen, GameRunningScreen, GameOverScreen
Function InitPrinterService(Priority)
set MyPriority to Priority
set CurrentState to InitPrinterState
Post ES_INIT event to this queue
Return true if the event posted successfully, false otherwise
Function PostPrinterService(ThisEvent)
Post event to this queue
Function RunPrinterService(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
switch (ThisEvent.EventType) {
case ES_INIT:
set CurrentState to InitPrinterState
Start PRINTER_TIMER for 500 ms
case ES_TIMEOUT:
if (ThisEvent.EventParam is PRINTER_TIMER) {
if (CurrentState is NOT GameRunningScreen)
Post ES_NEW_KEY event to TypeWriter FSM with next character
Restart PRINTER_TIMER for 500 ms
if (CurrentState is GameStartingScreen it reached the last character)
Post ES_START_ANIMATION_DONE to HubFSM to notify
end if
else // we are in GameRunning Screen
Decrease TimeRemaining by 1 second
Restart PRINTER_TIMER for 1000 ms
end if
end if
case ES_UPDATESCORE_SCREEN:
set LastScore to the score found in ThisEvent.EventParam
Post ES_NEW_KEY_SILENT events to TypeWriter FSM will corresponding score and time characters except the last one, then post ES_NEW_KEY with the last character
case ES_WELCOME_SCREEN:
Initialize message to welcome message at index 0
Start PRINTER_TIMER for 500 ms
set CurrentState to WelcomeScreen
set LastScore to 0 (reset scores)
case ES_START_ANIMATION_SCREEN:
Initialize message to Game Starting message at index 0
Start PRINTER_TIMER for 500 ms
set CurrentState to GameStartingScreen
case ES_GAMERUNNING_SCREEN:
Initialize message to Game Running message at index 0
Start PRINTER_TIMER for 500 ms
set CurrentState to GameRunningScreen
set TimeRemaining to 60 seconds
case ES_GAMEOVER_SCREEN:
Update Game Over message with corresponding winner given by ThisEvent.EventParam value
/*(If 1 -> add PLAYER1WINS)
(else if 2 -> add PLAYER2WINS)*/
Initialize message to Game Over message at index 0
Start PRINTER_TIMER for 500 ms
set CurrentState to GameOverScreen
return ReturnEvent
/*** EVENT CHECKER: Joystick Direction ***/
/* Description:
Reads the joystick analog value from ADC.
Converts joystick position into a speed value between -7 and +7.
Only posts an ES_DIR_CHANGE event when the speed value changes.
*/
Function CheckJoystickDirection
set static variable LastSpeedto 0(assumed)
read ADC value from joystick into adcValue
compute centered value using adcValue and the center value
if (absolute value of centered is less than "deadband" center)
set speed to 0 // joystick centered
else
calculate speed, clamped between max and min values using centered
end if
if (speed is not equal to static LastSpeed)
create new event
set event type to ES_DIR_CHANGE
set event parameter to speed
post event to ServoService
post event to PlayerService
update LastSpeed to speed
return true
end if
return false
/*** EVENT CHECKER: Shoot Button ***/
Function CheckButtonEvents
set ReturnValue to false
set static variable LastButtonState to 1(assumed not pressed)
read CurrentButtonState from button input pin
compare to static LastButtonState
if (CurrentButtonState is not equal to LastButtonState)
set ReturnValue to true
create new event
set event type to ES_BUTTON_DOWN
set event parameter to CurrentButtonState
post event to LaserFSM
post event to PlayerService
end if
update LastButtonState to CurrentButtonState
return ReturnValue
/*** EVENT CHECKER: Hub Game Line ***/
Function CheckGameEvents
set static LastHubState initial value 1 (assumed high)
define static Initialized flag false
if Initialized is false
configure RB13 pin as digital input
set Initialized to true
end if
read CurrentHubState from RB13 input pin
if CurrentHubState is not equal to LastHubState
create new event
if CurrentHubState is 0
set event type to ES_GAME_ON // falling edge
else
set event type to ES_GAME_OFF // rising edge
end if
set event parameter to 0
post event to all services
update LastHubState to CurrentHubState
return true
end if
update LastHubState to CurrentHubState
return false
Module-level variables: MyPriority, HubDriveState_t [Enum](HubHighZ, HubLowDrive), CurrentHubState
Function InitPlayerService(Priority)
set MyPriority to Priority
Post ES_INIT event to this queue
Return true if the event posted successfully, false otherwise
Function PostPlayerService(ThisEvent)
Post event to this queue
Function RunPlayerService(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
switch (ThisEvent.EventType)
case ES_INIT:
// nothing to do, already initialized
// next two cases do the same actions
case ES_DIR_CHANGE:
case ES_BUTTON_DOWN:
if (CurrentHubState is NOT HubLowDrive)
set HUB_IN_P_OUT_PIN to output
set HUB_IN_P_OUT_PIN to low
set CurrentHubState to HubLowDrive
Start PLAYER_ACTIVE_TIMER for 100 ms
end if
case ES_TIMEOUT:
if (ThisEvent.EventParam is PLAYER_ACTIVE_TIMER)
if (CurrentHubState != HubHighZ)
set HUB_IN_P_OUT_PIN to input
set CurrentHubState to HubHighZ
end if
end if
return ReturnEvent
Module-level variables: CurrentAngle, CurrentSpeed, CurrentServoState, ScanDirection
Defined States: Servo_Normal, Servo_GameOff
Function InitServoService(Priority)
set MyPriority to Priority
Initialize ADC on AN0 (RA0)
Initialize and setup PWM for the servo
Start servo centered
Start SERVO_TIMER for 10 ms
Post ES_INIT event to this queue
Function PostServoService(ThisEvent)
Post event to this queue
Function RunServoService(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
switch (ThisEvent.EventType)
case ES_INIT:
set CurrentServoState to Servo_GameOff
case ES_GAME_OFF:
set CurrentServoState to Servo_GameOff
set ScanDirection to 2
set CurrentSpeed to 0
case ES_GAME_ON:
set CurrentAngle to center (90deg)
set CurrentServoState to Servo_Normal
case ES_DIR_CHANGE:
if (CurrentServoState is Servo_Normal)
set CurrentSpeed to the integer from ThisEvent.EventParam
end if
case ES_TIMEOUT:
Restart SERVO_TIMER for 10 ms
if (CurrentServoState is Servo_Normal)
Update CurrentAngle using CurrentSpeed (increase or decrease)
else if (CurrentServoState is Servo_GameOff)
CurrentAngle += ScanDirection
if (CurrentAngle passed its max value)
set CurrentAngle to its max value
set ScanDirection = -2; // reverse
else if (CurrentAngle passed its min value)
set CurrentAngle to its min value
set ScanDirection = 2; // reverse
end if
end if
set the PWM to the appropriate value using the current angle and the PWMOperate_SetPulseWidthOnChannel function
return ReturnEvent
Module-level variables: MyPriority, CurrentState, AmmoCount
Defined States: InitLasetState, Waiting, Shooting, Off
Function InitLaserFSM(Priority)
set MyPriority to Priority
set CurrentState to InitLasetState
Post ES_INIT event to this queue
Return true if the event posted successfully, false otherwise
Function PostLaserFSM(ThisEvent)
Post event to this queue
Function RunLaserFSM(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
set NextState to CurrentState
// ANY STATE -> Off
if (The EventType is ES_GAME_OFF)
Set Laser to 0
Set Laser LED to 0
Update AmmoCount to the max ammo value
Update ammo neopixels
Start RELOAD_TIMER for 100 ms
Set CurrentState to Off
return ReturnEvent;
switch (CurrentState):
case InitLaserState:
if (ThisEvent.EventType is ES_INIT)
Initialize hardware (laser and laser LED)
Set AmmoCount to the max value of ammo
Set RELOAD_TIMER for 100 ms
Set CurrentState to Off
case Off:
Set the laser off
Set the laser LED off
Reset ammo
Start RELOAD_TIMER for 100 ms
// Animate ammo display
if (ThisEvent.EventType is an ES_TIMEOUT RELOAD_TIMER
Increase the AmmoCount to the respective value for animation
endif
// Game on -> Waiting
if (ThisEvent.EventType is ES_GAME_ON) {
Set ammo neopixels to full and displace them
Update neopixels
Post ES_SFX_START to Sfx FSM
Set CurrentState to Waiting
endif
case Waiting:
if (ThisEvent.EventType is ES_BUTTON_DOWN)
if AmmoCount is greater than 0
Set laser on to shoot and set laser LED on to show shooting
Decrease ammo count by one
Update the ammo neopixels
Post ES_SFX_SHOOT to Sfx FSM
Start SHOOT_TIMER for the shooting duration
Start RELOAD_TIMER for 1 sec
Set CurrentState to Shooting
end if
else if (ThisEvent.EventType is ES_TIMEOUT for RELOAD_TIMER)
Start RELOAD_TIMERfor 1000 ms
if (AmmoCount is less than the max value)
Increase ammo count by 1
Update ammo neopixels
UpdateAmmoDisplay();
case Shooting:
if (ThisEvent.EventType is ES_TIMEOUT)
if (ThisEvent.EventParam is SHOOT_TIMER)
Set laser and laser LED to off
Set CurrentState to Waiting
else if (ThisEvent.EventParam is RELOAD_TIMER)
if AmmoCount is less than max
Increase the value and update neopixel display
return ReturnEvent
Module-level variables: MyPriority, CurrentState
Defined States: InitSfxState, GameOn, GameOff
Function InitSfxFSM(Priority)
set MyPriority to Priority
set CurrentState to InitSfxState
Post ES_INIT event to this queue
Return true if the event posted successfully, false otherwise
Function PostSfxFSM(ThisEvent)
Post event to this queue
Function RunSfxFSM(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
switch (ThisEvent.EventType)
case ES_INIT:
Initialize hardware pins (pins for each audio sfx)
Set music line low
Set currentState to Game Off
case ES_GAME_ON:
Release music line, set high
Set currentState to Game On
case ES_GAME_OFF:
Set music line low
Set CurrentState to GameOff
case ES_TIMEOUT:
// Release line after pulse
if (ThisEvent.EventParam is SFX_START_TIMER) set SFX_START high
else if (ThisEvent.EventParam == SFX_END_TIMER) set SFX_END high
else if (ThisEvent.EventParam == SFX_SHOOT_TIMER) set SFX_SHOOT high
case ES_SFX_START:
if (CurrentState is GameOn)
Set start line low for start music
Start SFX_START_TIMER for 125ms to pulse line
case ES_SFX_END:
if (CurrentState is GameOn)
Set endline low for end music
Start SFX_END_TIMER for 125ms to pulse line
case ES_SFX_SHOOT:
if (CurrentState is GameOn)
Set shootline low for “pew” sound
Start SFX_SHOOT_TIMER for 125ms to pulse line
return ReturnEvent
/***************************************************************************
Module: WS2812.c
Description:
Bit-banged WS2812 (NeoPixel) driver using cycle-accurate timing
***************************************************************************/
DEFINE MAX_LEDS = 5
DEFINE WS_PIN as output pin A4
DEFINE WS_TRIS as direction register for pin A4
create array leds_buffer of size (MAX_LEDS * 3) // stores G,R,B per LED
/***************************************************************************
Initialization
***************************************************************************/
Function neopixel_init()
set WS_TRIS to OUTPUT
set WS_PIN to LOW
/***************************************************************************
Send a single WS2812 bit with precise timing
bit = 1 → longer high pulse
bit = 0 → shorter high pulse
(timing controlled with NOP loops)
***************************************************************************/
Function send_bit(bit)
if bit is 1
set WS_PIN HIGH
wait approximately 700ns using NOP loops
set WS_PIN LOW
wait approximately 600ns using NOP loops
else // bit is 0
set WS_PIN HIGH
wait approximately 350ns using NOP loops
set WS_PIN LOW
wait approximately 800ns using NOP loops
end if
/***************************************************************************
Send one byte of color data (MSB first)
***************************************************************************/
Function send_color_byte(color_byte)
for i from 7 down to 0
extract bit i from color_byte
call send_bit(bit)
/***************************************************************************
Send the entire LED buffer to the LED strip
Order per LED: G, R, B
After sending all data, hold LOW for >50us to latch
***************************************************************************/
Function neopixel_show()
for each LED index i from 0 to MAX_LEDS-1
send_color_byte( leds_buffer[i*3 + 0] ) // Green
send_color_byte( leds_buffer[i*3 + 1] ) // Red
send_color_byte( leds_buffer[i*3 + 2] ) // Blue
// latch and reset
set WS_PIN LOW
wait at least 50 microseconds (implemented as long NOP loop)
/***************************************************************************
Public: Set a single LED's color
***************************************************************************/
Function neopixel_set_pixel(index, r, g, b)
if index is outside range 0..MAX_LEDS-1
return
end if
leds_buffer[index*3 + 0] = g
leds_buffer[index*3 + 1] = r
leds_buffer[index*3 + 2] = b
/***************************************************************************
Public: Clear all LEDs
***************************************************************************/
Function neopixel_clear()
for i from 0 to (MAX_LEDS*3)-1
leds_buffer[i] = 0
See Github for the following:
DM_Display (from class)
PIC32_SPI_HAL (from class)
PIC32_AD_LIB (provided by instructor team)
PWM_PIC32 (provided by instructor team)