Файл: Interfacing with C plus plus-programing communication with microcontrolers (K. Bentley, 2006).pdf

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

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

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

Добавлен: 14.06.2025

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

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

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

8 DRIVING MOTORS - DC & STEPPER 223

interface board unpowered until the program is being executed since some bits of the port to be used may have previously been left in an on state.

NOTE

The two contacts on the 4-way terminal block labelled VM1 and VM1 GND are

used to connect power to the H-bridge (VM2 and VM2 GND for the other H- bridge).

The interface board is capable of providing +12V at up to 1A to drive motors. To

use this power supply, connect Vm1 (or Vm2 as appropriate) via an insulated wire

to the +12V of the power supply’s 2-way terminal block (where the power-pack

connects). A second insulated wire is used to connect the ground of the respective

H-bridge Vm1 GND (or Vm2 GND) to the ground of the power supply. Most

importantly, a wire needs to be connected across the two contacts of each 2-way terminal block. These 2-way terminal blocks have been provided to allow resistive drive schemes to be trialed by fitting resistors of suitable value inplace of the

wires.

Motors may need to be driven with voltage or current supply requirements differing

from those of the power supply on the interface board (+12V, 1A). In this case, an

external power supply up to a maximum of 30 V DC can be used to power the

motors. DO NOT at any time attempt to connect mains power to the board.

When using an external power supply, connect the positive contact of its output to the 4-way terminal block contact Vm1 (or Vm2 as appropriate). Also, connect the

negative contact of the external power supply to the 4-way terminal block contact

Vm1 GND (or Vm2 GND). The external power supply MUST NOT be connected to

the 2-way terminal block of the interface board power supply.

+12V (1A) power supply’s

2-way terminal block

OR - VE

External

+ VE

DC Power Supply

(Max 30V)

Vm1 M1 M2

Vm1 GND

Wire Link

J7

J6

J8

Figure 8-21 Connecting a DC power supply to power a DC motor.

224 8 DRIVING MOTORS - DC & STEPPER

Table 8-9 H-bridge to DC motor connections.

H Bridge 1

Connections

Vm1

Motor Power Supply +ve*

M1

Motor + terminal

M2

Motor - terminal

Vm1 GND

Motor Power Supply –ve*

Having determined all the connections, we can now proceed to implement a method to drive the motor, in particular to control its torque/speed. The most appropriate means to do this is by using Pulse Width Modulated (PWM) signals.

8.5.2 Generating Pulse Width Modulated Signals

As described earlier in Section 8.2.2, using pulse width modulation with wider pulse widths generates higher levels of motor speed/torque. In other words, the higher the duty cycle, the higher the motor speed. In our application the duty cycle is the proportion of time in a repetitive cycle that the motor will have power applied to it. This ratio is usually represented as a percentage. To control the speed we must control the duty cycle. Therefore, the design of the program should allow the duty cycle to be changed as desired. In general, exact timing is normally used to control duty cycle. In this chapter, we will not use exact time delays. Instead, duty cycle will be controlled by executing software loops. Different computers will have different execution speeds and therefore produce different PWM cycle times (frequencies), although the shape/duty cycle of the PWM signal will be the same.

Single PWM cycle, comprising 256 writes to the port at BASE address. The actual cycle time is determined by the speed of your computer.

Next PWM cycle. These cycles repeat continuously to generate a PWM signal.

Voltage

Time

N writes of 0x09 to the port thereby applying full voltage to the armature of the DC motor.

(256-N) writes of 0x00 to the port thereby removing the voltage to the armature.

Figure 8-22 Generation of a PWM signal.

The PWM signal can be generated as follows. Suppose one cycle consists of 256 uninterrupted writes to the port. The cycle time is then equal to the time the


8 DRIVING MOTORS - DC & STEPPER 225

computer would take to complete 256 writes to the port. Depending on the desired duty cycle, a portion of the 256 writes to the port can be used to send 0x09 to the port (see Table 8-8) to turn the motor on in the forward direction. The remainder of the writes can send 0x00 to the port to turn the motor off. For example, if we want 50% duty cycle, we will output 0x09 to the port for the first 128 writes and output 0x00 to the port for the remaining 128 writes. This is a means to control the speed of the motor. A number between 0 and 255 (forming 256 different speed settings) can be used to specify the speed. Figure 8-22 illustrates the PWM process.

The control of speed in the reverse direction can be achieved by using the same procedure, except the data written to the port during the ON portion of the PWM cycle is 0x06 instead of 0x09. In strict terms, the speed control our software uses is known as open-loop speed control. When using open-loop speed control we do not measure the speed of the motor nor apply corrective action for variations in speed. In other words, we do not have a form of feedback to measure and correct actual motor speed.

The member function definitions of the DCMotor class are shown in Listing 8-7. Referring to the earlier Listing 8-6, it can be seen that a default actual argument is specified for baseaddress. This is not evident in the function definitions in Listing 8-7.

Listing 8-7 Member functions of the DCMotor class.

DCMotor::DCMotor(int baseaddress):Motor(baseaddress)

{

}

void DCMotor::Forward()

{

int j;

for(j = 0; j < GetSpeed(); j++) WritePort0(0x09);

for(;j < 256; j++) WritePort0(0x00);

}

void DCMotor::Reverse()

{

int j;

for(j = 0; j < GetSpeed(); j++) WritePort0(0x06);

for(;j < 256; j++) WritePort0(0x00);

}

226 8 DRIVING MOTORS - DC & STEPPER

void DCMotor::Brake()

{

WritePort0(0x0C);

}

The Forward() function and the Reverse() functions are very similar. They differ in the actual argument passed to the WritePort0() function within the first for loop of each function. Therefore, we will only explain operation of the

Forward() function.

The Forward() function operates using the functions shown in bold typeface below:

void DCMotor::Forward()

{

for(int j = 0; j < GetSpeed(); j++)

WritePort0(0x09); for(;j < 256; j++)

WritePort0(0x00);

}

The highlighted functions are all member functions of various classes of the hierarchy. The function GetSpeed() is inherited from the abstract class AbstractMotor and the function WritePort0() is inherited from the

ParallelPort class.

As described previously, the speed of the motor is determined by the duty cycle of the PWM signal. The duty cycle is controlled by the number of writes to the port at BASE address that allows the H-bridge to apply the power supply voltage to the motor. One cycle of the PWM signal comprises 256 writes to the port at address BASE. Counting from 0 to 255 (inclusive) will give us 256 numbers. Therefore, 255 specifies 100% duty cycle (full speed) and 0 specifies 0% duty cycle (zero speed).

When the program enters the first for loop, the inherited data member Speed must have a valid value, i.e. a value between 0 and 255. The first for loop initialises its loop count variable j to 0. It then uses the inherited WritePort0() function to write 0x09 to the port at BASE. This action will apply full voltage to the motor. The loop count variable j is then incremented using j++. Next, the test expression j < GetSpeed() is evaluated. The Forward() function of the DCMotor class has no direct access to the inherited data member Speed as it is a private member of the AbstractMotor class. Therefore, to obtain the value of Speed, the public function GetSpeed() of the AbstractMotor class must be used. Provided the test condition evaluates to true, the body of the first for loop will execute again. For example, if the value of Speed is 5, the function WritePort0() will be executed for j equal to 0, 1, 2, 3 and 4. When j is


8 DRIVING MOTORS - DC & STEPPER 227

incremented to become 5, the test expression will evaluate to false and the first for loop will terminate. Control will then be transferred to the second for loop.

Note that the second for loop is unusual in that it does not have an initialising expression. The value of j is passed on to the next loop to complete the rest of the PWM cycle - which is why the loop count must not be re-initialised.

Using the values given in the example above, the value of j will be 5 upon entering the second for loop. The second for loop will run until the value of j exceeds 255. Each time the for loop executes 0x00 will be output to the BASE address part of the port, preventing voltage being supplied to the motor. When j becomes 256, the for loop will terminate, having completed one PWM cycle. This would produce a duty cycle of 5 ON writes out of 256 total writes in the cycle; i.e. 2% duty cycle.

Consecutive PWM cycles must be made to generate a continuous PWM signal. Therefore, the Forward() function must be called repeatedly until the user ceases to issue a forward command. This can be accomplished, for example, by implementing a while loop conditioned on !kbhit(). The while loop will continue provided the keyboard is not hit. The function kbhit() will return a non zero value when a key is pressed. When the logical negation operator (!) is placed in front, !kbhit() will return a non-zero value as long as a key is not pressed. Now the while loop will continue execution until a key is pressed. Each iteration of the while loop generates one PWM cycle and so the PWM signal will continue until a key is pressed. The while loop can be implemented outside the member functions, in say the main() function.

The Reverse() function operates in a similar manner to the Forward() function. The difference being that the first for loop writes a different value to the H-bridge to apply a voltage with reversed polarity to drive the motor in the opposite direction. The PWM signal is generated in the same manner as for the

Forward() function.

The Brake() function calls the WritePort0() function once to close the switches of the H-bridge and short-circuit the DC motor armature to brake the motor.

In Section 8.6 we explain how the user can use this class to drive a DC motor.

StepperMotor class

The StepperMotor class must cater for several types of stepper motors and their two modes of operation. These modes and their acronyms are shown in Table 8-10. The class definition for the StepperMotor class is given in Listing 8-8.

228 8 DRIVING MOTORS - DC & STEPPER

Table 8-10 Acronyms for Stepper Motors.

Stepper Motor

Mode

Acronym

Unipolar

Full-Step

UPFS

Unipolar

Half-Step

UPHS

Bipolar

Full-Step

BPFS

Bipolar

Half-Step

BPHS

Listing 8-8 New data type MOTORTYPE and StepperMotor class.

enum MOTORTYPE {UPFS, UPHS, BPFS, BPHS};

class StepperMotor : public Motor

{

private:

MOTORTYPE MotorType; unsigned char Switching[8]; int CycleIndex;

int MaxIndex;

public:

StepperMotor(MOTORTYPE motortype = UPFS, int baseaddress=0x378);

virtual void Forward(); virtual void Reverse(); virtual void Brake();

};

A new programmer-defined data type named MOTORTYPE has been created at the top of this listing. This data type is termed an enumerated data type because all possible values the data type can be given are listed in the type declaration. These values are named enumeration constants and are always of type int. Use mnemonic identifiers that are meaningful to improve readability of the program.

keyword enum Mnemonic identifiers

enum MOTORTYPE {UPFS, UPHS, BPFS, BPHS};

0 1 2 3

new data type name

These enumerated constants (integer values) are automatically assigned to the above identifiers.

Figure 8-23 Enumerated data types.


8 DRIVING MOTORS - DC & STEPPER 229

In the declaration shown in Figure 8-23, the new data type has the name MOTORTYPE. The enumerated constants 0, 1, 2, and 3 are automatically assigned to the mnemonic identifiers UPFS, UPHS, BPFS, and BPHS, respectively. We can declare a variable of this type as follows:

MOTORTYPE NewMotor;

Note that if you use a C compiler (Not a C++ compiler) then the equivalent declaration must be:

enum MOTORTYPE NewMotor;

NewMotor can take any value enumerated; i.e. the values listed between the two braces. Had we specified that the mnemonic UPFS was equal to say 2 as shown below, then UPHS, BPFS and BPHS would become 3, 4 and 5 respectively.

enum MOTORTYPE {UPFS = 2, UPHS, BPFS, BPHS};

Since all enumerated values are represented by integers, it is possible to carry out integer arithmetic on the data type. For example:

MOTORTYPE NewBreed;

NewBreed = UPFS + BPFS; //valid but meaningless!

While this is possible, it has no meaning whatsoever for our purposes. However, under certain circumstances integer arithmetic with enumerated types can be of benefit when used with care.

Referring to Table 8-10, the enumerated data type shown in Listing 8-8 can represent any of the stepper motor types we aim to control using our interface board. We can now return to study the StepperMotor class definition in Listing 8-8. It has four private data members described as follows.

MOTORTYPE MotorType

The first member data is MotorType. Once initialised this data member will store the type of stepper motor to be controlled.

unsigned char Switching[8]

This statement declares an array named Switching that has eight unsigned char elements. The program will load this array with the unique switching patterns for the type of stepper motor the user selects. These switching patterns are specified ahead when the functions for the class are defined.

int CycleIndex

The member data CycleIndex will be used as a subscript to scan through the array Switching and select the values of each of its single byte elements in the proper sequence to step-wise drive a stepper motor. To drive a stepping motor forward using full-steps, CycleIndex will have values 0, 1, 2, 3, 0, etc. To drive the same motor in reverse direction CycleIndex will have values 0, 3, 2, 1, 0, etc. Similarly, to drive a stepping motor forward using half-steps, CycleIndex

230 8 DRIVING MOTORS - DC & STEPPER

will have values 0, 1, 2, 3, 4, 5, 6, 7, 0, etc. When driving a stepping motor in reverse using half-steps, CycleIndex will have the values 0, 7, 6, 5, 4, 3, 2, 1, 0, etc.

int MaxIndex

This data member will be used to detect the position in the array Switching when a new cycle must recommence. As such MaxIndex stores the maximum value CycleIndex takes. For full-step control, only four elements of the Switching array are used, so the value of MaxIndex will be 4. However, all eight elements of the array Switching must be used for half-step control. In this case the value of MaxIndex will be 8.

The member function definitions of the StepperMotor class are given in Listing 8-9.

Listing 8-9 Member functions of the StepperMotor class.

StepperMotor::StepperMotor(MOTORTYPE motortype,

int baseaddress): Motor(baseaddress)

{

MotorType = motortype; CycleIndex = 0;

switch(MotorType)

{

case UPFS: MaxIndex = 4; Switching[0] = 0x11; Switching[1] = 0x12; Switching[2] = 0x22; Switching[3] = 0x21; break;

case UPHS: MaxIndex = 8; Switching[0] = 0x01; Switching[1] = 0x11; Switching[2] = 0x10; Switching[3] = 0x12; Switching[4] = 0x02; Switching[5] = 0x22; Switching[6] = 0x20; Switching[7] = 0x21; break;

case BPFS: MaxIndex = 4; Switching[0] = 0x99; Switching[1] = 0x69; Switching[2] = 0x66;