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

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

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

11 ANALOG-TO-DIGITAL CONVERSION 347

Input Signal and

Sampling Points

Digitised Waveform Reconstructed using

Sequential Equivalent Time Sampling

Figure 11-16 Equivalent time sampling.

Equivalent time sampling cannot be applied to the digitisation process when working with non-repetitive signals as shown in Figure 11-17. Instead, these signals need to be sampled and stored at a sufficiently high rate to provide enough detail in the reconstructed signal. Under these conditions many digital oscilloscopes are often challenged to provide adequate sampling rate and sufficient high-speed memory to store the digitised data. These two factors have a significant influence on the price of digital oscilloscopes.

Input Signal and

Sampling Points

Digitised

Waveform

Reconstructed

Figure 11-17 Real-time sampling of a non-repetitive waveform.

11.5 An Object Class for the ADC

In the last few chapters we learnt to design object classes to suit various objects such as the parallel port, the DAC, motors and the VCO. In a similar manner we can develop a software object for the ADC. The principle purpose of an analog-to- digital converter is to convert an analog voltage applied at its input to an integer bit number that can be read by the computer.

The conversion process for most analog-to-digital converters involves the following steps:

348 11 ANALOG-TO-DIGITAL CONVERSION

1.Start an analog-to-digital conversion.

2.Wait for the conversion to complete.

3.Read the converted data.

Some analog-to-digital converter subsystems have a multiplexed analog input (i.e. more than one analog input where only one analog input is switched to the ADC at any given time). In such cases the above set of steps must be preceded by a “Select Input Channel” operation. The ADC used with our interface board does not have a multiplexer to use with multiple analog input channels. Therefore, we will not need to incorporate channel selection.

We must design our object class to have a member function to implement the steps listed above. The ADC on the interface board is designed to communicate through the parallel port. Therefore, the ParallelPort object forms an ideal base class for the new ADC class.

The ADC class needs to have only one private data member to store the digital value read from the ADC. Apart from the constructors, the ADC class must have a function to carry out the analog-to-digital conversion and store the resulting digital value into the private data member. A function is also needed to provide access to this private data member. A class definition that encapsulates this data and functions is given in Listing 11-1.

Listing 11-1 The header file for the ADC class – adc.h.

#ifndef AdcH #define AdcH

#include "pport.h"

class ADC : public ParallelPort

{

private:

unsigned char ADCValue;

public:

ADC(int baseaddress=0x378); unsigned char ADConvert(); unsigned char GetADCValue();

};

#endif

The function ADConvert() is the most involved of the three functions and is discussed first. We must decide which parts of the parallel port will be used and their purpose before before being able to write the C++ statements for this function.

11 ANALOG-TO-DIGITAL CONVERSION 349

Figure 11-18 shows a block diagram of the ADC0804 with its input pins (on the left side) and output pins. The pin labels and descriptions are given in Table 11-2. These labels are used on the schematic diagram and can also be found near the ADC on the interface board.

 

ADC0804

VIN

D0

/CS

 

/START C.

D7

/READ

/DATA VALID

Figure 11-18 ADC0804 block diagram.

Table 11-2 Interface pins of the ADC.

Pin Label

Input Output

Function

VIN

ξ

Analog input voltage

/CS

ξ

Chip select (activates device)

/START C.

ξ

Start a conversion

/READ

ξ

Enable reading the digital data

/DATA VALID

ξ

Indicates conversion complete

D0-D7

ξ

Output digital data bits

 

 

 

Pin labels with a prefix of ‘/’ are active low.

The dots marking the Input and Output columns of Table 11-2 identify each signal as an input or an output with reference to the ADC. Now we need to evaluate a means of interfacing these signals to the parallel port.

Operation of signals

The voltage signal to be converted by the ADC is connected to its analog input pin labelled VIN. For testing purposes we can generate a suitable analog input voltage for the ADC using either the on-board potentiometer, the thermistor circuit, or the DAC operating in unipolar mode (jumper fitted across the header position marked LINK1).

The ADC’s input pins are configured as follows. The chip select pin (/CS) must be at logic-low for the ADC to operate. The read enable pin (/READ) must be held logic-low to enable reading the digital data from the ADC. The chip select signal is typically generated by the address decoding circuit of a hardware system that has several devices sharing a data bus. The interface board does not share a data bus, so

350 11 ANALOG-TO-DIGITAL CONVERSION

we can permanently activate the above two signals; i.e. connect them directly to GND. This reduces the number of signals we need to interface with the parallel port.

The computer must control the start conversion signal connected to the start conversion pin (/START C.). An analog-to-digital conversion is initiated by applying an active-low pulse to this pin. We can generate an active-low pulse by driving a high-level signal momentarily to a low-level, returning the signal to a high state.

Immediately after the ADC completes its conversion operation, the data valid pin (/DATA VALID) will produce a brief low-level pulse. Since this low-level pulse is short in duration, it may not be possible to detect it and therefore determine the precise moment conversion was completed. Typically, this pulse is latched using hardware to ensure that a program will reliably detect the end of conversion. The electronic circuitry on the interface board has been kept to a minimum and as such does not include a latch circuit. Therefore, our best option is to allow sufficient time for the conversion to complete before reading the digital data. In addition, using this approach will free us from the need to interface the /DATA VALID signal.

Configuration of Port data bits to interface the ADC

We now need to assign the data bits that will interface the ADC to the parallel port. When using the ADC it is possible that the DAC will be used to provide programmable input voltages to the ADC. We will assume this to be the case when allocating our data bits. The digital input and output requirements for the ADC and DAC are shown in Table 11-3.

Table 11-3 DAC & ADC digital input and output pins.

DAC

Digital Inputs

D0

D1

D2

D3

D4

D5

D6

D7

 

ADC

Digital Inputs

Digital Outputs

/CS

D0

/RD

D1

/START C.

D2

 

D3

 

D4

 

D5

D6

D7

/DATA VALID

Digital Inputs to the DAC: The eight digital input pins to the DAC need to be driven by parallel port output signals. Therefore, it makes sense to use output data bits (D0 to D7) of the port at address BASE to drive the DAC inputs (D0 to D7).

11 ANALOG-TO-DIGITAL CONVERSION 351

Digital Inputs to the ADC: To drive the ADC input pin /START C., we can use data bit D0 of the port at address BASE+2. The ADC data bus is configured to an active state by connecting /CS and /READ directly to GND using interconnecting leads. We can do this because our ADC’s output data is not connected to a shared data bus.

Digital Outputs from the ADC: The software must read eight digital output signals from the ADC being sent through the parallel port (/DATA VALID not used). The parallel port has five input signals (D3 to D7) available from the port at address BASE+1. Note that we have not used port address BASE+2 in input mode as it can be unreliable at higher data transfer rates.

The interface board has been fitted with a four-channel 2-to-1 multiplexer (as shown in Figure 11-19) to provide extra capability for transfer of data to the port. If we make use of this device, we can transfer eight data bits to the port using only four signals. We do this by separating the eight data bits from the ADC into two groups of four bits. The first group is selected by the multiplexer and the port then reads these four data bits. This is followed by selection of the second group of four bits that are then read by the port. Note that we need one output data bit from the port to control the multiplexer’s selection operation. Since the eight bits from the ADC represent one byte of data, driving the multiplexer’s Select input low will select the low nibble (D0 to D3). Conversely, driving the Select input high will select the high nibble (D4 to D7).

 

 

 

 

/DATA VALID

 

 

 

 

 

D0

D0

 

 

 

 

 

D1

D1

 

 

 

VIN

 

D2

D2

 

 

 

 

ADC

D3

D3

 

 

 

 

 

 

 

 

/READ

 

D4

D4

Interconnect

 

 

 

Leads

 

 

 

 

 

 

 

 

/CS

 

D5

D5

 

 

 

 

 

D6

D6

 

 

D0

START C.

 

D7

D7

 

BASE+2

 

 

 

 

 

 

Address

D1

 

 

 

Select

MULTIPLEXER

 

 

 

 

 

(SW position)

(MUX)

D3 (not used)

D4

D5 BASE+1 Address

D6

D7

Figure 11-19 Complete ADC system using the Multiplexer.

Now that we know how to read the eight bits of data from the ADC using only four transmission signals, we can establish the configuration for the remainder of the

352 11 ANALOG-TO-DIGITAL CONVERSION

parallel port data bits. We can use four input data bits (D4 to D7) of the port at address BASE+1 to read the four output signals from the multiplexer that transmits the ADC output byte as two nibbles.

If you should decide to modify the program to detect the narrow output pulse /DATA VALID from the ADC, then connect a lead from this pin to data bit D3 of the port at BASE+1 and write extra program statements to read its status.

Digital Input to the Multiplexer: We can drive the Select input of the multiplexer to control which nibble at its input pins is switched to its output by using an output data bit (D1) of the port at address BASE+2.

A summary of all connections for interfacing the parallel port to the ADC, to the DAC, and to the Multiplexer is given in Table 11-4. This table does not provide the internal connections needed on the interface board between the ADC and the Multiplexer - they are shown in Figure 11-19.

Table 11-4 Parallel Port interface connections for the DAC, ADC, and MUX.

BASE Address

 

BASE+1 Address

BASE+2 Address

D0

DAC, D0

D3

(ADC, /DATA VALID)

D0

ADC, /START C.

D1

DAC, D1

D4

MUX, D4

D1

MUX, Select

D2

DAC, D2

D5

MUX, D5

 

 

D3

DAC, D3

D6

MUX, D6

 

 

D4

DAC, D4

D7

MUX, D7

 

 

D5

DAC, D5

 

 

 

 

D6

DAC, D6

 

 

 

 

D7

DAC, D7

 

 

 

 

Note: 1. ADC inputs /CS and /READ must be connected to GND using interconnect leads.

2.ADC output /DATA VALID is not used for our program.

3.Set DAC to Unipolar mode by fitting the jumper across header position marked LINK1.

We are now in a position to define the member function ADConvert(). Listing 11-2 shows one possible definition of the function.

Listing 11-2 Member function ADConvert().

unsigned char ADC::ADConvert()

{

//Declare variables to store nibbles. unsigned char LowNibble, HighNibble;

//Start conversion pulse.

WritePort2(0x01); // set /START C to high

WritePort2(0x00); // pull /START C to low

11 ANALOG-TO-DIGITAL CONVERSION 353

WritePort2(0x01); // set /START C back to high

//Set Select signal of multiplexer (D1) to logic-high and

//maintain /START C high. This operation takes more time

//than the conversion of the ADC, so we do not need to

//check for signal /DATA VALID. */

WritePort2(0x03); // 0000 0011

//Conversion finished by this time.

//Read high nibble and nullify low nibble.

HighNibble = ReadPort1() & 0xF0;

//Set Select signal of multiplexer (D1) to logic-low. WritePort2(0x01); // 0000 0001

//Read low nibble, move data bits across into position

//and nullify high nibble.

LowNibble = (ReadPort1() >> 4) & 0x0F;

// Form complete byte.

ADCValue = HighNibble + LowNibble;

return ADCValue;

}

The three statements from Listing 11-2 shown in bold typeface need explanation. Note that when reading the port at address BASE+1, only the bits D4-D7 carry data coming from the ADC. The data from the 8-bit ADC is read into the PC using these four bits in two stages; first the high nibble (four bits) is read and stored, followed by reading and storing the low nibble. Then the high and low nibbles are added to obtain the complete 8-bit result (ADCValue).

Actual data

Garbage data

 

1 0 1 1 0 1 0 1

 

Byte read from port at BASE+1

1 1 1 1 0 0 0 0

 

0xF0 used in AND operation

1 0 1 1 0 0 0 0

 

HighNibble Result, garbage bits forced to 0

Garbage bits forced to be zero

Figure 11-20 Reading the high nibble and filtering out unwanted bits.

354 11 ANALOG-TO-DIGITAL CONVERSION

We use the inherited member function ReadPort1() to read the high nibble through the port at address BASE+1 and then clear all unused bits that contain unpredictable (garbage) data (lower four bits) by carrying out an AND operation with 0xF0. This operation is shown in Figure 11-20.

When reading the low nibble, we first read the port and then shift these data bits by 4 locations to the right to reside in the low nibble of the final data byte. After shifting we carry out an AND operation with 0x0F to clear all bits in the high nibble that can contain unpredictable data. This is shown in Figure 11-21.

Wanted data Garbage data bits will drop out during shift operation

1 1 0 1 0 1 0 1

Byte read from port at BASE+1

Possibility of new

Shift to right by 4 positions

garbage data

 

? ? ? ? 1 1 0 1

Result of shift operation

 

0 0 0 0 1 1 1 1

0x0F used in AND operation

0 0 0 0 1 1 0 1

LowNibble Result, garbage bits forced to 0

Garbage bits forced to be zero

New location of wanted data

Figure 11-21 Reading the low nibble and filtering out unwanted bits.

Now we have an 8-bit number (unsigned char) named LowNibble, which has some data in the lower four bits and definitely zeros in the upper four bits. We also have an 8-bit number named HighNibble, which has some data in the upper four bits and definitely zeros in the lower four bits. Then we add these two bytes together to form one complete byte named ADCValue which has all 8 bits carrying the data from the analog-to-digital converter. Figure 11-22 shows the formation of ADCValue.

0

0

0

0

1

1

0

1

LowNibble

1

0

1

1

0

0

0

0

HighNibble

1

0

1

1

1

1

0

1

ADCValue

Figure 11-22 Add low & high nibbles to form the ADC output.

The function given in Listing 11-2 can be re-written in a slightly more efficient form as given in Listing 11-3. The data member ADCValue has been used to combine an operation and eliminate the need for variables LowNibble and

HighNibble.

11 ANALOG-TO-DIGITAL CONVERSION 355

Listing 11-3 A more efficient version of ADConvert().

unsigned char ADC::ADConvert()

{

// Start conversion pulse.

WritePort2(0x01); // set /START C to high WritePort2(0x00); // pull /START C to low WritePort2(0x01); // set /START C back to high

//Set Select signal of multiplexer (D1) to logic-high and

//maintain /START C high. This operation takes more time

//than the conversion of the ADC, so we do not need to

//check for signal /DATA VALID. */

WritePort2(0x03); // 0000 0011

//Conversion finished by this time.

//Read high nibble and nullify low nibble.

ADCValue = ReadPort1() & 0xF0;

//Set Select signal of multiplexer (D1) to logic-low. WritePort2(0x01); // 0000 0001

//Read low nibble and assemble the final 8-bit number.

ADCValue += (ReadPort1() >> 4) & 0x0F;

return ADCValue;

}

The complete definition of the ADC class must include the definitions of its member functions as given in Listing 11-4. The member function GetADCValue() provides access to the final 8-bit number ADCValue for functions outside the ADC class.

Listing 11-4 Member function definitions of the ADC class – adc.cpp.

#include "adc.h"

ADC::ADC(int baseaddress) : ParallelPort(baseaddress)

{

ADCValue = 0;

}

unsigned char ADC::ADConvert()

{

356 11 ANALOG-TO-DIGITAL CONVERSION

WritePort2(0x01); // Start C. pulse

WritePort2(0x00);

WritePort2(0x01);

WritePort2(0x03); // Set Mux to read high nibble. ADCValue = ReadPort1() & 0xF0;

WritePort2(0x01); // Set Mux to read low nibble. ADCValue += (ReadPort1() >> 4) & 0x0F; // Read, combine.

return ADCValue;

}

unsigned char ADC::GetADCValue()

{

return ADCValue;

}

11.6 Measuring Voltage Using the ADC

Recall that in Chapter 10 we developed a program to measure an analog voltage using the VCO. The MeasurePeriod() function in the VCO program returned a number representing the pulse period (and hence input voltage) of the VCO. We should be able to use the same program with the VCO object replaced by the ADC object. The function ADConvert()can then generate a number proportional to the analog voltage. Note that we have used a DAC object in the VCO program to provide an analog voltage. We will keep the same DAC object operating exactly the same way to provide the analog input to the ADC (at VIN).

Listing 11-5 shows the main() function from Listing 10-6 reproduced with the modifications needed to use it with the ADC.

Listing 11-5 Measuring voltage using the ADC – voltage.cpp.

#include <conio.h> #include <bios.h>

#include "dac.h" #include "adc.h"

void main()

{

DAC Dac;