Файл: Programming Microcontrollers in C, 2-nd edit (Ted Van Sickle, 2001).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 15.06.2025
Просмотров: 4031
Скачиваний: 1
Timer Operations 267
grounded. Therefore, during each rotation the top side of the switch would jump from 12 volts to 0 volts and back. This voltage is larger than the maximum input for the microcontroller, so the 12 volt square wave was clamped to a maximum high value with a resistor and a diode connected to the +5 volt power supply of the EVM11 board. The schematic diagram for this test setup is shown in Figure 5-2 and a photograph of the complete system is shown in Figure 5-3.
M6811C11EVM |
MC33033 - MPM3002 Motor Drive |
||
33033 |
|||
OC1 |
PWM Output |
33k |
|
OC3 |
Pin g |
||
+5V |
12V |
||
1µƒ |
|||
33k |
|||
IC1 |
Time-Input |
||
8.2k |
|||
.01 |
|||
Magnet
Switch
Figure 5-2: Schematic Diagram of Motor Driver Test Circuit
Figure 5-3: Photograph Of
Assembled Motor Driver Test Circuit.
268 Chapter 5 Programming Large 8-Bit Systems
The program used to obtain the data in the above table was a slight modification of the motor control program listing given in Listing 5-6. The only change in this program was that the function @port void IC1_Isr(void) from Listing 5-7 was used. This routine stores in the location measured_period a value equal to the average of the last eight measured periods.
For several reasons, a reed switch is probably the worst example of an input sensor that you can find for a microcontroller. I chose this input because it is the poorest, and the result has been satisfactory. With reed switches, there are serious bounce problems on both the rising and falling edges of the signals. The life expectancy for a reed switch is relatively small, about ten million closures. During the course of preparing this text, I broke one reed switch and had probably a total of several hours of service out of the two used for these experiments. In practice, it is recommended that rotational measurements be made with optical interrupters. Do not, however, think that an optical interrupter is free from bounce problems. An optical interrupter will exhibit both bounce and also a relatively slow rise time. In such a case, it is necessary to use a software debounce technique and also an external Schmidt trigger circuit to create a quick and stable rise time that can be captured by the microcontroller.
The data for the above table were measured on the circuit shown in Figure 5-2. It was found that the PWM to voltage conversion was very accurate and repeatable with many changes in the system. For example, the prescaler value was changed from the value of 4 discussed above to 2 and up to 16. No difference could be noted in the PWM to voltage conversion over this range. Also, the input capture values were quite stable. The program in Listing 5-6 was compiled and used to complete the measurements. The PWM_count value was changed manually over the range of values shown above. The value for icap period is the value found in measured_period in the program. The voltage measured was the integrated value that was sent into the input of the MC33033 motor driver. The RPM was calculated as
60000
RPM =
measured_period
Data from this table are used to create an equation that relates RPM to the PWM_count. This equation will be used in the program to
Timer Operations 269
close the loop and regulate the motor speed. The relation that can be derived from the above data is
PWM_count= RPM +12528
7.875
This expression is accurate to about 1% across the range of interest. The measured data is the input capture count, which we will call p. What we must do is to calculate a new value for the PWM_count from a measured p. An error in p is called p and this expression means a change in p. The PWM_count will be designated as pc and also means a change in pc. We shall now attempt to arrive at an equation that expresses the necessary change in pc to correct for an error in p. The error in p is the difference between the desired cycle time and the measured cycle time. RPM in the above expression is converted to cycle time p by noting that the RPM is 60000 / shaft cycle time in milliseconds, and that the clock is ticking 500 times per millisecond. Therefore, we have
1 |
12528 +60000* 500 |
||||
pc |
= |
||||
p |
|||||
7.875 |
|||||
If there is a error in the cycle time say, ∆p, then the change in pc, ∆pc, is seen to be
p |
+ ∆p = 1591+ |
3809500 |
|
p+ ∆p |
|||
c |
c |
||
This expression, after some simplification and approximation reduces to
∆p = –3809500 + |
∆p |
(5-1) |
|
c |
p |
2 |
|
This value calculated by the above expression will be used to change the PWM_count that drives the motor. The data contained in measured_period are placed there by the input capture interrupt service routine. It is assumed that the data there are always current.
The calculation to determine the PWM_count correction is substantial. We shall use this approach here to determine the correction, but recognize that there is another approach that probably requires less total calculation. This approach is to use a lookup table.
270 Chapter 5 Programming Large 8-Bit Systems
We will see the look-up table in some detail in Chapter 6.
We will probably run into overflow problems if care is not exercised in the calculation of the correction ∆pc. In Equation 5-1 above, p is the time of a shaft rotation in milliseconds. This number can vary from 1000 to 20000 depending on the motor speed. The best approach we could use is to divide the constant -3809500 by the value of p. Then multiply the result by ∆p and finally divide the result by p a second time. This approach will minimize the overflow problems. In the expression be low, p is the measured_period, pc is the PWM_count, and ∆p is the calculated difference motor_period - measured_period.
FOREVER
{
if(old_motor_speed!=motor_speed)
{
motor_period=30000000lu/motor_speed; old_motor_speed=motor_speed; PWM_count= ((motor_speed+12528)/63)*8; PWM_count=limit(PWM_count); delpc=(3809500/motor_period);
delpc=delpc*(motor_period-measured_period)/p; PWM_count -= delpc;
/* read in the motor speed; */
}
}
Recall that division by 7.875 is needed in the calculation of PWM_Count. This floating-point operation is avoided by dividing the expression by 63/8. Most often, floating point operations can be approximated by integer operations with sufficient accuracy.
It was mentioned earlier that it is possible to extend the maximum measurable time to something greater than the time required to clock the timer counter register 65535 times. If the longest time exceeds this value, the timer overflow interrupt can be used to an advantage. Suppose that you want to measure a long time with input capture 1. When this measurement is started, the value in the TCNT will be saved, and the timer overflow interrupt will be enabled. Also a counter will be reset to zero. In the timer overflow interrupt service routine, the counter will be incremented. Eventually, an input capture will occur, and the time between inputs is calculated as the time remaining
Timer Operations 271
prior to the first TOI, plus 65535 times the number of TOIs that have occurred, plus the time indicated in the input capture register. With a scheme of this nature, there is essentially no limit to the length of time that can be measured accurately. The resolution of this measurement is the time increment of the prescaler output.
A velocity servo is a particularly difficult application. Remember that velocity or motor speed is the derivative of position. Any derivative operation is prone to noise, and the successful servo must be carefully put together. For example, the equation calculated above to determine the feedback term is essential to the proper operation of the system. An interesting challenge is to try to duplicate the operation of the system described here with a normal gain type feedback. Even though the program seems to be messy, this approach will work, and most other approaches give you a wildly hunting or oscillating system. The calculation of the proper feedback value will avoid many of these problems.
Another problem area will be found. If the above FOREVER loop is allowed to run without any time control, the computer will be calculating a new feedback value many times before the effect of an earlier calculation will be seen in the system performance. This operation again causes the system to become unstable, and usually the system never finds the final value but hunts over some wide range around it. This operation can be corrected by slowing the rate at which feedback corrections are calculated. In particular, several rates have been tested, and it was found that a stable, accurate system would be obtained with a new feedback value calculated each quarter of a second. This time control is shown in the listing below. The main application program is broken into two parts: the first part is the code that will be executed whenever the motor speed is changed and the second part is the code that will alter the PWM_count which is the feedback portion of the program. The first portion is executed under the control of an if statement
if(old_motor_speed!=motor_speed)
There is no means to change the motor speed in this program, and this change will be introduced in the next section. Therefore, it is expected that the portion of the code controlled by this if statement will execute the first time the program is executed, and it should never be executed again until the system is reset. The second if statement
if(tick)
272 Chapter 5 Programming Large 8-Bit Systems
will execute its control code each time tick is TRUE. tick is ini tialized to TRUE when it is created, and it is set to FALSE each time the if (tick) routine is entered in the application program. If you go to the end of Listing 5-7, you will find that tick is reset to TRUE about each quarter of a second in the PWM timer routine. This sequence will cause the feedback calculation to be executed about each quarter of a second.
The initialization portion of the program shown in Listing 5-7 is little changed from that of the one shown in Listing 5-6. As mentioned above, we have added an application section that contains the calculations to control the motor speed. Also, there is one simple function that is used by the application section.
#include “hc11e9.h”
#define DIVIDE_8_SHIFT |
3 |
#define COUNT_8 |
8 |
#define COUNT_MAX |
3300 |
#define COUNT_MIN |
1600 |
#define COUNT_ONE_QUARTER |
32 |
#define PERIOD |
0X1000 |
#define TIME_ON |
0x0800 |
#define IMPOSSIBLE |
3500 |
#define TOO_LOW |
100 |
@port void IC1_Isr(void); @port void OC2_Isr(void); @port void OC3_Isr(void);
long limit (long);
long measured_period,delpc; WORD time1,time2, motor_period, motor_speed=IMPOSSIBLE;
WORD old_motor_speed=TOO_LOW,rpm,mparray[COUNT_8]; long PWM_period=PERIOD, PWM_count=TIME_ON;
int tick=TRUE,count=0;
main()
{
Timer Operations 273
/* The initialization portion of the program */ TCTL2.EDG1B=ON; /* capture falling edge only */ OC1M.OC1M7=ON; /* sent OC1 out to PA7 */ OC1M.OC1M5=ON; /* couple OC1 to OC3 */ TMSK1.OC3I=ON; /* enable the OC3 interrupt */ TMSK1.IC1I=ON; /* enable the IC1 interrupt */ OC1D.OC1D5=ON; /* turn on OC3 with OC1 */ TCTL1.OL3=ON; /* toggle OC3 when OC3 occurs */ PACTL.DDRA7=ON; /* make OC1 an output to PA7 */ TOC1=TCNT+PWM_period; /* set OC1 */ TOC3=TOC1+PWM_count; /* set OC3 time on */ cli(); /* enable the system interrupts */
/* the applications portion of the program */ FOREVER
{ /* All of the numbers used below are derived in the text */
if(old_motor_speed!=motor_speed)
{
motor_period=30000000Lu/motor_speed; old_motor_speed=motor_speed; PWM_count= ((motor_speed+12528)/63)*8; PWM_count=limit(PWM_count);
}
if(tick)
{
tick=FALSE; delpc=(3809500Lu/motor_period); delpc=delpc*
(motor_period-measured_period)/ motor_period;
PWM_count-=delpc; PWM_count=limit(PWM_count); rpm=30000000L/measured_period;
}
/* input the new motor speed */
}
}
/* functions of the main program */
274Chapter 5 Programming Large 8-Bit Systems
/* range of acceptable PWM_count is 1600 to 3300 */ long limit(long x)
{
if(x<COUNT_MIN) return COUNT_MIN;
else if(x>COUNT_MAX) return COUNT_MAX;
else
return x;
}
/* The asynchronous service portion of the program */
@port void IC1_Isr( void) /*the motor speed
measurement*/
{
static int i; int j; time2=TIC1;
TFLG1=IC1F; /* reset IC1 interrupt flag */ mparray[i]=time2-time1;
if (++i==COUNT_8)
{
i=0; measured_period=0; for(j=0;j<COUNT_8;j++)
measured_period += mparray[j]; measured_period >>= DIVIDE_8_SHIFT;
}
time1=time2;
TOC2=time2+5*measured_period/8; /*debounce time 5/8 revolution */
TMSK1.IC1I=OFF; /* disable IC1 interrupt */ TMSK1.OC2I=ON; /* enable OC2 interrupt */
}
@port void OC2_Isr(void) /* the debounce isr */
{
TFLG1=IC1F|OC2F; /* reset interrupt flags */
Timer Operations 275
TMSK1.OC2I=OFF; /* disable OC2 interrupt */ TMSK1.IC1I=ON; /* enable IC1 interrupt */
}
@port void OC3_Isr( void) /* the PWM isr */
{
TFLG1=OC1F; /* reset OC1 interrupt flag */ TOC1+=PWM_period;
OC1D.OC1D7 ^=ON;
TFLG1=OC3F; /* reset OC3 interrupt flag */ TOC3=TOC1+PWM_count; if(++count==COUNT_ONE_QUARTER)
{
count=0; /* enter the speed control */ tick = TRUE; /* about each quarter of
of a second */
}
}
Listing 5-7: Motor Control Program
In this program, dynamic debounce is used. In the function IC1_Isr() the debounce time is a calculated value rather than a mere 3 ms as was done in Listing 5- 6. Here the debounce lock-out time is five-eighths of the current measured period. This value goes beyond the half-way point in the input waveform enough to avoid any bounce that will occur during the rise time of the input signal. The remainder of this program is quite similar to that in Listing 5-6.
We have completed most of the application. The remaining parts of the program are to find a means to read into the microcontroller the speed that is desired for the motor, and to integrate and debug the whole program. Usually, the speed is established by some measure ment being done with the microcontroller. Let us approach the problem a little differently. Instead of making the speed a result of a measurement, let’s enter the speed into the part through the asyn chronous serial port.
Serial Communications Interface (SCI)
The SCI is another of the very useful peripheral functions found on the MC68HC11 family of microcontrollers. In fact, the SCI is
276 Chapter 5 Programming Large 8-Bit Systems
found on parts from all families of Motorola microcontrollers. The SCI performs the operation of a full function universal asynchronous receiver transmitter (UART). It can operate with most standard baud rates, and several nonstandard rates. Control of this peripheral is through the BAUD register, the SCSR (serial communications status register), and both SCCR1 and SCCR2 (serial communications control registers 1 and 2). There is one additional register called the serial communications data register. This register when written to is the transmit data register, and when read the receiver data register. Both the transmit and receive data registers are double buffered.
The BAUD register contains two test bits and two divide control registers that set the baud rate for the SCI system. The bits SCP control the baud rate prescaler. This prescaler has the unlikely prescale value of divide-by-13 when both bits are set. This value sets the maximum baud rate to 9600 when the system is driven by an 8 MHz clock. With this prescaler value, all of the standard baud rates from 9600 down to 150 are available to the SCI. The second divider is comprised of SCR. The bit patterns in these bits will select any power of two divisors between 1— 20 –and 128—27. For our system, we will choose 9600 baud, which is sufficiently slow that there should be few communications problems. The code to set up this baud rate is as follows:
BAUD.SCP=3;
BAUD.SCR=0;
These lines of code must be added to the initialization portion of the program to set up the baud rate to 9600 baud for the SCI system.
We will use the SCI system to read in the required motor speed for the program started in the preceding section. Such a system must be able to read in data through the SCI, but it must also echo the same data back to the originating source to provide full duplex operation. Therefore, both serial transmit and receive must be implemented. With the above system, there is no serious need to implement an interrupt driven system. The computer is clearly not being used to its capacity, so we will add the SCI input and output sequence to the applications portion of the code and feel safe that no input data will ever be lost because of an overrun error. (An overrun error occurs when a new input overwrites an old input before the old input is processed.) Therefore, none of the bits in SCCR1 need be changed from their reset value. In the SCCR2 register, there are two