User Tools

Site Tools


lab_6

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Previous revision
lab_6 [2018/11/14 13:39]
lab_6 [2019/04/10 11:41] (current)
hutch
Line 1: Line 1:
 ===== Simon Lab ===== ===== Simon Lab =====
 +
 +{{youtube>​IxI0QVZK86A|?​rel=0&​allowfullscreen&​noborder&​560x315}}
  
 ==== Overview ==== ==== Overview ====
Line 18: Line 20:
 ==== Simon Implementation Strategy ==== ==== Simon Implementation Strategy ====
  
-Simon is much more complex than Tic-Tac-Toe,​ so we need to look at how the game is played in detail so we can come up with good implementation strategies. Our Simon game, as shown in the [[https://​www.youtube.com/​watch?​v=IxI0QVZK86A|video]], will use four colored regions on the LCD. The colored sequence will be shown using these colored regions and the user will attempt to follow the computer'​s sequence by touching the right sequence of colored regions.+Simon is much more complex than Tic-Tac-Toe,​ so we need to look at how the game is played in detail so we can come up with good implementation strategies. Our Simon game, as shown in the video above, will use four colored regions on the LCD. The colored sequence will be shown using these colored regions and the user will attempt to follow the computer'​s sequence by touching the right sequence of colored regions.
  
 Here is the organizational strategy that we will use: Here is the organizational strategy that we will use:
Line 56: Line 58:
  
 There are a couple of things to keep in mind with this approach. If you are doing things that must only be done once in either the initial or final state, you may need to add additional states (one after the initial state, one before the final state), or perhaps you can meet the need with a Mealy action. There are a couple of things to keep in mind with this approach. If you are doing things that must only be done once in either the initial or final state, you may need to add additional states (one after the initial state, one before the final state), or perhaps you can meet the need with a Mealy action.
- 
  
 ---- ----
  
-==== simonDisplay Code ====+==== Individual Descriptions of Simon Modules with Helps and Suggestions ​====
  
-SimonDisplay provides a function that draws a "​button",​ e.g., a small square in the region that indicates to the user the color that is assigned to the region (''​simonDisplay_drawButton()''​). It also provides a function to draw a square that fills in the entire region (''​simonDisplay_drawSquare()''​). This function serves two purposes: 1) it can be used when flashing the computer'​s sequence to the user, and 2) it can be used to provide touch feedback to the user. That is, when the user touches the smaller "​button",​ the game can then draw the bigger square in the touched area so that the user knows that the game acknowledges his or her touch. This will be discussed in more detail when we talk about the ''​buttonHandler''​ state machine. Both of these functions accept a region-number argument to determine where to draw the "​button"​ or square. +  ​[[Writing ​the simonDisplay ​Code]] 
- +  - [[Writing ​the buttonHandler Code]] 
-Finally, ​simonDisplay ​also computes the region number based upon the map shown in the ''​simonDisplay.h''​ file (''​simonDisplay_computeRegionNumber()''​). It also provides a convenience function that draws all of the buttons (''​simonDisplay_drawAllButtons()''​). +  - [[Writing ​the flashSequence Code]] 
- +  ​[[Writing ​the verifySequence Code]] 
-I have provided the entire ''​simonDisplay.h''​ file below. You must implement all of the functions as shown. Read the comments to understand what operations each function must perform. In addition to this file, I have also provided a ''​simonDisplay_runTest()''​ function and [[https://​www.youtube.com/​watch?​v=WPfn4OeQEcY|related video]] that you can use to test your source code. If you study the ''​.h''​ file, the ''​simonDisplay_runTest()''​ code and the video, you should be able to see how everything is supposed to work. Place the provided ''​simonDisplay_runTest()''​ code in your ''​simonDisplay.c''​ file so that the function has full access to the necessary functions. +  - [[Writing ​the globals Code]] 
- +  ​- ​[[Helps and Suggestions]]
-<code C simonDisplay.h>​ +
-#ifndef SIMONDISPLAY_H_ +
-#define SIMONDISPLAY_H_ +
- +
-#include <​stdbool.h>​ +
-#include <​stdint.h>​ +
- +
-// Width, height of the simon "​buttons"​ +
-#define SIMON_DISPLAY_BUTTON_WIDTH 60 +
-#define SIMON_DISPLAY_BUTTON_HEIGHT 60 +
- +
-// WIdth, height of the simon "​squares. +
-// Note that the video shows the squares as larger but you  +
-// can use this smaller value to make the game easier to implement speed-wise. +
-#define SIMON_DISPLAY_SQUARE_WIDTH ​ 120 +
-#define SIMON_DISPLAY_SQUARE_HEIGHT 120 +
- +
-// Given coordinates from the touch pad, computes the region number. +
- +
-// The entire touch-screen is divided into 4 rectangular regions, numbered 0 - 3. +
-// Each region will be drawn with a different color. Colored buttons remind +
-// the user which square is associated with each color. When you press +
-// a region, computeRegionNumber returns the region number that is used +
-// by the other routines. +
-/* +
-|----------|----------| +
-|          |          | +
-|    0     ​| ​    ​1 ​   | +
-|  (RED)   | (YELLOW) | +
------------------------ +
-|          |          | +
-|     ​2 ​   |    3     | +
-|  (BLUE) ​ |  (GREEN) | +
------------------------ +
-*/ +
- +
-// These are the definitions for the regions. +
-#define SIMON_DISPLAY_REGION_0 0 +
-#define SIMON_DISPLAY_REGION_1 1 +
-#define SIMON_DISPLAY_REGION_2 2 +
-#define SIMON_DISPLAY_REGION_3 3 +
- +
-int8_t simonDisplay_computeRegionNumber(int16_t x, int16_t y); +
- +
-// Draws a colored "​button"​ that the user can touch. +
-// The colored button is centered in the region but does not fill the region. +
-// If erase argument is true, draws the button as black background to erase it. +
-void simonDisplay_drawButton(uint8_t regionNumber,​ bool erase); +
- +
-// Convenience function that draws all of the buttons. +
-void simonDisplay_drawAllButtons();​ +
- +
-// Convenience function that erases all of the buttons. +
-void simonDisplay_eraseAllButtons();​ +
- +
-// Draws a bigger square that completely fills the region. +
-// If the erase argument is true, it draws the square as black background to "​erase"​ it. +
-void simonDisplay_drawSquare(uint8_t regionNo, bool erase); +
- +
-// Runs a brief demonstration of how buttons can be pressed and squares lit up to implement the user +
-// interface of the Simon game. The routine will continue to run until the touchCount has been reached, e.g., +
-// the user has touched the pad touchCount times. +
- +
-// I used a busy-wait delay (utils_msDelay) that uses a for-loop and just blocks until the time has passed. +
-// When you implement the game, you CANNOT use this function as we discussed in class. Implement the delay +
-// using the non-blocking state-machine approach discussed in class. +
-void simonDisplay_runTest(uint16_t touchCount);​ +
- +
-#endif /* SIMONDISPLAY_H_ */ +
-</​code>​ +
- +
-=== simonDisplay_runTest() === +
- +
-<code C simonDisplay_runTest.c>​ +
-#define TOUCH_PANEL_ANALOG_PROCESSING_DELAY_IN_MS 60 // in ms +
-#define MAX_STR 255 +
-#define TEXT_SIZE 2 +
-#define TEXT_VERTICAL_POSITION 0 +
-#define TEXT_HORIZONTAL_POSITION (DISPLAY_HEIGHT/​2) +
-#define INSTRUCTION_LINE_1 "Touch and release to start the Simon demo."​ +
-#define INSTRUCTION_LINE_2 "Demo will terminate after %d touches."​ +
-#define DEMO_OVER_MESSAGE_LINE_1 "Simon demo terminated"​ +
-#define DEMO_OVER_MESSAGE_LINE_2 "after %d touches."​ +
-#define TEXT_VERTICAL_POSITION 0 // Start at the far left. +
-#define ERASE_THE_SQUARE true  // drawSquare() erases if this is passed in. +
-#define DRAW_THE_SQUARE false  // drawSquare() draws the square if this is passed in. +
-#define ERASE_THE_BUTTON true  // drawSquare() erases if this is passed in. +
-#define DRAW_THE_BUTTON false  // drawSquare() draws the square if this is passed in. +
-// Runs a brief demonstration of how buttons can be pressed and squares lit up to implement the user +
-// interface of the Simon game. The routine will continue to run until the touchCount has been reached, e.g., +
-// the user has touched the pad touchCount times. +
- +
-// I used a busy-wait delay (utils_msDelay) that uses a for-loop and just blocks until the time has passed. +
-// When you implement the game, you CANNOT use this function as we discussed in class. Implement the delay +
-// using the non-blocking state-machine approach discussed in class. +
-void simonDisplay_runTest(uint16_t touchCount) { +
-  display_init(); ​        // Always initialize the display. +
-  char str[MAX_STR]; ​     // Enough for some simple printing. +
-  uint8_t regionNumber = 0;   // Convenience variable. +
-  uint16_t touches = 0;   // Terminate when you receive so many touches. +
-  // Write an informational message and wait for the user to touch the LCD. +
-  display_fillScreen(DISPLAY_BLACK); ​             // clear the screen. +
-  display_setCursor(TEXT_VERTICAL_POSITION,​ TEXT_HORIZONTAL_POSITION);​ // move to the middle of the screen. +
-  display_setTextSize(TEXT_SIZE); ​                // Set the text size for the instructions. +
-  display_setTextColor(DISPLAY_RED,​ DISPLAY_BLACK); ​  // Reasonable text color. +
-  sprintf(str,​ INSTRUCTION_LINE_1); ​                  // Copy the line to a buffer. +
-  display_println(str); ​                              // Print to the LCD. +
-  display_println(); ​                                 // new-line. +
-  sprintf(str,​ INSTRUCTION_LINE_2,​ touchCount); ​      // Copy the line to a buffer. +
-  display_println(str); ​                              // Print to the LCD. +
-  while (!display_isTouched()); ​      // Wait here until the screen is touched. +
-  while (display_isTouched()); ​       // Now wait until the touch is released. +
-  display_fillScreen(DISPLAY_BLACK); ​ // Clear the screen. +
-  simonDisplay_drawAllButtons(); ​     // Draw all of the buttons. +
-  bool touched = false; ​ // Keep track of when the pad is touched. +
-  int16_t x, y;  // Use these to keep track of coordinates. +
-  uint8_t z;      // This is the relative touch pressure. +
-  while (touches < touchCount) {  // Run the loop according to the number of touches passed in. +
-    if (!display_isTouched() && touched) {          // user has stopped touching the pad. +
-      simonDisplay_drawSquare(regionNumber,​ ERASE_THE_SQUARE); ​ // Erase the square. +
-      simonDisplay_drawButton(regionNumber,​ DRAW_THE_BUTTON); ​ //  Draw the button. +
-      touched = false;​ //​ Released the touch, set touched to false. +
-    } else if (display_isTouched() && !touched) { // User started touching the pad. +
-      touched = true;                             // Just touched the pad, set touched = true. +
-      touches++; ​                                 // Keep track of the number of touches. +
-      display_clearOldTouchData(); ​               // Get rid of data from previous touches. +
-      // Must wait this many milliseconds for the chip to do analog processing. +
-      utils_msDelay(TOUCH_PANEL_ANALOG_PROCESSING_DELAY_IN_MS);​  +
-      display_getTouchedPoint(&​x,​ &y, &​z); ​       // After the wait, get the touched point. +
-      regionNumber = simonDisplay_computeRegionNumber(x,​ y);// Compute the region number, see above. +
-      simonDisplay_drawSquare(regionNumber,​ DRAW_THE_SQUARE); ​ // Draw the square (erase = false). +
-    } +
-  } +
-  // Done with the demo, write an informational message to the user. +
-  display_fillScreen(DISPLAY_BLACK); ​       // clear the screen. +
-  // Place the cursor in the middle of the screen. +
-  display_setCursor(TEXT_VERTICAL_POSITION,​ TEXT_HORIZONTAL_POSITION);​  +
-  display_setTextSize(TEXT_SIZE);​ // Make it readable. +
-  display_setTextColor(DISPLAY_RED,​ DISPLAY_BLACK); ​ // red is foreground color, black is background color. +
-  sprintf(str,​ DEMO_OVER_MESSAGE_LINE_1); ​   // Format a string using sprintf. +
-  display_println(str); ​                    // Print it to the LCD. +
-  sprintf(str,​ DEMO_OVER_MESSAGE_LINE_2,​ touchCount); ​ // Format the rest of the string. +
-  display_println(str); ​ // Print it to the LCD. +
-+
- +
-</​code>​ +
- +
------ +
-==== buttonHandler ​State Machine ​Code ==== +
- +
-The ''​buttonHandler''​ state machine and associated code abstract the "​buttons"​ from the user. The ''​buttonHandler''​ state machine draws the buttons, draws a larger square over the button when a touch is detected, provides a function that can be called to detect when a release has been detected (really, a touch followed by a release) and the necessary ''​enable()''​ and ''​disable()''​ functions. During normal usage, you would call ''​buttonHandler_enable()''​ to start the state machine. Next you would continuously check to see if a button was touched and then released by calling ''​buttonHandler_releaseDetected()''​. If this function returns true, you would invoke ''​buttonHandler_getRegionNumber()''​ to find out what region/​color was touched. You would call ''​buttonHandler_disable()''​ to stop the state machine and allow it to return to its initial state. This process would begin again by calling ''​buttonHandler_enable()''​. +
- +
-**Note: because time-out (when the user takes too long to touch a colored button on the screen) is detected by verifySequence,​ it is possible to leave the buttonHandler hanging in a state (waiting for a touch, for example) if verifySequence detects a time-out. So, you need to design your state machine so that, no matter what state it is in, it will jump back to the proper beginning state anytime the enable/​disable flag becomes false. For example, if verifySequence detects time-out while the buttonHandler state-machine is waiting for the user to touch the display, verifySequence would invoke button_handler_disable(). When this happens, the button_handler state-machine must return to the state that it would normally return to if it had completely detected a touch-noTouch sequence.** +
- +
-I provide you with the ''​buttonHandler.h''​ file and the code for ''​buttonHandler_runTest()''​. Place runTest() function in your ''​buttonHandler.c''​ file so that it can access the necessary functions. You can see how ''​buttonHandler_runTest()''​ operates by watching the [[https://​www.youtube.com/​watch?​v=wTtFDRLLhfc|buttonHandler_runTest() video]]. +
- +
-**Note: ​the buttonHandler test code (and ultimately the simonControl code) draws the buttons the first time as the game is started. This simplifies the buttonHander state-machine. So, your buttonHandler does not need to draw the buttons when the game first starts up. You will do that in the simonControl code. Once the buttons are drawn for the first time, the buttonHandler will keep the display properly updated, as shown in the buttonHandler_runTest code.** +
- +
-<code C buttonHandler.h>​ +
-#ifndef BUTTONHANDLER_H_ +
-#define BUTTONHANDLER_H_ +
-#include <​stdint.h>​ +
-// Get the simon region numbers. See the source code for the region numbering scheme. +
-uint8_t buttonHandler_getRegionNumber();​ +
- +
-// Turn on the state machine. Part of the interlock. +
-void buttonHandler_enable();​ +
- +
-// Turn off the state machine. Part of the interlock. +
-void buttonHandler_disable();​ +
- +
-// The only thing this function does is return a boolean flag set by the buttonHandler state machine. To wit: +
-// Once enabled, the buttonHandler state-machine first waits for a touch. Once a touch is detected, the +
-// buttonHandler state-machine computes the region-number for the touched area. Next, the buttonHandler +
-// state-machine waits until the player removes their finger. At this point, the state-machine should +
-// set a bool flag that indicates the the player has removed their finger. Once the buttonHandler()  +
-// state-machine is disabled, it should clear this flag. +
-// All buttonHandler_releasedDetected() does is return the value of this flag.  +
-// As such, the body of this function should only contain a single line of code. +
-// If this function does more than return a boolean set by the buttonHandler state machine, you are going about +
-// this incorrectly. +
-bool buttonHandler_releaseDetected();​ +
- +
-// Initialize the state machine +
-void buttonHandler_init();​ +
- +
-// Standard tick function. +
-void buttonHandler_tick();​ +
- +
-// This tests the functionality of the buttonHandler state machine. +
-// buttonHandler_runTest(int16_t touchCount) runs the test until +
-// the user has touched the screen touchCount times. It indicates +
-// that a button was pushed by drawing a large square while +
-// the button is pressed and then erasing the large square and +
-// redrawing the button when the user releases their touch. +
-void buttonHandler_runTest(int16_t touchCount);​ +
- +
-#endif /* BUTTONHANDLER_H_ */ +
- +
-</​code>​ +
- +
-<code C buttonHandler_runTest.c>​ +
-#define RUN_TEST_TERMINATION_MESSAGE1 "​buttonHandler_runTest()" ​ // Info message. +
-#define RUN_TEST_TERMINATION_MESSAGE2 "​terminated." ​             // Info message. +
-#define RUN_TEST_TEXT_SIZE 2            // Make text easy to see. +
-#define RUN_TEST_TICK_PERIOD_IN_MS 100  // Assume a 100 ms tick period. +
-#define TEXT_MESSAGE_ORIGIN_X 0                  // Text is written starting at the right, and +
-#define TEXT_MESSAGE_ORIGIN_Y (DISPLAY_HEIGHT/​2) // middle. +
- +
-// buttonHandler_runTest(int16_t touchCount) runs the test until +
-// the user has touched the screen touchCount times. It indicates +
-// that a button was pushed by drawing a large square while +
-// the button is pressed and then erasing the large square and +
-// redrawing the button when the user releases their touch. +
- +
-void buttonHandler_runTest(int16_t touchCountArg) { +
-    int16_t touchCount = 0;                 // Keep track of the number of touches. +
-    display_init(); ​                        // Always have to init the display. +
-    display_fillScreen(DISPLAY_BLACK); ​     // Clear the display. +
-    // Draw all the buttons for the first time so the buttonHandler doesn'​t need to do this in an init state. +
-    // Ultimately, simonControl will do this when the game first starts up. +
-    simonDisplay_drawAllButtons(); ​          +
-    buttonHandler_init(); ​                  // Initialize the button handler state machine +
-    buttonHandler_enable();​ +
-    while (touchCount < touchCountArg) {    // Loop here while touchCount is less than the touchCountArg +
-        buttonHandler_tick(); ​              // Advance the state machine. +
-        utils_msDelay(RUN_TEST_TICK_PERIOD_IN_MS);​ +
-        if (buttonHandler_releaseDetected()) {  // If a release is detected, then the screen was touched. +
-            touchCount++; ​                      // Keep track of the number of touches. +
-            // Get the region number that was touched. +
-            printf("​button released: %d\n\r",​ buttonHandler_getRegionNumber());​ +
-            // Interlocked behavior: handshake with the button handler (now disabled). +
-            buttonHandler_disable();​ +
-            utils_msDelay(RUN_TEST_TICK_PERIOD_IN_MS);​ +
-            buttonHandler_tick(); ​              // Advance the state machine. +
-            buttonHandler_enable(); ​            // Interlocked behavior: enable the buttonHandler. +
-            utils_msDelay(RUN_TEST_TICK_PERIOD_IN_MS);​ +
-            buttonHandler_tick(); ​              // Advance the state machine. +
-        } +
-    } +
-    display_fillScreen(DISPLAY_BLACK); ​       // clear the screen. +
-    display_setTextSize(RUN_TEST_TEXT_SIZE); ​ // Set the text size. +
-    display_setCursor(TEXT_MESSAGE_ORIGIN_X,​ TEXT_MESSAGE_ORIGIN_Y);​ // Move the cursor to a rough center point. +
-    display_println(RUN_TEST_TERMINATION_MESSAGE1);​ // Print the termination message on two lines. +
-    display_println(RUN_TEST_TERMINATION_MESSAGE2);​ +
-+
-</​code>​ +
- +
------ +
-==== flashSequence ​State Machine ​Code ==== +
- +
-The ''​flashSequence''​ state machine flashes the computer'​s color sequence to the user so that the user can attempt to copy it. ''​flashSequence_enable()''​ allows the state machine to leave its initial state. ''​flashSequence_completed()''​ can be called to see if the machine has completed the task of flashing the color sequence to the user. ''​flashSequence_disable()''​ allows the state machine to return to its initial state (make sure that it has completed its task). I provide the ''​flashSequence_runTest()''​ code so you can test your state machine implementation to see if it is working correctly. Place the ''​flashSequence_runTest()''​ code in your ''​flashSequence.c''​ file so that it has full access to the necessary functions. You can watch the [[https://​www.youtube.com/​watch?​v=sRikFJVxYmU|''​flashSequence_runTest()''​]] video to see an example of the ''​flashSequence''​ state machine in action. +
- +
-<code C flashSequence.h>​ +
-/* +
- * flashSequence.h +
- * +
- ​* ​ Created on: Oct 28, 2014 +
- ​* ​     Author: hutch +
- */ +
- +
-#ifndef FLASHSEQUENCE_H_ +
-#define FLASHSEQUENCE_H_ +
- +
-// Turns on the state machine. Part of the interlock. +
-void flashSequence_enable();​ +
- +
-// Turns off the state machine. Part of the interlock. +
-void flashSequence_disable();​ +
- +
-// Other state machines can call this to determine if this state machine is finished. +
-bool flashSequence_isComplete();​ +
- +
-// Initialize the state machine +
-void flashSequence_init();​ +
- +
-// Standard tick function. +
-void flashSequence_tick();​ +
- +
-// Tests the flashSequence state machine. +
-void flashSequence_runTest();​ +
- +
-#endif /* FLASHSEQUENCE_H_ */ +
-</​code>​ +
- +
-<code c flashSequence_runTest.c>​ +
-// This will set the sequence to a simple sequential pattern. +
-// It starts by flashing the first color, and then increments the index and flashes the first +
-// two colors and so forth. Along the way it prints info messages to the LCD screen. +
-#define TEST_SEQUENCE_LENGTH 8 // Just use a short test sequence. +
-uint8_t flashSequence_testSequence[TEST_SEQUENCE_LENGTH] = { +
-    SIMON_DISPLAY_REGION_0,​ +
-    SIMON_DISPLAY_REGION_1,​ +
-    SIMON_DISPLAY_REGION_2,​ +
-    SIMON_DISPLAY_REGION_3,​ +
-    SIMON_DISPLAY_REGION_3,​ +
-    SIMON_DISPLAY_REGION_2,​ +
-    SIMON_DISPLAY_REGION_1,​ +
-    SIMON_DISPLAY_REGION_0};​ //​ Simple sequence. +
-#define INCREMENTING_SEQUENCE_MESSAGE1 "​Incrementing Sequence"​ //​ Info message. +
-#define RUN_TEST_COMPLETE_MESSAGE "​Runtest() Complete" ​         // Info message. +
-#define MESSAGE_TEXT_SIZE 2     // Make the text easy to see. +
-#define TWO_SECONDS_IN_MS 2000  // Two second delay. +
-#define TICK_PERIOD 75          // 200 millisecond delay. +
-#define TEXT_ORIGIN_X 0                  // Text starts from far left and +
-#define TEXT_ORIGIN_Y (DISPLAY_HEIGHT/​2) // middle of screen. +
- +
-// Print the incrementing sequence message. +
-void flashSequence_printIncrementingMessage() { +
-  display_fillScreen(DISPLAY_BLACK); ​ // Otherwise, tell the user that you are incrementing the sequence. +
-  display_setCursor(TEXT_ORIGIN_X,​ TEXT_ORIGIN_Y);//​ Roughly centered. +
-  display_println(INCREMENTING_SEQUENCE_MESSAGE1);//​ Print the message. +
-  utils_msDelay(TWO_SECONDS_IN_MS); ​  // Hold on for 2 seconds. +
-  display_fillScreen(DISPLAY_BLACK); ​ // Clear the screen. +
-+
- +
-// Run the test: flash the sequence, one square at a time +
-// with helpful information messages. +
-void flashSequence_runTest() { +
-  display_init();​  ​               // We are using the display. +
-  display_fillScreen(DISPLAY_BLACK);​ //​ Clear the display. +
-  globals_setSequence(flashSequence_testSequence,​ TEST_SEQUENCE_LENGTH);​ //​ Set the sequence. +
-  flashSequence_init(); ​              // Initialize the flashSequence state machine +
-  flashSequence_enable(); ​            // Enable the flashSequence state machine. +
-  int16_t sequenceLength = 1;         // Start out with a sequence of length 1. +
-  globals_setSequenceIterationLength(sequenceLength);​ //​ Set the iteration length. +
-  display_setTextSize(MESSAGE_TEXT_SIZE);​ // Use a standard text size. +
-  while (1) {                             // Run forever unless you break. +
-    flashSequence_tick();​  ​         // tick the state machine. +
-    utils_msDelay(TICK_PERIOD); ​  // Provide a 1 ms delay. +
-    if (flashSequence_isComplete()) { // When you are done flashing the sequence. +
-      flashSequence_disable(); ​         // Interlock by first disabling the state machine. +
-      flashSequence_tick(); ​            // tick is necessary to advance the state. +
-      utils_msDelay(TICK_PERIOD); ​      // don't really need this here, just for completeness. +
-      flashSequence_enable(); ​          // Finish the interlock by enabling the state machine. +
-      utils_msDelay(TICK_PERIOD); ​      // Wait 1 ms for no good reason. +
-      sequenceLength++; ​                // Increment the length of the sequence. +
-      if (sequenceLength > TEST_SEQUENCE_LENGTH) ​ // Stop if you have done the full sequence. +
-        break; +
-      // Tell the user that you are going to the next step in the pattern. +
-      flashSequence_printIncrementingMessage();​ +
-      globals_setSequenceIterationLength(sequenceLength); ​ // Set the length of the pattern. +
-    } +
-  } +
-  // Let the user know that you are finished. +
-  display_fillScreen(DISPLAY_BLACK); ​             // Blank the screen. +
-  display_setCursor(TEXT_ORIGIN_X,​ TEXT_ORIGIN_Y);//​ Set the cursor position. +
-  display_println(RUN_TEST_COMPLETE_MESSAGE); ​    // Print the message. +
-+
-</​code>​ +
- +
------- +
-==== verifySequence ​State Machine ​Code ==== +
- +
-The ''​verifySequence''​ state machine verifies that the user touched the colors in the correct sequence. It provides the ''​verifySequence_enable()''​ and ''​verifySequence_disable()''​ functions that operate as described for the ''​buttonHandler''​ and ''​flashSequence''​ state machines. In addition, it provides a function that can be called to determine if the ''​verifySequence''​ state machine has completed its task (''​verifySequence_complete()''​). It also provides two functions that can be called to determine if some error occurred: ''​verifySequence_isTimeOutError()''​ and ''​verifySequence_isUserInputError()''​. +
- +
-The ''​verifySequence''​ state machine operates as follows. First you enable the state machine by invoking ''​verifySequence_enable()''​. Once you enable the verifySequence state machine, it will use the buttonHandler state machine to accept the user sequence. Remember that the buttonHandler state machine draws the buttons, draws a larger square over the button when a touch is detected, provides a function that can be called to detect when a release has been detected (really, a touch followed by a release) and the necessary enable() and disable() functions. You use ''​buttonHandler_getRegionNumber()''​ to determine what color the user touched. Refer back to the system diagram at the top of the page to see how the buttonHandler state machine works with the verifySequence state machine. +
- +
-You check ''​verifySequence_complete()''​ to see if the state machine has finished its task of verifying the user-tapped color sequence. If this function returns true, the state machine has finished. Verify if an error occurred by calling both ''​verifySequence_isTimeOutError()''​ and ''​verifySequence_isUserInputError()''​. If neither of these functions returns true, the user touched the correct sequence of colored squares. Invoke ''​verifySequence_disable()''​ to synchronize state-machine operation. +
- +
-I have provided the entire contents of the ''​verifySequence.h''​ file. I also provided the code for ''​verifySequence_runTest()''​. You can see a demonstration of this test code running with my code by watching this [[https://​www.youtube.com/​watch?​v=VSF8d7UbnFQ|video]]. +
- +
-<code C verifySequence.h>​ +
-* verifySequence.h +
- * +
- ​* ​ Created on: Oct 28, 2014 +
- ​* ​     Author: hutch +
- */ +
- +
-#ifndef VERIFYSEQUENCE_H_ +
-#define VERIFYSEQUENCE_H_ +
- +
-// State machine will run when enabled. +
-void verifySequence_enable();​ +
- +
-// This is part of the interlock. You disable the state-machine and then enable it again. +
-void verifySequence_disable();​ +
- +
-// Used to detect if there has been a time-out error. +
-bool verifySequence_isTimeOutError();​ +
- +
-// Used to detect if the user tapped the incorrect sequence. +
-bool verifySequence_isUserInputError();​ +
- +
-// Used to detect if the verifySequence state machine has finished verifying. +
-bool verifySequence_isComplete();​ +
- +
-// Initialize the state machine +
-void verifySequence_init();​ +
- +
-// Standard tick function. +
-void verifySequence_tick();​ +
- +
-// Standard runTest function. +
-void verifySequence_runTest();​ +
- +
-#endif /* VERIFYSEQUENCE_H_ */ +
-</​code>​ +
- +
-verifySequence_runTest() code: +
-<code C verifySequence_runTest.c>​ +
-#define MESSAGE_X 0 +
-//#define MESSAGE_Y (display_width()/​4) +
-#define MESSAGE_Y (display_height()/​2) +
-#define MESSAGE_TEXT_SIZE 2 +
-//#define MESSAGE_STARTING_OVER +
-#define BUTTON_0 0  // Index for button 0 +
-#define BUTTON_1 1  // Index for button 1 +
-#define BUTTON_2 2  // Index for button 2 +
-#define BUTTON_3 3  // Index for button 3 +
-// Prints the instructions that the user should follow when +
-// testing the verifySequence state machine. +
-// Takes an argument that specifies the length of the sequence so that +
-// the instructions are tailored for the length of the sequence. +
-// This assumes a simple incrementing pattern so that it is simple to +
-// instruct the user. +
-void verifySequence_printInstructions(uint8_t length, bool startingOver) { +
-    display_fillScreen(DISPLAY_BLACK); ​             // Clear the screen. +
-    display_setTextSize(MESSAGE_TEXT_SIZE); ​    // Make it readable. +
-    display_setCursor(MESSAGE_X,​ MESSAGE_Y); ​   // Rough center. +
-    if (startingOver) {                                             // Print a message if you start over. +
-        display_fillScreen(DISPLAY_BLACK); ​         // Clear the screen if starting over. +
-        display_setTextColor(DISPLAY_WHITE); ​       // Print whit text. +
-        display_println("​Starting Over. "​); ​        // Starting over message. +
-    } +
-    // Print messages are self-explanatory,​ no comments needed. +
-    // These messages request that the user touch the buttons in a specific sequence. +
-    display_println("​Tap:​ "); +
-    display_println();​ +
-    switch (length) { +
-    case 1: +
-        display_println("​red"​);​ +
-        break; +
-    case 2: +
-        display_println("​red,​ yellow "); +
-        break; +
-    case 3: +
-        display_println("​red,​ yellow, blue "); +
-        break; +
-    case 4: +
-        display_println("​red,​ yellow, blue, green "); +
-        break; +
-    default: +
-        break; +
-    } +
-    display_println("​in that order."​);​ +
-    display_println();​ +
-    display_println("​hold BTN0 to quit."​);​ +
-+
- +
-// Just clears the screen and draws the four buttons used in Simon. +
-void verifySequence_drawButtons() { +
-    display_fillScreen(DISPLAY_BLACK); ​ // Clear the screen. +
-    simonDisplay_drawAllButtons(); ​     // Draw all the buttons. +
-+
- +
-// This will set the sequence to a simple sequential pattern. +
-#define MAX_TEST_SEQUENCE_LENGTH 4  // the maximum length of the pattern +
-uint8_t verifySequence_testSequence[MAX_TEST_SEQUENCE_LENGTH] = {0, 1, 2, 3};  // A simple pattern. +
-#define MESSAGE_WAIT_MS 4000  // Display messages for this long. +
- +
-// Increment ​the sequence length making sure to skip over 0. +
-// Used to change the sequence length during the test. +
-int16_t incrementSequenceLength(int16_t sequenceLength) { +
-    int16_t value = (sequenceLength + 1) % (MAX_TEST_SEQUENCE_LENGTH+1);​ +
-    if (value == 0) value++; +
-    return value; +
-+
- +
-// Used to select from a variety of informational messages. +
-enum verifySequence_infoMessage_t { +
-    user_time_out_e, ​           // means that the user waited too long to tap a color. +
-    user_wrong_sequence_e, ​     // means that the user tapped the wrong color. +
-    user_correct_sequence_e, ​   // means that the user tapped the correct sequence. +
-    user_quit_e ​                // means that the user wants to quite. +
-}; +
- +
-// Prints out informational messages based upon a message type (see above). +
-void verifySequence_printInfoMessage(verifySequence_infoMessage_t messageType) { +
-    // Setup text color, position and clear the screen. +
-    display_setTextColor(DISPLAY_WHITE);​ +
-    display_setCursor(MESSAGE_X,​ MESSAGE_Y);​ +
-    display_fillScreen(DISPLAY_BLACK);​ +
-  switch(messageType) { +
-  case user_time_out_e: ​ // Tell the user that they typed too slowly. +
-        display_println("​Error:"​);​ +
-        display_println();​ +
-        display_println(" ​ User tapped sequence"​);​ +
-        display_println(" ​ too slowly."​);​ +
-    break; +
-  case user_wrong_sequence_e: ​ // Tell the user that they tapped the wrong color. +
-        display_println("​Error:​ "); +
-        display_println();​ +
-        display_println(" ​ User tapped the"​);​ +
-        display_println(" ​ wrong sequence."​);​ +
-    break; +
-  case user_correct_sequence_e: ​ // Tell the user that they were correct. +
-        display_println("​User tapped"​);​ +
-        display_println("​the correct sequence."​);​ +
-    break; +
-  case user_quit_e: ​            // Acknowledge that you are quitting the test. +
-    display_println("​quitting runTest()."​);​ +
-    break; +
-  default: +
-    break; +
-  } +
-+
- +
-#define TICK_PERIOD_IN_MS 100 +
-// Tests the verifySequence state machine. +
-// It prints instructions to the touch-screen. The user responds by tapping the +
-// correct colors to match the sequence. +
-// Users can test the error conditions by waiting too long to tap a color or +
-// by tapping an incorrect color. +
-void verifySequence_runTest() { +
-    display_init(); ​ // Always must do this. +
-    buttons_init(); ​ // Need to use the push-button package so user can quit. +
-    int16_t sequenceLength = 1;  // Start out with a sequence length of 1. +
-    verifySequence_printInstructions(sequenceLength,​ false); ​ // Tell the user what to do. +
-    utils_msDelay(MESSAGE_WAIT_MS); ​ // Give them a few seconds to read the instructions. +
-    verifySequence_drawButtons(); ​   // Now, draw the buttons. +
-    // Set the test sequence and it's length. +
-    globals_setSequence(verifySequence_testSequence,​ MAX_TEST_SEQUENCE_LENGTH);​ +
-    globals_setSequenceIterationLength(sequenceLength);​ +
-    verifySequence_init(); ​   // Initialize the verifySequence state machine +
-    // Enable the verifySequence state machine. +
-    verifySequence_enable(); ​ // Everything is interlocked,​ so first enable the machine. +
-    // Need to hold button until it quits as you might be stuck in a delay. +
-    while (!(buttons_read() & BUTTONS_BTN0_MASK)) {  +
-        // verifySequence uses the buttonHandler state machine so you need to "​tick"​ both of them. +
-        verifySequence_tick(); ​ // Advance the verifySequence state machine. +
-        buttonHandler_tick(); ​  // Advance the buttonHandler state machine. +
-        utils_msDelay(TICK_PERIOD_IN_MS); ​      // Wait for a tick period. +
-        // If the verifySequence state machine has finished, check the result,  +
-        // otherwise just keep ticking both machines. +
-        if (verifySequence_isComplete()) { +
-            if (verifySequence_isTimeOutError()) {                // Was the user too slow? +
-                verifySequence_printInfoMessage(user_time_out_e);​ // Yes, tell the user that they were too slow. +
-            } else if (verifySequence_isUserInputError()) {       // Did the user tap the wrong color? +
-                verifySequence_printInfoMessage(user_wrong_sequence_e);​ // Yes, tell them so. +
-            } else { +
-                verifySequence_printInfoMessage(user_correct_sequence_e);​ // User was correct if you get here. +
-            } +
-            utils_msDelay(MESSAGE_WAIT_MS); ​                           // Allow the user to read the message. +
-            sequenceLength = incrementSequenceLength(sequenceLength); ​ // Increment the sequence. +
-            globals_setSequenceIterationLength(sequenceLength); ​       // Set the length for the verifySequence state machine. +
-            verifySequence_printInstructions(sequenceLength,​ true); ​   // Print the instructions. +
-            utils_msDelay(MESSAGE_WAIT_MS); ​                           // Let the user read the instructions. +
-            verifySequence_drawButtons(); ​                             // Draw the buttons. +
-            verifySequence_disable(); ​                                 // Interlock: first step of handshake. +
-            verifySequence_tick(); ​                                    // Advance the verifySequence machine. +
-            buttonHandler_tick();​ +
-            utils_msDelay(TICK_PERIOD_IN_MS); ​                         // Wait for tick period. +
-            verifySequence_enable(); ​                                  // Interlock: second step of handshake. +
-            utils_msDelay(TICK_PERIOD_IN_MS); ​                         // Wait for tick period. +
-        } +
-    } +
-    verifySequence_printInfoMessage(user_quit_e); ​ // Quitting, print out an informational message. +
-+
-</​code>​ +
- +
----- +
-==== globals Code ==== +
- +
-There a small amount of information that is shared between state machines. This global information is managed by code in ''​globals.h''​ and ''​globals.c''​. Rather than just directly reading and/or writing global variables directly, it is much better practice to create functional interfaces to these variables. The ''​globals''​ code provides functions to set the sequence of colors by copying a supplied array of region numbers. A function to get the sequence length and functions to get and set the sequence iteration length are also provided. The iteration length indicates how far along you are in the current sequence. You can see how these functions are used in the various ''​runTest()''​ functions that I have provided. +
-I provide ''​globals.h''​ and part of ''​globals.c''​. You will need to finish ''​globals.c''​. +
-<code C globals.h>​ +
-#ifndef GLOBALS_H_ +
-#define GLOBALS_H_ +
- +
-#define GLOBALS_MAX_FLASH_SEQUENCE 1000                  // Make it big so you can use it for a splash screen. +
- +
-// This is the length of the complete sequence at maximum length. +
-// You must copy the contents of the sequence[array into the global variable that you maintain. +
-// Do not just grab the pointer as this will fail. +
-void globals_setSequence(const uint8_t sequence[], uint16_t length); +
- +
-// This returns the value of the sequence at the index. +
-uint8_t globals_getSequenceValue(uint16_t index); +
- +
-// Retrieve the sequence length. +
-uint16_t globals_getSequenceLength();​ +
- +
-// This is the length of the sequence that you are currently working on. +
-void globals_setSequenceIterationLength(uint16_t length); +
- +
-// This is the length of the sequence that you are currently working on,  +
-// not the maximum length but the interim length as +
-// the person playing the game works through the pattern one color at a time. +
-uint16_t globals_getSequenceIterationLength();​ +
- +
-#endif /* GLOBALS_H_ */ +
- +
-</​code>​ +
- +
-<code C globals.c>​ +
-#include "​globals.h"​ +
- +
-// The length of the sequence. +
-// The static keyword means that globals_sequenceLength can only be accessed +
-// by functions contained in this file.  +
-static uint16_t globals_sequenceLength = 0;  // The length of the sequence. +
- +
-// This is the length of the sequence that you are currently working on,  +
-// not the maximum length but the interim length as +
-// the user works through the pattern one color at a time. +
-void globals_setSequenceIterationLength(uint16_t length) {globals_sequenceIterationLength = length;} +
-   +
-// You will need to implement the other functions. +
- +
-</​code>​+
  
 ---- ----
Line 689: Line 79:
 ==== simonMain.c ==== ==== simonMain.c ====
  
-You will call all of your tick functions and provide necessary initializations in the ''​main()''​ function ​that is contained by the ''​simonMain.c''​ file.+This file will contain ​your ''​main()''​ function.
  
 ---- ----
Line 717: Line 107:
 Follow [[http://​ecen330wiki.groups.et.byu.net/​wiki/​doku.php?​id=submitting_source_code|this procedure]] to submit your source code to learning-suite. Follow [[http://​ecen330wiki.groups.et.byu.net/​wiki/​doku.php?​id=submitting_source_code|this procedure]] to submit your source code to learning-suite.
  
- 
- 
-  
-==== Resources ==== 
-Links to all demonstration videos are provided here for ease of access. 
- 
-  * [[https://​youtu.be/​WPfn4OeQEcY?​list=PLByav2teIdI8VqwtHa8JdGmj9irX6Ff4h|simonDisplay_runTest() demonstration video]]. 
-  * [[https://​youtu.be/​wTtFDRLLhfc?​list=PLByav2teIdI8VqwtHa8JdGmj9irX6Ff4h|buttonHandler_runTest() demonstration video]]. 
-  * [[https://​youtu.be/​sRikFJVxYmU?​list=PLByav2teIdI8VqwtHa8JdGmj9irX6Ff4h|flashSequence_runTest() demonstration video.]] 
-  * [[https://​youtu.be/​VSF8d7UbnFQ?​list=PLByav2teIdI8VqwtHa8JdGmj9irX6Ff4h|verifySequence_runTest() demonstration video.]] 
-  * [[https://​youtu.be/​IxI0QVZK86A?​list=PLByav2teIdI8VqwtHa8JdGmj9irX6Ff4h|Simon demonstration video]]. 
- 
----- 
- 
-==== Helps: Generating Random Sequences ==== 
-If you include "​stdlib.h"​ you have access to two functions that help to generate random sequences. The first function ''​srand(seed)''​ allows you to provide a "​seed"​ value for the random number generator. Each time you call ''​rand()'',​ it will return an integer that is part of a pseudorandom sequence. However, each time you start your program from scratch, ''​rand()''​ will return the same sequence of integers. If, each time you run the program, you start out by invoking ''​srand(seed)''​ with a different seed value, the sequence will be different each time you run the program. So, how do you generate random seed values? In my Simon game I am incrementing a counter while waiting for the user to touch the screen. Once the user touches the screen, I take the counter value and use that as the seed value for ''​srand()''​. 
- 
-Review this [[http://​www.cplusplus.com/​reference/​cstdlib/​srand/​|link]] for a more detailed explanation of and another example of how to use ''​srand()''​ and ''​rand()''​. 
- 
- 
----- 
- 
-==== Helps: Enabling and disabling test code using ''#​ifdef''​ and ''#​endif''​ ==== 
-See the code below for an example of how to individually compile test code. This comes from my ''​simonMain.c''​ file. As currently configured, this code will run the ''​simonDisplay_runTest()''​. 
-<code C> 
-#define RUN_SIMON_DISPLAY_TEST 
- 
-#ifdef RUN_SIMON_DISPLAY_TEST 
-#define MAX_TOUCH_COUNT 10 
-int main() { 
-  simonDisplay_runTest(MAX_TOUCH_COUNT);​ 
-} 
-#endif 
- 
-//#define RUN_BUTTON_HANDLER_TEST 
-#ifdef RUN_BUTTON_HANDLER_TEST 
-#define MAX_TOUCH_COUNT 10 
-int main() { 
-  buttonHandler_runTest(MAX_TOUCH_COUNT);​ 
-} 
-#endif 
- 
-// #define RUN_FLASH_SEQUENCE_TEST 
-#ifdef RUN_FLASH_SEQUENCE_TEST 
-int main() { 
-  flashSequence_runTest();​ 
-} 
-#endif 
- 
-//#define RUN_VERIFY_SEQUENCE_TEST 
-#ifdef RUN_VERIFY_SEQUENCE_TEST 
-int main() { 
-  verifySequence_runTest();​ 
-} 
-#endif 
-</​code>​ 
- 
----- 
- 
-==== Helps: Using Function Pointers to Time the Duration of Individual Ticks ==== 
-I wrote a function called ''​tickTimer()''​ that made it relatively easy to measure how long various tick functions were taking. You may find it useful to debug timing issues. I'll provide the code here and you can modify it to suit you. Note that it uses a function-pointer so that you can pass in the pointer to a tick function so it can be invoked and timed. As currently written, it keeps track of which tick() function consumed the most time. I found it quite helpful while debugging my Simon game because I could see what state-machine was taking the most time at any point during the game. 
- 
-<code C> 
-typedef void (functionPointer_t)();​ 
-#define MAX_DURATION_TIMER_INDEX 2 
-static double maxDuration_g; ​                   // The max duration across all ticks. 
-static const char* maxDurationFunctionName_g; ​  // Name of the function with longest duration is here. 
-#define SIMON_CONTROL_TICK "​simonControl_tick()"​ 
-#define VERIFY_SEQUENCE_TICK "​verifySequence_tick()"​ 
-#define FLASH_SEQUENCE_TICK "​flashSequency_tick()"​ 
-#define BUTTON_HANDLER_TICK "​buttonHandler_tick()"​ 
- 
-// This assumes that the interval timer was initialized. 
-static void tickTimer(functionPointer_t* fp, 
-                      const char* functionName) { 
-    intervalTimer_reset(MAX_DURATION_TIMER_INDEX); ​ // Reset the timer. 
-    intervalTimer_start(MAX_DURATION_TIMER_INDEX); ​ // Start the timer. 
-    fp();                                           // Invoke the tick function passed in by argument. 
-    intervalTimer_stop(MAX_DURATION_TIMER_INDEX); ​  // Stop the timer. 
-    double duration; ​                               // Keep track of tick duration. 
-    duration = intervalTimer_getTotalDurationInSeconds(MAX_DURATION_TIMER_INDEX); ​ // Get the duration. 
-    if (maxDuration_g < duration) {                 // Keep the max duration. 
-        maxDuration_g = duration; 
-        maxDurationFunctionName_g = functionName; ​  // Keep track of which function had the max. duration. 
-    } 
-} 
-</​code>​ 
- 
-To use the code, just wrap ''​tickTimer() around your normal tick function as shown below. 
- 
-<code C> 
-  tickTimer(flashSequence_tick,​ FLASH_SEQUENCE_TICK); ​   // Tick a state machine. 
-  tickTimer(verifySequence_tick,​ VERIFY_SEQUENCE_TICK); ​ // Tick a state machine. 
-  tickTimer(buttonHandler_tick,​ BUTTON_HANDLER_TICK); ​   // Tick a state machine. 
-  tickTimer(simonControl_tick,​ SIMON_CONTROL_TICK); ​     // Tick a state machine. 
-  ... 
-</​code>​ 
  
lab_6.1542227950.txt.gz · Last modified: 2019/04/08 20:12 (external edit)