home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programming
/
powerprogramming1994.iso
/
progtool
/
microcrn
/
issue_42.arc
/
ADC42.C
< prev
next >
Wrap
Text File
|
1988-05-20
|
9KB
|
277 lines
/* Code to support A/D converter article from Micro C issue #42
by Bruce Eckel */
/***** Listing 1 *****/
/* Functions for the TI TLC532AIN
11-channel A/D converter. The chip is reset,
the TTL-level digital inputs at pins 16-21 are
read, and the 8-bit analog values at pins 16-25
are read. A sample driver can be found on the
Micro C BBS (DRIVER.C). Bruce Eckel, Eisys
Consulting, 1988 */
/* Since this file contains in-line assembly, it
must be compiled with the command:
"tcc -c -B adc.c" to create a ".obj" file. A
makefile is available on the BBS to automate all
this using Turbo C's "make" */
#undef DEBUG_FLAG /* define this for debugging */
#define BASE 0x238 /* address of printer card,
set by jumpers */
#define DATA BASE
#define CONTROL (BASE+2)
/* macro to put a '1' in a binary position: */
#define BIT(x) (1 << x)
/* set PBUS to input/tristate: */
#define PBUS_IN BIT(5)
#define PBUS_OUT 0
#define READ 0 /* read/write line high */
#define WRITE BIT(0) /* read/write line low
(signal inverted) */
#define CLOCK_HIGH 0 /* signal inverted */
#define CLOCK_LOW BIT(1)
#define REGISTER_0 0 /* signal not inverted */
#define REGISTER_1 BIT(2)
#define SELECT BIT(3) /* signal inverted */
#define DE_SELECT 0
/* global control byte to hold the status of
BASE+2, so we can change one pin at a time: */
unsigned char ctl_val;
/* prints a byte as ones and zeroes (for
debugging with a logic probe) */
void print_binary(unsigned char c) {
int i;
for (i = 7; i >= 0 ; i--)
/* note "ternary" if-then-else: */
printf("%c",c & (1 << i)? '1': '0');
}
#ifdef DEBUG_FLAG
void DEBUG(char * TEXT) {
printf(TEXT);
printf(" ctl_val = ");
print_binary(ctl_val); printf("\n");
getch();
/* so you can stop after every step;
press a key to continue */
}
#else DEBUG_FLAG
/* do nothing if we aren't debugging
(generates a compiler warning) */
void DEBUG(char * TEXT) {}
#endif DEBUG_FLAG
/* Note: when changing a bit without affecting
the others, the non-zero version of the signal
must be used. It is ORed to make the bit go
high, and it's bitwise invers (~) is ANDed to
make the bit go low. */
#define COMMAND(VALUE) \
(outportb(CONTROL, (VALUE))); DEBUG("command")
#define DATA_OUT(VALUE) \
(outportb(DATA, (VALUE))); DEBUG("data out")
#define DATA_IN (inportb(DATA))
#define CLOCK_DOWN \
COMMAND(ctl_val |= CLOCK_LOW); \
DEBUG("clock low")
#define CLOCK_UP \
COMMAND(ctl_val &= ~CLOCK_LOW); \
DEBUG("clock hi")
#define BUS_IDLE \
COMMAND(ctl_val=PBUS_IN|CLOCK_LOW|DE_SELECT);
/* BUS_IDLE: clock should always be left in the
"low" state. PBUS should always be left
tri-stated (input). Chip select should always
be high (not selected) */
#define CLEAR BUS_IDLE; CLOCK_UP; CLOCK_DOWN;
/* 'CLEAR' resets the tlc532's internal byte
pointer so it points to the high-byte register.
This occurs after the chip gets a full clock
cycle when it is de-selected. */
void reset_adc(void) {
/* call this once on power-up */
int i;
DATA_OUT(0xff); /* raise DATA lines */
/* lower reset line & enable PBUS output: */
COMMAND (ctl_val = PBUS_OUT | CLOCK_LOW |
DE_SELECT); DEBUG("reset low");
/* now we have to raise and lower the clock
3 times (kind of like a magic spell) */
for (i = 0; i < 3; i++)
{ CLOCK_UP; CLOCK_DOWN; }
/* Now return the bus to the idle state */
BUS_IDLE;
}
unsigned int digital_values() {
/* read the values from the digital pins */
unsigned int result;
COMMAND (ctl_val = PBUS_IN | READ | CLOCK_LOW
| REGISTER_1 | SELECT );
/* raise the clock & read the high byte */
CLOCK_UP;
/* shift it into the high byte: */
result = DATA_IN << 8; DEBUG("data in");
/* cycle the clock and read the low byte */
CLOCK_DOWN; CLOCK_UP;
/* don't disturb the high byte: */
result |= DATA_IN;
/* reset the bus & internal byte pointer */
CLEAR;
return result;
}
unsigned int conversion(int input_line) {
unsigned int result;
/* number of clocks for a conversion: */
int clock_counts = 28;
COMMAND (ctl_val = PBUS_OUT | WRITE |
CLOCK_LOW | REGISTER_1 | SELECT );
CLOCK_UP;
/* set bit 0 of high byte for conversion: */
DATA_OUT(1);
/* Clock in the high byte: */
CLOCK_DOWN; CLOCK_UP;
/* select pin to convert: */
DATA_OUT(input_line & 0xf);
BUS_IDLE;
/* So byte pointer is reset during
conversion. Clock is low */
/* If you don't own MASM, replace the inline
code with the following: */
/* while (clock_counts--)
{ CLOCK_UP; CLOCK_DOWN; } */
/* Here's the in-line assembly to speed up the
clocking. If you want the conversion()
function to run as fast as possible, write
the whole thing in assembly. */
/* register DX holds the port number: */
_DX = CONTROL;
/* register AL holds the output data: */
_AL = ctl_val;
/* register CX for decrement/looping */
_CX = clock_counts;
do_clocks:
/* raise the clock line (bit 1):*/
asm or al, 10b;
/* output the control value:*/
asm out dx,al;
/* lower the clock line: */
asm and al, 11111101b;
/* output the control value: */
asm out dx,al;
/* decrement cx and loop if not zero: */
asm loop do_clocks
COMMAND (ctl_val = PBUS_IN | READ | CLOCK_LOW
| REGISTER_0 | SELECT );
/* raise the clock line & read high byte */
CLOCK_UP;
/* shift it into the high byte: */
result = DATA_IN << 8; DEBUG("hi data in");
/* cycle the clock and read the low byte */
CLOCK_DOWN; CLOCK_UP;
result |= DATA_IN; DEBUG("low data in");
/* reset bus and internal byte pointer */
CLEAR;
return result;
}
/*** Listing 2 *****/
/* Driver for the TI TLC532AIN A/D converter functions from issue #42.
Digital inputs are constantly displayed. Pressing the desired channel
number (0-5, a-f) displays that channel. */
#include <dos.h>
#define VIDEO 0x10 /* video interrupt */
#define VC peek(0x40, 0x63) /* 6845 video controller base register */
#define BIT(x) (1 << x) /* macro to put a '1' in a binary position */
/* Declarations for TLC532AIN functions in file ADC.C */
void reset_adc(void); /* call this once on power-up */
unsigned int digital_values(void); /* read the values from the digital pins */
unsigned int conversion(int input_line); /* perform a conversion on a pin */
/* Move cursor to a place on the screen */
void gotoxy(int x, int y) { /* from Turbo C reference manual */
union REGS regs;
regs.h.ah = 2; /* set cursor position */
regs.h.dh = y;
regs.h.dl = x;
regs.h.bh = 0; /* video page 0 */
int86(VIDEO, ®s, ®s);
}
/* To understand these, see Fogg's 6845 article in issue #???? */
/* Turn cursor flashing off and on */
void cursor_off() { outportb(VC,10); outportb(VC+1,14); }
void cursor_on() { outportb(VC,10); outportb(VC+1,11); }
main()
{
int i = 1; /* sample channel 1 first -- it's the power supply */
unsigned int digital, analog;
reset_adc();
cursor_off(); /* makes the display much easier to look at */
while (1){
gotoxy(0,1);
printf ("sampling channel %d ",i); conversion(i);
while (!kbhit()) {
gotoxy(0,2);
printf("address: %d \n", (digital_values() >> 6) & 0xf);
analog = conversion(i);
printf(" EOC = %d ", analog & BIT(15) ? 1:0); /* not necessary */
printf(" conversion = 0x%x\n", analog & 0xff);
printf("digital values: ");
print_binary( digital_values() >> 10);
}
i = getch(); /* get the character from kbhit() */
if (i == 27) break; /* quit on an ESC */
if (i <= '9' && i >= '0') i -= '0'; /* convert ascii */
if (i <= 'f' && i >= 'a') i -= 'a' - 10; /* a to f becomes 10 - 15 */
i &= 0xf; /* other chars get masked */
}
cursor_on(); /* turn cursor back on when program is finished */
}
/**** listing 3 *****/
# A makefile for use with Turbo C's "make" program. This will compile
# the code (which includes in-line assembly) from Bruce Eckel's A/D
# converter article in issue #42 of Micro Cornucopia.
# All you have to do is type "make".
# where to look for the library files:
TCLIB = c:\turboc
# The target file is adc.exe, and it depends on the .obj files.
# The second line tells how to make the target from the .obj files
adc.exe : adc.obj driver.obj
tcc -oadc.exe -L$(TCLIB) adc.obj driver.obj
# The .obj files depend on the .c files. In the case of adc.c, we
# need to invoke the compiler with the in-line assembly option
adc.obj : adc.c
tcc -c -B adc.c
driver.obj : driver.c
tcc -c -I$(TCLIB) driver.c