Файл: Programming Microcontrollers in C, 2-nd edit (Ted Van Sickle, 2001).pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 15.06.2025

Просмотров: 4061

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Timer Operations 249

The instruction

OC1D.OC1D5=ON;

indicates that when OC1 occurs, OC3 should be turned on. The next instruction

TCTL1.OL3=ON;

causes OC3 to toggle when its time expires, and the instruction

PSCTL1.DDRA7=ON;

sets the DDRA7 bit so that signals from OC1 will be sent to the output pin PA7. TOC1 and TOC3 are initialized by the next two instructions. TOC1 is given a value of the contents of period greater than the contents of the timer counter register TCNT. TOC3 is set equal to the contents of TOC1 plus the time_on.

The routine then enters an endless loop. Within this loop, two flags are examined and if they are set, they are reset. Also, new times are calculated for when the next output compares should occur. The OC1F flag in TFLG1 is set whenever an Output Compare 1 occurs. This flag must then be reset. The instruction sequence

if(TFLG1 & OC1F)

{

TFLG1 = OC1F;

.

.

will accomplish the required assembly instructions to test the bit and reset it if the bit is turned on.

The time of the next OC1 is calculated as TOC1+period. In this particular instance, the bit OC1D.OC1D7 is complemented so that the output observed on PA7 will toggle with the occurrence of each OC1. TFLG1.OC3F is tested to determine if an Output Compare 3 has happened. If it has, this bit is reset, and TOC3 is set to a new value of TOC1+time_on. With this setup, OC3—PA5—will go on when each OC1 occurs, and will be reset an amount corresponding to time_on after it goes on. Therefore, the period of this system can be set and the on time can be any value less than the period.

Normally, the operation of a PWM is to generate an analog signal that is present continuously. The above program does perform this

250 Chapter 5 Programming Large 8-Bit Systems

task, but it also assumes that the computer does not have much else to do. Other tasks could be built into the above program, but it is absolutely necessary that the program have a cycle time that is shorter than the period of the PWM signal. If the loop time of the FOREVER loop is less than the PWM period, then you can use the above synchronous approach. It makes no difference when in the cycle that the two if statements in the FOREVER loop are executed. It is necessary that they be executed prior to the occurrence of the OC1 that designates the end of the period. If it becomes necessary to include so much code in the FOREVER loop that it is impossible to guarantee that the loop time will always be less than the period, then an asynchronous approach should be used. This program was compiled and executed on an evaluation module. The value of time_on was adjusted to determine the range of outputs that could be created with this program. The minimum time on must be greater than the time required to execute the code

if(TFLG1&OC3F)

{

TFLG1=OC3F; /* reset OC3 interrupt flag */ TOC3=TOC1+time_on;

}

when OC3F is found on. The reason for this timing is that TOC3 must be updated after TOC1 is given a new value and the update must be complete prior to the passing of time_on . Otherwise, the period of TOC1 will pass before the OC3 will occur. This time was measured, and it was found to be 6 clock cycles. Therefore, reliable performance is obtained with a minimum time on of 6 clock cycles.

The maximum time on was found to be 0xffe with the above code. The output signal at 0xfff was on all of the time with no single cycle off period as the program would indicate.

An interrupt can be requested whenever an output compare occurs. We are servicing two output compares in this case. One’s initial thought might be to have two interrupts, in this case one for OC1 and one for OC3. Is this approach really necessary? If it is guaranteed that the time_on parameter is always less than period, then an approach that can be used is to delay the reset of the OC1 flag until the OC3 has occurred. OC3 will always happen after OC1. The problem with this approach is that OC3 occurs very near the end of the period, and there might not be enough time to reset the OC1 interrupt flag


Timer Operations 251

prior to the expiration of the period. The following program can be used to demonstrate this approach:

#include “hc11e9.h”

/* This program will provide a PWM output to OC3, or

PA5. The period will be the integer value found in period,and the on time will be the integer value found in time_on. Keep time_on less than period. This program uses asynchro­ nous service of output compare occurrences. */

WORD period=0x1000, time_on=0x0800;

@port void OC3_Isr(void); /* need a prototype for the

ISR */

main()

{

OC1M.OC1M7=ON; /* sent OC1 tout to PA7 */ OC1M.OC1M5=ON; /* couple OC1 to OC3 */ TMSK1.OC3I=ON; /* enable the OC3 interrupt */ OC1D.OC1D5=ON; /* turn on OC3 when OC1 occurs */ TCTL1.OL3=ON; /* toggle OC3 when OC3 occurs */ PACTL.DDRA7=ON; /* make OC1 an output to PA7 */ TOC1=TCNT+period; /* set OC1 to the period */ TOC3=TOC1+time_on; /* set OC3 to the time on */ cli(); /* enable the system interrupts */ FOREVER

{ /* wait here forever */

}

}

@port void OC3_Isr( void)

{

TFLG1=OC1F; /* reset OC1 interrupt flag */ TOC1+=period;

OC1D.OC1D7 ^=ON; /* toggle the output */ TFLG1=OC3F; /* reset OC3 interrupt flag */ TOC3=TOC1+time_on;

}

Listing 5-5: System Using Asynchronous Time Service PWM1.C

252 Chapter 5 Programming Large 8-Bit Systems

Notice that this code is very similar to the earlier program shown in Listing 5-4. The interrupt service handling is set up by use of the @port construct and the two added instructions which first enable the OC3 interrupt and then enable the system interrupts. The code that was contained within the FOREVER loop in Listing 5-4 has been moved into the interrupt service routine in this program.

This program was run, and it was experimentally determined that the maximum value that can be allowed for time_on is 0xff0 when period is 0x1000. This maximum value indicates that 16 clock cycles or 8 microseconds at an 8-MHz crystal is needed to service the interrupt prior to the occurrence of an OC1. The minimum on time found here is slightly better than found with the code in Listing 5-4. In this case, the minimum on time is 1 clock cycle, which is the expected minimum value.

We have already seen that a significant time is required to process an interrupt, and it is usually impossible to have an output event occur during the interrupt service routine. With the above code it is easy to have a minimum on time of one clock cycle. This small time is accomplished by having the setting of the output signal not be attended by an interrupt. The interrupt will occur when the output signal is reset. Therefore, when the coupled output signal with OC1 goes high, the output compare on the coupled channel will have its event even if the time is as short as one clock cycle beyond the occurrence of OC1. Assume that the coupled channel is OC3. When the event occurs on OC3, an interrupt will occur. In this interrupt service routine, both OC1 and OC3 will have to be set up to operate in the correct manner. This operation has a problem with long on times. If the event associated with OC3 occurs a few clock cycles prior to the occurrence of the OC1 event, and the interrupt is caused by OC3, then the OC1 set-up might not be completed when the next OC1 time arrives. In this case, the whole base period would go out of kilter, and there would be at least one cycle of the output that would be based on the timer overflow cycle rather than the desired time base.

As is often found in engineering operations, there is a choice that can be made. If the interrupt is based on the reset time of the PWM cycle, then a minimum on period of one cycle can be achieved. With this choice of operation, the maximum on time will be several clock cycles—perhaps 20 to 30—short of 100% on. On the other hand, if the interrupt is based on the set time of the PWM cycle, the minimum


Timer Operations 253

on time is poor and the maximum on time can be up to one clock cycle shy of the base period. If you wish to have good performance at both ends, minimum on time at the same time as maximum on time, you can examine the on time in your program and if it is less than 50% of the base time period, control the PWM by an interrupt on reset. Otherwise, control the PWM by an interrupt on set. Implementation of the code for this approach is left to the exercises.

Output compare operations OC2 through OC4 and the programmable timer subsystem I4O5 are all identical. These versatile timers can be used to generate complex timing waveforms that are useful in keeping time, running stepper motors, etc. The examples shown above demonstrate how these outputs can be converted into a PWM digital to analog output. The coupling between OC1 and the other outputs provides an excellent mechanism for synchronizing two time events for the outside circuitry. Of course, these subsystems can create events in time, but they are completely unable to measure time. We will later examine the analysis of timed events that can be measured with the help of the input capture subsystems.

EXERCISES

1.Write the code that will determine from the on time for the PWM the interrupt operation that will allow the minimum off and maxi­ mum on time simultaneously.

2.Modify the code in Listing 5.4 to eliminate the need for a condi­ tional test if (OC1D.OC1D7 = = 1).

Input Capture Subsystems

On the MC68HC11EX family, there are three input capture timers and one timer that can be programmed as either an input capture or an output compare. These sub-systems are used to measure time interval. The same 16-bit timer used in the output compare systems is used to support the input captures. When an input occurs on one of the input capture pins, the value contained in the 16-bit timer is saved in a register designated for that pin, a flag is set, and the processor can request an interrupt. With input captures, we can measure period and hence frequency (or speed). One of the leading applications where the input capture is used is in automatic braking systems. We shall

254 Chapter 5 Programming Large 8-Bit Systems

choose a somewhat simpler system for an example, but the general approach used here is much the same as would be used in the design of an automatic braking system.

Suppose that we have a DC motor that is being controlled by an MC68HC11E9 and wish to be able to set the speed of the motor. The motor has a magnetic sensor whose output will cycle once each rotation of the motor. To determine the speed of the motor, we must measure the cycle time of the output from the sensor. Let us examine the minimum speed of the motor. The TCNT register is being clocked at a frequency that is either one-fourth of the crystal frequency of the system or it can be altered by a prescaler value of either 4, 8, or 16. The 16-bit TCNT register will overflow every 65,536 clocks at its input. Therefore, if we use an 8-MHz clock, the timer overflow period will be 32.768 ms with no prescaler, 131.072 ms with a prescaler value of 4, 262.144 ms with a prescaler value of 8, or 524.288 ms with a prescaler value of 16. The minimum speed at which the motor can move and still be detected unambiguously must be greater than one revolution in the timer overflow time. If the motor rotates any slower than this value, the differences in times between two inputs will be such that the program cannot tell if the period is longer than one TCNT overflow time or a very short time. It is possible to extend the minimum unambiguous time that can be measured by the system through the use of a timer overflow interrupt, but let’s examine an approach without these steps and then look at the time extended approach later.

The maximum practical time to be measured with the divide-by-16 prescaler is 500 milliseconds. The minimum rotation speed must be greater than 1 revolution per 500 milliseconds or 120 revolutions per minute. The motor that we will use in this example has a minimum speed of 1000 rpm, so that the prescaler need not be as great as 16. We will use a prescaler value of four, which will provide about a minimum speed of 500 rpm so that there is a safety factor in our measurement.

With the divide-by-four prescaler, the minimum time that can be measured is ( 4 [crystal to bus frequency division]* 4 [prescaler divide ratio ]/ 8 MHz [clock frequency] ), which is 2 microseconds. At this time interval, the maximum speed of the motor shaft that can be measured is one revolution in 2 microseconds, or 500000 * 60 revolutions per minute. It is quite clear that such a system is better suited to handle high-speed operations than low speed. If there is no


Timer Operations 255

clear reason otherwise, it is usually best to operate a control system at the maximum possible speed. One reason that is important is the operation of the PWM DAC that accompanies the operation of the control system. It is usually best to have a PWM run at the highest practical speed. If the PWM is slow, then conversion of the pulse output from the system to a DC voltage is difficult and not very accurate on an instantaneous basis.

The following code segment might be used as an interrupt service routine to handle the input capture operation.

@port void IC1_Isr( void)

{

TFLG1=IC1F; /* reset IC1 interrupt flag */ measured_period=TIC1-time1;

time1=TIC1;

}

Here it is assumed that the maximum time between input captures is less than the time of a timer overflow. In this case, the time is merely the difference between the current value and the preceding value which is stored in time1.

This approach has only one major problem. Most inputs such as will be obtained from reed switches, push buttons, and even optical interrupt type devices will be noisy when the contact is closed. This noise is called switch bounce, and it will always be present with a contact closure. Therefore, the switch must be debounced in some manner before its data are reliable.

The most common way of debouncing a contact closure is to observe the closure, and then wait a time and observe if the contact is still closed. If closed, it is assumed that the contact is good; otherwise, another wait period in allowed to elapse and the contact is observed again. This procedure is repeated until the contact is closed on a pair of successive observations. Only then is it assumed that the contact is closed. The time between observations is the subject of much engineering debate. Often the designer can place an oscilloscope on the contact and repeatedly close and open it. With proper synchronization of the instrument, it is possible to see the signal caused by the bouncing contacts. If this time can be measured, then having a debounce time of perhaps twice the bounce time will probably give a safe time for the debounce. However, you should not assume that

256 Chapter 5 Programming Large 8-Bit Systems

this value will be correct over the lifetime of the contact. As the mechanism ages, it is possible that the bounce time will change, and it will always change in the worst possible way. Therefore, be safe, and keep the debounce time at least twice the bounce time, and perhaps longer for the sake of safety. The range of values used for debounce times ranges from 2 to 30 milliseconds.

This system is measuring the time of an input. How can we stick an arbitrary time for debounce into our measurement equation and be reliable? It is possible to do the debounce and not have the time as part of the measured interval. The contact closure will cause an observable input. Our real concern with debounce is to guarantee that no additional inputs occur after the initial input has been processed. Therefore, we can implement a debounce by merely not re-enabling the input capture interrupt until the debounce time has passed. This way, the time of the first edge seen in the input sequence is processed, and all other inputs caused by bounce will not be processed because the input capture interrupt is disabled. The time that the interrupt is disabled can also be adjusted to meet the particular needs of the program. For example, in the system we will construct below, the speed is measured with a reed switch and a magnet on the motor shaft. The circuit will be built such that the closing of the switch will cause a voltage to fall from +12 volts to 0 volts. There is a resistor between the 12-volt supply and the switch. The bottom side of the switch is grounded, and as the switch is closed and opened, the voltage measured at the top of the switch will change from 12 volts to ground. When the switch closes, it is expected that it will bounce and the voltage will “chatter” between 0 and 12 volts. Surprisingly, the opening of the switch, while it will exhibit less bounce, will also generate these unwanted signals. If we want to block out all of the error signals that can be introduced by the bounce, it is necessary that the time following the initial signal drop be protected, as well as a time around the signal rise that occurs midway during the shaft rotation. In the first example, we will provide a fixed time for the debounce. Later when the program is measuring the speed of the motor, we will provide a debounce time that is somewhat longer than one half the measured period to block out all bounce signals that occur on both the rising and falling edge of the input signal.

The input capture subsystem will capture any specified input when enabled. These inputs are captured whether the interrupts are enabled or not. In other words, if an input occurs after the one that was captured,


Timer Operations 257

it will be captured and the original saved value will be lost. Therefore, the very first operation in the interrupt service routine for an input capture should be to save the contents of the input capture register. Then subsequent inputs from bounces will not affect the measured time. Another problem can occur, however. Whenever an input occurs on an input capture line, not only are the time data captured, but also, the appropriate input capture interrupt flag is set. If this flag is set, an interrupt will be requested whenever the input capture interrupt is enabled. Therefore, in the debounce interrupt routine, the input capture interrupt flag for the channel being used should also be reset.

We still must control the time that the input capture is disabled. How this control is implemented depends on the system. If the system is not busy, one might calculate a value that equals the contents of the TCR plus the number of timer counter ticks in the debounce time. This value could then be compared with the contents of the TCR, and when the TCR equals the calculated value, the input capture interrupt would be reenabled, and control returned to the interrupted program. This approach provides an accurate debounce time, but the processor is devoted entirely to the measurement of this time during the debounce time. It is not wise to tie up a microcontroller for milliseconds at a time and lock out other important actions that might take place during that time.

If all other events that are occurring within the microcontroller are interrupt driven, the programmer could re-enable the system interrupts prior to entering the delay time. This approach would at least keep the processor available for other asynchronous events that might occur during the debounce period.Yet another approach would be to disable the input capture interrupt and within the applications program, provide a time measurement that would have to expire before the input capture interrupt is reenabled. Both of these methods can be implemented without the use of an output compare. If an output compare is available, it could be used to execute the debounce timeout. This output compare would be set up in the input capture interrupt service routine. Also within the input capture interrupt service routine, the input capture interrupt would be disabled. The output compare interrupt service routine would disable the output compare operation and reenable the input capture. This approach is by far the best, and will be used here. The code for this method of debounce is included in the listing shown in Listing 5-6. In this case, a fixed debounce time is used. We will see the variable debounce time in a later program.

258 Chapter 5 Programming Large 8-Bit Systems

A beginning program or framework from which to build this application is shown in Listing 5-6. All this program contains is the setup of the interrupts and the interrupt service routines. Input capture 1 will serve as input from the motor shaft encoder. The motor will be controlled by a PWM signal. This signal will be filtered to provide a DC signal that can drive the motor. As with the MC68HC05 programs, this code is broken into three basic parts: 1) the initialization section, 2) the applications section, and 3) the asynchronous service section. In this case, there is no applications section, so it is designated by a FOREVER command followed by an empty block.

The program begins with the inclusion of the HC11E9.H header file. Immediately following this entry are the prototype entries for all of the interrupt service routines. The declarations of the global variables follows. The first set of variables are all associated with the input capture, and the second line of variables controls the output compare portion of the program.

Notice that the bit set-up instructions are all grouped so that the bits from each register are set in the same location. It is not necessary to group these instructions; however, if they are grouped by register as is done here, the compiler will use a single bit manipulation instruction to set all of the bits in each register. The first 8-bit manipulation instructions in the following code will require only six assembly instructions as they are grouped.

#include “hc11e9.h”

#define MS3_DEBOUNCE 1500 #define PERIOD 0X1000 #define TIME_ON 0x0800

@port void IC1_Isr(void); @port void OC2_Isr(void); @port void OC3_Isr(void);

WORD measured_period, time1, delpc;

WORD PWM_period=PERIOD, time_on=TIME_ON;

main()

{

TCTL2.EDG1B=ON;/* capture falling edge only */