Inspired by the classic design of traditional arcade machines, this chassis serves as the backbone of our game, housing all the essential components that bring it to life. It contains the LED display matrix, electronic components, rail mechanism, coin slot system, and the conveyor mechanism that drive the game's interactive features.
We laser cut ⅛” thick duron for each part of this design, using box joints to carefully align each component. This not only ensured perfect fit and stability but also allowed for easy assembly and disassembly throughout the build. This material was chosen for its cost-effectiveness and flexibility. Its durability and ease of handling allowed for multiple revisions, ensuring the final build was both functional and efficient.
Our rail system consists of five key components: the rail, stoppers, slider, guides, and the limit switch holder (UFO). To streamline prototyping, we made the UFO and slider modular. This approach allowed us to print only the sections that needed improvement, rather than reprinting the entire assembly, saving time. Separating the parts in this way offers several advantages: it enables faster iteration, reduces material waste, and allows for easier customization and testing of individual components. The modular design also simplifies disassembly and testing of different limit switch attachments, making debugging and optimization more efficient.
The steering wheel is designed in the shape of a UFO and is fastened atop a cylinder that rotates freely within a nested hollow cylinder. The wheel features a gap where a string sits, connecting it to two shoulder screws and the sliding UFO. This setup allows the rotational motion of the wheel to be converted into the horizontal motion of the sliding UFO, creating a smooth and responsive control mechanism.
This system is designed to guide a poker chip along a path that aligns between a constant Red LED and an IR LED. Two carefully positioned holes securely house the LEDs, effectively blocking external light and ensuring uninterrupted performance. This design maximizes the LEDs' functionality while minimizing interference from ambient light.
Stretched between two shafts is a canvas adorned with hand-sewn acrylic astronauts and asteroids. Each asteroid is 0.125 inches thick, while each astronaut is 0.25 inches thick. This setup allows us to strategically activate one of the two limit switches, each with varying lengths. The canvas rotates around the two shafts, which are constructed by press-fitting duron circles onto a shaft and then wrapping them with a layer of corrugated cardboard to increase friction and ensure smooth rotation. Each shaft is securely mounted to the duron using 5mm bearings for added stability and durability.
This mechanism uses a rubber band to keep the joystick in its default position, which is set to the furthest negative y position. This position corresponds to a conveyor speed of 0, ensuring the system remains stationary when the joystick is at rest.
The motor drives the conveyor belt, which is essential for moving objects (such as astronauts) in the game. It is powered by a 5V supply and its speed is controlled via MOSFETs (Q1, Q2), which regulate the power sent to the motor based on input from the joystick. The motor’s operation is critical for gameplay, as it adjusts the speed of the conveyor in response to player commands
The joystick is used solely to control the speed of the conveyor. It operates on a 3.3V supply and features two analog axes (VRx and VRy) and a switch (SW). The vertical axis (VRy) controls the speed of the conveyor, with the player pushing the joystick forward to increase speed. The microcontroller processes the joystick's analog input to adjust the motor's speed, allowing players to control how fast the conveyor moves during the game.
The IR object sensor is used to detect the presence of a coin inserted into the system. It operates on a 5V power supply and functions by emitting infrared light via an IR emitter. When an object, such as a coin, interrupts the infrared light beam, the light is reflected back to the sensor. The sensor detects this reflection and sends a digital signal to the microcontroller (PIC32) to indicate that a coin has been inserted. This provides a reliable and quick method for detecting coin insertion, allowing the system to trigger the appropriate actions, such as registering the coin or advancing to the next step in the process.Additionally, a large red LED is used to visually indicate the status of coin insertion. The LED turns off when the coin is successfully detected, providing clear feedback to the user that the coin has been processed.
The limit switches are used to detect the presence of an astronaut or an asteroid within the game. When the UFO moves along its path, the limit switches are triggered as soon as an astronaut or asteroid makes contact with them. These switches send a digital signal to the microcontroller (PIC32), indicating the presence of the object. The microcontroller then responds accordingly, such as by registering the abduction of an astronaut or accounting for a collision with an asteroid. The limit switches ensure precise detection of these objects, enabling the game to react in real-time to the player's actions and interactions within the game environment.
The LED matrix display shows real-time game information, including the countdown timer and score. Powered by a 5V supply, it consists of a grid of LEDs controlled by the microcontroller (PIC32) through shift registers. The microcontroller updates the display to show the remaining time, score, and system status, using serial communication to efficiently control the LEDs.
Define constants:
ONE_SEC = 1000 // Timer duration in milliseconds
Define module-level variables:
MyPriority - priority of this service
inputText[64] - buffer for input text
CountDownText[64] - buffer for countdown text
ScoreText[64] - buffer for score text
textLength - length of the current text
StartGameCountDown - countdown value for game start
MyScore - current game score
textPointer[12], ScorePointer[12] - temporary buffers for formatting text
GAMESTART - boolean indicating if the game has started
currentState - current state of the service
Define static strings for different messages:
ReadyTextPointer = "READY.."
StartTextPointer = "LETS CRUSH IT!!"
VictoryTextPointer = "CROWM MY KING!!"
LoserTextPointer = "KEEP PUSHING!!"
LongIdlePointer = "Restarting..."
Coin12Pointer = "COIN: 1/2"
Function: InitDisplayerService(Priority)
Set service priority to Priority
Initialize currentState to NOT_GOSTATE
Print "Starting DisplayerService..."
Create an ES_INIT_DISPLAYER event
Post the event to the service queue
Return true if successful, otherwise false
Function: PostDisplayerService(ThisEvent)
Post ThisEvent to the service queue using the stored priority
Return true if successful, otherwise false
Function: RunDisplayerService(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
Switch on ThisEvent.EventType:
Case ES_INIT_DISPLAYER:
Print "Displayer Service Initialized"
Case ES_NEW_SCORE:
Update MyScore with ThisEvent.EventParam
Print the new score to console
Case ES_READY_COUNTDOWN:
Set StartGameCountDown from ThisEvent.EventParam (scaled down)
Format textPointer with countdown value
Format ScorePointer with current score
If GAMESTART is false:
Display the countdown number centered
If countdown reaches zero:
Set GAMESTART to true
Print "Game started"
Else if countdown is greater than zero:
Combine countdown and score into CountDownText
Display the combined text centered
Case ES_NEW_INPUT_TEXT:
Switch on ThisEvent.EventParam:
Case READY:
Copy ReadyTextPointer to inputText
Case GO:
Copy StartTextPointer to inputText
Update currentState to GOSTATE
Case COIN12:
Copy Coin12Pointer to inputText
Case END:
If MyScore >= 50:
Copy VictoryTextPointer to inputText
Else:
Copy LoserTextPointer to inputText
Case IDLE:
Copy LongIdlePointer to inputText
Calculate textLength for inputText
Display the inputText centered
Case ES_TIMEUP:
Clear inputText, CountDownText, ScoreText, textPointer, and ScorePointer
Reset GAMESTART to false
Reset currentState to NOT_GOSTATE
Print "Game ended. Resetting display."
Default:
Do nothing
Return ReturnEvent
Function: QueryDisplayerService()
Return currentState
/****************************************************************************
Module
DisplayerService.c
Description
A service to display input text centered on the LED matrix display.
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
#include "DisplayerService.h"
#include "DM_Display64.h"
#include "PIC32_SPI_HAL.h"
#include <string.h>
#include <stdio.h>
/*----------------------------- Module Defines ----------------------------*/
#define ONE_SEC 1000
/*---------------------------- Module Variables ---------------------------*/
static uint8_t MyPriority;
static char inputText[64];
static char CountDownText[64];
static char ScoreText[64];
static uint8_t textLength;
static uint16_t StartGameCountDown;
static uint16_t MyScore;
static char textPointer[12];
static char ScorePointer[12];
static char *ReadyTextPointer = "READY..";
static char *StartTextPointer = "LETS CRUSH IT!!";
static char *VictoryTextPointer = "CROWM MY KING!!";
static char *LoserTextPointer = "KEEP PUSHING!!";
static char *LongIdlePointer = "Restarting...";
static char *Coin12Pointer = "COIN: 1/2";
static bool GAMESTART = false;
static DisplayerState_t currentState;
/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
Function
InitDisplayerService
Description
Initializes the DisplayerService and prepares the display for centered text.
****************************************************************************/
bool InitDisplayerService(uint8_t Priority)
{
MyPriority = Priority;
currentState = NOT_GOSTATE;
puts("\rStarting DisplayerService...\r");
ES_Event_t ThisEvent = { .EventType = ES_INIT_DISPLAYER };
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
PostDisplayerService
Description
Posts an event to this service's queue.
****************************************************************************/
bool PostDisplayerService(ES_Event_t ThisEvent)
{
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
RunDisplayerService
Description
Handles events and displays the input text centered on the display.
****************************************************************************/
ES_Event_t RunDisplayerService(ES_Event_t ThisEvent)
{
ES_Event_t ReturnEvent = { .EventType = ES_NO_EVENT };
switch (ThisEvent.EventType)
{
case ES_INIT_DISPLAYER:
puts("Displayer Service Initialized\r");
break;
case ES_NEW_SCORE:
MyScore = ThisEvent.EventParam;
printf("New score received: %d\r\n", MyScore);
break;
case ES_READY_COUNTDOWN:
StartGameCountDown = ThisEvent.EventParam / 10;
sprintf(textPointer, "%02d", StartGameCountDown);
sprintf(ScorePointer, "SCORE:%04d", MyScore);
if (!GAMESTART)
{
strncpy(CountDownText, textPointer, sizeof(CountDownText) - 1);
textLength = strlen(CountDownText);
DM_CenterDisplayText(CountDownText, textLength, 2);
if (StartGameCountDown == 0)
{
GAMESTART = true;
puts("Game started in DisplayerService!\r");
}
}
else if (StartGameCountDown > 0)
{
snprintf(CountDownText, sizeof(CountDownText), "%s %s", textPointer, ScorePointer);
textLength = strlen(CountDownText);
DM_CenterDisplayText(CountDownText, textLength, 5);
}
break;
case ES_NEW_INPUT_TEXT:
switch (ThisEvent.EventParam)
{
case READY:
strncpy(inputText, ReadyTextPointer, sizeof(inputText) - 1);
break;
case GO:
strncpy(inputText, StartTextPointer, sizeof(inputText) - 1);
currentState = GOSTATE;
break;
case COIN12:
strncpy(inputText, Coin12Pointer, sizeof(inputText) - 1);
break;
case END:
strncpy(inputText, (MyScore >= 50) ? VictoryTextPointer : LoserTextPointer, sizeof(inputText) - 1);
break;
case IDLE:
strncpy(inputText, LongIdlePointer, sizeof(inputText) - 1);
break;
default:
break;
}
textLength = strlen(inputText);
DM_CenterDisplayText(inputText, textLength, 5);
break;
case ES_TIMEUP:
memset(inputText, 0, sizeof(inputText));
memset(CountDownText, 0, sizeof(CountDownText));
memset(ScoreText, 0, sizeof(ScoreText));
memset(textPointer, 0, sizeof(textPointer));
memset(ScorePointer, 0, sizeof(ScorePointer));
GAMESTART = false;
currentState = NOT_GOSTATE;
puts("Game ended. Resetting display.\r");
break;
default:
break;
}
return ReturnEvent;
}
/****************************************************************************
Function
QueryDisplayerService
Description
Returns the current state of the DisplayerService.
****************************************************************************/
DisplayerState_t QueryDisplayerService(void)
{
return currentState;
}
/*
* File: DM_Display.h
* Author: Ed
*
* Created on July 15, 2021, 11:54 AM
* Updated 2023-10-18 Aligned DM_AddChar2DisplayBuffer
*/
#ifndef DM_DISPLAY_H
#define DM_DISPLAY_H
/****************************************************************************
Function
DM_TakeInitDisplayStep
Parameter
None
Returns
bool: true when there are no more initialization steps to perform; false
while there still more steps to be taken
Description
Initializes the MAX7219 4-module display performing 1 step for each call:
First, bring put it in shutdown to disable all displays, return false
Next fill the display RAM with Zeros to insure blanked, return false
Then Disable Code B decoding for all digits, return false
Then, enable scanning for all digits, return false
The next setup step is to set the brightness to minimum, return false
Copy our display buffer to the display, return false
Finally, bring it out of shutdown and return true
Example
while ( false == DM_TakeInitDisplayStep() )
{} // note this example is for non-event-driven code
****************************************************************************/
bool DM_TakeInitDisplayStep( void );
/****************************************************************************
Function
DM_ClearDisplayBuffer
Parameter
None
Returns
Nothing (void)
Description
Clears the contents of the display buffer.
Example
BDM_ClearDisplayBuffer();
****************************************************************************/
void DM_ClearDisplayBuffer( void );
/****************************************************************************
Function
DM_ScrollDisplayBuffer
Parameter
uint8_t: The number of Columns to scroll
Returns
Nothing (void)
Description
Scrolls the contents of the display buffer by the indicated number of
columns.
Example
DM_ScrollDisplayBuffer(4);
****************************************************************************/
void DM_ScrollDisplayBuffer( uint8_t NumCols2Scroll);
/****************************************************************************
Function
DM_TakeDisplayUpdateStep
Parameter
None
Returns
bool: true when all rows have been copied to the display; false otherwise
Description
Copies the contents of the display buffer to the MAX7219 controllers 1 row
per call.
Example
while (false == DM_TakeDisplayUpdateStep())
{} // note this example is for non-event-driven code
****************************************************************************/
bool DM_TakeDisplayUpdateStep( void );
/****************************************************************************
Function
DM_AddChar2DisplayBuffer
Parameter
unsigned char: The character to be added to the display
Returns
Nothing (void)
Description
Copies the bitmap data from the font file into the rows of the frame buffer
at the right-most character position in the buffer
Example
DM_AddChar2DisplayBuffer('A');
****************************************************************************/
void DM_AddChar2DisplayBuffer( unsigned char Char2Display);
/****************************************************************************
Function
DM_PutDataIntoBufferRow
Parameter
uint32_t: The new row data to be stored in the display buffer
uint8_t: The row (0->7) into which the data will be stored.
Returns
bool: true for a legal row number; false otherwise
Description
Copies the raw data from the Data2Insert parameter into the specified row
of the frame buffer
Example
DM_PutDataInBufferRow(0x00000001, 0);
****************************************************************************/
bool DM_PutDataIntoBufferRow( uint64_t Data2Insert, uint8_t WhichRow);
/****************************************************************************
Function
DM_QueryRowData
Parameter
uint8_t: The row of the display buffer to be queried
uint32_t *: pointer to variable to hold the data from the buffer
Returns
bool: true for a legal row number; false otherwise
Description
copies the contents of the specified row of the frame buffer into the
location pointed to by pReturnValue
Example
DM_QueryRowData(0,&ReturnedValue);
****************************************************************************/
bool DM_QueryRowData( uint8_t RowToQuery, uint64_t * pReturnValue);
#endif /* DM_DISPLAY_H */
### Module: EventCheckers
### Function: Check4Keystroke
- Purpose: Check for new keystroke inputs from the keyboard.
- Steps:
1. If `IsNewKeyReady()`:
- Create an event of type `ES_NEW_KEY` with `GetNewKey()` as parameter.
- Post the event to all services.
- Return `true`.
2. Otherwise, return `false`.
---
### Function: Check4IRRiseFall
- Purpose: Detect changes in the state of the IR sensor for coin detection.
- Steps:
1. Initialize `lastInputState` to `HI` (static variable).
2. Read `SW1` as `currentInputState`.
3. If `currentInputState` differs from `lastInputState`:
- If `currentInputState` is `LO`:
- Print "Coin detected!".
- Create an event of type `ES_COININPUT` with the current time as parameter.
- Post the event to `CoinInsertionService`.
- Set `ReturnVal` to `true`.
- Update `lastInputState` to `currentInputState`.
4. Return `ReturnVal`.
---
### Function: CheckForJoystickSignal
- Purpose: Monitor joystick ADC values for significant changes.
- Steps:
1. Initialize `LastJoystickValue` to `0` (static variable).
2. Allocate `adcResults[MAX_CHANNELS]` array.
3. If `ADC_ConfigAutoScan(JOYSTICK_ADC_CHANNEL)` fails, return `false`.
4. Perform `ADC_MultiRead(adcResults)`.
5. Store the result of channel 0 in `CurrentJoystickValue`.
6. If `abs(CurrentJoystickValue - LastJoystickValue)` exceeds `CHANGE_THRESHOLD`:
- Create and post an event `ES_JoystickUpdate` with `CurrentJoystickValue` to `JoyStickService`.
- Create and post an event `ES_MOTOR_WORKING` with `CurrentJoystickValue` to `MotorService`.
- Update `LastJoystickValue`.
- Return `true`.
7. Otherwise, return `false`.
---
### Function: Check4Switch1
- Purpose: Detect state changes on `LIMIT_SW1`.
- Steps:
1. Initialize `lastInput1State` to `0` (static variable).
2. Read `LIMIT_SW1` as `currentInput1State`.
3. If `currentInput1State` differs from `lastInput1State`:
- If `currentInput1State` is `LO`:
- Print "Button1 PRESSED".
- Create an event `ES_PersonPickedUp` with the current time as parameter.
- Post the event to `ScoreService`.
- Set `ReturnVal` to `true`.
- Update `lastInput1State` to `currentInput1State`.
4. Return `ReturnVal`.
---
### Function: Check4Switch2
- Purpose: Detect state changes on `LIMIT_SW2`.
- Steps:
1. Initialize `lastInput2State` to `0` (static variable).
2. Read `LIMIT_SW2` as `currentInput2State`.
3. If `currentInput2State` differs from `lastInput2State`:
- If `currentInput2State` is `LO`:
- Print "Button2 PRESSED".
- Create an event `ES_RockCollision` with the current time as parameter.
- Post the event to `ScoreService`.
- Set `ReturnVal` to `true`.
- Update `lastInput2State` to `currentInput2State`.
4. Return `ReturnVal`.
/****************************************************************************
Module
EventCheckers.c
Description
This module contains event checkers for various hardware inputs and conditions,
using the Gen2 Events and Services Framework.
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "ES_Events.h"
#include "ES_Port.h"
#include "EventCheckers.h"
#include "PIC32_AD_Lib.h"
#include "ES_Timer.h"
#include "MotorService.h"
#include "JoyStickService.h"
#include "CoinInsertionService.h"
#include "ScoreService.h"
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h> // For abs function
/*----------------------------- Module Defines -----------------------------*/
#define SW1 PORTAbits.RA2
#define HI 1
#define LO 0
#define JOYSTICK_ADC_CHANNEL BIT12HI
#define CHANGE_THRESHOLD 10
#define IDLE_THRESHOLD 900
#define MAX_CHANNELS 8
#define LIMIT_SW1 PORTAbits.RA4
#define LIMIT_SW2 PORTBbits.RB4
/*---------------------------- Module Variables ---------------------------*/
// None needed for this module
/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
Function
Check4Keystroke
Description
Checks for a new keystroke from the keyboard and posts an event if detected.
Returns
bool - true if a new key event was detected, false otherwise.
****************************************************************************/
bool Check4Keystroke(void)
{
if (IsNewKeyReady())
{
ES_Event_t ThisEvent = { .EventType = ES_NEW_KEY, .EventParam = GetNewKey() };
ES_PostAll(ThisEvent);
return true;
}
return false;
}
/****************************************************************************
Function
Check4IRRiseFall
Description
Detects changes in the state of an IR sensor (e.g., coin detection) and posts
an event if a change is detected.
Returns
bool - true if a change in state was detected, false otherwise.
****************************************************************************/
bool Check4IRRiseFall(void)
{
static bool lastInputState = HI;
bool currentInputState = SW1;
bool ReturnVal = false;
if (currentInputState != lastInputState)
{
if (currentInputState == LO)
{
DB_printf("Coin detected!\n");
ES_Event_t CoinInputEvent = { .EventType = ES_COININPUT, .EventParam = ES_Timer_GetTime() };
PostCoinInsertionService(CoinInputEvent);
ReturnVal = true;
}
lastInputState = currentInputState;
}
return ReturnVal;
}
/****************************************************************************
Function
CheckForJoystickSignal
Description
Checks for significant changes in joystick ADC values and posts events
for joystick updates and motor operation.
Returns
bool - true if a significant joystick signal was detected, false otherwise.
****************************************************************************/
bool CheckForJoystickSignal(void)
{
static uint16_t LastJoystickValue = 0;
uint32_t adcResults[MAX_CHANNELS];
bool SignalDetected = false;
if (!ADC_ConfigAutoScan(JOYSTICK_ADC_CHANNEL))
return false;
ADC_MultiRead(adcResults);
uint16_t CurrentJoystickValue = (uint16_t)adcResults[0];
if (abs(CurrentJoystickValue - LastJoystickValue) > CHANGE_THRESHOLD)
{
ES_Event_t JoystickEvent = { .EventType = ES_JoystickUpdate, .EventParam = CurrentJoystickValue };
PostJoyStickService(JoystickEvent);
ES_Event_t MotorEvent = { .EventType = ES_MOTOR_WORKING, .EventParam = CurrentJoystickValue };
PostMotorService(MotorEvent);
SignalDetected = true;
LastJoystickValue = CurrentJoystickValue;
}
return SignalDetected;
}
/****************************************************************************
Function
Check4Switch1
Description
Detects state changes on LIMIT_SW1 and posts an event if a button press
is detected.
Returns
bool - true if a button press was detected, false otherwise.
****************************************************************************/
bool Check4Switch1(void)
{
static bool lastInput1State = 0;
bool currentInput1State = LIMIT_SW1;
bool ReturnVal = false;
if (currentInput1State != lastInput1State)
{
if (currentInput1State == LO)
{
DB_printf("Button1 PRESSED\n");
ES_Event_t ThisEvent = { .EventType = ES_PersonPickedUp, .EventParam = ES_Timer_GetTime() };
PostScoreService(ThisEvent);
ReturnVal = true;
}
lastInput1State = currentInput1State;
}
return ReturnVal;
}
/****************************************************************************
Function
Check4Switch2
Description
Detects state changes on LIMIT_SW2 and posts an event if a button press
is detected.
Returns
bool - true if a button press was detected, false otherwise.
****************************************************************************/
bool Check4Switch2(void)
{
static bool lastInput2State = 0;
bool currentInput2State = LIMIT_SW2;
bool ReturnVal = false;
if (currentInput2State != lastInput2State)
{
if (currentInput2State == LO)
{
DB_printf("Button2 PRESSED\n");
ES_Event_t ThisEvent = { .EventType = ES_RockCollision, .EventParam = ES_Timer_GetTime() };
PostScoreService(ThisEvent);
ReturnVal = true;
}
lastInput2State = currentInput2State;
}
return ReturnVal;
}
/****************************************************************************
Module
ES_EventCheckWrapper.h
Description
This is a wrapper header file for all of the header files that include
prototypes for event checking functions.
Notes
History
When Who What/Why
-------------- --- --------
12/19/16 20:12 jec Started coding
*****************************************************************************/
#ifndef ES_EventCheckWrapper_H
#define ES_EventCheckWrapper_H
// This is the header for the event checkers for the template project
#include "EventCheckers.h"
// Here you would #include the header files for any other modules that
// contained event checking functions
#endif // ES_EventCheckWrapper_H
/****************************************************************************
Module
EventCheckers.h
Description
header file for the event checking functions
Notes
History
When Who What/Why
-------------- --- --------
10/18/15 11:50 jec added #include for stdint & stdbool
08/06/13 14:37 jec started coding
*****************************************************************************/
#ifndef EventCheckers_H
#define EventCheckers_H
// the common headers for C99 types
#include <stdint.h>
#include <stdbool.h>
// prototypes for event checkers
bool Check4Keystroke(void);
bool CheckForJoystickSignal(void);
bool Check4IRRiseFall(void);
bool Check4Switch1(void);
bool Check4Switch2(void);
#endif /* EventCheckers_H */
Define constants:
NUM_ROWS = 8
NumModules = 8
DM_START_SHUTDOWN = 0x0C00
DM_END_SHUTDOWN = 0x0C01
DM_DISABLE_CODEB = 0x0900
DM_ENABLE_SCAN = 0x0B07
DM_SET_BRIGHT = 0x0A05
Define data structures:
DM_Row_t - Union to represent rows of the display (64-bit or byte-by-byte).
InitStep_t - Enum for initialization steps.
Global variables:
DM_Display - Buffer to hold display data.
CurrentInitStep - Tracks the current initialization step.
Function: DM_TakeInitDisplayStep()
Perform the next initialization step based on CurrentInitStep:
- Shutdown the display.
- Clear the buffer.
- Disable Code B decoding.
- Enable digit scanning.
- Set brightness.
- Copy buffer data to display.
- End shutdown and complete initialization.
Return true if initialization is complete.
Function: DM_TakeDisplayUpdateStep()
Update one row of the display using the buffer.
Return true if all rows are updated.
Function: DM_ScrollDisplayBuffer(NumCols2Scroll)
Shift all rows in DM_Display to the left by NumCols2Scroll.
Function: DM_AddChar2DisplayBuffer(Char2Display)
Add bitmap data for Char2Display to the rightmost column of DM_Display.
Function: DM_ClearDisplayBuffer()
Set all rows in DM_Display to zero.
Function: DM_PutDataIntoBufferRow(Data2Insert, WhichRow)
Insert Data2Insert into the specified row of DM_Display.
Function: sendCmd(Cmd2Send)
Send a command to all MAX7219 modules via SPI.
Function: sendRow(RowNum, RowData)
Send a specific row of data to the MAX7219 display modules.
Additional text-related functions:
- DM_CenterDisplayText
- DM_DisplayScoreText
Use these functions to manipulate and center text on the display.
/****************************************************************************
Module
DM_Display64.c
Description
Source file for the Dot Matrix LED Hardware Abstraction Layer (HAL)
used in ME218.
Notes
This module controls an 8x8 LED dot matrix display using the MAX7219.
History
When Who What/Why
-------------- --- --------
10/03/21 12:32 jec Started coding.
2023-10-18 klg Aligned DM_AddChar2DisplayBuffer.
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
#include <xc.h>
#include <stdbool.h>
#include "PIC32_SPI_HAL.h"
#include "DM_Display64.h"
#include "FontStuff.h"
/*----------------------------- Module Defines ----------------------------*/
#define NumModules 8
#define NUM_ROWS 8
#define NUM_ROWS_IN_FONT 3
#define DM_START_SHUTDOWN 0x0C00
#define DM_END_SHUTDOWN 0x0C01
#define DM_DISABLE_CODEB 0x0900
#define DM_ENABLE_SCAN 0x0B07
#define DM_SET_BRIGHT 0x0A05
/*---------------------------- Module Types ---------------------------*/
typedef union {
uint64_t FullRow;
uint8_t ByBytes[NumModules];
} DM_Row_t;
typedef enum {
DM_StepStartShutdown = 0,
DM_StepFillBufferZeros,
DM_StepDisableCodeB,
DM_StepEnableScanAll,
DM_StepSetBrightness,
DM_StepCopyBuffer2Display,
DM_StepEndShutdown
} InitStep_t;
/*---------------------------- Module Variables ---------------------------*/
static DM_Row_t DM_Display[NUM_ROWS];
static InitStep_t CurrentInitStep = DM_StepStartShutdown;
/*---------------------------- Module Functions ---------------------------*/
static void sendCmd(uint16_t Cmd2Send);
static void sendRow(uint8_t RowNum, DM_Row_t RowData);
/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
Function
DM_TakeInitDisplayStep
Description
Initializes the MAX7219 display module, performing one step per call.
Returns
bool - true if initialization is complete, false otherwise.
****************************************************************************/
bool DM_TakeInitDisplayStep(void) {
static uint8_t rowIndex = 0;
bool ReturnVal = false;
switch (CurrentInitStep) {
case DM_StepStartShutdown:
sendCmd(DM_START_SHUTDOWN);
CurrentInitStep++;
break;
case DM_StepFillBufferZeros:
DM_ClearDisplayBuffer();
CurrentInitStep++;
break;
case DM_StepDisableCodeB:
sendCmd(DM_DISABLE_CODEB);
CurrentInitStep++;
break;
case DM_StepEnableScanAll:
sendCmd(DM_ENABLE_SCAN);
CurrentInitStep++;
break;
case DM_StepSetBrightness:
sendCmd(DM_SET_BRIGHT);
CurrentInitStep++;
break;
case DM_StepCopyBuffer2Display:
if (DM_TakeDisplayUpdateStep()) {
CurrentInitStep++;
}
break;
case DM_StepEndShutdown:
sendCmd(DM_END_SHUTDOWN);
CurrentInitStep = DM_StepStartShutdown;
ReturnVal = true;
break;
default:
break;
}
return ReturnVal;
}
/****************************************************************************
Function
DM_TakeDisplayUpdateStep
Description
Updates the display row-by-row using the current buffer.
Returns
bool - true if all rows are updated, false otherwise.
****************************************************************************/
bool DM_TakeDisplayUpdateStep(void) {
static int8_t WhichRow = 0;
bool ReturnVal = false;
sendRow(WhichRow, DM_Display[WhichRow]);
WhichRow++;
if (WhichRow == NUM_ROWS) {
ReturnVal = true;
WhichRow = 0;
}
return ReturnVal;
}
/****************************************************************************
Function
DM_ScrollDisplayBuffer
Description
Scrolls the contents of the display buffer left by the specified columns.
****************************************************************************/
void DM_ScrollDisplayBuffer(uint8_t NumCols2Scroll) {
for (uint8_t row = 0; row < NUM_ROWS; row++) {
DM_Display[row].FullRow <<= NumCols2Scroll;
}
}
/****************************************************************************
Function
DM_AddChar2DisplayBuffer
Description
Adds a character to the rightmost position of the display buffer.
****************************************************************************/
void DM_AddChar2DisplayBuffer(unsigned char Char2Display) {
for (uint8_t row = 0; row < NUM_ROWS; row++) {
DM_Display[row].ByBytes[0] |= getFontLine(Char2Display, row);
}
}
/****************************************************************************
Function
DM_ClearDisplayBuffer
Description
Clears the display buffer by setting all rows to zero.
****************************************************************************/
void DM_ClearDisplayBuffer(void) {
for (uint8_t row = 0; row < NUM_ROWS; row++) {
DM_Display[row].FullRow = 0;
}
}
/****************************************************************************
Function
DM_PutDataIntoBufferRow
Description
Inserts data into a specific row of the display buffer.
Returns
bool - true if the operation is successful, false otherwise.
****************************************************************************/
bool DM_PutDataIntoBufferRow(uint64_t Data2Insert, uint8_t WhichRow) {
if (WhichRow < NUM_ROWS) {
DM_Display[WhichRow].FullRow = Data2Insert;
return true;
}
return false;
}
/****************************************************************************
Function
sendCmd
Description
Sends a command to all MAX7219 modules.
****************************************************************************/
static void sendCmd(uint16_t Cmd2Send) {
for (uint8_t i = 0; i < (NumModules - 1); i++) {
SPIOperate_SPI1_Send16(Cmd2Send);
}
SPIOperate_SPI1_Send16Wait(Cmd2Send);
}
/****************************************************************************
Function
sendRow
Description
Sends a single row of data to the MAX7219 display.
****************************************************************************/
static void sendRow(uint8_t RowNum, DM_Row_t RowData) {
RowNum = NUM_ROWS - (RowNum + 1);
for (uint8_t i = 0; i < (NumModules - 1); i++) {
SPIOperate_SPI1_Send16((((uint16_t)(RowNum + 1)) << 8) |
BitReverseTable256[RowData.ByBytes[i]]);
}
SPIOperate_SPI1_Send16Wait((((uint16_t)(RowNum + 1)) << 8) |
BitReverseTable256[RowData.ByBytes[NumModules - 1]]);
}
/* Additional Text Manipulation Functions */
// These include DM_CenterDisplayText and other related functions for text alignment and updates.
/*
* File: DM_Display.h
* Author: Ed
*
* Created on July 15, 2021, 11:54 AM
* Updated 2023-10-18 Aligned DM_AddChar2DisplayBuffer
*/
#ifndef DM_DISPLAY_H
#define DM_DISPLAY_H
/****************************************************************************
Function
DM_TakeInitDisplayStep
Parameter
None
Returns
bool: true when there are no more initialization steps to perform; false
while there still more steps to be taken
Description
Initializes the MAX7219 4-module display performing 1 step for each call:
First, bring put it in shutdown to disable all displays, return false
Next fill the display RAM with Zeros to insure blanked, return false
Then Disable Code B decoding for all digits, return false
Then, enable scanning for all digits, return false
The next setup step is to set the brightness to minimum, return false
Copy our display buffer to the display, return false
Finally, bring it out of shutdown and return true
Example
while ( false == DM_TakeInitDisplayStep() )
{} // note this example is for non-event-driven code
****************************************************************************/
bool DM_TakeInitDisplayStep( void );
/****************************************************************************
Function
DM_ClearDisplayBuffer
Parameter
None
Returns
Nothing (void)
Description
Clears the contents of the display buffer.
Example
BDM_ClearDisplayBuffer();
****************************************************************************/
void DM_ClearDisplayBuffer( void );
/****************************************************************************
Function
DM_ScrollDisplayBuffer
Parameter
uint8_t: The number of Columns to scroll
Returns
Nothing (void)
Description
Scrolls the contents of the display buffer by the indicated number of
columns.
Example
DM_ScrollDisplayBuffer(4);
****************************************************************************/
void DM_ScrollDisplayBuffer( uint8_t NumCols2Scroll);
/****************************************************************************
Function
DM_TakeDisplayUpdateStep
Parameter
None
Returns
bool: true when all rows have been copied to the display; false otherwise
Description
Copies the contents of the display buffer to the MAX7219 controllers 1 row
per call.
Example
while (false == DM_TakeDisplayUpdateStep())
{} // note this example is for non-event-driven code
****************************************************************************/
bool DM_TakeDisplayUpdateStep( void );
/****************************************************************************
Function
DM_AddChar2DisplayBuffer
Parameter
unsigned char: The character to be added to the display
Returns
Nothing (void)
Description
Copies the bitmap data from the font file into the rows of the frame buffer
at the right-most character position in the buffer
Example
DM_AddChar2DisplayBuffer('A');
****************************************************************************/
void DM_AddChar2DisplayBuffer( unsigned char Char2Display);
/****************************************************************************
Function
DM_PutDataIntoBufferRow
Parameter
uint32_t: The new row data to be stored in the display buffer
uint8_t: The row (0->7) into which the data will be stored.
Returns
bool: true for a legal row number; false otherwise
Description
Copies the raw data from the Data2Insert parameter into the specified row
of the frame buffer
Example
DM_PutDataInBufferRow(0x00000001, 0);
****************************************************************************/
bool DM_PutDataIntoBufferRow( uint64_t Data2Insert, uint8_t WhichRow);
/****************************************************************************
Function
DM_QueryRowData
Parameter
uint8_t: The row of the display buffer to be queried
uint32_t *: pointer to variable to hold the data from the buffer
Returns
bool: true for a legal row number; false otherwise
Description
copies the contents of the specified row of the frame buffer into the
location pointed to by pReturnValue
Example
DM_QueryRowData(0,&ReturnedValue);
****************************************************************************/
bool DM_QueryRowData( uint8_t RowToQuery, uint64_t * pReturnValue);
#endif /* DM_DISPLAY_H */
/****************************************************************************
Module
ScoreService.c
Description
Service for handling scoring operations, managing states, and reacting
to events under the Gen2 framework.
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "ScoreService.h"
#include "DisplayerService.h"
#include "dbprintf.h"
/*----------------------------- Module Defines ----------------------------*/
#define ONE_SEC 1000
#define IDLE_BUTTON1_TIME (ONE_SEC * 0.5)
/*---------------------------- Module Variables ---------------------------*/
static ScoreState_t CurrentState; // Current state of the scoring service
static uint8_t MyPriority; // Priority of the service
static uint16_t MyScore; // Current score
static uint8_t Button1; // Button 1 state
static uint8_t Button2; // Button 2 state
/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
Function
InitScoreService
Parameters
uint8_t Priority - The service priority level
Returns
bool - true if initialization succeeds, false otherwise
Description
Initializes the scoring service and sets up initial states and variables.
****************************************************************************/
bool InitScoreService(uint8_t Priority)
{
MyPriority = Priority;
MyScore = 0;
CurrentState = InitScoringState;
// Configure buttons as inputs
ANSELAbits.ANSA0 = 0;
ANSELAbits.ANSA1 = 0;
TRISAbits.TRISA4 = 1;
TRISBbits.TRISB4 = 1;
Button1 = PORTAbits.RA4;
Button2 = PORTBbits.RB4;
// Post initial event
ES_Event_t ThisEvent = { .EventType = ES_INIT };
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
PostScoreService
Parameters
ES_Event_t ThisEvent - The event to post
Returns
bool - true if the event was successfully posted, false otherwise
Description
Posts an event to this service's queue.
****************************************************************************/
bool PostScoreService(ES_Event_t ThisEvent)
{
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
RunScoreService
Parameters
ES_Event_t ThisEvent - The event to process
Returns
ES_Event_t - ES_NO_EVENT if no errors, otherwise an error event
Description
Processes events and updates the state machine for scoring operations.
****************************************************************************/
ES_Event_t RunScoreService(ES_Event_t ThisEvent)
{
ES_Event_t ReturnEvent = { .EventType = ES_NO_EVENT };
switch (CurrentState)
{
case InitScoringState:
if (ThisEvent.EventType == ES_INIT)
{
// Remain in InitScoringState until further action
}
else if (ThisEvent.EventType == ES_GAME_START)
{
MyScore = 0; // Reset score
DB_printf("Game started, score reset to %u\n", MyScore);
CurrentState = PendingState;
// Notify DisplayerService of the new score
ES_Event_t NewEvent = { .EventType = ES_NEW_SCORE, .EventParam = MyScore };
PostDisplayerService(NewEvent);
}
break;
case PendingState:
if (ThisEvent.EventType == ES_RockCollision)
{
CurrentState = LosePointsState;
if (MyScore > 0)
{
MyScore -= 3;
}
else
{
DB_printf("CANNOT GO BELOW ZERO.\n");
}
DB_printf("New Score = %u\n", MyScore);
ES_Event_t NewEvent = { .EventType = ES_NEW_SCORE, .EventParam = MyScore };
PostDisplayerService(NewEvent);
}
else if (ThisEvent.EventType == ES_PersonPickedUp)
{
CurrentState = AddPointsState;
MyScore += 10;
DB_printf("New Score = %u\n", MyScore);
ES_Event_t NewEvent = { .EventType = ES_NEW_SCORE, .EventParam = MyScore };
PostDisplayerService(NewEvent);
}
else if (ThisEvent.EventType == ES_TIMEUP)
{
CurrentState = InitScoringState;
// Notify CountdownService of scoring termination
ES_Event_t ScoreEndEvent = { .EventType = ES_SCORE_TERMINATE };
PostCountdownService(ScoreEndEvent);
DB_printf("### Scoring Terminates now. ###\n");
}
break;
case LosePointsState:
if (ThisEvent.EventType == ES_LosePoint)
{
CurrentState = PendingState;
}
else if (ThisEvent.EventType == ES_TIMEUP)
{
CurrentState = InitScoringState;
ES_Event_t ScoreEndEvent = { .EventType = ES_SCORE_TERMINATE };
PostCountdownService(ScoreEndEvent);
DB_printf("### Scoring Terminates now. ###\n");
}
break;
case AddPointsState:
if (ThisEvent.EventType == ES_RockCollision)
{
CurrentState = PendingState;
}
else if (ThisEvent.EventType == ES_TIMEUP)
{
CurrentState = InitScoringState;
ES_Event_t ScoreEndEvent = { .EventType = ES_SCORE_TERMINATE };
PostCountdownService(ScoreEndEvent);
DB_printf("### Scoring Terminates now. ###\n");
}
break;
default:
break;
}
return ReturnEvent;
}
/****************************************************************************
Function
QueryScoreService
Parameters
None
Returns
ScoreState_t - The current state of the scoring service
Description
Returns the current state of the scoring service.
****************************************************************************/
ScoreState_t QueryScoreService(void)
{
return CurrentState;
}
/****************************************************************************
Module
ScoreService.c
Description
Service for handling scoring operations, managing states, and reacting
to events under the Gen2 framework.
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "ScoreService.h"
#include "DisplayerService.h"
#include "dbprintf.h"
/*----------------------------- Module Defines ----------------------------*/
#define ONE_SEC 1000
#define IDLE_BUTTON1_TIME (ONE_SEC * 0.5)
/*---------------------------- Module Variables ---------------------------*/
static ScoreState_t CurrentState; // Current state of the scoring service
static uint8_t MyPriority; // Priority of the service
static uint16_t MyScore; // Current score
static uint8_t Button1; // Button 1 state
static uint8_t Button2; // Button 2 state
/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
Function
InitScoreService
Parameters
uint8_t Priority - The service priority level
Returns
bool - true if initialization succeeds, false otherwise
Description
Initializes the scoring service and sets up initial states and variables.
****************************************************************************/
bool InitScoreService(uint8_t Priority)
{
MyPriority = Priority;
MyScore = 0;
CurrentState = InitScoringState;
// Configure buttons as inputs
ANSELAbits.ANSA0 = 0;
ANSELAbits.ANSA1 = 0;
TRISAbits.TRISA4 = 1;
TRISBbits.TRISB4 = 1;
Button1 = PORTAbits.RA4;
Button2 = PORTBbits.RB4;
// Post initial event
ES_Event_t ThisEvent = { .EventType = ES_INIT };
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
PostScoreService
Parameters
ES_Event_t ThisEvent - The event to post
Returns
bool - true if the event was successfully posted, false otherwise
Description
Posts an event to this service's queue.
****************************************************************************/
bool PostScoreService(ES_Event_t ThisEvent)
{
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
RunScoreService
Parameters
ES_Event_t ThisEvent - The event to process
Returns
ES_Event_t - ES_NO_EVENT if no errors, otherwise an error event
Description
Processes events and updates the state machine for scoring operations.
****************************************************************************/
ES_Event_t RunScoreService(ES_Event_t ThisEvent)
{
ES_Event_t ReturnEvent = { .EventType = ES_NO_EVENT };
switch (CurrentState)
{
case InitScoringState:
if (ThisEvent.EventType == ES_INIT)
{
// Remain in InitScoringState until further action
}
else if (ThisEvent.EventType == ES_GAME_START)
{
MyScore = 0; // Reset score
DB_printf("Game started, score reset to %u\n", MyScore);
CurrentState = PendingState;
// Notify DisplayerService of the new score
ES_Event_t NewEvent = { .EventType = ES_NEW_SCORE, .EventParam = MyScore };
PostDisplayerService(NewEvent);
}
break;
case PendingState:
if (ThisEvent.EventType == ES_RockCollision)
{
CurrentState = LosePointsState;
if (MyScore > 0)
{
MyScore -= 3;
}
else
{
DB_printf("CANNOT GO BELOW ZERO.\n");
}
DB_printf("New Score = %u\n", MyScore);
ES_Event_t NewEvent = { .EventType = ES_NEW_SCORE, .EventParam = MyScore };
PostDisplayerService(NewEvent);
}
else if (ThisEvent.EventType == ES_PersonPickedUp)
{
CurrentState = AddPointsState;
MyScore += 10;
DB_printf("New Score = %u\n", MyScore);
ES_Event_t NewEvent = { .EventType = ES_NEW_SCORE, .EventParam = MyScore };
PostDisplayerService(NewEvent);
}
else if (ThisEvent.EventType == ES_TIMEUP)
{
CurrentState = InitScoringState;
// Notify CountdownService of scoring termination
ES_Event_t ScoreEndEvent = { .EventType = ES_SCORE_TERMINATE };
PostCountdownService(ScoreEndEvent);
DB_printf("### Scoring Terminates now. ###\n");
}
break;
case LosePointsState:
if (ThisEvent.EventType == ES_LosePoint)
{
CurrentState = PendingState;
}
else if (ThisEvent.EventType == ES_TIMEUP)
{
CurrentState = InitScoringState;
ES_Event_t ScoreEndEvent = { .EventType = ES_SCORE_TERMINATE };
PostCountdownService(ScoreEndEvent);
DB_printf("### Scoring Terminates now. ###\n");
}
break;
case AddPointsState:
if (ThisEvent.EventType == ES_RockCollision)
{
CurrentState = PendingState;
}
else if (ThisEvent.EventType == ES_TIMEUP)
{
CurrentState = InitScoringState;
ES_Event_t ScoreEndEvent = { .EventType = ES_SCORE_TERMINATE };
PostCountdownService(ScoreEndEvent);
DB_printf("### Scoring Terminates now. ###\n");
}
break;
default:
break;
}
return ReturnEvent;
}
/****************************************************************************
Function
QueryScoreService
Parameters
None
Returns
ScoreState_t - The current state of the scoring service
Description
Returns the current state of the scoring service.
****************************************************************************/
ScoreState_t QueryScoreService(void)
{
return CurrentState;
}
Initialize global variables:
- `MyPriority`: Priority of the service
- `CurrentState`: Current state of the CountdownService
- `CountdownValue`: Countdown timer value (default: 61)
---
Function: InitCountdownService(Priority)
- Set `MyPriority` to Priority.
- Initialize `CurrentState` to `InitCountdown`.
- Set `CountdownValue` to 61.
- Post an `ES_INIT` event to the service queue.
- Return true if successful, false otherwise.
---
Function: PostCountdownService(Event)
- Post `Event` to the service queue.
- Return true if successful, false otherwise.
---
Function: RunCountdownService(Event)
- Initialize `ReturnEvent` as `ES_NO_EVENT`.
Switch (CurrentState):
Case `InitCountdown`:
If Event.Type == `ES_INIT`:
- Placeholder for any initialization logic.
Else If Event.Type == `ES_GAME_START`:
- Transition to `Waiting4AllServices2Terminate`.
- Start milestone timer with `MILESTONE_PERIOD`.
- Notify other services about game start if required.
Case `Waiting4AllServices2Terminate`:
If Event.Type == `ES_SCORE_TERMINATE` or `ES_MOTOR_TERMINATE`:
- Check if all dependent services (Motor and Score) are in initial states:
If both are in initial states:
- Print "Motor and Scoring Services Stopped."
- Post `ES_GAME_OVER` to all services.
- Reset `CurrentState` to `InitCountdown`.
Case `AllServicesCompleted`:
- Do nothing; no further actions needed.
Default:
- Do nothing for unknown states.
Return `ReturnEvent`.
---
Function: QueryCountdownService()
- Return the current state (`CurrentState`) of the service.
/****************************************************************************
Module
CountdownService.c
Description
Service for managing a countdown timer and coordinating state transitions
based on events, adhering to the Gen2 framework template.
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "CountdownService.h"
#include "MotorService.h"
#include "ScoreService.h"
/*----------------------------- Module Defines ----------------------------*/
#define MILESTONE_PERIOD 950 // Timer duration in milliseconds for countdown updates
/*---------------------------- Module Variables ---------------------------*/
// Current state of the CountdownService
static CountdownState_t CurrentState;
// Priority of this service
static uint8_t MyPriority;
// Countdown value
static uint8_t CountdownValue;
/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
Function
InitCountdownService
Parameters
uint8_t Priority - the priority of this service
Returns
bool - true if initialization was successful, false otherwise
Description
Initializes the CountdownService and sets up initial states and variables.
****************************************************************************/
bool InitCountdownService(uint8_t Priority)
{
MyPriority = Priority;
CurrentState = InitCountdown;
CountdownValue = 61; // Initialize countdown value
// Post the initial transition event
ES_Event_t ThisEvent = { .EventType = ES_INIT };
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
PostCountdownService
Parameters
ES_Event_t ThisEvent - the event to post
Returns
bool - true if the event was successfully posted, false otherwise
Description
Posts an event to this state machine's queue.
****************************************************************************/
bool PostCountdownService(ES_Event_t ThisEvent)
{
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
RunCountdownService
Parameters
ES_Event_t ThisEvent - the event to process
Returns
ES_Event_t - ES_NO_EVENT if no errors, or another event otherwise
Description
Runs the state machine logic for the CountdownService.
****************************************************************************/
ES_Event_t RunCountdownService(ES_Event_t ThisEvent)
{
ES_Event_t ReturnEvent = { .EventType = ES_NO_EVENT }; // Default return value
switch (CurrentState)
{
case InitCountdown:
if (ThisEvent.EventType == ES_INIT)
{
// Placeholder for initialization actions
}
else if (ThisEvent.EventType == ES_GAME_START)
{
CurrentState = Waiting4AllServices2Terminate;
// Start the milestone timer
ES_Timer_InitTimer(MILESTONE_TIMER, MILESTONE_PERIOD);
// TODO: Notify display service about game start (if required)
}
break;
case Waiting4AllServices2Terminate:
if (ThisEvent.EventType == ES_SCORE_TERMINATE || ThisEvent.EventType == ES_MOTOR_TERMINATE)
{
// Check if all required services have returned to their initial states
if (QueryMotorService() == InitMotorState && QueryScoreService() == InitScoringState)
{
DB_printf("#####Motor and Scoring Services Stopped.#####\n");
// Post game over event to all relevant services
ES_Event_t GameOverEvent = { .EventType = ES_GAME_OVER };
ES_PostAll(GameOverEvent);
// Reset state machine
CurrentState = InitCountdown;
}
}
break;
case AllServicesCompleted:
// Final state; no actions needed
break;
default:
break;
}
return ReturnEvent;
}
/****************************************************************************
Function
QueryCountdownService
Parameters
None
Returns
CountdownState_t - the current state of the CountdownService
Description
Returns the current state of the CountdownService.
****************************************************************************/
CountdownState_t QueryCountdownService(void)
{
return CurrentState;
}
/****************************************************************************
Header file for CountdownService
based on the Gen2 Events and Services Framework
****************************************************************************/
#ifndef CountdownService_H
#define CountdownService_H
// Event Definitions
#include "ES_Configure.h" /* gets us event definitions */
#include "ES_Types.h" /* gets bool type for returns */
// typedefs for the states
// State definitions for use with the query function
typedef enum
{
InitCountdown, CountdownUpdate, Waiting4AllServices2Terminate, AllServicesCompleted
} CountdownState_t;
// Public Function Prototypes
bool InitCountdownService(uint8_t Priority);
bool PostCountdownService(ES_Event_t ThisEvent);
ES_Event_t RunCountdownService(ES_Event_t ThisEvent);
CountdownState_t QueryCountdownService(void);
#endif /* CountdownService_H */
Initialize global variables:
- `MyPriority`: Service priority
- `GameCountDown`: Countdown timer for game (default: 60)
- `StartGameCountDown`: Pre-game countdown (default: 4)
- `GAMESTART`: Boolean indicating if the game has started
- `CurrentState`: Current state of the LED service
---
Function: InitMyCenteredLEDService(Priority)
- Set `MyPriority` to Priority.
- Scale `GameCountDown` and `StartGameCountDown` by frequency.
- Post an initial event (`ES_INIT_CENTRED`) to the service queue.
- Return true if successful, false otherwise.
---
Function: PostMyCenteredLEDService(Event)
- Post Event to the service queue.
- Return true if successful, false otherwise.
---
Function: RunMyCenteredLEDService(Event)
- Initialize `ReturnEvent` as `ES_NO_EVENT`.
Switch (Event.Type):
Case `ES_LONG_IDLE`:
- Post `ES_NEW_INPUT_TEXT` with `IDLE` to the display service.
- Post `ES_TIMEUP` to all services.
- Reset countdown timers and set state to `IDLEState`.
Case `ES_GAME_START`:
- Clear display buffer.
- Post `ES_READY_COUNTDOWN` to start the countdown timer.
- Transition to `ReadyState`.
Case `ES_TIMEOUT`:
- If timer matches countdown:
Switch (CurrentState):
Case `ReadyState`:
- Post `READY` to the display and transition to `CountDown`.
Case `CountDown`:
- Decrement countdown and post `ES_READY_COUNTDOWN`.
- If game hasn't started and countdown < 10:
- Post `GO` to the display, set game as started.
- If countdown reaches 0:
- Post `END` to the display and `ES_TIMEUP` to all services.
- Reset timers and transition to `IDLEState`.
Case `ES_READY_COUNTDOWN`:
- Decrement `StartGameCountDown`.
Return `ReturnEvent`.
---
Function: QueryMyCenteredLEDService()
- Return `CurrentState`.
/****************************************************************************
Module
MyCenteredLEDService.c
Description
A service to display input text centered on the LED matrix display.
History
When Who What/Why
-------------- --- --------
11/12/24 14:30 You Created the service for centered text display
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "MyCenteredLEDService.h"
#include "DM_Display64.h"
#include "DisplayerService.h"
/*----------------------------- Module Defines ----------------------------*/
#define GAMECOUNTDOWN 60
#define STARTGAMECOUNTDOWN 4
#define ONE_SEC 1000
/*---------------------------- Module Variables ---------------------------*/
static uint8_t MyPriority;
static uint16_t GameCountDown = GAMECOUNTDOWN;
static uint16_t StartGameCountDown = STARTGAMECOUNTDOWN;
static bool GAMESTART = false;
static LEDState_t CurrentState;
/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
Function
InitMyCenteredLEDService
Description
Initializes the service and prepares the display for centered text.
****************************************************************************/
bool InitMyCenteredLEDService(uint8_t Priority)
{
MyPriority = Priority;
GameCountDown *= 10;
StartGameCountDown = StartGameCountDown * 10 - 1;
puts("Starting MyCenteredLEDService\r");
ES_Event_t ThisEvent = { .EventType = ES_INIT_CENTRED };
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
PostMyCenteredLEDService
Description
Posts an event to this service's queue.
****************************************************************************/
bool PostMyCenteredLEDService(ES_Event_t ThisEvent)
{
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
RunMyCenteredLEDService
Description
Handles events and displays the input text centered on the display.
****************************************************************************/
ES_Event_t RunMyCenteredLEDService(ES_Event_t ThisEvent)
{
ES_Event_t ReturnEvent = { .EventType = ES_NO_EVENT };
switch (ThisEvent.EventType) {
case ES_LONG_IDLE:
DB_printf("ES_LONG_IDLE received\n");
ES_Event_t NewEvent = { .EventType = ES_NEW_INPUT_TEXT, .EventParam = IDLE };
PostDisplayerService(NewEvent);
ES_Event_t EndEvent = { .EventType = ES_TIMEUP };
ES_PostAll(EndEvent);
GameCountDown = GAMECOUNTDOWN * 10;
StartGameCountDown = STARTGAMECOUNTDOWN * 10 - 1;
GAMESTART = false;
CurrentState = IDLEState;
break;
case ES_GAME_START:
puts("Game start initiated\n");
DM_ClearDisplayBuffer();
ES_Event_t CountdownEvent = { .EventType = ES_READY_COUNTDOWN };
ES_Timer_InitTimer(COUNTDOWM_TIMER, ONE_SEC);
DB_printf("Starting the countdown\n");
CurrentState = ReadyState;
break;
case ES_TIMEOUT:
if (ThisEvent.EventParam == COUNTDOWM_TIMER) {
switch (CurrentState) {
case ReadyState:
ES_Event_t ReadyEvent = { .EventType = ES_NEW_INPUT_TEXT, .EventParam = READY };
PostDisplayerService(ReadyEvent);
ES_Timer_InitTimer(COUNTDOWM_TIMER, ONE_SEC);
CurrentState = CountDown;
break;
case CountDown:
StartGameCountDown--;
ES_Event_t CountdownEvent = { .EventType = ES_READY_COUNTDOWN, .EventParam = StartGameCountDown };
PostDisplayerService(CountdownEvent);
if (!GAMESTART) {
ES_Timer_InitTimer(COUNTDOWM_TIMER, ONE_SEC / 10);
if (StartGameCountDown < 10) {
ES_Event_t GoEvent = { .EventType = ES_NEW_INPUT_TEXT, .EventParam = GO };
PostDisplayerService(GoEvent);
StartGameCountDown = GameCountDown + 10;
GAMESTART = true;
}
} else if (StartGameCountDown == 0) {
ES_Event_t EndEvent = { .EventType = ES_NEW_INPUT_TEXT, .EventParam = END };
PostDisplayerService(EndEvent);
ES_Event_t TimeUpEvent = { .EventType = ES_TIMEUP };
ES_PostAll(TimeUpEvent);
GameCountDown = GAMECOUNTDOWN * 10;
StartGameCountDown = STARTGAMECOUNTDOWN * 10 - 1;
GAMESTART = false;
CurrentState = IDLEState;
} else {
ES_Timer_InitTimer(COUNTDOWM_TIMER, ONE_SEC / 10);
}
break;
default:
break;
}
}
break;
case ES_READY_COUNTDOWN:
StartGameCountDown--;
break;
default:
break;
}
return ReturnEvent;
}
/****************************************************************************
Function
QueryMyCenteredLEDService
Description
Returns the current state of the service.
****************************************************************************/
LEDState_t QueryMyCenteredLEDService(void)
{
return CurrentState;
}
/****************************************************************************
Module
MyCenteredLEDService.h
Revision
1.0.1
Description
Header file for the MyCenteredLEDService module. This service displays
input text centered on the LED matrix display.
Notes
History
When Who What/Why
-------------- --- --------
11/12/24 14:30 You Created the header file for centered text display
****************************************************************************/
#ifndef MY_CENTERED_LED_SERVICE_H
#define MY_CENTERED_LED_SERVICE_H
#define READY 1
#define GO 2
#define END 3
/*----------------------------- Include Files -----------------------------*/
#include "ES_Types.h"
/*----------------------------- Module Defines ----------------------------*/
typedef enum {
IDLEState,
ReadyState,
CountDown,
PostGoTransition,
CountDownEnd,
} LEDState_t;
#define MAX_TEXT_LENGTH 32 // Maximum length of the input text
bool InitMyCenteredLEDService(uint8_t Priority);
bool PostMyCenteredLEDService(ES_Event_t ThisEvent);
ES_Event_t RunMyCenteredLEDService(ES_Event_t ThisEvent);
#endif /* MY_CENTERED_LED_SERVICE_H */
Define constants:
- ONE_SEC = 1000
Define global variables:
- CurrentState: Tracks the current state of the joystick service.
- LatestJoystickValue: Stores the most recent joystick value.
- MyPriority: Priority level assigned to this service.
---
Function: InitJoyStickService(Priority)
- Save the provided Priority to MyPriority.
- Print initialization message to indicate service start.
- Configure hardware:
- Set RB12 as an analog input.
- Set CurrentState to `InitPState`.
- Post an initial event (`ES_JOY_INIT`) to the service queue.
- Return true if the event was successfully posted, false otherwise.
---
Function: PostJoyStickService(ThisEvent)
- Post ThisEvent to the service queue using MyPriority.
- Return true if the event was successfully posted, false otherwise.
---
Function: RunJoyStickService(ThisEvent)
- Initialize ReturnEvent with `ES_NO_EVENT` as the default.
Switch (CurrentState):
Case InitPState:
- If ThisEvent is `ES_JOY_INIT`:
- Print a message indicating service initialization.
- Transition CurrentState to `JoystickActive`.
Case JoystickActive:
- If ThisEvent is `ES_JOYSTICK_UPDATE`:
- Update LatestJoystickValue with ThisEvent.EventParam.
- Print the new joystick value.
- If the displayer state is `GOSTATE` and the joystick value exceeds a threshold (1010):
- Initialize a timer (`TIMEOUT_TIMER`) for 20 seconds.
- Else If ThisEvent is `ES_TIMEOUT`:
- If LatestJoystickValue exceeds the threshold (1010):
- Create an `ES_LONG_IDLE` event.
- Post the event to the displayer service.
- Print a message indicating the idle event was triggered.
Default:
- Print an error message indicating an unknown state.
Return ReturnEvent
/****************************************************************************
Module
JoyStickService.c
Description
Service for handling joystick inputs and coordinating state transitions
based on joystick events, adhering to the Gen2 Events and Services Framework.
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
#include "JoyStickService.h"
#include <xc.h>
#include "DM_Display.h"
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "MotorService.h"
#include "DisplayerService.h"
#include "CoinInsertionService.h"
/*---------------------------- Module Defines ----------------------------*/
#define ONE_SEC 1000
/*---------------------------- Module Variables ---------------------------*/
// Current state of the joystick service
static JoyStickState_t CurrentState;
// Latest value read from the joystick
static uint16_t LatestJoystickValue;
// Priority of this service
static uint8_t MyPriority;
/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
Function
InitJoyStickService
Parameters
uint8_t Priority - the service priority level to be assigned
Returns
bool - true if initialization succeeds, false otherwise
Description
Initializes the joystick service, sets up hardware configurations, and
posts an initial event to indicate the service is ready.
****************************************************************************/
bool InitJoyStickService(uint8_t Priority)
{
// Save the assigned priority for future event posting
MyPriority = Priority;
// Print initialization message
puts("Initiating JoyStick Service, get ready to play\r\n");
// Configure hardware: Set RB12 as analog input
TRISBbits.TRISB12 = 1;
ANSELBbits.ANSB12 = 1;
// Set the initial state
CurrentState = InitPState;
// Post the initial transition event
ES_Event_t ThisEvent = { .EventType = ES_JOY_INIT };
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
PostJoyStickService
Parameters
ES_Event_t ThisEvent - The event to post to the queue
Returns
bool - true if the event was successfully posted, false otherwise
Description
Posts an event to this service's queue.
****************************************************************************/
bool PostJoyStickService(ES_Event_t ThisEvent)
{
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
RunJoyStickService
Parameters
ES_Event_t ThisEvent - The event to process
Returns
ES_Event_t - ES_NO_EVENT if no errors, otherwise an error event
Description
Processes events and updates the state machine for joystick service.
****************************************************************************/
ES_Event_t RunJoyStickService(ES_Event_t ThisEvent)
{
ES_Event_t ReturnEvent = { .EventType = ES_NO_EVENT }; // Default return event
switch (CurrentState)
{
case InitPState:
if (ThisEvent.EventType == ES_JOY_INIT)
{
puts("Joystick Service Initialized\r\n");
CurrentState = JoystickActive;
}
break;
case JoystickActive:
if (ThisEvent.EventType == ES_JOYSTICK_UPDATE)
{
// Update the latest joystick value and print it
LatestJoystickValue = ThisEvent.EventParam;
printf("Joystick Active - New Value: %d\r\n", LatestJoystickValue);
// Check displayer state and joystick threshold
if (QueryDisplayerService() == GOSTATE && LatestJoystickValue > 1010)
{
ES_Timer_InitTimer(TIMEOUT_TIMER, ONE_SEC * 20);
}
}
else if (ThisEvent.EventType == ES_TIMEOUT)
{
// Trigger an idle event if the joystick value exceeds a threshold
if (LatestJoystickValue > 1010)
{
ES_Event_t EndEvent = { .EventType = ES_LONG_IDLE };
PostMyCenteredLEDService(EndEvent);
printf("No movement detected! Triggering idle event.\r\n");
}
}
break;
default:
puts("Unknown State\r\n");
break;
}
return ReturnEvent;
}
/*------------------------------- Footnotes -------------------------------*/
/*------------------------------ End of file ------------------------------*/
/****************************************************************************
Header file for Test Harness Service0
based on the Gen 2 Events and Services Framework
****************************************************************************/
#ifndef MyJoyStickService_H
#define MyJoyStickService_H
#include <stdint.h>
#include <stdbool.h>
#include "ES_Events.h"
#include "ES_Port.h" // needed for definition of REENTRANT
#include "PIC32_SPI_HAL.h"
#include "DM_Display.h"
// Public Function Prototypes
typedef enum
{
InitPState,
WaitingForUpdate,
JoystickActive
} JoyStickState_t;
//static JoyStickState_t CurrentState = InitPState;
bool InitJoyStickService(uint8_t Priority);
bool PostJoyStickService(ES_Event_t ThisEvent);
ES_Event_t RunJoyStickService(ES_Event_t ThisEvent);
#endif /* ServTemplate_H */
Define constants:
ONE_SEC = 1000
FIVE_SEC = ONE_SEC * 5
Define module-level variables:
CurrentCoinState - to track the current state of the coin insertion process
MyPriority - to store the priority of this service
Function: InitCoinInsertionService(Priority)
Disable analog functionality on Port A
Set TRIS register for RA2 as input
Set service priority to Priority
Set initial state to InitPCoinState
Create an initialization event ES_COIN_INIT
Post the event to the service queue
Return true if successful, otherwise false
Function: PostCoinInsertionService(ThisEvent)
Post ThisEvent to the service queue using the stored priority
Return true if successful, otherwise false
Function: RunCoinInsertionService(ThisEvent)
Initialize ReturnEvent as ES_NO_EVENT
Switch(CurrentCoinState):
Case InitPCoinState:
If ThisEvent is ES_COIN_INIT:
Transition to WaitForCoin1 state
Print "Coin: 0 / 2" to console or display
Case WaitForCoin1:
If ThisEvent is ES_COININPUT:
Start DebouncingTimer for 0.2 seconds
Start CoinPendingTimer for 5 seconds
Else if ThisEvent is ES_TIMEOUT from DebouncingTimer:
Transition to WaitForCoin2 state
Print "Coin: 1 / 2" to console or display
Else if ThisEvent is ES_TIMEOUT from CoinPendingTimer:
Print "Coin: 0 / 2" to console or display
Case WaitForCoin2:
If ThisEvent is ES_COININPUT:
Print "Coin: 2 / 2. Game Start!" to console or display
Create an ES_GAME_START event with the current time
Post the event to all services
Transition to GameInProgress state
Else if ThisEvent is ES_TIMEOUT from CoinPendingTimer:
Transition to WaitForCoin1 state
Print "Coin: 0 / 2" to console or display
Case GameInProgress:
If ThisEvent is ES_GAME_OVER:
Transition to WaitForCoin1 state
Print "Game Finished. Coin: 0 / 2" to console or display
Default:
Do nothing
Return ReturnEvent
Function: QueryCoinInsertionService()
Return the current state (CurrentCoinState)
/****************************************************************************
Module
CoinInsertionService.c
Description
Implements a flat state machine for handling coin insertion events using
the Gen2 Events and Services Framework.
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "CoinInsertionService.h"
/*----------------------------- Module Defines ----------------------------*/
#define ONE_SEC 1000
#define FIVE_SEC (ONE_SEC * 5)
/*---------------------------- Module Variables ---------------------------*/
static CoinState_t CurrentCoinState;
static uint8_t MyPriority;
/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
Function
InitCoinInsertionService
Parameters
uint8_t Priority - the priority of this service
Returns
bool - true if successful, false otherwise
Description
Initializes the coin insertion service, setting the initial state and
posting an initialization event.
****************************************************************************/
bool InitCoinInsertionService(uint8_t Priority)
{
ANSELA = 0; // Disable analog on Port A
TRISAbits.TRISA2 = 1;
MyPriority = Priority;
CurrentCoinState = InitPCoinState;
ES_Event_t ThisEvent = { .EventType = ES_COIN_INIT };
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
PostCoinInsertionService
Parameters
ES_Event_t ThisEvent - the event to post
Returns
bool - true if successful, false otherwise
Description
Posts an event to this service's queue.
****************************************************************************/
bool PostCoinInsertionService(ES_Event_t ThisEvent)
{
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
RunCoinInsertionService
Parameters
ES_Event_t ThisEvent - the event to process
Returns
ES_Event_t - ES_NO_EVENT if no errors, otherwise an error event
Description
Runs the state machine for handling coin insertion events.
****************************************************************************/
ES_Event_t RunCoinInsertionService(ES_Event_t ThisEvent)
{
ES_Event_t ReturnEvent = { .EventType = ES_NO_EVENT };
switch (CurrentCoinState)
{
case InitPCoinState:
if (ThisEvent.EventType == ES_COIN_INIT)
{
CurrentCoinState = WaitForCoin1;
DB_printf("Coin: 0 / 2\n");
// TODO: Send "Coin: 0/2" to display
}
break;
case WaitForCoin1:
if (ThisEvent.EventType == ES_COININPUT)
{
ES_Timer_InitTimer(DebouncingTimer, ONE_SEC * 0.2);
ES_Timer_InitTimer(CoinPendingTimer, FIVE_SEC);
}
else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == DebouncingTimer)
{
CurrentCoinState = WaitForCoin2;
DB_printf("Coin: 1 / 2\n");
// TODO: Send "Coin: 1/2" to display
}
else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == CoinPendingTimer)
{
DB_printf("Coin: 0 / 2\n");
// TODO: Send "Coin: 0/2" to display
}
break;
case WaitForCoin2:
if (ThisEvent.EventType == ES_COININPUT)
{
DB_printf("Coin: 2 / 2. Game Start!\n");
// TODO: Send "Coin: 2/2" or a welcome message to display
ES_Event_t GameStartEvent = { .EventType = ES_GAME_START, .EventParam = ES_Timer_GetTime() };
ES_PostAll(GameStartEvent);
CurrentCoinState = GameInProgress;
}
else if (ThisEvent.EventType == ES_TIMEOUT && ThisEvent.EventParam == CoinPendingTimer)
{
CurrentCoinState = WaitForCoin1;
DB_printf("Coin: 0 / 2\n");
// TODO: Send "Coin: 0/2" to display
}
break;
case GameInProgress:
if (ThisEvent.EventType == ES_GAME_OVER)
{
CurrentCoinState = WaitForCoin1;
DB_printf("Game Finished. Coin: 0 / 2\n");
// TODO: Send "Coin: 0/2" to display
}
break;
default:
break;
}
return ReturnEvent;
}
/****************************************************************************
Function
QueryCoinInsertionService
Parameters
None
Returns
CoinState_t - the current state of the coin insertion state machine
Description
Returns the current state of the coin insertion state machine.
****************************************************************************/
CoinState_t QueryCoinInsertionService(void)
{
return CurrentCoinState;
}
/****************************************************************************
Header file for template Flat Sate Machine
based on the Gen2 Events and Services Framework
****************************************************************************/
#ifndef COINSERVICE_H
#define COINSERVICE_H_H
// Event Definitions
#include "ES_Configure.h" /* gets us event definitions */
#include "ES_Types.h" /* gets bool type for returns */
#include <stdint.h>
#include <stdbool.h>
#include "ES_Events.h"
#include "ES_Port.h" // needed for definition of REENTRANT
#include "PIC32_SPI_HAL.h"
#include "DM_Display.h"
#define COIN12 10
// typedefs for the states
// State definitions for use with the query function
typedef enum
{
InitPCoinState, WaitForCoin1, WaitForCoin2,
GameInProgress
}CoinState_t;
// Public Function Prototypes
bool InitCoinInsertionService(uint8_t Priority);
bool PostCoinInsertionService(ES_Event_t ThisEvent);
ES_Event_t RunCoinInsertionService(ES_Event_t ThisEvent);
CoinState_t QueryCoinInsertionService(void);
#endif /* FSMTemplate_H */
Initialize global variables:
- `MyPriority`: Priority of the service.
- `idx`: Index for scrolling text (default: 0).
- `str`: String to display ("STANFORD ").
- `strlen`: Length of the string.
---
Function: InitAutoScrollingService(Priority)
- Save `Priority` to `MyPriority`.
- Post an `ES_INIT_AUTOLED` event to the service queue.
- Return true if the event is posted successfully, false otherwise.
---
Function: PostAutoScrollingService(Event)
- Post `Event` to the service queue.
- Return true if the event is posted successfully, false otherwise.
---
Function: RunAutoScrollingService(Event)
- Initialize `ReturnEvent` as `ES_NO_EVENT`.
Switch (Event.Type):
Case `ES_INIT_AUTOLED`:
- Start a timer (`SERVICE2_TIMER`) for HALF_SEC.
- Print initialization message.
Case `ES_TIMEOUT`:
If `Event.Param` is `SERVICE2_TIMER`:
- Check if the end of the string is reached:
If yes, reset `idx` to 0.
- Restart the timer with HALF_SEC duration.
- Create a new event (`ES_NEW_CHAR2PRINT`) with the next character in the string.
- Post the event to `MyLEDService`.
- Print the scrolling text.
- If `QueryMyCenteredLEDService()` returns `CountDown`:
- Stop the timer.
Case `ES_GAME_START`:
- Stop the timer.
- Print message indicating scrolling is stopped for the game.
Case `ES_GAME_OVER`:
- Reset `idx` to 0.
- Post an `ES_INIT_AUTOLED` event to restart scrolling.
Default:
- Do nothing.
Return `ReturnEvent`.
/****************************************************************************
Module
AutoScrollingService.c
Description
Service for managing auto-scrolling text on an LED display.
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "MyLEDService.h"
#include "AutoScrollingService.h"
#include "MyCenteredLEDService.h"
#include "DM_Display.h"
/*----------------------------- Module Defines ----------------------------*/
#define ONE_SEC 1000
#define HALF_SEC (ONE_SEC / 2)
/*---------------------------- Module Variables ---------------------------*/
static uint8_t MyPriority;
static uint8_t idx = 0;
static char str[] = "STANFORD ";
static uint8_t strlen = sizeof(str);
/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
Function
InitAutoScrollingService
Parameters
uint8_t Priority - Priority of this service.
Returns
bool - true if initialization is successful, false otherwise.
Description
Initializes the service and posts the initial transition event.
****************************************************************************/
bool InitAutoScrollingService(uint8_t Priority)
{
MyPriority = Priority;
ES_Event_t ThisEvent = { .EventType = ES_INIT_AUTOLED };
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
PostAutoScrollingService
Parameters
ES_Event_t ThisEvent - Event to post to the queue.
Returns
bool - true if the event was posted successfully, false otherwise.
Description
Posts an event to this service's queue.
****************************************************************************/
bool PostAutoScrollingService(ES_Event_t ThisEvent)
{
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
RunAutoScrollingService
Parameters
ES_Event_t ThisEvent - Event to process.
Returns
ES_Event_t - ES_NO_EVENT if no errors, otherwise another event.
Description
Handles events to manage auto-scrolling text and coordinates with other services.
****************************************************************************/
ES_Event_t RunAutoScrollingService(ES_Event_t ThisEvent)
{
ES_Event_t ReturnEvent = { .EventType = ES_NO_EVENT };
switch (ThisEvent.EventType)
{
case ES_INIT_AUTOLED:
ES_Timer_InitTimer(SERVICE2_TIMER, HALF_SEC);
printf("AutoScrollingService Initialized.\n");
break;
case ES_TIMEOUT:
if (ThisEvent.EventParam == SERVICE2_TIMER)
{
if (idx == strlen - 1)
{
idx = 0;
}
ES_Timer_InitTimer(SERVICE2_TIMER, HALF_SEC);
ES_Event_t NextCharEvent = { .EventType = ES_NEW_CHAR2PRINT, .EventParam = str[idx++] };
PostMyLEDService(NextCharEvent);
printf("Scrolling text: %s\n", str);
}
if (QueryMyCenteredLEDService() == CountDown)
{
ES_Timer_StopTimer(SERVICE2_TIMER);
}
break;
case ES_GAME_START:
ES_Timer_StopTimer(SERVICE2_TIMER);
printf("Scrolling stopped for game start.\n");
break;
case ES_GAME_OVER:
idx = 0;
ES_Event_t RestartEvent = { .EventType = ES_INIT_AUTOLED };
PostAutoScrollingService(RestartEvent);
break;
default:
break;
}
return ReturnEvent;
}
/****************************************************************************
Module
EventCheckers.h
Description
header file for the event checking functions
Notes
History
When Who What/Why
-------------- --- --------
10/18/15 11:50 jec added #include for stdint & stdbool
08/06/13 14:37 jec started coding
*****************************************************************************/
#ifndef EventCheckers_H
#define EventCheckers_H
// the common headers for C99 types
#include <stdint.h>
#include <stdbool.h>
// prototypes for event checkers
bool Check4Keystroke(void);
bool CheckForJoystickSignal(void);
bool Check4IRRiseFall(void);
bool Check4Switch1(void);
bool Check4Switch2(void);
#endif /* EventCheckers_H */
### Motor Service Pseudo Code
1. **Initialization (InitMotorService)**:
- Set priority and initial state (`InitMotorState`).
- Configure PWM on RB3 for motor control:
- Set RB3 as digital output.
- Initialize PWM library and map to Timer2.
- Set PWM frequency to 50 Hz.
- Initialize PWM duty cycle to 0%.
- Post an `ES_MOTOR_INIT` event to signal readiness.
2. **Posting Events (PostMotorService)**:
- Post incoming events to the Motor Service queue.
3. **Run State Machine (RunMotorService)**:
- Handle events based on the current state:
- **InitMotorState**:
- On `ES_MOTOR_INIT`, transition to `MotorActiveState`.
- On `ES_GAME_START`, start a 6-second timer for motor operation.
- On `ES_TIMEOUT`, transition to `MotorActiveState`.
- **MotorActiveState**:
- On `ES_MOTOR_WORKING`, process joystick value:
- Clamp value between 0 and 1024.
- Map to a duty cycle percentage (0-100%).
- Set PWM duty cycle.
- On `ES_TIMEUP`:
- Stop motor by setting PWM duty cycle to 0%.
- Post `ES_MOTOR_TERMINATE` event to `CountdownService`.
- Reset to `InitMotorState`.
4. **Query Current State (QueryMotorService)**:
- Return the current state of the Motor Service.
/****************************************************************************
Module
MotorService.c
Description
Service for controlling motor operations using PWM and handling state
transitions based on events, adhering to the Gen2 Events and Services Framework.
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
#include "MotorService.h"
#include <xc.h>
#include "DM_Display.h"
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "PWM_PIC32.h"
/*----------------------------- Module Defines ----------------------------*/
#define ONE_SEC 1000
/*---------------------------- Module Variables ---------------------------*/
static MotorState_t CurrentMotorState; // Current state of the Motor Service
static uint8_t MyPriority; // Priority of this service
/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
Function
InitMotorService
Parameters
uint8_t Priority - the service priority level to be assigned
Returns
bool - true if initialization succeeds, false otherwise
Description
Initializes the motor service, sets up PWM configurations, and posts
an initial event to indicate readiness.
****************************************************************************/
bool InitMotorService(uint8_t Priority)
{
MyPriority = Priority;
puts("Initiating Motor Service...\r\n");
// Configure RB3 as digital output for PWM
TRISBbits.TRISB3 = 0; // Set RB3 as output
ANSELBbits.ANSB3 = 0; // Set RB3 as digital
CurrentMotorState = InitMotorState;
// Initialize the PWM library
if (!PWMSetup_BasicConfig(1) ||
!PWMSetup_AssignChannelToTimer(1, _Timer2_) ||
!PWMSetup_SetFreqOnTimer(50, _Timer2_) ||
!PWMSetup_MapChannelToOutputPin(1, PWM_RPB3))
{
puts("PWM initialization failed\r\n");
return false;
}
// Set initial PWM duty cycle to 0%
PWMOperate_SetDutyOnChannel(0, 1);
// Post the initial motor initialization event
ES_Event_t ThisEvent = { .EventType = ES_MOTOR_INIT };
if (ES_PostToService(MyPriority, ThisEvent))
{
puts("Motor Service initialized successfully!\r\n");
return true;
}
return false;
}
/****************************************************************************
Function
PostMotorService
Parameters
ES_Event_t ThisEvent - The event to post to the queue
Returns
bool - true if the event was successfully posted, false otherwise
Description
Posts an event to the Motor Service's queue.
****************************************************************************/
bool PostMotorService(ES_Event_t ThisEvent)
{
return ES_PostToService(MyPriority, ThisEvent);
}
/****************************************************************************
Function
RunMotorService
Parameters
ES_Event_t ThisEvent - The event to process
Returns
ES_Event_t - ES_NO_EVENT if no errors, otherwise another event
Description
Processes events and updates the state machine for the Motor Service.
****************************************************************************/
ES_Event_t RunMotorService(ES_Event_t ThisEvent)
{
ES_Event_t ReturnEvent = { .EventType = ES_NO_EVENT };
switch (CurrentMotorState)
{
case InitMotorState:
if (ThisEvent.EventType == ES_MOTOR_INIT)
{
puts("Motor Service Initialized\r\n");
CurrentMotorState = MotorActiveState;
}
else if (ThisEvent.EventType == ES_GAME_START)
{
DB_printf("Motor Start!\n");
ES_Timer_InitTimer(MOTOR_TIMER, ONE_SEC * 6);
}
else if (ThisEvent.EventType == ES_TIMEOUT)
{
DB_printf("Motor Timeout Received!\n");
CurrentMotorState = MotorActiveState;
}
break;
case MotorActiveState:
if (ThisEvent.EventType == ES_MOTOR_WORKING)
{
uint16_t joystickValue = ThisEvent.EventParam;
// Clamp joystick value to maximum range
if (joystickValue > 1024)
{
joystickValue = 1024;
}
// Map joystick value (0-1024) to duty cycle (0-100%)
uint8_t dutyCycle = ((1024 - joystickValue) * 100) / 1024;
printf("Setting PWM Duty Cycle: %d%%\r\n", dutyCycle);
// Set the duty cycle on PWM Channel 1
if (!PWMOperate_SetDutyOnChannel(dutyCycle, 1))
{
puts("Failed to set PWM duty cycle\r\n");
}
}
else if (ThisEvent.EventType == ES_TIMEUP)
{
PWMOperate_SetDutyOnChannel(0, 1);
// Post motor termination event
ES_Event_t NewEvent = { .EventType = ES_MOTOR_TERMINATE, .EventParam = ES_Timer_GetTime() };
DB_printf("### Motor Service Terminated ###\n");
PostCountdownService(NewEvent);
CurrentMotorState = InitMotorState;
}
break;
default:
puts("Unknown Motor State\r\n");
break;
}
return ReturnEvent;
}
/****************************************************************************
Function
QueryMotorService
Parameters
None
Returns
MotorState_t - the current state of the Motor Service
Description
Returns the current state of the Motor Service.
****************************************************************************/
MotorState_t QueryMotorService(void)
{
return CurrentMotorState;
}
/****************************************************************************
Header file for Test Harness Service0
based on the Gen 2 Events and Services Framework
****************************************************************************/
#ifndef MyMotorService_H
#define MyMotorService_H
#include <stdint.h>
#include <stdbool.h>
#include "ES_Events.h"
#include "ES_Port.h" // needed for definition of REENTRANT
#include "PIC32_SPI_HAL.h"
#include "DM_Display.h"
// Public Function Prototypes
typedef enum {
InitMotorState,
MotorActiveState
} MotorState_t;
bool InitMotorService(uint8_t Priority);
bool PostMotorService(ES_Event_t ThisEvent);
ES_Event_t RunMotorService(ES_Event_t ThisEvent);
MotorState_t QueryMotorService(void);
#endif /* ServTemplate_H */