Файл: Interfacing with C plus plus-programing communication with microcontrolers (K. Bentley, 2006).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 14.06.2025
Просмотров: 3606
Скачиваний: 1
6 DIGITAL-TO-ANALOG CONVERSION 131
function named GetLastOutput() is also added to the derived class. It returns the value stored in LastOutput. Therefore, the only statement within the body of the GetLastOutput() function is:
return LastOutput;
Any function in the program that requires the value of the last data number output to the DAC (from the port at address BASE) must call the DAC class’s public function GetLastOutput(). This is the only way to access the value stored in the private data member LastOutput and ensures that functions outside the DAC class have no direct access to it. Once again, this demonstrates the controlled access to private data of a class by an object-oriented program.
Both constructors have been modified to initialise LastOutput to 0 by including the statement:
LastOutput = 0;
Therefore, at the time of instantiating a DAC object, the constructor will initialise the new data member LastOutput to 0.
We have modified the WritePort0() function in order to store the latest value output to the DAC by adding the line:
LastOutput = data;
However, the definition of the WritePort0() function given in Listing 6-7 will fail to compile. This is because the WritePort0() function of the derived class DAC is trying to use the inherited data member BaseAddress which is private to the base class. Although the private data of the base class is inherited, the derived classes cannot access this private data as shown in Figure 6-15.
One means to allow access to BaseAddress is by relaxing its access attributes. This can be done by declaring BaseAddress in the base class with protected access. Then BaseAddress can be accessed by all functions of all derived classes provided the classes are derived using a public or protected base class access specifier. Access specifiers are described in more detail ahead in section 6.5.1. The modified class definition of the base class is given in Listing 6-8.
NOTE
Declare variables as private unless you plan to derive other classes using the
current class as a base class. When you want to use the current class as a base class to derive new classes, carefully determine the variables of the current class you would want the derived class to have access to. Declare only these variables
as protected in the current class.
132 6 DIGITAL-TO-ANALOG CONVERSION
Derived Class |
Derived Class |
Inherited Base Class Members |
Inherited Base Class’s Members |
Parallel Port |
Parallel Port |
private |
protected |
BaseAddress |
BaseAddress |
InDataPort1 |
private |
public |
InDataPort1 |
ParallelPort ( ) |
public |
. |
ParallelPort ( ) |
. |
. |
. |
. |
Derived Class Members |
Derived Class Members |
DAC |
DAC |
private |
private |
LastOutput |
LastOutput |
public |
public |
DAC ( ) |
DAC ( ) |
DAC (baseaddress) |
DAC (baseaddress) |
WritePort0 ( ) |
WritePort0 ( ) |
GetLastOutput ( ) |
GetLastOutput ( ) |
Case (a) |
Case (b) |
Figure 6-15 Private and protected access specifiers.
Listing 6-8 Base class ParallelPort - BaseAddress as protected data member.
class ParallelPort
{
protected:
unsigned int BaseAddress;
private:
unsigned char InDataPort1;
public:
ParallelPort(); ParallelPort(int baseaddress);
void WritePort0(unsigned char data); void WritePort2(unsigned char data); unsigned char ReadPort1();
};
6 DIGITAL-TO-ANALOG CONVERSION 133
class DAC : public ParallelPort
{
private:
unsigned char LastOutput;
public:
DAC();
DAC(int baseaddress);
void WritePort0(unsigned char data); unsigned char GetLastOutput();
};
A program that uses the class definition shown above is given in Listing 6-9.
6.5.1 Access specifiers
Consider the line:
class DAC : public ParallelPort
The keyword public in this line is an access specifier. Access specifiers change the access attributes as follows. The protected variables of the ParallelPort class can be accessed by the member functions of the DAC class, if and only if, the
DAC class is derived from ParallelPort using a public or protected base class access specifier. Access specifiers can also be private. Figure 6-16 shows how the access specifiers determine access attributes of inherited members.
Access Specifier public
When deriving a class using a public base class access specifier, all inherited public members of the base class will become public members of the derived class, all inherited protected members of the base class will become protected members of the derived class. All inherited private members will remain private to the base class, and so the derived class cannot access them.
Access Specifier protected
When deriving a class using a protected base class access specifier, all inherited public and protected members of the base class will become protected members of the derived class. All inherited private members will remain private to the base class, and so the derived class cannot access them.
Access Specifier private
When deriving a class using a private base class access specifier, all inherited public and protected members of the base class will become private members of the derived class. All inherited private members will remain private to the base class, and so the derived class cannot access them.
134 6 DIGITAL-TO-ANALOG CONVERSION
BASE CLASS
Private
Protected
Public
Private
Protected
Public
Private
Protected
Public
DERIVED CLASS |
|||
|
Privateaccess |
specifier |
No access |
|
Private |
|||
Private |
|
Protected |
specifieraccess |
No access |
Protected |
||
|
accessPublic |
Protected |
|
|
specifier |
No access |
|
Protected |
||
Public |
Figure 6-16 Access specifiers determine access attributes of derived class members.
6.5.2 Polymorph Functions
The attempt to redefine the member function WritePort0() in Listing 6-9 is quite legitimate. However, WritePort0() is a function the DAC class inherited from the base class ParallelPort. To allow derived classes to redefine
inherited functions, the inherited functions must derived class definition as done in Listing 6-9 (and there are two WritePort0() functions: one
be explicitly included in the Listing 6-7). In this example, of them belonging to the
ParallelPort class; and the other belonging to the DAC class. The existence of functions of the same name throughout a class hierarchy is termed polymorphism. These functions not only have the same name, but also the same number of parameters, same types of parameters, and the same sequence of parameters.
The declaration of the DAC class is given in Listing 6-8. Despite the DAC class inheriting the function WritePort0() from the base class ParallelPort, it is explicitly coded again in the DAC class. This allows us to redefine the body of the WritePort0() function to suit the needs of the DAC class.
6 DIGITAL-TO-ANALOG CONVERSION 135
NOTE
There is a clear difference between the term polymorphism and overloading.
Overloaded functions also have the same function name. They differ in the number or type of parameters passed to the functions. In addition, overloaded functions do not need to be member functions.
The complete program, including the class hierarchy that can be compiled without errors is given in Listing 6-9.
Listing 6-9 Digital-to-analog conversion with the expanded DAC object.
/***************************************************** In this program, the compilation error has been eliminated by changing the access attribute of BaseAddress in the base class (ParallelPort) from private to protected. Now the functions of the publicly derived class can access the inherited BaseAddress.
This accessibility is only available to the derived classes of the base class and to the base class
itself. The function WritePort0(), which is re-declared in the derived class, can now be modified without any compilation errors.
*****************************************************/
#include <iostream.h> #include <stdio.h> #include <conio.h> #include <dos.h>
class ParallelPort
{
protected:
unsigned int BaseAddress;
private:
unsigned char InDataPort1;
public:
ParallelPort(); ParallelPort(int baseaddress);
void WritePort0(unsigned char data); void WritePort2(unsigned char data);
136 6 DIGITAL-TO-ANALOG CONVERSION
unsigned char ReadPort1();
};
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);
//Invert 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;
}
class DAC : public ParallelPort
{
private:
unsigned char LastOutput;
public:
DAC();
DAC(int baseaddress);
void WritePort0(unsigned char data);
6 DIGITAL-TO-ANALOG CONVERSION 137
unsigned char GetLastOutput();
};
DAC::DAC()
{
LastOutput = 0;
}
DAC::DAC(int baseaddress) : ParallelPort(baseaddress)
{
LastOutput = 0;
}
void DAC::WritePort0(unsigned char data)
{
outportb(BaseAddress,data); LastOutput = data;
}
unsigned char DAC::GetLastOutput()
{
return LastOutput;
}
void main()
{
DAC D_to_A; |
|||
D_to_A.WritePort0(0); |
", |
D_to_A.LastOutput); // Does |
|
// printf("\nDAC byte:%3d |
|||
printf("\nDAC byte:%3d |
", |
// not work, why? |
|
D_to_A.GetLastOutput()); |
|||
cout << " |
Measure voltage |
and press a key" << endl; |
|
getch(); |
|||
D_to_A.WritePort0(32); |
", |
D_to_A.GetLastOutput()); |
|
printf("\nDAC byte:%3d |
|||
cout << " |
Measure voltage |
& then press a key" << endl; |
|
getch(); |
|||
D_to_A.WritePort0(64); |
", |
D_to_A.GetLastOutput()); |
|
printf("\nDAC byte:%3d |
|||
cout << " |
Measure voltage |
and press a key" << endl; |
|
getch(); |
|||
138 6 DIGITAL-TO-ANALOG CONVERSION
D_to_A.WritePort0(128);
printf("\nDAC byte:%3d ", D_to_A.GetLastOutput()); cout << " Measure voltage and press a key" << endl; getch();
D_to_A.WritePort0(255);
printf("\nDAC byte:%3d ", D_to_A.GetLastOutput()); cout << " Measure voltage and press a key" << endl; getch();
}
In the above program, the constructor DAC() is called at the time of instantiating the DAC class object D_to_A. Referring to the function definition of the default DAC() constructor; it makes a call to the default constructor of the class ParallelPort before entering the body of the DAC() constructor. The default constructor of the ParallelPort class will initialise BaseAddress to 0x378 (and set InDataPort1 to 0). Then execution of the body of the constructor DAC() begins. It will initialise the value of the DAC class’s private data member
LastOutput to 0.
Note that the member function GetLastOutput() is called when the value of LastOutput needs to be printed onscreen. This needs to be done because the printf() function does not have direct access to the private data member
LastOutput.
The definition of the WritePort0() function can be modified slightly to revert the access attribute of BaseAddress back to private for the following reasons. Consider the function WritePort0() from Listing 6-9 reproduced in Listing 6-10.
Listing 6-10 WritePort0() function of the DAC class.
void DAC::WritePort0(unsigned char data)
{
outportb(BaseAddress,data); LastOutput = data;
}
The only time BaseAddress is accessed is when the data is sent out the port. The polymorphic function WritePort0() of the ParallelPort class can do this. It has no problem in accessing BaseAddress since the function and the data are in the same class. It is possible to call the polymorphic function WritePort0() of the ParallelPort class from inside the polymorphic function WritePort0() of the DAC class by using the scope resolution operator;
6 DIGITAL-TO-ANALOG CONVERSION 139
the double colon (::). This process is shown in Figure 6-17 and is implemented by modifying the fragment of code from Listing 6-10 to become that given in Listing 6-11.
Derived Class |
Derived Class |
Inherited Base Class Members |
Inherited Base Class’s Members |
Parallel Port |
Parallel Port |
private |
private |
BaseAddress |
BaseAddress |
InDataPort1 |
InDataPort1 |
public |
public |
ParallelPort ( ) |
ParallelPort ( ) |
WritePort0 (data) |
WritePort0 ( ) |
. |
. |
. |
. |
. |
. |
Derived Class Members |
Derived Class Members |
DAC |
DAC |
private |
private |
LastOutput |
LastOutput |
public |
public |
DAC ( ) |
DAC ( ) |
DAC (baseaddress) |
DAC (baseaddress) |
WritePort0 ( ) |
WritePort0 ( ) |
GetLastOutput () |
GetLastOutput ( ) |
Case (a) |
Case (b) |
Figure 6-17 Use of inherited polymorphic functions (BaseAddress private again).
Listing 6-11 Calling a polymorphic function of a base class.
void DAC::WritePort0(unsigned char data)
{
ParallelPort::WritePort0(data);
LastOutput = data;
}
In the ParallelPort class definition, the access attribute of the data member BaseAddress can now be set back to private as shown in Figure 6-17. The new and preferred program is given in Listing 6-12.