Wednesday, April 3, 2013

Generating Time Delays

Time delays tend to be very useful when using microcontrollers.  Because things happen so quickly, us humans tend to miss visual and audible signals that we set up from the MCU unless we slow them down.  Similarly, even in an embedded system that isn't intended to communicate with people, you may find a need to slow some instructions down to keep your system running smoothly.  For instance, think of the robotic arms that swing in and weld cars together on the assembly line.  If the arms swung into position over the metal car parts, turned on the welding electrodes, and then turned them off in the next instruction without a time delay, they would only weld for one millionth of a second (assuming a 4MHz oscillator).  Hardly what I would call a reliable weld.

Lucky for us, C provides an easy way of generating time delays: the __delay_us(x) and __delay_ms(x) macros.  We're going to leave it up to the compiler to generate the proper delay code, but first we have to tell the compiler what speed oscillator we're using so it has enough information for the calculations.  We'll use the #define preprocessor directive to define our crystal frequency.  It doesn't matter if we're using a crystal, a resonator, a RC oscillator, etc.  For a 4MHz oscillator, such as we're using with the 12F629's internal RC oscillator, the preprocessor directive is:

#define _XTAL_FREQ 4000000

The crystal frequency is provided in Hertz, so for 4MHz, we need to put four million.  The use of these macros and their requirements is described in higher detail in the XC8 Compiler User's Guide.  It states:

"It is often more convenient to request a delay in time-based terms, rather than in cycle counts. The macros __delay_ms(x) and __delay_us(x) are provided to meet this need. These macros convert the time-based request into instruction cycles that can be used with _delay(n). In order to achieve this, these macros require the prior definition of preprocessor symbol _XTAL_FREQ, which indicates the system frequency. This symbol should equate to the oscillator frequency (in hertz) used by the system."

For the delay itself, the XC8 compiler gives us an option for delays in microseconds and milliseconds.  You write those as:

__delay_us(x);
__delay_ms(x);

Double underscore before the delay, single underscore in between.  Microsecond is "us" and millisecond is ms.  X is representative of the number of micro or milliseconds we want in our delay.  The compiler will generate a delay as long as we have defined the crystal frequency.  As a note, an error will be generated during the compiling process if our delay number is too large.  The delay instructions must be followed by a semicolon.  The compiler flags these delay instructions with a red underline and assumes it's an error, but that is just a compiler issue and we can ignore it.  The program will successfully compile despite these flags.

Let's see a delay in action.  Our new program in figure 1 is another slight alteration of our first program, thus the hardware has not changed.
Figure 1
After the configuration word we have defined our oscillator frequency.  Just as in our second program we have aliased GP0 and GP3 for easier reading and writing of the program.  In this example, when we press and hold down the button, the LED will flash on and off at a rate of 1 flash per second.  Inside the button press while function, we first turn on the LED and then hold it on for 500ms, or 0.5 seconds.  The LED is then turned off and held in the off state for another 500ms.  If we don't include the off delay, the LED would turn on for 500ms and then turn off for only one microsecond.

Because we're in a while loop, the flashing of the LED will continue for as long as we hold down the button.  When we release the button, the button pin will read 0, and the while loop will not execute, thus the LED will remain off.

As a point of interest, I should make note that these delay instructions are considered "dumb".  In other words, they may or may not be precise.  These routines are good enough for LEDs and other timing operations that don't need to be spot on, but for operations where precise timing is very important, such as timing the return of a sonar pulse, I'm afraid that the __delay instruction won't cut it.  For more precise timing operations, alternative methods must be sought.  Of course, the accuracy of timing operations depends on more than just software, but also the oscillator type we chose and its accuracy.

In a later lesson, we'll talk about how to generate longer time delays.  What happens when we want to delay a program for seconds?  Minutes?  An hour?  We can do all these things, and we'll see how to soon.

November 25, 2014: Post edited to include additional reference material for a more thorough lesson.

3 comments:

  1. Impossible to copy and paste this nice example.

    ReplyDelete
  2. This was done for 2 reasons. 1. It's easier to include the code as an image as doing so retains proper format of the code and association with MPLab and 2. It forces the student to write code themselves rather than a copy/paste for maximum education.

    ReplyDelete