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

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

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

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

Добавлен: 14.06.2025

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

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

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

8 DRIVING MOTORS - DC & STEPPER 239

delete MotorPtr;

Since MotorPtr is a pointer to the abstract base class Motor, the previous statement will call the default destructor of the Motor class and will only deallocate the space occupied by a Motor class object. The delete statement will not free the memory space occupied by the actual object in use (such as DCMotor), thereby generating a memory leak. We can demonstrate this event using an example program that has a simple class structure and a simple main() function as shown in Listing 8-14. Note: to further simplify the example, the Base and Derived classes do not have data members. We have also included cout statements within the body of the two destructors to show when each destructor is called. If the destructors did not have these cout statements, they would be identical to the default destructors generated by the compiler.

Listing 8-14 Use of non-virtual destructors.

#include <conio.h> #include <iostream.h>

class Base

{

public:

Base(){}

~Base()

{

cout << "Base type object deleted" << endl;

}

};

class Derived : public Base

{

public:

Derived(){};

~Derived()

{

cout << "Derived type object deleted " << endl;

}

};

void main()

{

Base *BasePtr;

BasePtr = new Derived; // BasePtr points to an object // of type Derived.

240 8 DRIVING MOTORS - DC & STEPPER

delete BasePtr;

// Deletes object

}

The pointer identifier BasePtr is declared to be of type Base. However, it is used to point to a dynamically allocated class object that is of type Derived. We use the following statement with the intention of deleting the dynamically allocated object of class Derived:

delete BasePtr;

We would expect this statement to call the destructor of the derived class. However, the following message is displayed when this program is executed:

Base type object deleted

This indicates that the destructor of the Derived class has not been called as intended to destroy the dynamically allocated Derived type object. We change the program to operate correctly by making the destructor ~Base() virtual. The modified program listing is shown in Listing 8-15.

Listing 8-15 Use of virtual destructors.

#include <conio.h> #include <iostream.h>

class Base

{

public:

Base(){} virtual ~Base()

{

cout << "Base type object deleted" << endl;

}

};

class Derived : public Base

{

public:

Derived(){};

~Derived()

{

cout << "Derived type object deleted " << endl;

}

};

void main()


8 DRIVING MOTORS - DC & STEPPER 241

{

Base *BasePtr;

BasePtr = new Derived;

// BasePtr

points to an object

// of type Derived.

delete BasePtr; // Deletes object

}

Note that in Listing 8-15 the keyword virtual is added in front of the destructor name ~Base(). This provides the link to all virtual destructors down to the next level of the class hierarchy so the correct destructors will be called. If you run this program you will see the following printed on the screen:

Derived type object deleted

Base type object deleted

This demonstrates that the delete statement has called the Derived class destructor and the Base class destructor, properly relinquishing the memory allocated for the Derived and Base class objects. Note: when an object of a derived class is instantiated, the constructor function of the base class is called first followed by a call to the constructor of the derived class. The derived class inherits the members of the base class that are instantiated in this manner. Therefore, it is important to include virtual destructors so that any memory allocation from within the derived class and its base class is properly relinquished.

Now we can return our attention to the motor control program. To allow the delete statements to properly de-allocate the dynamically allocated objects, we must provide a set of destructors; one destructor for each class of the motor class hierarchy, and we must make them virtual destructors. The bodies of these destructors can be empty. We simply need to establish a network of virtual destructors throughout the class hierarchy so that proper late binding will take place for the destructors. The modified class definitions are given in Listing 8-16 through to Listing 8-18.

C++

Virtual Destructor Names

In a class hierarchy all virtual functions must have the same function signature;

i.e. they must have the same function name, same number of formal arguments

and the same types of formal arguments in each virtual function. However,

virtual destructors have different names throughout the hierarchy. Despite

having different destructor function signatures, late binding will enable the

correct set of destructors to be deployed in response to a delete statement.

242 8 DRIVING MOTORS - DC & STEPPER

Listing 8-16 AbstractMotor class with virtual destructor.

class AbstractMotor

{

private:

int Speed;

public:

AbstractMotor();

void SetSpeed(int speed); int GetSpeed();

virtual void Off()=0; virtual void Forward()=0; virtual void Reverse()=0; virtual void Brake()=0; virtual ~AbstractMotor(){}

};

Listing 8-17 ParallelPort class with virtual destructor.

class ParallelPort

{

private:

unsigned int BaseAddress; unsigned char InDataPort1;

public:

ParallelPort(); ParallelPort(int baseaddress);

void WritePort0(unsigned char data); void WritePort2(unsigned char data); unsigned char ReadPort1();

virtual ~ParallelPort(){}

};

Listing 8-18 Motor class with virtual destructor.

class Motor : public AbstractMotor, public ParallelPort

{

public:

Motor(int baseaddress=0x378); void Off();

virtual void Forward()=0;


8 DRIVING MOTORS - DC & STEPPER 243

virtual void Reverse()=0; virtual void Brake()=0; virtual ~Motor(){}

};

Now that we have proper destructors in our classes, we can write a complete program using virtual functions to control a motor and free memory as intended. Such a program is shown in Listing 8-19. Note that virtual destructors are not added to the DCMotor class or the StepperMotor class since these two classes are the terminal classes of the hierarchy. However, the program would still function properly if virtual destructors had been added to these two classes.

NOTE

Ensure the interface board is unpowered before connecting any type of motor. This needs to be done for the following reason.

Before first running the program, the port controlling the motor will not be under

control of the program and may be in an unknown state. This unknown state can

be such that the port’s logic states would drive the transistors to short-circuit the

motor’s power supply (damaging the transistors and possibly the power supply).

The program instructs the user to apply power to the board once it has set these

bits to a safe state. When the program ends, it sets the used bits of the port to a safe state to prevent any damage to the transistors or the power supply.

Connect a DC motor to the interface board as given in Table 8-7 and Table 8-9.

Stepper motors are connected to the interface board as shown in Figure 8-25 and

Figure 8-26.

If the motor does not drive as expected, first check for incorrect connections.

Listing 8-19 Complete program to control 'a Motor' using Virtual Functions.

//**************************************************

//Program to operate a Motor using Virtual Functions.

//**************************************************

#include <dos.h> #include <conio.h> #include <stdio.h> #include <stdlib.h> #include <iostream.h>

class ParallelPort

{

private:

244 8 DRIVING MOTORS - DC & STEPPER

unsigned int BaseAddress; unsigned char InDataPort1;

public:

ParallelPort(); ParallelPort(int baseaddress);

void WritePort0(unsigned char data); void WritePort2(unsigned char data); unsigned char ReadPort1();

virtual ~ParallelPort(){}

};

ParallelPort::ParallelPort()

{

BaseAddress = 0x378; InDataPort1 = 0;

}

ParallelPort::ParallelPort(int baseaddress)

{

BaseAddress = baseaddress; InDataPort1 = 0;

}

void ParallelPort::WritePort0(unsigned char data)

{

outportb(BaseAddress,data);

}

void ParallelPort::WritePort2(unsigned char data)

{

outportb(BaseAddress+2,data ^ 0x0B);

}

unsigned char ParallelPort::ReadPort1()

{

InDataPort1 = inportb(BaseAddress+1);

//Inverting Most significant bit to compensate

//for internal inversion by printer port hardware. InDataPort1 ^= 0x80;

//Filter to clear unused data bits D0, D1 and D2 to zero. InDataPort1 &= 0xF8;

return InDataPort1;

}


8 DRIVING MOTORS - DC & STEPPER 245

class AbstractMotor

{

private:

int Speed;

public:

AbstractMotor();

void SetSpeed(int speed); int GetSpeed();

virtual void Off()=0; virtual void Forward()=0; virtual void Reverse()=0; virtual void Brake()=0; virtual ~AbstractMotor(){}

};

AbstractMotor::AbstractMotor()

{

Speed =0;

}

void AbstractMotor::SetSpeed(int speed)

{

Speed = speed;

if(Speed > 255) Speed = 255; // Limit upper value if(Speed < 0) Speed = 0; // Limit lower value

}

int AbstractMotor::GetSpeed()

{

return Speed;

}

class Motor : public AbstractMotor, public ParallelPort

{

public:

Motor(int baseaddress=0x378); void Off();

virtual void Forward()=0; virtual void Reverse()=0; virtual void Brake()=0; virtual ~Motor(){}

};

Motor::Motor(int baseaddress): ParallelPort(baseaddress)

246 8 DRIVING MOTORS - DC & STEPPER

{

Off();

}

void Motor::Off()

{

WritePort0(0x00);

}

class DCMotor : public Motor

{

public:

DCMotor(int baseaddress=0x378); virtual void Forward();

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

};

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);

}

void DCMotor::Brake()

{

WritePort0(0x0C);

}

8 DRIVING MOTORS - DC & STEPPER 247

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();

};

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;