Implement the State Machines
Table of contents
Overview
In this task you will implement several state machines and test each of them separately to verify correct operation. You will be guided how to “connect” the state machines as needed later in this task and the next.
State Machines
- transmitter state machine: This state machine generates the square wave for each of the 10 specific frequencies.
- trigger state machine: this machine debounces the gun-trigger and activates the transmitter state machine.
- hitLedTimer state machine: this state machine lights up the hit-indication LED on the gun for 1/2 second each time a hit is detected.
- lockoutTimer state machine: this state machine provides a lock-out period used to ensure that you detect only one “hit” for each trigger firing by your opponent. Moreover, this approach ensures that you only detect a hit from one opponent at a time. Thus if you are simultaneously fired upon and actually “hit” by two different players, you only report the first detected “hit”.
General Requirements
- You must follow the coding standard.
General Notes
- Assume a “tick” rate of 25 Hz (40 ms period) for all state machines when making any assumptions about timing.
Warnings
- Do not use any types other than integer types (uint16_t, uint32_t, int16_t, etc.) in any of the state machines. DO NOT USE ANY FLOATING TYPES SUCH AS float or double! The interrupt-service routine cannot handle them. If you use them in your state machines, you will likely see very strange behavior in your system.
Important Numbers
- Transmitter Frequencies: Use the tick counts that were provided in filter.h
- System “Tick” Rate: 25 Hz.
- Detector Lock Out Time: 1/2 second.
- Transmitter Waveform Length: 200 ms.
Resources
Source Code
Note that the following files are provided in your ecen390 project directory. Refer to the comments above each function for more details.
- ltag/main/main.c
- ltag/main/transmitter.h
- ltag/main/trigger.h
- ltag/main/hitLedTimer.h
- ltag/main/lockoutTimer.h
You are expected to create and implement the following files. See the provided header files (.h) for a description of each function.
- ltag/main/transmitter.c
- ltag/main/trigger.c
- ltag/main/hitLedTimer.c
- ltag/main/lockoutTimer.c
Specifications
Transmitter State Machine
- The transmitter state machine generates a square-wave waveform by driving a sequence of ‘1’s and ‘0’s onto a GPIO pin.
- Each time the transmitter state machine is activated with a call to
transmitter_run()
, it must generate the waveform for 200 ms. - The transmitter state machine must be able to generate the 10 player frequencies.
- You set the waveform frequency with:
transmitter_setFrequencyNumber(uint16_t frequencyNumber)
. - Minimum frequency is selected when frequencyNumber = 0, maximum frequency is selected when frequencyNumber = 9.
- All waveforms must be 50% duty cycle.
Trigger State Machine
- The trigger state machine must debounce both the press and the release of the gun trigger (see Debouncing Switches).
- Tie the trigger state machine to the transmitter. A trigger pull will invoke transmitter_run().
- The trigger state machine activates the transmitter state machine after the press of the trigger has been debounced.
- The trigger state machine will not recognize another press of the trigger until the trigger has been released and debounced.
- Each press/release of the trigger must activate the transmitter state machine only once.
Hit LED Timer State Machine
- The hit-indicator LEDs must illuminate for 1/2 second when activated.
Lockout Timer State Machine
- Lockout timer state machine is a simple timer.
- The time interval for the lockout is 1/2 second.
Implementation Details
Debouncing Switches
Mechanical switches are imperfect. When pushed or released they often “bounce” between closed and open states for a short period of time (10 milliseconds or so). For example, assume that a push-button is wired such that it is read as a ‘1’ when pressed and read as a ‘0’ when released. If you continually read the switch when it is first pressed, you may read the switch initially as a ‘1’ but then a few milliseconds later, read it as ‘0’, then ‘1’, then ‘0’ and so forth. This process may repeat itself a few times until the output of the switch becomes completely stable. When a switch is initially pressed (or released) and quickly changes values, we say that the switch “bounces”.
We want our laser tag system to deliver exactly one shot for each press/release of the trigger. The easiest way to debounce a switch is to use a state machine that, in turn, uses a delay to determine when the switch has stopped bouncing. Here is a simple algorithm you can implement in a state machine and use to detect when a switch has stopped bouncing. It assumes that it will take no longer than 40 ms for the switch to stop bouncing. This is a reasonable assumption for a switch that is in good working order.
- Step 1: Wait until you detect a press of the button.
- Step 2: Once you detect the press of the button, start a counter that expires at 40 ms.
- Step 3: If the button changes value before the counter expires, reset the counter and go back to Step 1.
- Step 4: If the counter expires before the switch changes value, you can assume that the switch is debounced.
You need to use this process for both the press and release of the switch or push-button.
Keyword volatile
As a recommendation, declare all of your state machine global variables volatile
, even the current state variable. Otherwise, you will need to analyze each of your global variables for the condition explained below to see if they need to be volatile
. This condition potentially applies to all state machine global variables (uint32_t, etc.), not just boolean variables.
In some cases it is necessary to add the keyword volatile
to a variable declaration. Consider an example with the hitLedTimer state machine. The variable timerStartFlag
, when set to true, starts the hitLedTimer state machine. Later, when the state machine is finished, it will set timerStartFlag
to false. In the partial example shown below, you can see that the keyword, volatile
has been added to the declaration for the variable, timerStartFlag
.
// When true, the timer starts running.
volatile static bool timerStartFlag = false;
void hitLedTimer_start() {
timerStartFlag = true;
}
The volatile
keyword essentially tells the compiler to avoid optimizing code that accesses this variable. Here is the principle at work. Specifically, if a variable is written to by a function called from isr_function()
(e.g., your tick functions), and that same variable is read by a function called from main()
, you must use the volatile
keyword in the declaration for that variable. The volatile
keyword informs the compiler that it should not attempt to optimize read access to the variable.
Macros for Debug and Pass off
The following DPRINTF macro is a helpful alternative to the normal printf function call since it can be switched off by making sure DEBUG is not defined. If you have many DPRINTFs sprinkled throughout your code, you can turn them all off at once while still leaving them in your code for later use.
The DPCHAR macro is a low-overhead way to print a character to the console and is useful for passing off the trigger state machine. The macros can be left in the code for later test and debug, but all of them can be disabled when running under normal use by not having DEBUG defined (commended out).
Place the following code in your .c file near the top, but below other #include lines.
// Uncomment for debug prints
// #define DEBUG
#if defined(DEBUG)
#include <stdio.h>
#include "xil_printf.h"
#define DPRINTF(...) printf(__VA_ARGS__)
#define DPCHAR(ch) outbyte(ch)
#else
#define DPRINTF(...)
#define DPCHAR(ch)
#endif
When hardware interrupts are enabled, make sure debug prints called from your state machines are not enabled. Otherwise, you will get interrupt overruns and unusual behavior. The only exception is when DPCHAR() is selectively used (e.g. when passing off your trigger state machine).
For more ideas on getting visibility into the operation of your code, here’s a link to a page describing how to debug state machines.