Interfacing with C plus plus-programing communication with microcontrolers (K. Bentley, 2006)
.pdf134 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.
140 6 DIGITAL-TO-ANALOG CONVERSION
Listing 6-12 Use of polymorphic functions.
/*********************************************************
In this program, the access attribute of the data member BaseAddress has been changed back to private.
BaseAddress is accessed via the polymorphic WritePort0() function of the base class, which can access BaseAddress.
*********************************************************/
#include <iostream.h> #include <stdio.h> #include <conio.h>
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();
};
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)
{
6 DIGITAL-TO-ANALOG CONVERSION 141
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); unsigned char GetLastOutput();
};
DAC::DAC()
{
LastOutput = 0;
}
DAC::DAC(int baseaddress) : ParallelPort(baseaddress)
{
LastOutput = 0;
}
void DAC::WritePort0(unsigned char data)
{
ParallelPort::WritePort0(data);
LastOutput = data;
}
unsigned char DAC::GetLastOutput()
{
return LastOutput;
142 6 DIGITAL-TO-ANALOG CONVERSION
}
void main()
{
DAC D_to_A;
D_to_A.WritePort0(0);
printf("\nDAC byte:%3d ", D_to_A.GetLastOutput()); cout << " Measure voltage and press a key" << endl; getch();
D_to_A.WritePort0(32);
printf("\nDAC byte:%3d ", D_to_A.GetLastOutput()); cout << " Measure voltage and press a key" << endl; getch();
D_to_A.WritePort0(64);
printf("\nDAC byte:%3d ", D_to_A.GetLastOutput()); cout << " Measure voltage and press a key" << endl; getch();
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();
}
Having learnt this elegant means of manipulating private data of a base class from inside a derived class, we can complete our improvements to the DAC class by changing the name of the WritePort0() function of the DAC class to something more appropriate. Let us choose the name SendData() as a replacement name for the function WritePort0() of the DAC class.
The class definition and the complete program to carry out the exact same tasks as the program in Listing 6-12, is given in Listing 6-13. We will be using this final version of the DAC class when we need to use the DAC system on the interface board in future chapters.
6 DIGITAL-TO-ANALOG CONVERSION 143
Listing 6-13 Replacing WritePort0() of DAC class by SendData().
/***************************************************** In this program, the Function WritePort0() of the DAC class is given the new name SendData() which is more appropriate for the DAC class.
*****************************************************/
#include <iostream.h> #include <stdio.h> #include <conio.h> #include <dos.h>
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();
};
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)
{