Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| learn:microprocessor:tutorials:software-timing-delays:start [2017/12/21 19:16] – Martha | learn:microprocessor:tutorials:software-timing-delays:start [2022/10/04 11:38] (current) – external edit 127.0.0.1 | ||
|---|---|---|---|
| Line 3: | Line 3: | ||
| The purpose of this project is to investigate methods of creating software time delays to pace processor operations. The timing methods that control when a process is to be executed can be implemented by using polling techniques that sample a free running clock or by executing code that requires a fixed amount of time. This project also demonstrates the use of time delays for signal conditioning. | The purpose of this project is to investigate methods of creating software time delays to pace processor operations. The timing methods that control when a process is to be executed can be implemented by using polling techniques that sample a free running clock or by executing code that requires a fixed amount of time. This project also demonstrates the use of time delays for signal conditioning. | ||
| + | |||
| + | \\ | ||
| + | < | ||
| + | <a href="/ | ||
| + | </ | ||
| + | \\ | ||
| + | \\ | ||
| + | < | ||
| + | <a href="/ | ||
| + | </ | ||
| + | \\ | ||
| + | \\ | ||
| + | < | ||
| + | <a href="/ | ||
| + | </ | ||
| + | \\ | ||
| + | \\ | ||
| + | < | ||
| + | <a href="/ | ||
| + | </ | ||
| + | \\ | ||
| + | \\ | ||
| ---- | ---- | ||
| Line 13: | Line 35: | ||
| ==== Inventory ==== | ==== Inventory ==== | ||
| - | * 1 [[http://store.digilentinc.com/ | + | * 1 [[https://digilent.com/shop/ |
| * [[http:// | * [[http:// | ||
| - | * 1 [[http://store.digilentinc.com/ | + | * 1 [[https://digilent.com/shop/ |
| - | * 1 Logic analyzer or oscilloscope (Digilent [[http://store.digilentinc.com/ | + | * 1 Logic analyzer or oscilloscope (Digilent [[https://digilent.com/ |
| ---- | ---- | ||
| + | |||
| + | ==== Time Delays: How Long Does It Take? ==== | ||
| + | Usually, the greater the amount of time required to execute software, the greater the detriment to system performance. However, there are instances when the program actually runs too fast and we are challenged with developing ways of slowing it down. Each instruction that the processor executes requires a finite amount of time. Sometimes we can use the inherent code execution time to our advantage by using program loops to generate a delay. Consider the program shown in Listing 1 that adds two integer variables and assigns the result to a third integer variable: | ||
| + | |||
| + | **Listing 1.** | ||
| + | < | ||
| + | /* | ||
| + | * File: P1.c | ||
| + | * Author: Richard Wall | ||
| + | * | ||
| + | * Created on July 5, 2013, 12:56 PM | ||
| + | */ | ||
| + | |||
| + | #include < | ||
| + | #include " | ||
| + | |||
| + | int main(void) | ||
| + | { | ||
| + | int a = 5, b = 10, c; | ||
| + | |||
| + | c = a+b; | ||
| + | | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Each C source code statement is implemented by translating it into instructions that a given processor understands. The processor then acts on those instructions. The actual processor-level instructions that implement this statement can be represented in assembly language. Unlike C, which is considered a “high level” language that is independent of hardware, assembly language is specific to the particular processor on which it runs. The assembly language instructions are subsequently converted to a machine language that the processor actually uses. Although it is beyond the scope of this project to teach assembly language programming, | ||
| + | |||
| + | **Listing 2.** Assembly language code generated for the add statement. | ||
| + | < | ||
| + | 9D000038 | ||
| + | 9D00003C | ||
| + | 9D000040 | ||
| + | 9D000044 | ||
| + | </ | ||
| + | |||
| + | The first two columns of Listing 2 are 32-bit numbers expressed in hexadecimal notation. The first column shows the memory address where the machine code is stored. The second column is the actual machine code that the processor uses to implement the instruction shown in third and fourth columns. The third column gives the type of assembly language instructions used and the processor memory registers that are involved are given in the fourth column. Any text that follows a semicolon is processed as a comment, as illustrated by the fifth column. | ||
| + | |||
| + | To determine how many cycles each instruction takes, we need to look at the assembly language for the MIPS architecture that the PIC™32MX uses. The conversion of C instructions to assembly code can be different depending on the selection of compiler optimization options. If the PIC32MX is running the core clock at 80 MHz, one CPU cycle is 12.5 ns (i.e., the period of one clock cycle is the inverse of the clock frequency). | ||
| + | |||
| + | Due to the pipeline architecture used by the MIPS core, it is difficult to isolate a segment of code and determine its execution time strictly based upon the number of instructions. By using the Stopwatch diagnostics tool embedded in the MPLAB® X IDE to time the four instructions in Listing 2, we find that 31 CPU cycles are required. (See the Determining Delay Constants tab on the right for instructions on how to use the MPLAB X Stopwatch feature.) This equates to 387.5 ns when the core clock is running at 80 MHz. Setting aside the difficulties of determining time to execute from lines of assembly code, the sheer number of instructions is a relative indicator. The execution time required to complete any particular C instruction is determined by the complexity of the C statement, the assembly code that the C instruction is translated into, and the speed at which the processor is running. | ||
| + | |||
| + | It is sufficient to say that each C instruction can require multiple assembly language instructions to implement and each assembly instruction requires time to execute. The bottom line is that the execution of C code requires time and we can use this time to generate predictable time delays. An alternate method to generate a time delay is to use one of the timer resources of the processor. We will investigate both of these two methods of creating a delay function in this project. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Pacing the Computer ==== | ||
| + | When the processor executes code faster than the application requires, microprocessor based systems have time to spare. Say, for example, that there is an application where an LED is to be toggled between on and off at a one-second rate. The LATGINV instruction used to toggle the I/O pin only requires four processor clock cycles accounting for 50 ns of the desired one second period. Since the microprocessor always wants to be executing the next line of code at a rate of 80 million instructions per second, additional instructions must be executed to implement a time delay operation. In most cases, the time delay function has no other purpose than to kill time. | ||
| + | |||
| + | Two approaches for developing a delay function will be explored in this project. One of them requires no special hardware but instead relies strictly upon the execution time of a software loop. We will refer to this approach as the software delay method. The other uses one of the processor' | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Pure Software Delays ==== | ||
| + | First, let us focus on the software delay method by considering a software loop to generate a delay function. There are three ways to write a software loop: a “for” loop, a “while” loop, and a “do -while” loop. An example of using a for loop is: for (i = 0; i < COUNTS_PER_MS; | ||
| + | |||
| + | **Listing 3.** Software delay function. | ||
| + | < | ||
| + | /* The following define statements is declared in chipKIT_PROM_MX.h | ||
| + | #define LEDA BIT_2 // IOPORT B | ||
| + | */ | ||
| + | |||
| + | /* The following define statement is declared in Project2.h | ||
| + | #define COUNTS_PER_MS | ||
| + | */ | ||
| + | |||
| + | void sw_msDelay (unsigned int mS) | ||
| + | { | ||
| + | int i; | ||
| + | | ||
| + | { | ||
| + | for (i = 0; i< COUNTS_PER_MS; | ||
| + | { | ||
| + | // do nothing | ||
| + | } | ||
| + | LATBINV = LEDA; // Toggle LEDA each ms for instrumentation | ||
| + | } | ||
| + | } // SW Stop breakpoint | ||
| + | </ | ||
| + | |||
| + | Given that the “mS” variable that specifies the number of milliseconds to delay is an unsigned integer, the range, or longest possible delay, is 4, | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Calibrating Time for Software Delays ==== | ||
| + | When using the “sw_msDelay” function shown in Listing 3 above, a couple of questions come to mind. “What value should be used for COUNTS_PER_MS and how accurate is the delay?” Subsequently, | ||
| + | |||
| + | We suggest that software code be added to allow the software operation to be observable. One way to accomplish this is by toggling an I/O pin on the microprocessor. This requires that external instrumentation be connected to the pin of the board connected to that I/O port for the bit being toggled. Suitable instrumentation for this test includes frequency counters, oscilloscopes, | ||
| + | |||
| + | An effective alternative for measuring delay time is to use the MPLAB X Stopwatch tool described in the third tab on the right. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Hardware-Assisted Delays ==== | ||
| + | The hardware-assisted software delay approach to generating a delay function makes use of one of the processor' | ||
| + | |||
| + | The PIC32MX processor family has several internal timers. For this project, we will use the core timer to generate delays of arbitrary periods of time. //The core timer ticks once for every two times the system clock does//. Therefore, the number of core oscillator counts per millisecond equals one half of the system clock frequency divided by 1000. The first three #define statements in Listing 4 establish the number of core oscillator cycles in a millisecond. The while loop in the “hw_DelayMs” function shown in Listing 4 implements the total millisecond delay operation. | ||
| + | |||
| + | **Listing 4.** Hardware-assisted software delay function. | ||
| + | < | ||
| + | /* The following define statements are declared in chipKIT_PROM_MX.h */ | ||
| + | // #define GetSystemClock() | ||
| + | // #define GetInstructionClock() (GetSystemClock()/ | ||
| + | // #define CORE_TICKS_per_MS | ||
| + | // #define LEDA | ||
| + | void hw_DelayMs(unsigned int mS) | ||
| + | { | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | } /* SW Stop breakpoint */ | ||
| + | </ | ||
| + | |||
| + | In Listing 4, the start time is immediately taken directly from the core timer using the ReadCoreTimer() function. The variable “mS” passed to hw_DelayMs is the total number of milliseconds to wait. The variable, tWait, is assigned to the number of ticks per millisecond multiplied by the number of milliseconds to wait, mS. Line 11 of Listing 4 contains a while loop that continuously reads the core timer and subtracts the initial core timer value saved in the variable tStart. The delay is complete when this difference is equal to or greater than the value computed for tWait. Repeatedly reading the core timer value is referred to as “polling” the core timer. | ||
| + | |||
| + | The delay period resolution of the hardware-assisted delay method is established by the core oscillator frequency. The range of delay is limited by the largest value that an unsigned integer data type used for the variable, tWait , can represent. For an 80 MHz system frequency, this results in 2< | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Handling Timer Rollover for Hardware-Assisted Delays ==== | ||
| + | We will be referencing the instrumentation test points provided on the PmodSTEP™ for this project. Specifically, | ||
| + | |||
| + | **Listing 5.** | ||
| + | < | ||
| + | / | ||
| + | * | ||
| + | * File: Project2.h | ||
| + | * Author: | ||
| + | * Date: May 22, 2013 | ||
| + | * | ||
| + | */ | ||
| + | |||
| + | /* Software timer definition */ | ||
| + | #define COUNTS_PER_MS | ||
| + | |||
| + | /* Function Prototypes */ | ||
| + | void system_init (void); | ||
| + | void sw_DelayMs (unsigned int mS); /* Software only delay */ | ||
| + | void hw_DelayMs (unsigned int mS); /* Hardware assisted SW delay */ | ||
| + | </ | ||
| + | |||
| + | **Listing 6.** | ||
| + | < | ||
| + | / | ||
| + | * | ||
| + | | ||
| + | | ||
| + | | ||
| + | * | ||
| + | | ||
| + | | ||
| + | * | ||
| + | | ||
| + | |||
| + | #include < | ||
| + | #include " | ||
| + | #include " | ||
| + | #include " | ||
| + | |||
| + | int main() | ||
| + | { | ||
| + | int mS = 1; /* Set total delay time - change as needed */ | ||
| + | | ||
| + | | ||
| + | { | ||
| + | LATBINV = LEDB; /* Toggle LEDB each delay period */ | ||
| + | /* Run with only one of the two following statements uncommented */ | ||
| + | sw_DelayMs (mS); /* Software only delay */ | ||
| + | // hw_DelayMs (mS); /* Hardware assisted SW delay */ | ||
| + | |||
| + | } | ||
| + | | ||
| + | * should never execute */ | ||
| + | } | ||
| + | |||
| + | /* system_init FUNCTION DESCRIPTION *********************************** | ||
| + | * SYNTAX: | ||
| + | * KEYWORDS: | ||
| + | * DESCRIPTION: | ||
| + | | ||
| + | * RETURN VALUE: | ||
| + | * END DESCRIPTION *****************************************************/ | ||
| + | void system_init(void) | ||
| + | { | ||
| + | /* Setup processor board */ | ||
| + | | ||
| + | | ||
| + | | ||
| + | } | ||
| + | |||
| + | /* sw_DelayMs Function Description ************************************ | ||
| + | * SYNTAX: | ||
| + | * DESCRIPTION: | ||
| + | | ||
| + | | ||
| + | * KEYWORDS: | ||
| + | * PARAMETER1: | ||
| + | * RETURN VALUE: | ||
| + | * Notes: | ||
| + | | ||
| + | *END DESCRIPTION ******************************************************/ | ||
| + | void sw_DelayMs(unsigned int mS) | ||
| + | { | ||
| + | /* Use code from Listing 3 */ | ||
| + | } | ||
| + | |||
| + | /* hw_DelayMs Function Description ************************************** | ||
| + | * SYNTAX: | ||
| + | * DESCRIPTION: | ||
| + | | ||
| + | | ||
| + | * KEYWORDS: | ||
| + | * PARAMETER1: | ||
| + | * RETURN VALUE: | ||
| + | * END DESCRIPTION ******************************************************/ | ||
| + | void hw_DelayMs(unsigned int mS) | ||
| + | { | ||
| + | /* Use code from Listing 4 */ | ||
| + | } | ||
| + | // End of Project2.c | ||
| + | </ | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Project Testing ==== | ||
| + | Use the LATBINV = LEDA; and LATBINV = LEDB ; instructions to toggle the two LEDs. You need both instructions to implement the two different delay programs. Connecting an oscilloscope or logic analyzer to the test point for LEDB (RB3) will allow you to measure the total delay being called for from the main function. Connecting the oscilloscope / logic analyzer probe to the test point for LEDA (RB2) will measure the millisecond for loop delay but only for the sw_msDelay function. (Remember to connect the oscilloscope or logic analyzer ground to the PmodSTEP ground pin. See the first tab on the top right for the pin locations.) | ||
| + | |||
| + | Figure 1 is an example of the instrumentation to determine the timing for a 20 ms delay. The LEDA trace toggles each 1 ms resulting in a frequency of 500 Hz. The LEDB trace toggles each 20 ms resulting in a frequency of 25 Hz. | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | Figure 1. Screen capture for 20 ms delay. | ||
| + | Screenshot of Digilent WaveForms running on Microsoft Windows 7. | ||
| + | |||
| + | Complete the data entries for Table 1 using the signal provided by instruments connected to the test point for LEDB and then the MPLAB X Stopwatch tool. When using the MPLAB X Stopwatch tool, place the Stopwatch Start for the first executable line of code in the delay function and set the Stopwatch Stop for the delay function closing brace as noted in the comments for Listing 3 and Listing 4. | ||
| + | |||
| + | ^ Design Total Delay — ms ^ Software Delay — Measured — ms |^ Hardware Delay — Measured — ms || | ||
| + | | ^ LEDB ^ Stopwatch | ||
| + | | 1 | 1 | 0.99775 | ||
| + | | 10 | 10 | 9.97491 | ||
| + | | 100 | 100 | 99.74654 | ||
| + | | 1000 | 1000 | Not valid | 1000 | Not valid | | ||
| + | |||
| + | (Solution values are in red and should be removed prior to distribution to students.) | ||
| + | |||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Try It on Your Own! ==== | ||
| + | Now that you've completed this project, you should take a look at the following questions and problems: | ||
| + | |||
| + | < | ||
| + | <ol type=" | ||
| + | < | ||
| + | </br> | ||
| + | <font color=" | ||
| + | </ol> | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | <ol start=" | ||
| + | < | ||
| + | </br> | ||
| + | <font color=" | ||
| + | |||
| + | A couple of the things the software delay method has going for it is that it is easy to understand the concept, and it is easy to write the code to implement. However, beyond the scope of this project, software interrupts can also be preempted by an interrupt. This results in extending the delay period. The disadvantage is that the processor is completely tied up just incrementing a number unless interrupted. The bottom line is that they are simple by consume processor time.</ | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | </br> | ||
| + | <font color=" | ||
| + | |||
| + | Should the software delay loop be interrupted, | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | </br> | ||
| + | <font color=" | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | </br> | ||
| + | <font color=" | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | </br> | ||
| + | <font color=" | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | </br> | ||
| + | <font color=" | ||
| + | </ | ||
| + | |||
| + | </ol> | ||
| + | </ | ||
| + | |||
| + | {{tag> | ||

