This shows you the differences between two versions of the page.
milestone_3_task_2 [2023/02/20 17:06] scott [General Requirements] |
milestone_3_task_2 [2023/03/02 17:53] (current) scott [Source Code] |
||
---|---|---|---|
Line 16: | Line 16: | ||
==== General Requirements ==== | ==== General Requirements ==== | ||
- | - You only need to implement the functionality required to achieve the pass-off requirements (see the [[milestone_3_task_2#test_and_pass_off|Test and Pass Off section]] for these requirements). Don't worry about tying everything together in this milestone. That will happen in the next task. | ||
- All functionality must be implemented as state machines using the techniques that were taught during ECEn 330. Please review the required [[https://byu-cpe.github.io/ecen330/other/coding-state-machines/|approach]] for implementing and debugging state machines. Part of your grade will be derived from the quality of your code and your ability to follow these guidelines. | - All functionality must be implemented as state machines using the techniques that were taught during ECEn 330. Please review the required [[https://byu-cpe.github.io/ecen330/other/coding-state-machines/|approach]] for implementing and debugging state machines. Part of your grade will be derived from the quality of your code and your ability to follow these guidelines. | ||
- Completely follow the specifications for each state machine. | - Completely follow the specifications for each state machine. | ||
Line 28: | Line 27: | ||
* For this project, implement ''isr_init()'' and ''isr_function()'' in ''isr.c''. The ''isr.h'' header file is included in the ecen390 project directory. | * For this project, implement ''isr_init()'' and ''isr_function()'' in ''isr.c''. The ''isr.h'' header file is included in the ecen390 project directory. | ||
* Interval timer 0 is used to measure cumulative time spent in the interrupt service routine. You cannot use it for anything else. However, interval timers 1 and 2 are available for your use. | * Interval timer 0 is used to measure cumulative time spent in the interrupt service routine. You cannot use it for anything else. However, interval timers 1 and 2 are available for your use. | ||
- | * In the next task, the code that I give you will use all of the interval timers so, if you use them now, remember to disable any code that uses the interval timers for the next task (Task 3). | + | * In the next task, the provided code will use all of the interval timers so, if you use them now, remember to disable any code that uses the interval timers for the next task (Task 3). |
==== Warnings ==== | ==== Warnings ==== | ||
Line 63: | Line 62: | ||
* drivers/switches.h | * drivers/switches.h | ||
* include/leds.h | * include/leds.h | ||
+ | * include/mio.h | ||
+ | * include/utils.h | ||
* lasertag/main.c | * lasertag/main.c | ||
* lasertag/transmitter.h | * lasertag/transmitter.h | ||
Line 89: | Line 90: | ||
* Minimum frequency is selected when frequencyNumber = 0, maximum frequency is selected when frequencyNumber = 9. | * Minimum frequency is selected when frequencyNumber = 0, maximum frequency is selected when frequencyNumber = 9. | ||
* All waveforms must be [[http://en.wikipedia.org/wiki/Duty_cycle | 50% duty cycle]]. | * All waveforms must be [[http://en.wikipedia.org/wiki/Duty_cycle | 50% duty cycle]]. | ||
- | * You will set the waveform frequency by reading the ZYBO slide switches in a function called from ''main()'' and then calling ''transmitter_setFrequencyNumber()''. Do **not** read the slide switches in the transmitter state machine. | + | * You will set the waveform frequency by reading the ZYBO slide switches from main program code and then calling ''transmitter_setFrequencyNumber()''. **Do not** read the slide switches in the transmitter state machine which will be called under interrupt. |
* Include the file "filter.h" so that you can have access to the frequency tick counts that are provided there. If you need half-period counts for your state machine, you can divide the values by 2. | * Include the file "filter.h" so that you can have access to the frequency tick counts that are provided there. If you need half-period counts for your state machine, you can divide the values by 2. | ||
* The state machine must ensure that JF-1 is a '0' when inactive. Otherwise you will drain the battery because the gun's "shoot" LED will constantly illuminate. | * The state machine must ensure that JF-1 is a '0' when inactive. Otherwise you will drain the battery because the gun's "shoot" LED will constantly illuminate. | ||
Line 136: | Line 137: | ||
==== Debouncing Switches ==== | ==== 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 quickly changes values this way when it is initially pressed (or released), we say that the switch "bounces". | + | 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". |
- | Switch bouncing is problematic for us because we want our system to run reliably in single-shot mode, delivering exactly one shot for each press/release of the trigger. The easiest way to debounce a switch for us 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 50 ms for the switch to stop bouncing. This is a reasonable assumption for a switch that is in good working order. | + | 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 50 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 1: Wait until you detect a press of the button. | ||
Line 200: | Line 201: | ||
</code> | </code> | ||
- | During ''trigger_init()'' (this gets called as soon as ''main()'' starts to execute), I check to see if the "trigger" is currently pressed. If it is, I presume that the gun cable is not connected as pins "float" to a high logic value when they are left unconnected. Otherwise, the OR of the push-button and the actual gun-trigger will always evaluate to '1' when you run the program with the gun-cable unconnected. Note that the variable ''ignoreGunInput'' is a static ''bool'' variable that initialized to 0 (false). This allows me to perform all of my testing with the BTN0 pushbutton but also allows me to test with the gun. I just have to make sure that the gun is connected when I start or download the program and then I can use either the gun-trigger or BTN0. | + | During ''trigger_init()'', which gets called before ''triggerPressed()'', I check to see if the "trigger" is currently pressed. If it is, I presume that the gun cable is not connected. This is because pins commonly "float" to a high logic value when they are left unconnected. Otherwise, the OR of the push-button and the actual gun-trigger will always evaluate to '1' when you run the program with the gun-cable unconnected. Note that the variable ''ignoreGunInput'' is a static ''bool'' variable that is initialized to 0 (false). This allows me to perform all of my testing with the BTN0 push button, but it also allows me to use the gun when it is connected. I just have to make sure that the gun is connected when I start or download the program. Then I can use either the gun-trigger or BTN0 to signal a trigger press. |
<code C> | <code C> | ||
Line 324: | Line 325: | ||
void transmitter_runTest() { | void transmitter_runTest() { | ||
printf("starting transmitter_runTest()\n"); | printf("starting transmitter_runTest()\n"); | ||
- | mio_init(false); | ||
- | buttons_init(); // Using buttons | ||
- | switches_init(); // and switches. | ||
transmitter_init(); // init the transmitter. | transmitter_init(); // init the transmitter. | ||
- | transmitter_enableTestMode(); // Prints diagnostics to stdio. | + | while (!(buttons_read() & BUTTONS_BTN3_MASK)) { // Run continuously until BTN3 is pressed. |
- | while (!(buttons_read() & BUTTONS_BTN1_MASK)) { // Run continuously until BTN1 is pressed. | + | |
uint16_t switchValue = switches_read() % FILTER_FREQUENCY_COUNT; // Compute a safe number from the switches. | uint16_t switchValue = switches_read() % FILTER_FREQUENCY_COUNT; // Compute a safe number from the switches. | ||
transmitter_setFrequencyNumber(switchValue); // set the frequency number based upon switch value. | transmitter_setFrequencyNumber(switchValue); // set the frequency number based upon switch value. | ||
Line 339: | Line 336: | ||
printf("completed one test period.\n"); | printf("completed one test period.\n"); | ||
} | } | ||
- | transmitter_disableTestMode(); | ||
do {utils_msDelay(BOUNCE_DELAY);} while (buttons_read()); | do {utils_msDelay(BOUNCE_DELAY);} while (buttons_read()); | ||
printf("exiting transmitter_runTest()\n"); | printf("exiting transmitter_runTest()\n"); | ||
Line 411: | Line 407: | ||
=== Testing Non-Continuous Mode === | === Testing Non-Continuous Mode === | ||
- | You can access this signal at the "Transmitter Probe" pin on the development board. To test the transmitter state machine, do the following in a while-loop in ''transmitter_runTest''. In an endless loop in ''transmitter_runTest()'' do the following: | + | You can access this signal at the "Transmitter Probe" pin on the development board. To test the transmitter state machine, do the following in ''transmitter_runTest''. |
- | - Read the slide switches and use the numerical value of the switches as the frequency index. When all switches are in the down position (closest to the bottom of the board), you should generate the waveform for frequency 0. Sliding switch (SW0) upward would select frequency 1, and so forth. | + | - Loop until Button 3 is pressed: |
- | - Set the frequency using the ''transmitter_setFrequencyNumber()'' function. | + | - Read the slide switches and use the numerical value of the switches as the frequency index. When all switches are in the down position (closest to the bottom of the board), you should generate the waveform for frequency 0. Sliding switch (SW0) upward would select frequency 1, and so forth. |
- | - Invoke ''transmitter_run()''. | + | - Set the frequency using the ''transmitter_setFrequencyNumber()'' function. |
- | - Use the utils_msDelay() function to delay for approximately 400 ms, long enough to demonstrate the operation of the transmitter on the oscilloscope. | + | - Invoke ''transmitter_run()'' and then wait for ''transmitter_running()'' to return false. |
+ | - Use the utils_msDelay() function to delay for approximately 400 ms, long enough to demonstrate the operation of the transmitter on the oscilloscope. | ||
+ | - Wait for the release of Button 3. | ||
- | Demonstrate to the TA, using an oscilloscope probe attached to "Transmitter Probe" pin, that the waveform is the correct shape and frequency for each of the 10 slide-switch settings. You should be able to see that the waveform is generated for 200 ms with a "dead time" of about 400 ms where the output goes to 0 between each successive generation of the waveform. | + | Demonstrate to a TA, using an oscilloscope probe attached to "Transmitter Probe" pin, that the waveform is the correct shape and frequency for each of the 10 slide-switch settings. You should be able to see that the waveform is generated for 200 ms with a "dead time" of about 400 ms where the output goes to 0 between each successive generation of the waveform. |
=== Testing Continuous mode === | === Testing Continuous mode === | ||
Access and display the transmitter output as described above. In your test function, do the following: | Access and display the transmitter output as described above. In your test function, do the following: | ||
- | - Invoke ''transmitter_init()'', ''transmitter_setContinuousMode(true)'', ''transmitter_run()'' prior to entering an endless loop. | + | - Invoke ''transmitter_setContinuousMode(true)'', ''transmitter_run()'' prior to entering an endless loop. |
- | - In an endless while-loop: | + | - Loop until Button 3 is pressed: |
- Set the frequency using the ''transmitter_setFrequencyNumber()'' function. | - Set the frequency using the ''transmitter_setFrequencyNumber()'' function. | ||
+ | - Wait for the release of Button 3. | ||
- | Demonstrate to the TA that the waveform that is displayed is continuous and changes to the correct frequency as the slide switches are changed. | + | Demonstrate to a TA that the displayed waveform is continuous and changes to the correct frequency as the slide switches are changed. |
{{::transmitterprobepicture.jpg?400|}} | {{::transmitterprobepicture.jpg?400|}} | ||
Line 435: | Line 434: | ||
As you transition to the state when the "press" of the trigger has been successfully debounced, print the 'D' character to the console using the DPCHAR() macro shown above. Make sure that you only print the 'D' character once as you enter the state when the trigger switch has been successfully debounced. Similarly, print out a 'U' character when the release of the trigger has been debounced. If you only print out a single 'D' and a single 'U' for each press-release of BTN0, the trigger is probably getting debounced properly. | As you transition to the state when the "press" of the trigger has been successfully debounced, print the 'D' character to the console using the DPCHAR() macro shown above. Make sure that you only print the 'D' character once as you enter the state when the trigger switch has been successfully debounced. Similarly, print out a 'U' character when the release of the trigger has been debounced. If you only print out a single 'D' and a single 'U' for each press-release of BTN0, the trigger is probably getting debounced properly. | ||
- | In ''trigger_runTest()'', enable the trigger state machine and demonstrate the required behavior to the TA. | + | In ''trigger_runTest()'', enable the trigger state machine and demonstrate the required behavior to a TA. |
==== Hit LED Timer ==== | ==== Hit LED Timer ==== | ||
- | Goal: demonstrate that your hitLedTimer state machine illuminates LD0 and drives a '1' onto the JF-3 pin for 1/2 second each time it is activated. You will see LED1 blink on and off at 1/2-second intervals if this is working correctly. LED1 is directly connected to JF-3 and is provided for debug and pass off. | + | Goal: demonstrate that your hitLedTimer state machine illuminates LD0 and drives a '1' onto the JF-3 pin for 1/2 second each time it is activated. You will see LED1 blink on for 1/2-second intervals if this is working correctly. LED1 is directly connected to JF-3 and is provided for debug and pass off. |
In your ''hitLedTimer_runTest()'' function do the following inside a while-loop: | In your ''hitLedTimer_runTest()'' function do the following inside a while-loop: | ||
- | * Step 1: invoke ''hitLedTimer_start()'', | + | * Step 1: invoke ''hitLedTimer_start()''. |
* Step 2: wait until ''hitLedTimer_running()'' is false (use another while-loop for this). | * Step 2: wait until ''hitLedTimer_running()'' is false (use another while-loop for this). | ||
- | * Delay for 300 ms using utils_msDelay(). | + | * Step 3: Delay for 300 ms using utils_msDelay(). |
* Go back to Step 1. | * Go back to Step 1. | ||
- | Demonstrate to the TA that LD0 and LED1 is blinking at a rate of 1/2 second (this will be approximate because this will be a visual inspection). Also show the TA that pin "Hit Detected" has the appropriate waveform by attaching an oscilloscope probe to the "Hit Detected" pin. | + | Demonstrate to a TA that LD0 and LED1 blink on for 1/2 second (this is an approximate visual inspection). |
{{::hitledpicture.jpg?400|}} | {{::hitledpicture.jpg?400|}} | ||
Line 462: | Line 461: | ||
- Print out the time duration from the interval timer. | - Print out the time duration from the interval timer. | ||
- | Show the TA the execution of ''lockoutTimer_runTest()''. | + | Show a TA the execution of ''lockoutTimer_runTest()''. |
==== Code Submission ==== | ==== Code Submission ==== |