Файл: 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