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.

New Concepts, The if statement, and Aliasing

We're going to examine creating an alias for our inputs and outputs to help us better read our code.  Additionally, we're going to rewrite our first program a different way to examine the "if" statement.  We don't need to change anything about our hardware, so this lesson will only look at the software.  Let's get started.

Aliasing Pins

When your programs start getting long, and you're utilizing a large number of inputs and outputs, it becomes hard to keep track of what each pin is being used for.  Assigning a name, or alias to some of the pins can make writing and reading the code easier.  This is helpful only in the program-writing phase, and doesn't effect the program in any way.

Looking at our first project, we want to be able to reference GP3 as "button" and GP0 as "led".  To do that, we need to define those bits as our alias names.  That is done using the #define preprocessor directive.
#define button GPIObits.GP3
#define led GPIObits.GP0

We can now write:
while(button==1)
led=1;

The if Statement

The if statement is a way for a program to make a decision about whether or not to execute one or more instructions based on some condition.  In our case, the condition is whether or not the button is pressed.  Rather than saying "while the button is pressed, the LED is on" like we did in the first program, we're going to simply rephrase it and say "if the button is on, turn on the LED".

When using an if statement, we don't need to use braces if only one instruction follows the if.  If more than one instruction is to be executed when the if condition is true, we do need the braces.  This example only contains one instruction after the if.  The program in figure 1 will operate similarly to our first program.  It should be noted though, that the if statement is not a loop like while is.  The operator that follows if will be evaluated, and if it's true, the instruction will be executed.  The program will then continue on to the rest of the instructions regardless of whether or not the button is still pressed.  Since we have only one other instruction which turns off the LED, when we press and hold the button in this program the LED will flash on and off in rapid succession because each time we loop around the if is executed.  Because of the speed of instruction execution in a microcontroller, the LED flashes on and off about 500,000 times per second, making it appear as though the LED remains lit.  However, because of this rapid on/off cycle, the LED will appear to be dimmer than when we used the while loop.  When the button is not pressed, the if statement will not be executed at all, and the LED will remain off.  Compiling the program reveals that this is a smaller program than in example 1, using one less word of program memory space.

Figure 1
This program is intended only as an example of making decisions using an if statement.  More complex programs tend to process information on the condition of some variable, where using an if statement makes more sense than in this example.

Tuesday, April 2, 2013

The Microcontroller Oscillator

This lesson is dedicated to learning about the Microcontroller Oscillator.  I'm going to keep this lesson short in an effort to provide the basic information we need for use on the blog.  Many books dedicated to the microcontroller will elaborate further on the Oscillator, clock, and program flow.

Every microcontroller requires a clock signal, a continuously running fixed-frequency logic square wave, to execute instructions.  This clock signal is generated by an oscillator.  The change between a high and a low is called an Edge (figure 1).
Figure 1

Many essential timing functions in addition to the CPU depend on the clock.  The oscillator frequency is measured in Hertz, or pulses per second.  PIC microcontroller oscillators operate in the tens of thousands to tens of millions of hertz, usually between 32KHz and up to 80MHz for some of the more advanced 32-bit devices.  The oscillator pulses enter the microcontroller, where internal circuitry divides the clock into four even quadrature clocks.  The four clock pulses of the internal circuitry are referred to as Q1, Q2, Q3, and Q4, and all four of them together make up one instruction cycle during which one instruction is executed.

Remember that the instruction set of the PIC microcontrollers are bit-oriented instructions in the Assembly language, not C.  Each instruction requires one instruction cycle to execute with the exception of instructions that branch from normal program flow such as CALL or GOTO.

Each instruction must be fetched from memory, decoded by the processor, executed via some operation, and any data for the current instruction written.  Broadly speaking, Q1 fetches the next instruction, Q2 reads the data for the current instruction, Q3 executes the current instruction, and Q4 writes any data.  All four quadrature clocks together make up one instruction cycle.  A fetch requires one instruction cycle while an execute requires a separate instruction cycle.  Under some microcontroller architectures, this effectively means that each instruction requires two full instruction cycles to process.  PIC microcontrollers however, have separate buses for program memory and data memory, so they implement instruction pipelining where one instruction is being executed while the next instruction is simultaneously being fetched.  The clock/instruction cycle and instruction pipelining are shown in figure 2.
Figure 2
As shown in the upper portion, each instruction cycle is made up of four clock pulses from the oscillator.  The MCU handles a task on each quadrature clock, and the program counter is incremented after one full instruction cycle.

The instruction pipeline flow shows how pipelining works.  When the MCU is powered on, the first instruction cycle fetches the first instruction.  During the second instruction cycle, the first instruction is executed while the second instruction is being fetched, then this instruction is executed while the third instruction is fetched and so on.  When we use an instruction that branches program flow, the CPU forces the address of the instruction onto the stack and then changes the program counter to the address of the branch.  This means that the instruction that was fetched before this change is no longer needed and is flushed from the pipeline, causing an extra instruction cycle to be needed.  As can be seen in figure 2, while instruction 2 is being executed, instruction 3 is being fetched.  Instruction 3 is a CALL, which branches program flow.  Instruction 3 is then executed while instruction 4 is fetched, but instruction 3 changed the program counter to point to the address of SUB_1 so instruction 4 is no longer needed.  Therefore, during the next instruction cycle, instruction 4 is flushed while the instruction at SUB_1 is being fetched.  This pipelining effectively means that each instruction requires only one instruction cycle to complete.

Timing Operations

As stated earlier, timing operations carried out by the MCU operate off the oscillator clock signal.  The oscillator is divided by the MCU into four separate pulses, the quadrature clocks.  Because the MCU operates in such a way that four pulses of the clock oscillator equal one instruction cycle, the clock speed is effectively reduced by a division factor of four (Fosc/4).  Therefore, if we use a 4MHz oscillator, the MCU's instruction frequency is 1MHz, or one million instructions per second.  In other words, one instruction is executed every 0.000001 seconds (1uS).

Using this knowledge, we can now determine how long a particular sequence will take to execute by realizing the instruction frequency time, and counting how many instructions are to be executed (being sure to account for any branching that would require two cycles).  This can be useful in calculating timing delays or other timing functions.  We'll see some examples of timing operations in later lessons.

Oscillator Clock Options

An oscillator clock signal is typically generated by two types of oscillators, a Resistor-Capacitor (RC) oscillator, or a Crystal or Ceramic oscillator.  Some microcontrollers have an internal RC oscillator that can be used to generate the clock signal.  Higher speed clocks require greater power consumption by the MCU while lower speed clocks require less power.  The clock source is selected by the user as part of the configuration word.  Examples of a RC clock and Crystal/Resonator clock are shown in figure 3.
Figure 3
XT - Crystal.  This is the standard crystal configuration.  It is intended for crystals or resonators in the range of 1 to 4MHz.
HS - High Speed.  This is a higher drive version of the XT configuration.  It is intended for crystal frequencies of about 4MHz and greater, and/or ceramic resonators.  It leads to the highest current consumption of all the oscillator modes.
LP - Low Power.  This mode is intended for low-frequency crystal applications and gives the lowest power consumption possible.  It is suitable for crystals of 200kHz or below.
RC - Resistor-Capacitor.  The lowest-cost method of providing an oscillator signal is the resistor-capacitor oscillator.  An RC oscillator is also the least accurate, since the nominal frequency of oscillation can be predicted with only limited accuracy and will even then drift with a change in temperature, supply voltage, and time.  An RC oscillator is a good option when timing operations aren't critical.  Some PIC chips come equipped with an internal RC oscillator eliminating the need for the external circuitry.