home *** CD-ROM | disk | FTP | other *** search
- /* 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
-