ADC Data Register.
ADC LSB. Offset=0x0, Byte 0. Offset=0, Word 0.
|
D7 |
D6 |
D5 |
D4 |
D3 |
D2 |
D1 |
D0 |
|
AD7 |
AD6 |
AD5 |
AD4 |
AD3 |
AD2 |
AD1 |
AD0 |
ADC MSB. Offset=0x1, Byte 1. Offset=0, Word 0.
|
D15 |
D14 |
D13 |
D12 |
D11 |
D10 |
D9 |
D8 |
|
AD15 |
AD14 |
AD13 |
AD12 |
AD11 |
AD10 |
AD9 |
AD8 |
|
NAME |
DIRECTION |
DEFAULT |
DESCRIPTION |
|
AD[15:0] |
r |
- na - |
ADC data word. AD0 is the least significant bit. AD15 is the most significant data bit. A reading of 010 (0x0000) represents a negative full scale input and a value of 6553510 (0xFFFF) represents an input of positive full scale. The two registers can both be read simultaneously by simply reading the data as a 16-bit I/O transaction (Examples: “in ax, dx” or “inpw(base_address+0)” ). |
The ADC LSB register provides the least significant data byte and the ADC MSB register provides the most significant data byte. Each of the two registers can be read in any order (when FIFO Superset is not enabled). Writing any data to the ADC LSB register issues a software trigger.
Data bandwidth between the STX104 and CPU (PC/104 ISA bus) can be doubled by simply reading the ADC register as a 16-bit register. Software examples are shown below. Further improvement bus bandwidth can be had by limiting the CPU-burst readouts (i.e. Insw() function) to the data fragment buffer size; thus eliminating I/O wait states.
When FIFO Superset is enabled (M1 jumper installed) the ADC data is read out of the FIFO as either a word or a byte at a time. To read out the data correctly as a byte at a time, you must first read the LSB and then the MSB. Once the MSB is read, the FIFO is considered to be read and FIFO is advanced to the next value to be read (if not empty). You may continue to read the data until the FIFO empty flag is set. It is possible to continuously sample analog inputs (continuous ADC sampling or triggering) and continuously read FIFO without any breaks in sampling.
It is the programmer’s responsibility to be sure that data is read out in the correct sequence, as well as starting and stopping the acquisition as required.
|
OUTPUT RANGE |
UNIPOLAR JUMPER J9 * |
ADC CFG REG G1 |
ADC CFG REG G0 |
RESOLUTION |
NEG FULL SCALE VOLTAGE |
POS FULL SCALE VOLTAGE |
NEG FULL SCALE HEX |
POS FULL SCALE HEX |
|
+/- 10 Volts |
0 |
0 |
0 |
305 uV |
-10.00 |
+10.00 |
0x0000 |
0xFFFF |
|
+/- 5 Volts |
0 |
0 |
1 |
153 uV |
-5.000 |
+5.000 |
0x0000 |
0xFFFF |
|
+/- 2.5 Volts |
0 |
1 |
0 |
76 uV |
-2.500 |
+2.500 |
0x0000 |
0xFFFF |
|
+/- 1.25 Volts |
0 |
1 |
1 |
38 uV |
-1.250 |
+1.250 |
0x0000 |
0xFFFF |
|
0 - 10 Volts |
1 |
0 |
0 |
153 uV |
0.000 |
+10.00 |
0x0000 |
0xFFFF |
|
0 - 5 Volts |
1 |
0 |
1 |
76 uV |
0.000 |
+5.000 |
0x0000 |
0xFFFF |
|
0 - 2.5 Volts |
1 |
1 |
0 |
38 uV |
0.000 |
+2.500 |
0x0000 |
0xFFFF |
|
0 - 1.25 Volts |
1 |
1 |
1 |
19 uV |
0.000 |
+1.250 |
0x0000 |
0xFFFF |
* '1' = Jumper installed. '0' = Jumper not installed.
Using the REP INSW instruction (or Insw() function in Linux) provides a very high speed mechanism for reading out ADC data from the FIFO. This instruction is available on 286 CPUs and above. In fact, this is faster and easier to use than DMA; both in implementation and execution. Using REP INSW alleviates video problems related with DMA as well as improved CPU timing management (i.e. DMA adds timing holes in the system that are not easily managed by simple real-time kernels). The speed of any ISA bus I/O transaction is dependent on the CPU ISA bus speed which is usually set in the BIOS setup; use this setting with caution as it may affect performance of other cards and/or can cause data loss. This is usually available on many CPU cards.
Limiting the size of the sample count to the STX104 data fragment buffer in the Insw() function call will eliminate I/O bus wait states and further enhance overall throughput. In most cases this will improve throughput by another factor of two.
Examples of how to read the ADC data:
union { unsigned int word; unsigned char byte[2]; } ad_value[16]; ... adc_value(channel).byte[0] = inp(base_address+0); adc_value(channel).byte[1] = inp(base_address+1); /* adc_value(channel).word now contains the full ADC word value */ ...
unsigned int ad_value[16]; ... adc_value(channel) = inpw(base_address+0); ...
union { unsigned int word; unsigned char byte[2]; } ad_value[16]; ... if ( fifo_not_empty() == true ) /* function to check fifo status */ { adc_value(channel).byte[0] = inp(base_address+0); adc_value(channel).byte[1] = inp(base_address+1); /* adc_value(channel).word now contains the full ADC word value */ } ...
unsigned ad_value[16]; ... if ( fifo_not_empty() == true ) /* function to check fifo status */ { adc_value(channel) = inpw(base_address+0); } ...
typedef unsigned int WORD; void insw(WORD port, void *buf, int count) { _ES = FP_SEG(buf); /* Segment of buf */ _DI = FP_OFF(buf); /* Offset of buf */ _CX = count; /* Number to read */ _DX = port; /* Port */ asm REP INSW; } void main() { WORD *data[512]; ... insw(0x300, data, 512); /* assumes 512 samples (or two blocks) in FIFO */ ... }
/**************************************************************************** Apex Embedded Systems Revised: 03MAR08 STX104 Analog Input Demo ****************************************************************************/ #include <conio.h> #include <stdio.h> #include <dos.h> /****************************************************************************/ float adc_gain[8] = { 20.0/65536.0, 10.0/65536.0, 5.0/65536.0, 2.5/65536.0, 10.0/65536.0, 5.0/65536.0, 2.5/65536.0, 1.25/65536.0 }; float adc_offset[8] = { -10.0, -5.0, -2.5, -1.25, 0.0, 0.0, 0.0, 0.0 }; /****************************************************************************/ float Adc_Voltage ( void ) { long sum; int index; float voltage; /* take several samples and average */ sum = 0; for ( index=0; index <256; index++ ) { outportb(0x300, 0x00); /* start a sample */ while ( (inportb(0x308) & 0x80) != 0x00 ); /* wait for sample */ sum = sum + ((long)inpw(0x300)); } sum = sum / 256; index = inportb( 0x30B ) & 0x07; voltage = ( (float) sum ) * adc_gain[index] + adc_offset[index]; return voltage; } /****************************************************************************/ void main() { int x,y; unsigned char channels; channels = 0x00; /* scan channel zero only */ /* base address set to factory default at 0x300 */ /* initialize STX104 */ outportb(0x309, 0x00); /* no interrupts, no DMA and s/w trigger */ /* Applies to firmware revision 080214H only: for non-zero first_channel please write to channel register twice to correct for errata issue. This is transparent for other firmware revisions (simply means that acquisition controller is reset twice). */ outportb(0x302, channels); outportb(0x302, channels); while ((inportb(0x308) & 0x80 ) == 0x80 ); /* wait */ printf("0) +10.0V input\n"); printf("1) +5.0V input\n"); printf("2) +2.5V input\n"); printf("3) +1.25V input\n"); printf("9) Quit this test\n"); do { y = inportb( 0x30B ) & 0x07; if ( (y & 0x04) == 0x00 ) printf("Bipolar: "); else printf("Unipolar: "); y = y & 0x03; printf(" Gain="); if ( y == 0 ) printf("10V, "); else if ( y == 1 ) printf(" 5V, "); else if ( y == 2 ) printf(" 2.5V, "); else if ( y == 3 ) printf(" 1.25V,"); printf(" Voltage = %+10.6f \r", Adc_Voltage()); if ( kbhit() ) { y = getch(); /* set gain */ if ( y == '0' ) { x = 0; outportb( 0x30B, x ); } else if ( y == '1' ) { x = 1; outportb( 0x30B, x ); } else if ( y == '2' ) { x = 2; outportb( 0x30B, x ); } else if ( y == '3' ) { x = 3; outportb( 0x30B, x ); } else if ( y == '9' ) x = 9; } } while ( x != 9 ); printf("\n\n"); }
|
Copyright © 1997-2008 by Apex Embedded Systems. All rights reserved. Updated on Wednesday, April 02, 2008.
|
|
What do you think about this topic? Send feedback!
|