An analog value is continuous, not discrete, as shown in figure 17.1. In the previous chapters, techniques were discussed for designing logical control systems that had inputs and outputs that could only be on or off. These systems are less common than the logical control systems, but they are very important. In this chapter we will examine analog inputs and outputs so that we may design continuous control systems in a later chapter.
Typical analog inputs and outputs for PLCs are listed below. Actuators and sensors that can be used with analog inputs and outputs will be discussed in later chapters.
This chapter will focus on the general principles behind digital-to-analog (D/A) and analog-to-digital (A/D) conversion. The chapter will show how to output and input analog values with a PLC.
To input an analog voltage (into a PLC or any other computer) the continuous voltage value must be ’sampled’ and then converted to a numerical value by an A/D converter. Figure 17.2 shows a continuous voltage changing over time. There are three samples shown on the figure. The process of sampling the data is not instantaneous, so each sample has a start and stop time. The time required to acquire the sample is called the ’sampling time’. A/D converters can only acquire a limited number of samples per second. The time between samples is called the sampling period ’T’, and the inverse of the sampling period is the sampling frequency (also called sampling rate). The sampling time is often much smaller than the sampling period. The sampling frequency is specified when buying hardware, but for a PLC a maximum sampling rate might be 20Hz.
A more realistic drawing of sampled data is shown in Figure 17.3. This data is noisier, and even between the start and end of the data sample there is a significant change in the voltage value. The data value sampled will be somewhere between the voltage at the start and end of the sample. The maximum (Vmax) and minimum (Vmin) voltages are a function of the control hardware. These are often specified when purchasing hardware, but reasonable ranges are;
The number of bits of the A/D converter is the number of bits in the result word. If the A/D converter is ’8 bit’ then the result can read up to 256 different voltage levels. Most A/D converters have 12 bits, 16 bit converters are used for precision measurements.
The parameters defined in Figure 17.3 can be used to calculate values for A/D converters. These equations are summarized in Figure 17.4. Equation 17.1 relates the number of bits of an A/D converter to the resolution. Equation 17.2 gives the error that can be expected with an A/D converter given the range between the minimum and maximum voltages, and the resolution (this is commonly called the quantization error). Equation 17.3 relates the voltage range and resolution to the voltage input to estimate the integer that the A/D converter will record. Finally, equation 17.4 allows a conversion between the integer value from the A/D converter, and a voltage in the computer.
Consider a simple example, a 10 bit A/D converter can read voltages between -10V and 10V. This gives a resolution of 1024, where 0 is -10V and 1023 is +10V. Because there are only 1024 steps there is a maximum error of ±9.8mV. If a voltage of 4.564V is input into the PLC, the A/D converter converts the voltage to an integer value of 746. When we convert this back to a voltage the result is 4.570V. The resulting quantization error is 4.570V-4.564V=+0.006V. This error can be reduced by selecting an A/D converter with more bits. Each bit halves the quantization error.
If the voltage being sampled is changing too fast we may get false readings, as shown in Figure 17.6. In the upper graph the waveform completes seven cycles, and 9 samples are taken. The bottom graph plots out the values read. The sampling frequency was too low, so the signal read appears to be different that it actually is, this is called aliasing.
The Nyquist criterion specifies that sampling frequencies should be at least twice the frequency of the signal being measured, otherwise aliasing will occur. The example in Figure 17.6 violated this principle, so the signal was aliased. If this happens in real applications the process will appear to operate erratically. In practice the sample frequency should be 4 or more times faster than the system frequency.
There are other practical details that should be considered when designing applications with analog inputs;
• Noise - Since the sampling window for a signal is short, noise will have added effect on the signal read. For example, a momentary voltage spike might result in a higher than normal reading. Shielded data cables are commonly used to reduce the noise levels.
• Delay - When the sample is requested, a short period of time passes before the final sample value is obtained.
• Multiplexing - Most analog input cards allow multiple inputs. These may share the A/D converter using a technique called multiplexing. If there are 4 channels using an A/D converter with a maximum sampling rate of 100Hz, the maximum sampling rate per channel is 25Hz.
• Signal Conditioners - Signal conditioners are used to amplify, or filter signals coming from transducers, before they are read by the A/D converter.
• Resistance - A/D converters normally have high input impedance (resistance), so they affect circuits they are measuring.
• Single Ended Inputs - Voltage inputs to a PLC can use a single common for multiple inputs, these types of inputs are called ’single’ ended inputs. These tend to be more prone to noise.
• Double Ended Inputs - Each double ended input has its own common. This reduces problems with electrical noise, but also tends to reduce the number of inputs by half.
Analog outputs are much simpler than analog inputs. To set an analog output an integer is converted to a voltage. This process is very fast, and does not experience the timing problems with analog inputs. But, analog outputs are subject to quantization errors. Figure 17.11 gives a summary of the important relationships. These relationships are almost identical to those of the A/D converter.
Assume we are using an 8 bit D/A converter that outputs values between 0V and 10V. We have a resolution of 256, where 0 results in an output of 0V and 255 results in 10V. The quantization error will be 20mV. If we want to output a voltage of 6.234V, we would specify an output integer of 160, this would result in an output voltage of 6.250V. The quantization error would be 6.250V-6.234V=0.016V.
The current output from a D/A converter is normally limited to a small value, typically less than 20mA. This is enough for instrumentation, but for high current loads, such as motors, a current amplifier is needed. This type of interface will be discussed later. If the current limit is exceeded for 5V output, the voltage will decrease (so don’t exceed the rated voltage). If the current limit is exceeded for long periods of time the D/A output may be damaged.
Any computer running a process should use a real-time operating system. The purpose of a real-time operating system is primarily to ensure that a process runs within a specified time interval, normally a small fraction of a system. This capability is often not a common part of most operating systems, but it is relatively easy to add. When it is not a real-time process, it common for another process to monopolize the processor and cause erratic delays. When this happens the control program may not respond to a control event for a second or more. This would generally be a bad thing in a time critical system.
- need to be able to specify how often a process runs.
- system clock for slower processes.
Listing 16.1 - DAS08 Driver Header File (das08_io.h)
#include "../include/global.h"
#define ADCHIGH 0 // AD Data Registers
/* A/D Status and Control Register */
/* Auxiliary port on analog bus */
/* Programmable Gain Register */
/* Counter Load & Read Registers */
#define CCONFIGPORT 7 // Counter Control Register
/* 82C55 Digital I/O Registers */
#define PORTCL 12345 /* real port is 0x30e bits 0-3 */
#define PORTCH 6789 /* real port is 0x30e bits 4-7 */
int base; // card setup information
int portA; // port data directions
int *data_portA; // hooks to global values
Listing 16.2 - DAS08 Driver File (das08_io.cpp)
#include "../include/process.h"
int bits[]={0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
base = CARDBASE; // default cardbase
chan0 = BIP10VOLTS; // default AD ranges
int das08::configure(char *file_name){
if((fp_in = fopen(file_name, "r")) != NULL){
if((params[0] != ’#’) && (strlen(params) > 3)){
} else if(strncmp("A0", params, 2) == 0){
if(strncmp("BIP10VOLTS", &(params[2]), 10) == 0){ chan0 = BIP10VOLTS;
} else if(strncmp("BIP5VOLTS", &(params[2]), 9) == 0){ chan0 = BIP5VOLTS;
} else if(strncmp("BIP2PT5VOLTS", &(params[2]), 12) == 0){ chan0 = BIP2PT5VOLTS;
} else if(strncmp("BIP1PT25VOLTS", &(params[2]), 13) == 0){ chan0 = BIP1PT25VOLTS;
} else if(strncmp("BIPPT625VOLTS", &(params[2]), 13) == 0){ chan0 = BIPPT625VOLTS;
} else if(strncmp("UNI10VOLTS", &(params[2]), 10) == 0){ chan0 = UNI10VOLTS;
} else if(strncmp("UNI5VOLTS", &(params[2]), 9) == 0){ chan0 = UNI5VOLTS;
} else if(strncmp("UNI2PT5VOLTS", &(params[2]), 12) == 0){ chan0 = UNI2PT5VOLTS;
} else if(strncmp("UNI1PT25VOLTS", &(params[2]), 13) == 0){ chan0 = UNI1PT25VOLTS;
error_log(MINOR, "Unrecognized DAS08 analog A0 output range");
} else if(strncmp("A1", params, 2) == 0){
if(strncmp("BIP10VOLTS", &(params[2]), 10) == 0){ chan1 = BIP10VOLTS;
} else if(strncmp("BIP5VOLTS", &(params[2]), 9) == 0){ chan1 = BIP5VOLTS;
} else if(strncmp("BIP2PT5VOLTS", &(params[2]), 12) == 0){ chan1 = BIP2PT5VOLTS;
} else if(strncmp("BIP1PT25VOLTS", &(params[2]), 13) == 0){ chan1 = BIP1PT25VOLTS;
} else if(strncmp("BIPPT625VOLTS", &(params[2]), 13) == 0){ chan1 = BIPPT625VOLTS;
} else if(strncmp("UNI10VOLTS", &(params[2]), 10) == 0){ chan1 = UNI10VOLTS;
} else if(strncmp("UNI5VOLTS", &(params[2]), 9) == 0){ chan1 = UNI5VOLTS;
} else if(strncmp("UNI2PT5VOLTS", &(params[2]), 12) == 0){ chan1 = UNI2PT5VOLTS;
} else if(strncmp("UNI1PT25VOLTS", &(params[2]), 13) == 0){ chan1 = UNI1PT25VOLTS;
error_log(MINOR, "Unrecognized DAS08 analog A1 output range");
} else if(strncmp("PAI", params, 3) == 0){ portA = DIGITALIN;
} else if(strncmp("PAO", params, 3) == 0){ portA = DIGITALOUT;
} else if(strncmp("PBI", params, 3) == 0){ portB = DIGITALIN;
} else if(strncmp("PBO", params, 3) == 0){ portB = DIGITALOUT;
} else if(strncmp("PCLI", params, 4) == 0){ portCL = DIGITALIN;
} else if(strncmp("PCLO", params, 4) == 0){ portCL = DIGITALOUT;
} else if(strncmp("PCHI", params, 4) == 0){ portCH = DIGITALIN;
} else if(strncmp("PCHO", params, 4) == 0){ portCH = DIGITALOUT;
error_log(MINOR, "DAS08 argument not recognized");
error_log(MINOR, "Could not connect to DAS08 board - memory is probably in use");
if(portA == DIGITALIN){DIn(PORTA, data_portA);
} else {DOut(PORTA, data_portA[0]);}
if(portB == DIGITALIN){DIn(PORTB, data_portB);
} else {DOut(PORTB, data_portB[0]);}
if(portCL == DIGITALIN){DIn(PORTCL, data_portCL);
} else {DOut(PORTCL, data_portCL[0]);}
if(portCH == DIGITALIN){DIn(PORTCH, data_portCH);
} else {DOut(PORTCH, data_portCH[0]);}
DOut(PORTAUX, data_portXO[0]);
error_log(MINOR, "Could not release the DAS08 board - memory is probably in use");
int das08::DConfigPort(int Port, int Direction){
// This command configures a port as an input or output.
// The Direction field can be either DIGITALIN or DIGITALOUT
// depending on whether the port is to be configured as an
// input or output. Valid ports are PORTA, PORTB, PORTCL and
// PORTCH. Direction bit can be either DIGITALIN or DIGITALOUT.
//printf("Configuring port %d with direction %d \n", Port, Direction);
OldByte = inb(DCONFIGPORT + base); /*read the current register*/
if(Direction == DIGITALIN){ /* determine mask for DIGITALIN */
if(Port == PORTA){ mask = 0x10;
} else if(Port == PORTB){ mask = 0x02;
} else if(Port == PORTC){ mask = 0x09;
} else if(Port == PORTCL){ mask = 0x01; Port = PORTC;
} else if(Port == PORTCH){ mask = 0x08; Port = PORTC;
error_log(MINOR, "Digital port must be PORTA, PORTB, PORTC, PORTCL or PORTCH");
NewByte = OldByte | mask; /* new data for register */
} else if(Direction == DIGITALOUT){ /* determine mask for DIGITALOUT */
if(Port == PORTA){ mask = 0xef;
} else if(Port == PORTB){ mask = 0xfd;
} else if(Port == PORTC){ mask = 0xf6;
} else if(Port == PORTCL){ mask = 0xfe; Port = PORTC;
} else if(Port == PORTCH){ mask = 0xf7; Port = PORTC;
error_log(MINOR, "Digital port must be PORTA, PORTB, PORTC, PORTCL or PORTCH");
NewByte = OldByte & mask; /* new value for register */
error_log(MINOR, "Direction must be set to DIGITALIN or DIGITALOUT");
//printf("port thingy %d %d \n", NewByte, DCONFIGPORT);
outb(NewByte, DCONFIGPORT + base); /* write config data to register */
return error; /* no errors detected */
int das08::DBitIn(int Port, int BitNum, int *BitData){
// This function determines whether a bit within the
// requested port is set. The value (1 or 0) is returned
// in the variable pointer sent to the function. Port may
// be PORTA, PORTB, PORTCL or PORTCH. BitNum must be in the
if((Port == PORTCL) || (Port == PORTCH)){ data = inb(PORTC + base);
} else { data = inb(Port + base);}
//printf("GOT %d %d %d %d \n", Port, data, BitNum, BitData[0]);
if((Port == PORTA) || (Port == PORTB) || (Port == PORTC)){
if((BitNum >= 0) && (BitNum <= 7)){
error_log(MINOR, "Bit numbers should be between 0 and 7");
} else if((Port == PORTCL) || (Port == PORTAUX)) {
if((BitNum >= 0) && (BitNum <= 3)){
error_log(MINOR, "Bit numbers should be between 0 and 3");
if((BitNum >= 4) && (BitNum <= 7)){
error_log(MINOR, "Bit numbers should be between 4 and 7");
} else if(Port == DCONFIGPORT) {
error_log(MINOR, "Input port not recognized");
if((mask & data) != 0) BitData[0] = 1;
int das08::DBitOut(int Port, int BitNum, int BitValue){
// This function sets a bit of the requested port to either
// a zero or a one. Port may be PORTA, PORTB, PORTCL or
// PORTCH. BitNum must be in the range 0 - 7. BitValue
if((Port == PORTCL) || (Port == PORTCH)){
if((Port == PORTAUX) && (BitValue == 1)){
//printf("ddo %x %x \n", mask, OldByte);
} else if((Port == PORTAUX) && (BitValue == 0)) {
} else if(((Port==PORTA) || (Port==PORTB) || (Port == PORTC)) && (BitValue==1)){
}else if(((Port==PORTA) || (Port==PORTB) || (Port==PORTC)) && (BitValue == 0)){
} else if((Port == PORTCL) && (BitValue == 1)){
} else if((Port == PORTCL) && (BitValue == 0)){
} else if((Port == PORTCH) && (BitValue == 1)){
} else if((Port == PORTCH) && (BitValue == 0)){
if((Port == PORTCL) || (Port == PORTCH))
//printf("OUT %d %d\n", NewByte, Port + base);
if(error == NO_ERROR) outb(NewByte, Port + base);
int das08::DIn(int Port, int *Value){
// This function reads the byte value of the specified port
// and returns the result in the variable pointer sent to the
// function. Valid ports are PORTA, PORTB, PORTCL and PORTCH.
// result = DBitIn(DCONFIGPORT, 4, &BitData);
// result = DBitIn(DCONFIGPORT, 1, &BitData);
// result = DBitIn(DCONFIGPORT, 0, &BitData)
// + DBitIn(DCONFIGPORT, 3, &BitData);
// result = DBitIn(DCONFIGPORT, 0, &BitData);
// result = DBitIn(DCONFIGPORT, 3, &BitData);
// } else if(Port == PORTAUX){
// error_log(MINOR, "ERROR: Port not recognized");
//printf("sss %d %d \n", Port, result);
// if((error == NO_ERROR) && (BitData == 0)){
// error_log("ERROR: Port not configured for read");
temp = inb(PORTC + base); /* read the port data */
Value[0] = (temp & 0x0f); /* mask off the high bits */
temp = inb(PORTC + base); /* read the port data */
Value[0] = (temp & 0xf0); /* mask off the low bits */
Value[0] = 0x7 & (int)((inb(Port + base) / 16));
Value[0] = 0xff & inb(Port + base); /* read the port data */
int das08::DOut(int Port, int ByteValue){
// This function writes the byte value to the specified port.
// Valid ports are PORTA, PORTB, PORTCL and PORTCH.
ByteValue = (0x07 & inb(Port+base)) | (ByteValue * 16);
if((ByteValue > 255) || (ByteValue < 0)){
//printf("Writing byte %d to port %d\n", ByteValue, Port);
outb((ByteValue & 0x0f), PORTC + base);
outb((ByteValue & 0xf0), PORTC + base);
outb(ByteValue, Port + base); /* write the port data */
return error; /* no errors detected */
int das08::C8254Config(int CounterNum, int Config){
/* BCD = 0xfe - 16-bit binary count
BCD = 0xf1 - 4 decade Binary Coded Decimal */
case HIGHONLASTCOUNT: mask = 0xf1; break;
case ONESHOT: mask = 0xf3; break;
case RATEGENERATOR: mask = 0xf5; break;
case SQUAREWAVE: mask = 0xf7; break;
case SOFTWARESTROBE: mask = 0xf9; break;
case HARDWARESTROBE: mask = 0xfb; break;
default: error = ERROR;; break;
case 1: counter = 0x3f; break;
case 2: counter = 0x7f; break;
case 3: counter = 0xbf; break;
default: error = ERROR; break;
NewByte = (BCD & mask) & counter;
//printf("The value of TempByte & mask is --> %x.\n", NewByte);
outb(NewByte, CCONFIGPORT + base);
int das08::CLoad(int CounterNum, int value)
int TempByte, TempByte1, Register, CounterMask;
int WriteLowByteMask1 = 0x20; /* RL1 | */
int WriteLowByteMask2 = 0xef; /* RL0 & */
int WriteHighByteMask1 = 0xdf; /* RL1 & */
int WriteHighByteMask2 = 0x10; /* RL0 | */
long HighByteValue, LowByteValue;
case 1: Register = LOADREAD1; CounterMask = 0x3f; break;
case 2: Register = LOADREAD2; CounterMask = 0x7f; break;
case 3: Register = LOADREAD3; CounterMask = 0xbf; break;
default: error = ERROR; break;
HighByteValue = (int)strtol(HighByte, NULL, 0);
LowByteValue = (int)strtol(LowByte, NULL, 0);
TempByte = (CounterMask | WriteLowByteMask1) & WriteLowByteMask2;
//printf("The value in config low is --> %x.\n", TempByte1);
outb(TempByte1, CCONFIGPORT + base);
outb(LowByteValue, Register + base);
//printf("The register chosen is --> %x.\n", Register);
//printf("The value read in counter low is --> %x.\n", test);
TempByte = (0x30 & WriteHighByteMask1) | WriteHighByteMask2;
//printf("The value in config high is --> %x.\n", TempByte);
outb(TempByte, CCONFIGPORT + base);
outb(HighByteValue, Register + base);
outb(TempByte, CCONFIGPORT + base);
//printf("The value in counter high is --> %x.\n", test);
int das08::CIn(int CounterNum, int *CountValue){
int ReadLowByteMask1 = 0x20; /* RL1 | */
int ReadLowByteMask2 = 0xef; /* RL0 & */
int ReadHighByteMask1 = 0xdf; /* RL1 & */
int ReadHighByteMask2 = 0x10; /* RL0 | */
case 1: Register = LOADREAD1; break;
case 2: Register = LOADREAD2; break;
case 3: Register = LOADREAD3; break;
default: error = ERROR; break;
TempByte = (0x3f | ReadLowByteMask1) & ReadLowByteMask2;
outb(TempByte, CCONFIGPORT + base);
CountValue1 = inb(Register + base);
//printf("The low value is --> %x.\n", CountValue1);
TempByte = (0x3f & ReadHighByteMask1) | ReadHighByteMask2;
outb(TempByte, CCONFIGPORT + base);
CountValue2 = inb(Register + base);
//printf("The high value is --> %x.\n", CountValue2);
int das08::AIn(int ADChannel, int *Value){
// This function requires three arguments to perform the
// analog to digital conversion. ADChannel must be in the
// range 0-7 and Range must be a valid range code
// i.e. BIP5VOLTS. The value of the conversion will be
// returned to the address specificed through the pointer
// variable. This value will be in the range 0-4095.
int value1, value2, value3, curr_status, new_status, ADbusy;
int ADValue_low, ADValue_low1, ADValue_low2, ADValue_high;
curr_status = inb(ADCSTATUS + base); /* current value in status */
case 0:ADCmask1 = 0xf8;ADCmask2 = 0x00;break;
case 1:ADCmask1 = 0xf9;ADCmask2 = 0x01;break;
case 2:ADCmask1 = 0xfa;ADCmask2 = 0x02;break;
case 3:ADCmask1 = 0xfb;ADCmask2 = 0x03;break;
case 4:ADCmask1 = 0xfc;ADCmask2 = 0x04;break;
case 5:ADCmask1 = 0xfd;ADCmask2 = 0x05;break;
case 6:ADCmask1 = 0xfe;ADCmask2 = 0x06;break;
case 7:ADCmask1 = 0xff;ADCmask2 = 0x07;break;
default:error = ERROR;; break; /* error */
outb(chan0, GAIN + base); /* set the gain/range value */
new_status = (curr_status & ADCmask1) | ADCmask2;
outb(new_status, ADCSTATUS + base); /* set the channel number */
outb(0x00, ADCLOW + base); /* start a 12 bit A/D conversion */
while((error == NO_ERROR) && (EOC == 1)){ /* check for end of conversion */
ADbusy = inb(ADCSTATUS + base); /* read status register */
EOC = 1; /* A/D still converting */
EOC = 0; /* A/D done converting */
ADValue_low = inb(ADCLOW + base); /* get the lower eight bits */
ADValue_high = inb(ADCHIGH + base); /* get the upper four bits */
ADValue_low1 = (ADValue_low & 0x0f); /* mask off bits 4-7 */
ADValue_low2 = (ADValue_low & 0xf0); /* mask off bits 0-3 */
case 0x40:value3 = 1024;break;
case 0x50:value3 = 1280;break;
case 0x60:value3 = 1536;break;
case 0x70:value3 = 1792;break;
case 0x80:value3 = 2048;break;
case 0x90:value3 = 2304;break;
case 0xa0:value3 = 2560;break;
case 0xb0:value3 = 2816;break;
case 0xc0:value3 = 3072;break;
case 0xd0:value3 = 3328;break;
case 0xe0:value3 = 3584;break;
case 0xf0:value3 = 3840;break;
default: error = ERROR; /* error - unknown conversion result */
*Value = value1+value2+value3; /* total value for conversion */
return error; /* no errors detected */
int das08::AOut(int DAChannel, int DAValue){
// This function performs a digital to analog conversion
// routine. The DAChannel must be either 0 or 1 and the
// digital value must be in the range 0-4095.
int low, high, DACLOW, DACHIGH;
case 0:DACLOW = DAC0LOW;DACHIGH = DAC0HIGH;break;
case 1:DACLOW = DAC1LOW;DACHIGH = DAC1HIGH;break;
/* The following table converts the digital value into
three hex values encompassing two 8-bit registers. The
layout of the registers follow:
low - DA7 DA6 DA5 DA4 DA3 DA2 DA1 DA0
high - x x x x DA11 DA10 DA9 DA8 */
} else if((DAValue >= 256) && (DAValue <= 511)){
} else if((DAValue >= 512) && (DAValue <= 767)) {
} else if((DAValue >= 768) && (DAValue <= 1023)) {
} else if((DAValue >= 1024) && (DAValue <= 1279)) {
} else if((DAValue >= 1280) && (DAValue <= 1535)) {
} else if((DAValue >= 1536) && (DAValue <= 1791)) {
} else if((DAValue >= 1792) && (DAValue <= 2047)) {
} else if((DAValue >= 2048) && (DAValue <= 2303)){
} else if((DAValue >= 2304) && (DAValue <= 2559)){
} else if((DAValue >= 2560) && (DAValue <= 2815)){
} else if((DAValue >= 2816) && (DAValue <= 3071)){
} else if((DAValue >= 3072) && (DAValue <= 3327)){
} else if((DAValue >= 3328) && (DAValue <= 3583)){
} else if((DAValue >= 3584) && (DAValue <= 3839)){
} else if((DAValue >= 3840) && (DAValue <= 4095)){
error = ERROR; /* error - D/A value must be 0-4095 */
outb(low, DACLOW + base); /* write the low byte value */
outb(high, DACHIGH + base); /* write the high byte value */
return error; /* no errors detected */
Listing 16.1 - DAS08 Driver Test File (testdaq.cpp)
printf("\n\n------------ DAS08 Test Harness Menu --------------\n");
printf("1. Digital Configure\n");
printf("2. Digital Input Bit\n");
printf("3. Digital Input Word\n");
printf("4. Digital Output Bit\n");
printf("5. Digital Output Word\n\n");
printf("6. Counter Configure\n");
printf("7. Counter Load Value\n");
printf("8. Counter Input Value\n\n");
printf("9. Analog Input Value\n");
printf("10. Analog Output Value\n\n");
A->DConfigPort( query(CHOOSE_PORT, NULL, 0),
query(CHOOSE_DIRECTION, NULL, 0));
A->DBitIn( query(CHOOSE_PORT, NULL, 0),
query(QUERY, "Choose a bit (0-7): ", 0), &value);
printf("The Bit Value is [%d] \n", value);
A->DIn( query(CHOOSE_PORT, NULL, 0), &value);
printf("The Value is [%d] or [%d]hex\n", value, value);
A->DBitOut( query(CHOOSE_PORT, NULL, 0),
query(QUERY, "Choose a bit (0-7): ", 0),
query(QUERY, "Choose a value (0 or 1): ", 0));
A->DOut( query(CHOOSE_PORT, NULL, 0),
query(QUERY, "Choose a value (-128 to 127): ", 0));
A->C8254Config( query(CHOOSE_COUNTER, NULL, 0),
query(CHOOSE_CONFIG, NULL, 0));
A->CLoad( query(CHOOSE_COUNTER, NULL, 0),
query(QUERY, "Enter a value in the form 0x____ : ", 0));
A->CIn( query(CHOOSE_COUNTER, NULL, 0), &value);
printf("The Counter value was [%d]\n", value);
A->AIn( query(QUERY, "Enter Channel Number (0-7): ", 0), &value);
printf("The value is [%d]\n", value);
A->AOut( query(QUERY, "Enter Channel Number (0-1): ", 0),
query(QUERY, "Enter Value (0- 4095): ", 0));
printf("ERROR: Choice not recognized\n");
int query(int type, char *text, int def){
printf("%s [%d]: ", text, def);
} else if(type == CHOOSE_PORT){
printf("Which port (1=A, 2=B, 3=C, 4=CH, 5=CL, 6=AUX): ");
if(value == 6) return PORTAUX;
} else if(type == CHOOSE_COUNTER){
printf("Which counter (1, 2, 3): ");
if((value >= 1) || (value <= 3)) return value;
} else if(type == CHOOSE_CONFIG){
printf("Which mode (1=HighOnLastCount, 2=OneShot, 3=RateGenerator, 4=SquareWave, 5=SoftwareStrobe, 6=HardwareStrobe): ");
if(value == 1) return HIGHONLASTCOUNT;
if(value == 2) return ONESHOT;
if(value == 3) return RATEGENERATOR;
if(value == 4) return SQUAREWAVE;
if(value == 5) return SOFTWARESTROBE;
if(value == 6) return HARDWARESTROBE;
} else if(type == CHOOSE_DIRECTION){
printf("Which direction (1=In, 2=Out): ");
if(value == 1) return DIGITALIN;
if(value == 2) return DIGITALOUT;
void error_log(int code, char *string){
printf("ERROR %d: %s \n", code, string);
• A/D conversion will convert a continuous value to an integer value.
• D/A conversion is easier and faster and will convert a digital value to an analog value.
• Resolution limits the accuracy of A/D and D/A converters.
• Sampling too slowly will alias the real signal.
• Analog inputs are sensitive to noise.
• The analog I/O cards are configured with a few words of memory.
• BTW and BTR functions are needed to communicate with the analog I/O cards.
1. You need to read an analog voltage that has a range of -10V to 10V to a precision of +/-0.05V. What resolution of A/D converter is needed?
2. We are given a 12 bit analog input with a range of -10V to 10V. If we put in 2.735V, what will the integer value be after the A/D conversion? What is the error? What voltage can we calculate?
3. We need to select a digital to analog converter for an application. The output will vary from -5V to 10V DC, and we need to be able to specify the voltage to within 50mV. What resolution will be required? How many bits will this D/A converter need? What will the accuracy be?
4. Write a program that will input an analog voltage, do the calculation below, and output an analog voltage.
5. Develop a program to sample analog data values and calculate the average, standard deviation, and the control limits. The general steps are listed below.
2. Randomly select values and calculate the average and store in memory. Calculate the standard deviation of the stored values.
3. Compare the inputs to the standard deviation. If it is larger than 3 deviations from the mean, halt the process.
4. If it is larger than 2 then increase a counter A, or if it is larger than 1 increase a second counter B. If it is less than 1 reset the counters.
5. If counter A is =3 or B is =5 then shut down.
To use a data aquisition card to aquire data.
The daq card will be placed into a Linux computer and then controlled with the drive programs listed in this chapter.
1. Visit the computer boards web site (www.computerboards.com) and review the manual for the DAS-08 ISA board.
1. Complete the tutorial for the DAS-08 DAQ card.
2. Modify the tutorial program so that the analog input value from the board is read once a second and written to a database.