Файл: Programming Microcontrollers in C, 2-nd edit (Ted Van Sickle, 2001).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 15.06.2025
Просмотров: 4077
Скачиваний: 1
Timer Operations 259
OC1M.OC1M7=ON; /* sent OC1 tout 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 when OC1 occurs */ TCTL1.OL3=ON; /* toggle OC3 when OC3 occurs */ PACTL.DDRA7=ON; /* make OC1 an output to PA7 */ TOC1=TCNT+PWM_period; /* set OC1 to the period */ TOC3=TOC1+time_on; /* set OC3 time on */
cli(); /* enable the system interrupts */
FOREVER
{
/* put application code here */
}
}
@port void IC1_Isr( void)
{
time1=TIC1;
TFLG1=IC1F; /* reset IC1 interrupt flag */ measured_period=TIC1-time1; TOC2=TCNT+MS3_DEBOUNCE; /* 3 ms debounce time */ TMSK1.IC1I=OFF; /* disable IC1 interrupt */ TMSK1.OC2I=ON; /* enable OC2 interrupt */
}
@port void OC2_Isr(void)
{
TFLG1=IC1F|OC2F; /*reset IC1 & OC2 interrupt flag */
TMSK1.OC2I=OFF; /* disable OC2 interrupt */ TMSK1.IC1I=ON; /* enable IC1 interrupt */
}
@port void OC3_Isr( void)
{
TFLG1=OC1F; /* reset OC1 interrupt flag */ TOC1+=PWM_period;
OC1D.OC1D7 ^=ON;
260 Chapter 5 Programming Large 8-Bit Systems
TFLG1=OC3F; /* reset OC3 interrupt flag */ TOC3=TOC1+time_on;
}
Listing 5-6: Motor Control Program Framework
The interrupt service routine for IC1 resets the IC1F bit in the TFLG1 register. Then the elapsed time from the last input capture time is calculated. The result of this calculation is saved in period1 which is the current elapsed time required for the motor to rotate one revolution. At the end of this calculation, the contents of the input capture register is saved in time1 to be used as the old time in the next calculation.
The PWM output from OC3 will be used to drive motor 1 whose shaft sensor is fed into IC1. The value in speed will be the desired speed of the motor in revolutions per minute. The range of this number will be 1000<=RPM<=10000, and it will have to be put in place through a debugger for this test. This range of motor speed was chosen to sat isfy the speed of a small DC motor used in the final system. Let us plan a servo type operation where the motor is driven by a feedback loop that is controlled by the speed error. Therefore, within the applications loop, there must be code to check and control the PWM signal to force the speed1 parameter to match the input speed parameter.
The input speed is measured in RPM, and the values measured by the microcontroller are all times. A question that should be considered is whether the number placed in speed1 will actually be times or should this number be RPM. To calculate the time, note that there is one interrupt per revolution of the shaft and one minute is 60 seconds, so RPM/60 will be revolutions per second. What we need is the number of bus cycles in the time corresponding to this period. With a prescaler count of four, the clocking speed of the TCNTregister is one count every two microseconds, or 500,000 counts per second. Therefore, the conversion from RPM to time for a revolution of the shaft in bus cycle counts is
Counts = 500000* 60 = 30000000
RPM RPM
At 3000 RPM, the count value will be 10000 while at 16000 RPM the count value will be 1875. These numeric values can be handled by the timer system in the MC68HC11.
The above expression can be used to calculate RPM from the count value as
Timer Operations 261
RPM = 30000000
Counts
Neither calculation is convenient in a microcontroller. The numerator is larger than an int but smaller than a long. Both Counts and RPM have a range that can be contained in an int. One would expect that the conversion to time should be done on the input speed. That way, the division would be done only once for each speed setting. If the measured times were converted to RPM each time a time were measured, the computer program would be loaded with complicated divisions in its real time application portion. But let’s examine the nature of the error with the different types of measures. Suppose the input RPM were converted to time and the measurements were based on time. Then the error signal, which is the difference between the measured value and the desired value, would have a wrong sense. That is if the motor is moving too slowly, then the error signal would be negative, which would cause the motor to go even slower. If the calculation were done based on RPM and the motor were going too slow, the error signal would be positive. In this case, the control would drive the motor faster which is the needed correction.
The difficulty with the time-based system could be solved by using the negative value of the error signal for the feedback control. A simple analysis will show the potential problem with this approach. Suppose that we work with two counts, Cd and Cm. Let us use the value K for the constant in the above equation. Therefore,
1 |
1 |
||||||
e = Cd |
– Cm = K |
– |
|||||
RPMd |
RPMm |
||||||
The error signal is seen to be |
|||||||
RPM – RPM |
|||||||
e = K |
m |
d |
|||||
RPMd * RPMm |
|||||||
When the two speeds are nearly the same, this expression is very nearly proporstional to the difference between the two speeds. However, when one of the speeds deviates significantly from the other, it will cause the resultant error signal to be less sensitive to difference than would be expected. Also, there is the problem that occurs if the motor is stopped and the error signal in that case is undefined.
262 Chapter 5 Programming Large 8-Bit Systems
If the counts are converted to RPM prior to calculation of the error signal, the reduction of sensitivity for large errors would not be a problem. The problem that occurs when the motor is stopped still exists. In this case, no input capture would occur when the motor is not rotating, and the count value would be undefined. There seems to be no clear-cut reason to choose the time or the velocity measurement. Each has advantages and each has drawbacks. The drawbacks are about the same for each, so we will choose the case that has the simplest program. Therefore, the time-based or count-based system will be used.
Now comes the interesting problem of the calculation of 3000000/ RPM. Each time the desired speed of the motor is input to the system, this calculation must be completed. The straightforward calculation of this value will yield code as follows:
#include “hc11e9.h”
WORD count(WORD RPM)
{
unsigned long num = 30000000;
return num/RPM;
}
The listing file of the compiled version of this function is
1 ; Compilateur C pour MC68HC11 (COSMIC-France)
2.include”macro.h11"
3.list +
4.psect _text
5; 1 #include “hc11e9.h”
6.psect _data
7_Register_Set:
8 |
0000 |
1000 |
.word 4096 |
|
9 |
; |
2 |
||
10 |
; |
3 |
WORD count(WORD RPM) |
|
11 |
; |
4 |
{ |
|
12.psect _text
13_count:
14 0000 BD0000 |
jsr |
c_kents |
Timer Operations 263
15 0003 0C .byte 12
16 .set OFST=12
17 ; 5 unsigned long num = 120000000;
18 |
0004 |
CC0E00 ldd #3584 |
||
19 |
0007 |
ED0A |
std OFST-2,x |
|
20 |
0009 |
CC0727 ldd #1831 |
||
21 |
000C |
ED08 |
std OFST-4,x |
|
22 |
; 6 |
|||
23 |
; 7 return num/RPM; |
|||
24 |
000E |
EC0C |
ldd OFST+0,x |
|
25 |
0010 |
6F02 |
clr 2,x |
|
26 |
0012 |
6F03 |
clr 3,x |
|
27 |
0014 |
ED06 |
std OFST-6,x |
|
28 |
0016 |
EC02 |
ldd 2,x |
|
29 |
0018 |
ED04 |
std OFST-8,x |
|
30 |
001A |
EC08 |
ldd OFST-4,x |
|
31 |
001C |
ED02 |
std 2,x |
|
32 |
001E |
EC00 |
ldd 0,x |
|
33 |
0020 |
C3FFF7 |
addd #-9 |
|
34 |
0023 |
188F |
xgdy |
|
35 |
0025 |
EC0A |
ldd OFST-2,x |
|
36 |
0027 |
BD0000 |
jsr c_ludv |
|
37 |
002A |
AE00 |
lds 0,x |
|
38 |
002C |
38 |
pulx |
|
39 |
002D |
39 |
rts |
|
40 |
; 8 } |
|||
41 |
; 9 |
|||
42 |
.public _count |
|||
43 |
.public _Register_Set |
|||
44 |
.external |
c_kents |
||
45 |
.external |
c_ludv |
||
46 |
.end |
|||
This function requires 0x2d bytes (45 bytes) of code and that does not count the functions c_ludv and c_kents that must be linked to this function. Our innocuous little one-line piece of code creates a rather formidable piece of assembly code. If at all possible, it would be desirable to shorten this function. A slight modification of the above function code is
264Chapter 5 Programming Large 8-Bit Systems
#include “hc11e9.h”
WORD count(WORD RPM)
{
return 30000000lu/RPM;
}
The compiled version of this program is
1 ; Compilateur C pour MC68HC11 (COSMIC-France)
2.include”macro.h11"
3.list +
4.psect _text
5; 1 #include “hc11e9.h”
6 |
.psect |
_data |
||
7 |
_Register_Set: |
|||
8 |
0000 |
1000 |
.word 4096 |
|
9 |
; |
2 |
||
10 ; |
3 |
WORD count(WORD RPM) |
||
11 ; |
4 |
{ |
||
12.psect _text
13_count:
14 0000 |
BD0000 |
jsr c_kents |
15 0003 |
08 .byte 8 |
16.set OFST=8
17; 5 return 120000000lu/RPM;
18 |
0004 |
EC08 |
ldd OFST+0,x |
|
19 |
0006 |
6F02 |
clr 2,x |
|
20 |
0008 |
6F03 |
clr 3,x |
|
21 |
000A |
ED06 |
std OFST-2,x |
|
22 |
000C |
EC02 |
ldd 2,x |
|
23 |
000E |
ED04 |
std OFST-4,x |
|
24 |
0010 |
CC0727 |
ldd |
#1831 |
25 |
0013 |
ED02 |
std 2,x |
|
26 |
0015 |
EC00 |
ldd 0,x |
|
27 |
0017 |
C3FFFB |
addd #-5 |
|
28 |
001A |
188F |
xgdy |
|
29 |
001C |
CC0E00 |
ldd |
#3584 |
30 |
001F |
BD0000 |
jsr |
c_ludv |
Timer Operations 265
31 |
0022 |
AE00 |
lds 0,x |
32 |
0024 |
38 |
pulx |
33 |
0025 |
39 |
rts |
34 |
; 6 } |
||
35 |
; 7 |
||
36 |
.public _count |
||
37 |
.public _Register_Set |
||
38 |
.external |
c_kents |
|
39 |
.external |
c_ludv |
|
40 |
.end |
||
Note that this version differs from the first only in that the number 30000000 is not created in a declaration, but rather is created in line when it is needed. This version requires 37 bytes of code.
The purpose of this exercise is to demonstrate that there are usually many different ways that any part of a program can be approached. If the programmer grabs the first idea and plods through without any careful examination of the code being generated by the compiler, the program will almost always suffer. The assembly listings of programs should be examined, and where it seems as if the compiler is creating clumsy code, a new approach should be considered. Writing C code for a microcontroller is a joint exercise by the programmer and the compiler to create the most efficient overall program.
Let’s return to the problem: we wish to set the speed of a motor and the measurable control signal is the time of a revolution. Most servo type devices work to position an output. Ours must set a speed which is a different problem from most. The input will be an RPM which will be converted to a time. Our system must compare the desired time with the measured time and correct the speed to make the two times match. A signal to drive the motor will be generated based on the desired time, and this signal will be adjusted by the error signal calculated as the difference between the measured time and the desired time. The following pseudocode sequence will accomplish the desired operation.
FOREVER
{
do
convert to time;
calculate the required PWM output count; apply calculated time error to PWM count;
266 Chapter 5 Programming Large 8-Bit Systems
read in the motor speed;
while ( motor speed does not change);
}
For the moment, let us defer the problem of getting the desired motor speed. This value will be converted to time by use of the count() routine and will be saved in motor_period. Next, we need to calculate the required motor voltage. For this calculation, let us revert to an experimental technique that can be used usefully. An open loop system was set up to test the operation of the motor system. Several input values were entered, and the performance of the motor was recorded. Table 5-2 shows the result of these measurements.
PWM_Count |
PWM_Count |
icap period |
period ms |
voltage |
RPM |
Hex |
decimal |
counts |
|||
0x700 |
1792 |
15812 |
31.62 |
2.186 |
1897 |
0x780 |
1920 |
11405 |
22.81 |
2.341 |
2630 |
0x800 |
2048 |
8581 |
17.17 |
2.498 |
3494 |
0880 |
2176 |
6841 |
13.68 |
2.653 |
4385 |
0x900 |
2304 |
5616 |
11.23 |
2.809 |
5342 |
0x980 |
2432 |
4759 |
9.52 |
2.964 |
6304 |
0xa00 |
2560 |
4059 |
8.12 |
3.118 |
7391 |
0xa80 |
2688 |
3530 |
7.06 |
3.273 |
8499 |
0xb00 |
2816 |
3085 |
6.17 |
3.424 |
9724 |
0xb80 |
2944 |
2736 |
5.47 |
3.578 |
10965 |
Table 5-2: Motor Performance Measurements
The data for this table were gathered by the use of an M68HC11EVM and a simple motor driver. This driver is a demonstration board provided by Motorola to show the use of the MC33033 pulse width motor driver and the MPM3002 FET motor driver H bridge. A small DC motor with a speed range of 1000 to 11000 rpm was mounted on the board, and the appropriate circuitry was added to interface the board to the computer. This interface consisted of a simple RC integrator to receive the PWM signal from the computer board and a circuit to measure the rotation of the motor. This latter circuit was quite crude. A magnetic reed switch was mounted near the motor shaft and a magnet cemented to the shaft. For each rotation of the shaft, the reed switch would close and open one time. A resistor was connected between the 12 volts of the motor driver and one side of the switch; the other side of the switch was