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

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

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

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

Добавлен: 14.06.2025

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

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

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

5 OBJECT-ORIENTED PROGRAMMING 85

Class Definition – Improvement I

We need to provide the user with the ability to specify the base address according to hardware requirements. This is best done at the time of creating the ParallelPort object – ie at the time of calling the constructor. Our default constructor will need to be replaced with a constructor that allows us to set the member data BaseAddress to a desired value at the time of creating the object.

New constructor: We pass the parameter baseaddress to the constructor, which then assigns this value to the member data BaseAddress. Now the original outportb function will work as intended. You will have noticed in Listing 5-4 that the member function is defined separately from the class definition. The reason for this is explained in the box presented previously titled “In-line member functions”.

Listing 5-3 The class definition for ParallelPort - with a constructor.

class ParallelPort

{

private:

unsigned int BaseAddress;

public:

ParallelPort(int baseaddress); // constructor void WritePort0(unsigned char data);

};

Listing 5-4 Definitions of member functions for the improved ParallelPort class.

ParallelPort::ParallelPort(int baseaddress) // constructor

{

BaseAddress = baseaddress;

}

void ParallelPort::WritePort0(unsigned char data)

{

outportb(BaseAddress,data);

}

86 5 OBJECT-ORIENTED PROGRAMMING

NOTE

The member data BaseAddress is different to baseaddress. The

parameter baseaddress is a placeholder for the actual argument one would pass at the time of calling the constructor and plays a role only at the time of calling the constructor. On the contrary, the member data BaseAddress will

reside in memory storing data for the entire life of the object; in most cases, for the

whole life of the program.

Class Definition – Improvement II

Because most users will operate their parallel port at address 0x378, ideally we would like to have a constructor that by default sets the BASE address to 0x378. We still need the option of being able to set the port to other address values as was implemented in the previous class definition.

We achieve this by adding a second constructor (overloaded constructors) that sets the member data BaseAddress to 0x378 by default. This programmer-defined default constructor takes no parameters (ie. a default constructor) but assigns the member data BaseAddress to 0x378 inside its body. The class definition and member function definitions are given in Listing 5-5 and Listing 5-6.

Listing 5-5 Class definition for ParallelPort with a default constructor.

class ParallelPort

{

private:

unsigned int BaseAddress;

public:

ParallelPort(); // default constructor

ParallelPort(int baseaddress); // constructor void WritePort0(unsigned char data);

};

5 OBJECT-ORIENTED PROGRAMMING 87

Listing 5-6 Member function definitions for the class definition in Listing 5-5.

ParallelPort::ParallelPort() // default constructor

{

BaseAddress = 0x378;

}

ParallelPort::ParallelPort(int baseaddress) // constructor

{

BaseAddress = baseaddress;

}

void ParallelPort::WritePort0(unsigned char data)

{

outportb(BaseAddress,data);

}

If in our program we do not pass a value to the constructor, the default constructor will be called and the BASE address will default to 0x378. Alternatively we can set the BASE address to say 0x3BC by passing this value when creating a ParallelPort object as shown:

ParallelPort(0x3BC);

In this case the second constructor (takes a parameter) will be called to create the ParallelPort object. Although constructors commonly initialise some or all of the member data, a constructor can be programmed to carry out any actions a regular function may perform.

An important point to note is that the member function WritePort0() has not been specifically informed of the member variable BaseAddress – that is, BaseAddress has not been declared within this function. This is not necessary since BaseAddress and WritePort0() are both members of the same class, hence WritePort0() has unrestricted access to BaseAddress as explained in Section 5.3.3.

5.5 Using Class Objects in Programs

We will now use the ParallelPort object type developed in the previous section to output a byte of data to the port at BASE address. The object-oriented program that performs this task seems lengthy, however, the advantages of objectoriented programming will be realised as we progress through later chapters.


88 5 OBJECT-ORIENTED PROGRAMMING

Listing 5-7 Writing to port at address BASE using object-oriented approach.

/***************************************************** WRITING TO A PORT (object-oriented approach)

The program uses the fundamental ParallelPort object class to output a byte of data to the interface board.

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

#include <dos.h>

class ParallelPort

{

private:

unsigned int BaseAddress;

public:

ParallelPort(); ParallelPort(int baseaddress);

void WritePort0(unsigned char data);

};

ParallelPort::ParallelPort() // default constructor

{

BaseAddress = 0x378;

}

ParallelPort::ParallelPort(int baseaddress) // constructor

{

BaseAddress = baseaddress;

}

void ParallelPort::WritePort0(unsigned char data)

{

outportb(BaseAddress,data);

}

void main()

{

ParallelPort OurPort; // object instantiation

OurPort.WritePort0(255); // calling a member function

}

The class definition and function definitions are the same as developed earlier and have already been explained. What has not been explained is the body of the

5 OBJECT-ORIENTED PROGRAMMING 89

main() function. This is where a programmer uses the object classes to develop an application. The first of the two lines in main() creates an object named OurPort of type ParallelPort. The second line calls OurPort’s member function WritePort0()and outputs the value 255 to the port. The actual port address used by WritePort0() is assigned when we create the OurPort object by calling one of the two constructors. This process will now be explained in more detail.

Object Creation (instantiation)

The first line in main() is:

ParallelPort OurPort;

This is a declaration line and is similar to:

int a; // Data type is int, variable name is a.

Similarly; ParallelPort is the data type, which is an object type. OurPort is the name of the variable. The only difference is that int is a fundamental built-in data type whereas ParallelPort is a data type we developed.

In C++, the declaration lines not only inform the compiler the name (such as OurPort) and type of the object (such as ParallelPort), but also call the constructor of that object class to create the object. The creation of the object OurPort is what we mentioned earlier as instantiation – it brings into life a real usable object that will reside in memory. It is important to know the difference between ‘an object’ (also known as a class object) and ‘an object type’ (also known as the object class). The object is OurPort and the object type is ParallelPort. Thus, we have created an object named OurPort of object type ParallelPort.

Referring to the class definition given in Listing 5-5, we see that there are two constructor functions available. They are:

ParallelPort();

ParallelPort(int baseaddress);

Which of these two constructors were used by the declaration line to instantiate the OurPort object? The compiler will automatically select the correct constructor based on the parameters passed (or the absence of them). Since we did not specify a value for the parameter baseaddress at the time of instantiating the OurPort object, the compiler will call the first of the above two constructors, being the default constructor.

On the other hand, the declaration line in the main() function could have been:

ParallelPort OurPort(0x3BC);

In this case, the compiler will not call the default constructor, since a value for a parameter has been specified. Instead, the other constructor (which accepts a parameter) will be called to instantiate the object OurPort:


90 5 OBJECT-ORIENTED PROGRAMMING

ParallelPort(int baseaddress);

In the example above, when creating a ParallelPort object, the parameter baseaddress is replaced by the actual argument 0x3BC. The value 0x3BC will then be assigned to the data member BaseAddress (see the body of the constructor in Listing 5-6).

Returning to the program in Listing 5-7, the created object named OurPort will have all the features given in the class definition. It has its own private data member named BaseAddress and three public member functions. Two of the functions are constructors with the name ParallelPort and the other function is named WritePort0.

Sending data to the Port

The object we created named OurPort, makes use of its member function WritePort0() to send the value 255 out the port. The syntax for accessing this member function uses a period placed after the object name followed by the member name:

OurPort.WritePort0(255);

The other members of the object are accessed in exactly the same manner, although their access attributes may restrict access. For example, despite the correct syntax, any function outside the class trying to execute the following instruction will fail:

OurPort.BaseAddress = 0; // Will not work!

This is because BaseAddress is a private data member of the class and functions outside of the class cannot change it (the main() function is not part of the class). This illegal attempt to access the member data BaseAddress will be detected during compilation and a compilation error will be reported. The next section will present additional examples of access attributes used in our program.

Note that the WritePort0() function takes in one parameter. This parameter has been replaced by the actual argument 255. In binary, the decimal value of 255 will be represented with all eight bits set to 1.

The operation of the program can be checked by connecting the PC to the interface board as shown in Table 3-1. Passing the parameter 255 to the WritePort0()function should generate eight lit LEDs. You can change this number to any value between 0 and 255 inclusive. If you re-run the program you should see the LEDs light up accordingly.

5.5.1 Examples using Access Attributes

In the preceding section, an illegal attempt was made to assign the value 0 to data member BaseAddress, as now shown again:

OurPort.BaseAddress = 0; // Will not work!

5 OBJECT-ORIENTED PROGRAMMING 91

In that program, BaseAddress was declared as a private data member, which prevented the main() function from gaining access to change its value.

Instead of declaring BaseAddress as a private member, it can be declared as a public member. This being the case, it can be accessed by any function in the program and the compilation error will no longer appear. Listing 5-8 shows how this is done. Note that if you do not have a second parallel port (LPT2:) in your computer, the address 0x3BC does not exist, and the program will not function. However, it will compile error-free. Also, ensure the data cable that connects with the parallel port is plugged into the D25 connector of the correct port.

Listing 5-8 Declaring BaseAddress as a public data member.

/***************************************************** Note that the member variable BaseAddress is now declared under the public access attribute. Therefore it can be changed from within the main function and there will be NO compilation errors.

If your computer DOES NOT have a second parallel port, attempting to use the port address 0x3BC will FAIL!

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

#include <dos.h>

class ParallelPort

{

public: // Private access attribute has been // changed to public.

unsigned int BaseAddress;

public:

ParallelPort(); ParallelPort(int baseaddress);

void WritePort0(unsigned char data);

};

ParallelPort::ParallelPort()

{

BaseAddress = 0x378;

}

ParallelPort::ParallelPort(int baseaddress)

{

BaseAddress = baseaddress;

}


92 5 OBJECT-ORIENTED PROGRAMMING

void ParallelPort::WritePort0(unsigned char data)

{

outportb(BaseAddress,data);

}

void main()

{

ParallelPort OurPort;

OurPort.BaseAddress = 0x3BC; // Will NOT cause a // compilation error

OurPort.WritePort0(255);

}

Declaring member data as public is considered poor programming practice. The main purpose of using object-oriented programming is to form encapsulated objects, that are to some extent protected against misuse. This will not be the case if member data are declared to be public.

If a member variable needs to be changed, then it can be changed through a member function specifically designed for that purpose. This concept is demonstrated by the ChangeAddress() function shown in Listing 5-9.

Listing 5-9 The acceptable way to change a data member of an object.

/***************************************************** WRITING TO A PORT (Adding a member function to change the private member data).

Note: attempting to use the port address 0x3BC will fail if your computer doesn’t have a second parallel port.

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

#include <dos.h>

class ParallelPort

{

private: // Access attribute has been changed // back to private.

unsigned int BaseAddress;

public:

ParallelPort(); ParallelPort(int baseaddress);


5 OBJECT-ORIENTED PROGRAMMING 93

void WritePort0(unsigned char data);

// New public member function added.

void ChangeAddress(unsigned int newaddress);

};

ParallelPort::ParallelPort()

{

BaseAddress = 0x378;

}

ParallelPort::ParallelPort(int baseaddress)

{

BaseAddress = baseaddress;

}

void ParallelPort::WritePort0(unsigned char data)

{

outportb(BaseAddress,data);

}

// New public member function defined.

void ParallelPort::ChangeAddress(unsigned int newaddress)

{

BaseAddress = newaddress;

}

void main()

{

ParallelPort OurPort;

// The correct way to manipulate a private data member OurPort.ChangeAddress(0x3BC);

OurPort.WritePort0(255);

}

The program statement shown below from the previous listing will legally change the value of the private data member BaseAddress to 0x3BC:

OurPort.ChangeAddress(0x3BC);

In this illustrative example we have shown how a private data member can be changed using a public member function. The public member function ChangeAddress() has complete access to the private data member BaseAddress since it is a member function of that same object class. We will not use the ChangeAddress() function in our proper ParallelPort class.

94 5 OBJECT-ORIENTED PROGRAMMING

Instead, the user can pass the desired value for the base address to the constructor so the BaseAddress can be set to a different value than its default value of 0x378 (set by the default constructor).

5.6 Parallel Port Class – Stage II

In the first stage we created an object class named ParallelPort which provided the required functionality to use the port associated with address BASE. We now need this object to also use the port at address BASE+1. Note that the port at address BASE is used as an output port and the port at address BASE+1 is an input port. The new object’s intended functionality is:

ξ

Ability to specify the base address of the parallel port.

ξ

Send data through port at address BASE.

ξ

Receive data through port at address BASE+1.

Adding further functionality to an existing object is an ideal situation for using class derivation. However, there is no justification to develop a hierarchy of classes for each part of the parallel port, since there is no great use of parts of the parallel port. It is most appropriate to develop the entire parallel port as one object. Therefore, in this second stage we will add the extra functionality to the ParallelPort class so it can also use the port at address BASE+1.

The class definition for the new ParallelPort class is given in Listing 5-10, with additions shown in bold text. It contains the declarations for the member data and the member functions. All data members of the class ParallelPort are declared as private. All the member functions are declared as public. As before, BaseAddress is one of the data members and the function WritePort0()is included so that data can be sent out to port at address BASE.

Listing 5-10 New class definition for the object ParallelPort.

class ParallelPort

{

private:

unsigned int BaseAddress; unsigned char InDataPort1;

public:

ParallelPort(); // default constructor ParallelPort(int baseaddress); // constructor void WritePort0(unsigned char data);

unsigned char ReadPort1();

};