A/D Conversion Using the ADC0838

copyright, Christine S. Samuels
Department of Electrical Engineering
Morgan State University
Baltimore, MD 21239
August 7, '96

Introduction.

The ADC0838 is a versatile 8-bit analog to digital converter which includes an 8-channel multiplexer. The device permits both single ended and differential measurements and reverse differential measurements. The reference voltage and the span may be also varied so as to optimize performance wh en measuring quantities having a dynamic range which is substantially less than 0.0 to 5.0 Volts.

The ADC0838 is available for less than $5.00 from most suppliers including Jameco.

Unlike the ADC0804 parallel A/D which has three digital inputs (/CS, /RD and /WR) and eight outputs for the data, the digital interface associated with the ADC0838 consists of either four or three digital signal leads (plus ground). Input /CS is used to select the A/D and input CLK is used to clock the device. Data is serially transferred to the A/D on ADC0838 input "Data In" (DI) and is serially read from "Data Out" (DO). Thus, if input DI and output DO are isolated from one another, the interface consists of four digital signal leads. If they are combined, and a bidirectional bi t is used for both writing and reading, the interface consists of three signal leads.

Although this discussion deals with the ADC0838, the material is relevant to other similar devices in the National Semiconductor family, such devices as the Linear Technology LTC1098 8-bit A/D and to the Dallas Semiconductor DS1620 Digital Thermometer and Thermostat.

Overview of the Operation.

The device is selected and reset by bringing /CS low. As it is the high to low transition which initiates the sequence, the user might first bring /CS to a logic one and then to a logic zero. CLK should be at logic zero during this sequence.

The A/D is then waiting for a 5-bit control word. Use of the term "control word" is mine. National uses the term, "MUX assignment word". The most significant bit is the "start" bit and is always a logic one. The remaining four bits are used to configure the multiplexer which is discussed in detail below. This 5-bit control word is output serially to the A/D, one bit at a time starting with the most significant bit, by bringing DI to the desired state, and then bringing the CLK from a logic zero to a logic one.

During all of this time, A/D output DO is in a high impedance state. Upon conclusion of sending the 5-bit control word, A/D output DO comes out of tri-state and it is a logic zero for one clock pulse.

The data is then read from the A/D on DO, beginning with the most significant bit. The next bit is available on DO after each clock pulse. Thus, after eight clock pluses, the data transfer is complete.

Note that as each clock pulse is output, the A/D is actually performing the A/D conversion on that bit. That is, after the first clock pulse, the most significant bit is determined based on whether it is above or below the half way point between V_ref and the voltage at COM. After the next clock pulse, the bit is determined based on whether it is above or below the the mid point of the range to which it has been isolated. Thus, although the data sheet may specify a conversion time of 32 usecs, the actual conversion is performed while clocking and reading the data, one bit at a time.

At this point, I am done. Actually, National provided a feature for the data to again be read in a least significant bit first manner, but this capability is not used in this discussion.

Detailed Discussion - Single Ended, 8-Channel Operation.

See Figure #1.

Note that eight parameters are measured. These are dummied using potentiometers configured such that the voltage at each channel may be varied between 0.0 and 5.0 Volts.

Plus 5 VDC is supplied to terminal V_ref and ground at COM. Thus, each measurement is quantized into one of 256 levels over the range of 0.0 to 5.0 VDC.

Note that DI and DO are handled separately; parallel port bit Data_2 is used to control the state of DI and input BSY on the STATUS port is used to read DO.

Let's turn back to the control word which configures the multiplexer.

Recall that, the most significant bit is the "Start bit" which is always logic one. This is not shown in the above table.

The next bit is SGL/DIF. As we desire to make single ended measurements, that is, the voltage is referenced to the voltage on the COM terminal, this is set to a logic one.

The final three bits select the channel, but it is not straight forward! Note that the channel's least significant bit appears first, followed then by the two most significant bits. It appears the designers at National had to make a trade-off of increasing the complexity to the user at the exp ense of providing functionality, but, we can deal with the minor confusion. Indeed, National has done so by terming the bit ODD, which is in fact, the channel's least significant bit. Thus, the order is;

least_sig_bit most_sig_bit middle_bit

Program AD0838_1.C implements a system which reads the voltage on each of eight channels in turn and displays them on the terminal.

At line 63, the control word is developed. Note the first bit is the start bit, and is always a one. In this case, the next bit is also a logic one indicating single ended measurements. In line 69, the least significant bit of the channel is added, followed at line 70 by the two higher order bits of the channel.

The control word is serially output on Data_2 using function send_control_word at line 74.

The A/D data is then read using function get_val at line 49. Note the extra clock pulse at line 53. Each bit is then read using the BUSY input. Recall that the BUSY input is inverted by the hardware. Thus, the ^0x80.

Knowing the A/D band, the voltage is calculated at line 27.

/*
** Program AD0838_1.C
**
** Illustrates how to make single ended measurements on any of 
** 8-channels.  Assumes V_ref = 5.0 VDC
**
** Data_2, term 4 ---------- DI, term 17
** Data_1, term 3 ---------- CLK, term 16
** Data_0, term 2 ---------- /CS, term 18
**                           DO, term 14 ----------- BSY, term 11
**
** Christine Samuels, MSU, 8 August '96
*/

#include <stdio.h>                                            /* 1 */
#include <dos.h>                                              /* 2 */
                                                              /* 3 */
#define DATA 0x03bc                                           /* 4 */
#define STATUS DATA+1                                         /* 5 */
#define CONTROL DATA+2                                        /* 6 */
                                                              /* 7 */
int data = 0x00; /* note global */                            /* 8 */
                                                              /* 9 */
int make_measurement(int channel);                            /* 10 */
int get_val(void);                                            /* 11 */
int make_control_word(int channel);                           /* 12 */
void send_control_word (int word);                            /* 13 */
void clock();                                                 /* 14 */
void CS_high(void);                                           /* 15 */
void CS_low(void);                                            /* 16 */
                                                              /* 17 */
void main(void)                                               /* 18 */
{                                                             /* 19 */
   int n, data[8];                                            /* 20 */
   float voltage[8];                                          /* 21 */
                                                              /* 22 */
   for(n=0; n<8; n++)                                         /* 23 */
   /* make measurements at each of 8 channels */              /* 24 */
   {                                                          /* 25 */
      data[n] = make_measurement(n);                          /* 26 */
      voltage[n] = 5.0 * data[n]/ 256.0;                      /* 27 */
   }                                                          /* 28 */
   for(n=0; n<8; n++)                                         /* 29 */
   {                                                          /* 30 */
      printf("The voltage of channel %d is: %.2f.\n",         /* 31 */
               n, voltage[n]);                                /* 32 */
   }                                                          /* 33 */
}                                                             /* 34 */
                                                              /* 35 */
int make_measurement(int channel)                             /* 36 */
/* makes meas on specified channel*/                          /* 37 */
{                                                             /* 38 */
   int control_word, value;                                   /* 39 */
   CS_high();                                                 /* 40 */
   CS_low();  /* reset the A/D */                             /* 41 */
   control_word = make_control_word(channel);                 /* 42 */
   send_control_word(control_word);                           /* 43 */
   value = get_val();                                         /* 44 */
   CS_high();  /* turn off A/D */                             /* 45 */
   return(value);                                             /* 46 */
}                                                             /* 47 */
                                                              /* 48 */
int get_val(void)                                             /* 49 */
{                                                             /* 50 */
   int bit, value=0x00, n;                                    /* 51 */
   /* DO is now out of high impedance state */                /* 52 */
   clock();                                                   /* 53 */
   for(n=7; n>=0; n--)                                        /* 54 */
   {                                                          /* 55 */
      bit = ((inportb(STATUS)^0x80)>>7)&0x01;                 /* 56 */
      value = value | (bit << n);                             /* 57 */
      clock();                                                /* 58 */
   }                                                          /* 59 */
   return(value);                                             /* 60 */
}                                                             /* 61 */
                                                              /* 62 */
int make_control_word(int channel)                            /* 63 */
{                                                             /* 64 */
   int word, lsb;                                             /* 65 */
                                                              /* 66 */
   word = 0x18; /* 1 1000 */                                  /* 67 */
   lsb = channel & 0x01;                                      /* 68 */
   word = word | (lsb <<2);  /* 1 1 lsb 0 0 */                /* 69 */
   word = word | (channel >>1); /* 1 1 lsb msb middle */      /* 70 */
   return(word);                                              /* 71 */
}                                                             /* 72 */
                                                              /* 73 */
void send_control_word (int word)                             /* 74 */
/* Send word, one bit at a time on Data_2 starting with msb   /* 75 */
it */                                                         /* 76 */
{                                                             /* 77 */
   int n, bit;                                                /* 78 */
   for (n=4; n>=0; n--)                                       /* 79 */
   {                                                          /* 80 */
      bit = (word >>n) & 0x01; /* isolate the bit */          /* 81 */
      data = data & (~0x04);   /* zero position 2 */          /* 82 */
      data = data | (bit <<2); /* insert bit */               /* 83 */
      outportb(DATA, data);                                   /* 84 */
      clock();                                                /* 85 */
   }                                                          /* 86 */
}                                                             /* 87 */
                                                              /* 88 */
void clock(void)                                              /* 89 */
/* bring Data_1 high and then low */                          /* 90 */
{                                                             /* 91 */
   data = data | 0x02;                                        /* 92 */
   outportb(DATA, data);                                      /* 93 */
   delay(25);                                                 /* 94 */
   data = data & (~0x02);                                     /* 95 */
   outportb(DATA, data);                                      /* 96 */
   delay(25);                                                 /* 97 */
}                                                             /* 98 */
                                                              /* 99 */
void CS_high(void)                                            /* 100 */
{                                                             /* 101 */
   data = data | 0x01;                                        /* 102 */
   outportb(DATA, data);                                      /* 103 */
}                                                             /* 104 */
                                                              /* 105 */
void CS_low(void)                                             /* 106 */
{                                                             /* 107 */
   data = data & (~0x01);                                     /* 108 */
   outportb(DATA, data);                                      /* 109 */
}                                                             /* 110 */

Single Ended, 8-Channel Operation using Bidirectional Bit on the Control Port.

See Figure #2 The only difference from the above is that A/D terminals DI and DO are tied together and parallel port signal /STROBE is used to both output to and input from the A/D. Recall that /STROBE is the least significant bit on the Control Port and as it is open collector, it may be used as an input when the output is forced to logic one. Please, also recall that there are three hardware inversions associated with both outputting and inputting using the Control Port. Thus, the ^0x0b.

In the following, only the changes to the above program are shown.

In line 81, the control word is shifted out on the /STROBE lead.

After the control word has been completely shifted out, the /STROBE lead is forced high (line 84). /STROBE may then be used as an input in the get_val function.

In the get_val function, the data is read on the /STROBE lead (line 55).

/*
** Program AD0838_2.C
**
** Illustrates modifications to AD0838_1.C to use /STROBE as
** bidirectional bit to output to DI and input from DO.
**
** Assumes V_ref = 5.0 VDC
**
** /STROBE, term 1 ---------- DI, term 17 ---------- DO, term 14
** Data_1, term 3 ---------- CLK, term 16
** Data_0, term 2 ---------- /CS, term 18
**
** Christine Samuels, MSU, 8 August '96
*/

/* The only changes are in functions get_val() and send_control_word
*/


int get_val(void)                                             /* 49 */
{                                                             /* 50 */
   int bit, value=0x00, n;                                    /* 51 */
   clock();                                                   /* 52 */
   for(n=7; n>=0; n--)                                        /* 53 */
   {                                                          /* 54 */
      bit = (inportb(CONTROL)^0x0b)&0x01;                     /* 55 */
      value = value | (bit << n);                             /* 56 */
      clock();                                                /* 57 */
   }                                                          /* 58 */
   return(value);                                             /* 59 */
}                                                             /* 60 */
                                                              /* 61 */

void send_control_word (int word)                             /* 73 */
/* Send word, one bit at a time on Control_0 (/STROBE)        /* 74 */
** starting with msbit */                                     /* 75 */
{                                                             /* 76 */
   int n, bit;                                                /* 77 */
   for (n=4; n>=0; n--)                                       /* 78 */
   {                                                          /* 79 */
      bit = (word >>n) & 0x01; /* isolate the bit */          /* 80 */
      outportb(CONTROL, bit^0x0b);                            /* 81 */
      clock();                                                /* 82 */
   }                                                          /* 83 */
   outportb(CONTROL, 0x01^0x0b); /* force /STROBE high */     /* 84 */
}                                                             /* 85 */