Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Interfacing with C plus plus-programing communication with microcontrolers (K. Bentley, 2006)

.pdf
Скачиваний:
192
Добавлен:
12.08.2013
Размер:
3.18 Mб
Скачать

174 7 DRIVING LEDS

.

.

.

VoidPtr = &a; // int address assigned to void // pointer

VoidPtr = &b; // float address assigned to void // pointer

VoidPtr = Add; // function address assigned to // void pointer

The advantage when using pointers to void is that the same pointer can be used to point to many different types of entities without needing to create specific pointers to specific objects.

7.5.9 The Pointer this

In object-oriented programming, each object maintains an invisible pointer named this which points to itself. Although invisible, if need be, the this pointer can be used exactly like any other pointer. The member functions of a particular class can determine exactly which object the function should operate on by using the this pointer. To understand how the pointer this operates, consider the function GetLastOutput() of the DAC class described in Chapter 6:

unsigned char DAC::GetLastOut()

{

return LastOutput;

}

If we make the this pointer visible, the function would appear as follows:

unsigned char DAC::GetLastOut()

{

return this->LastOutput;

}

Now suppose we had created two DAC objects:

DAC Dac1, Dac2;

We will call the GetLastOutput() function once each for each of the two objects as shown below:

Dac1.GetLastOutput();

Dac2.GetLastOutput();

When the first of these functions is executed, the this pointer will point to the address of the object Dac1 and thus this->LastOutput will select the LastOutput member of the Dac1 object. When the second GetLastOutput() function prefixed with Dac2 is called, the this pointer

7 DRIVING LEDS 175

will point to the address of the object Dac2, and this->LastOutput will select the LastOutput member of the object Dac2.

To demonstrate situations where the this pointer is used explicitly, consider the constructor of the ParallelPort class:

ParallelPort::parallelPort(int baseaddress)

{

BaseAddress = baseaddress;

}

We have deliberately named the parameter baseaddress. However, if instead we named it BaseAddress, the constructor would be:

ParallelPort::ParallelPort(int BaseAddress)

{

BaseAddress = BaseAddress; // confusion!

}

As can be seen, the parameter cannot be differentiated from the member data. The solution to this is to modify the function as follows:

ParallelPort::parallelPort(int BaseAddress)

{

this->BaseAddress = BaseAddress;

}

Within the body, the part-statement this->BaseAddress definitely refers to the member data.

7.6 Using Pointers

To demonstrate the use of pointers, we will create an array of numbers that can be sent out to the interface board to light up the LEDs in a specific pattern. This array will then be scanned using a pointer to fetch consecutive values from the array. The port at address BASE will be used to output the numbers to the interface board.

7.6.1 Number arrays for the LEDs

Firstly we will develop a program which ‘walks a LED’ along the bank of eight LEDs. This program uses a fixed pattern.

Walking LEDs – Fixed Array Defined Within the Class

We will be using an array and scanning it cyclically to light up the LEDs using the port at address BASE. The effect of cyclic scanning will be the appearance of a “walking LED” across the bank of 8 LEDs. The array will contain eight elements, each element used to light just one LED of the group. When we move from one

176 7 DRIVING LEDS

element to the next element in the array, the LED that is currently lit will turn off and the adjacent LED in the direction of the ‘walk’ will light up.

Table 7-3 shows each hexadecimal number and corresponding binary bit pattern for each array element that in-turn must be output to the port.

Table 7-3 LED pattern values stored in Pattern array.

Array

 

 

 

Binary Number

 

 

Hex

 

Element

 

D7

D6

D5

D4

D3

D2

D1

D0

value

Pattern[0]

 

 

0

0

0

0

0

0

0

1

0x01

 

Pattern[1]

 

0

0

0

0

0

0

1

0

0x02

 

Pattern[2]

 

 

0

0

0

0

0

1

0

0

0x04

 

Pattern[3]

 

 

0

0

0

0

1

0

0

0

0x08

 

Pattern[4]

 

0

0

0

1

0

0

0

0

0x10

 

Pattern[5]

 

0

0

1

0

0

0

0

0

0x20

 

Pattern[6]

 

0

1

0

0

0

0

0

0

0x40

 

Pattern[7]

 

1

0

0

0

0

0

0

0

0x80

 

A new object class named LEDs will be created which will have Pattern as a data member and also have the functionality to initialise the Pattern array to the desired values. The contents of the array Pattern will be fixed for the class and cannot be changed by the user within the main() function. The class must also have a function to sequentially output the appropriate values in Pattern to the port at address BASE. The LEDs class can be derived from the ParallelPort class to inherit the required interface functionality. Listing 7-3 shows the class definition.

Listing 7-3 LEDs class definition.

class LEDs : public ParallelPort

{

private:

unsigned char Pattern[8]; int PatternIndex;

public:

LEDs();

LEDs(int baseaddress); void LightLEDs();

};

7 DRIVING LEDS 177

The private data member PatternIndex is required to store the number of the LED that was previously lit so cycling can be controlled as we move through the array to produce the ‘LED walk’. Note that Pattern is an array of eight unsigned char elements. The elements need to be unsigned char to provide just 8 bits in each element, and to avoid the added complications that would be involved if signed numbers were used instead.

The member functions for the class can now be defined as shown in Listing 7-4.

Listing 7-4 Member functions for the LEDs class.

LEDs::LEDs()

{

// Fill in the Pattern array for(int i = 0; i < 8; i++)

*(Pattern + i) = 1 << i;

PatternIndex = 0; // initialise to 0

}

LEDs::LEDs(int baseaddress) : ParallelPort(baseaddress)

{

// Fill in the Pattern array for(int i = 0; i < 8; i++)

*(Pattern +i) = 1 << i;

PatternIndex = 0; // initialise to 0

}

void LEDs::LightLEDs()

{

while(!kbhit())

// key press terminates function

{

 

WritePort0(*(Pattern + PatternIndex++));

// Reset PatternIndex when it gets to 8 if(PatternIndex == 8) PatternIndex = 0;

delay(500);

}

}

The array name Pattern (without the subscripts) is a pointer and points to the first element of the array. Therefore:

178 7 DRIVING LEDS

Pattern + i

points to the ith element of the array. To refer to the value pointed to by Pattern + i, we must de-reference it as follows:

*(Pattern + i)

The constructors of the LEDs class initialise the array by left-shifting the number 1 by i bit places using:

1 << i

The constructors will initialise the Pattern array with the values shown in Table 7-3 as their respective for loops complete each iteration of the statement:

*(Pattern + i) = 1 << i;

The while loop in the LightLEDs() function is conditioned on !kbhit() and will continue to execute provided a key is not hit. Inside the while loop the inherited WritePort0() function is used to write the array Pattern to the port at address BASE – one element at a time. Each element is accessed using PatternIndex as an offset with respect to the starting memory address of the array Pattern. The constant integer pointer Pattern is added the value of PatternIndex each time. This allows the Pattern array to be scanned from beginning to end. When the end is reached, PatternIndex is reset to 0 to allow a new cycle of scanning the Pattern array to repeat. Note that PatternIndex is post-incremented within the expression:

*(Pattern + PatternIndex++)

In this expression, the current value of PatternIndex is used to evaluate the current address of the element to access. Following this activity, the value of PatternIndex is incremented. A delay of 500 ms is included to provide sufficient time to see the LED walk. Connect the BASE address signals on the interface board (U13) to the LED Driver IC (U3) to test the complete program shown below.

Listing 7-5 Complete program to 'Walk a LED'.

// Complete Program to 'walk' a LED #include <iostream.h>

#include <conio.h> #include <dos.h>

class ParallelPort

{

private:

unsigned int BaseAddress;

7 DRIVING LEDS 179

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)

{

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 LEDs : public ParallelPort

{

180 7 DRIVING LEDS

private:

unsigned char Pattern[8]; int PatternIndex;

public:

LEDs();

LEDs(int baseaddress); void LightLEDs();

};

LEDs::LEDs()

{

// Fill in the Pattern array for(int i = 0; i < 8; i++)

*(Pattern + i) = 1 << i;

PatternIndex = 0;

// initialise to 0

}

 

LEDs::LEDs(int baseaddress) : ParallelPort(baseaddress)

{

 

array

// Fill in the Pattern

for(int i = 0; i < 8;

i++)

*(Pattern + i) = 1

<< i; // Shift '1' left 'i' places

PatternIndex = 0;

//

// and fill Pattern array.

initialise to 0

}

 

 

void LEDs::LightLEDs()

 

 

{

//

keypress terminates function

while(!kbhit())

{

 

 

WritePort0(*(Pattern + PatternIndex++));

// Reset PatternIndex when it reaches 8 if(PatternIndex == 8) PatternIndex = 0;

delay(500);

}

}

void main()

{

LEDs Leds;

7 DRIVING LEDS 181

Leds.LightLEDs(); // Displays a 'walking' LED. getch();

cout << endl << "Halted !” << endl;

cout << “Press a key to continue" << endl; getch();

Leds.LightLEDs(); // 'Walking' restarts with the LED // alight in the next position.

}

Walking LEDs – User Definable Contents with Fixed Array Size

The program shown in Listing 7-5 is rather inflexible. The contents of the array Pattern are fixed within the class. It is more appropriate to give the user the ability to define the contents of the array, therefore, the LEDs class must be modified.

The user will define the contents of the array used to light the LEDs. Therefore, the constructors of the LEDs class are not needed to initialise this array. Instead, the class can maintain a pointer that points to the array the user will define. The userdefined array can be scanned by the LightLEDs() function if the starting address of the array and its size are known. Therefore, the LEDs class only needs to have a data member to store the address of the array and another data member to store its size.

To facilitate these changes we will replace the member data Pattern (unsigned char array) with PatternPtr, a pointer type (pointer to unsigned char). A member function must also be included to extract the address of the array and to assign it to the pointer maintained within the class. Since the size of the array can now be arbritrarily set, a new data member must be included to store the maximum number of elements in the array. This new member is used to determine when the final element of the array has been scanned, whereupon PatternIndex can then be reset to 0. The modifications to the LEDs class are shown in Listing 7-6.

Listing 7-6 Modified LEDs class.

class LEDs : public ParallelPort

{

private:

unsigned char* PatternPtr; int PatternIndex;

int MaxIndex;

public:

182 7 DRIVING LEDS

LEDs();

LEDs(int baseaddress);

void SetPatternAddress(unsigned char* pattern, int maxidx);

void LightLEDs();

};

The definitions of the member functions of this class are given in Listing 7-7.

Listing 7-7 Member function definitions for the modified class.

LEDs::LEDs()

{

MaxIndex = 0;

PatternIndex = 0;

}

LEDs::LEDs(int baseaddress) : ParallelPort(baseaddress)

{

MaxIndex = 0;

PatternIndex = 0;

}

void LEDs::SetPatternAddress(unsigned char* pattern, int maxidx)

{

PatternPtr = pattern; // Pointer PatternPtr assigned // address of pattern.

MaxIndex = maxidx;

}

void LEDs::LightLEDs()

{

if(MaxIndex <= 0)

{

cout << "No Patterns to display " << endl; return;

}

while(!kbhit())

{

WritePort0(*(PatternPtr + PatternIndex++));

// Reset PatternIndex when it gets to MaxIndex. if(PatternIndex == MaxIndex) PatternIndex = 0;

7 DRIVING LEDS 183

delay(500);

}

}

Listing 7-8 showns a main() function which asks a user to enter patterns into the LED pattern array during program execution.

Listing 7-8 Main function – user fills in the array (LED pattern).

void main()

{

unsigned char LightPattern[8]; int UserPattern;

LEDs Leds; int i;

cout << "Enter 8 user patterns in the range 0x00-0xFF "; cout << endl;

for(i = 0; i < 8; i++) // fill 8 element Array

{

cin >> UserPattern;

*(LightPattern + i) = UserPattern;

}

Leds.SetPatternAddress(LightPattern, 8); Leds.LightLEDs();

getch();

Leds.LightLEDs();

}

Note that in the main()function shown in Listing 7-8, the programmer is required to code the array name (LightPattern[8]) with the number of elements of the array (each element will store one of the patterns sent out to the LEDs). The user enters the actual values for each sequential pattern at run-time. The local variable UserPattern, of type int, is used to read integer data. The data is then assigned to the array LightPattern of type unsigned char through the use of the array name LightPattern as a pointer.

The statement in Listing 7-8:

Leds.SetPatternAddress(LightPattern,8);

can be replaced by: