home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / microcrn / issue_42.arc / ADC42.C < prev    next >
Text File  |  1988-05-20  |  9KB  |  277 lines

  1. /* Code to support A/D converter article from Micro C issue #42
  2.    by Bruce Eckel */
  3.  
  4. /***** Listing 1 *****/
  5.  
  6. /* Functions for the TI TLC532AIN
  7. 11-channel A/D converter.  The chip is reset,
  8. the TTL-level digital inputs at pins 16-21 are
  9. read, and the 8-bit analog values at pins 16-25
  10. are read.  A sample driver can be found on the
  11. Micro C BBS (DRIVER.C).  Bruce Eckel, Eisys
  12. Consulting, 1988 */
  13. /* Since this file contains in-line assembly, it
  14. must be compiled with the command:
  15. "tcc -c -B adc.c" to create a ".obj" file.  A
  16. makefile is available on the BBS to automate all
  17. this using Turbo C's "make" */
  18.  
  19. #undef DEBUG_FLAG /* define this for debugging */
  20.  
  21. #define BASE 0x238  /* address of printer card,
  22.                        set by jumpers */
  23. #define DATA BASE
  24. #define CONTROL (BASE+2)
  25. /* macro to put a '1' in a binary position: */
  26. #define BIT(x)  (1 << x)
  27.  /* set PBUS to input/tristate: */
  28. #define PBUS_IN  BIT(5)
  29. #define PBUS_OUT  0
  30. #define READ  0  /* read/write line high  */
  31. #define WRITE  BIT(0) /* read/write line low
  32.                         (signal inverted) */
  33. #define CLOCK_HIGH  0 /* signal inverted */
  34. #define CLOCK_LOW  BIT(1)
  35. #define REGISTER_0  0 /* signal not inverted */
  36. #define REGISTER_1  BIT(2)
  37. #define SELECT   BIT(3) /* signal inverted */
  38. #define DE_SELECT 0
  39.  
  40. /* global control byte to hold the status of
  41. BASE+2, so we can change one pin at a time: */
  42. unsigned char ctl_val;
  43.  
  44. /* prints a byte as ones and zeroes (for
  45. debugging with a logic probe) */
  46. void print_binary(unsigned char c) {
  47.     int i;
  48.     for (i = 7; i >= 0 ; i--)
  49.     /* note "ternary" if-then-else: */
  50.         printf("%c",c & (1 << i)? '1': '0');
  51. }
  52.  
  53. #ifdef DEBUG_FLAG
  54. void DEBUG(char * TEXT) {
  55.     printf(TEXT);
  56.     printf("  ctl_val = ");
  57.     print_binary(ctl_val); printf("\n");
  58.     getch();
  59.     /* so you can stop after every step;
  60.        press a key to continue */
  61. }
  62. #else DEBUG_FLAG
  63. /* do nothing if we aren't debugging
  64. (generates a compiler warning) */
  65. void DEBUG(char * TEXT) {}
  66. #endif DEBUG_FLAG
  67.  
  68. /* Note: when changing a bit without affecting
  69. the others, the non-zero version of the signal
  70. must be used.  It is ORed to make the bit go
  71. high, and it's bitwise invers (~) is ANDed to
  72. make the bit go low. */
  73. #define COMMAND(VALUE) \
  74.  (outportb(CONTROL, (VALUE))); DEBUG("command")
  75. #define DATA_OUT(VALUE) \
  76. (outportb(DATA, (VALUE))); DEBUG("data out")
  77. #define DATA_IN         (inportb(DATA))
  78. #define CLOCK_DOWN \
  79.  COMMAND(ctl_val |= CLOCK_LOW); \
  80.  DEBUG("clock low")
  81. #define CLOCK_UP \
  82.  COMMAND(ctl_val &= ~CLOCK_LOW); \
  83.  DEBUG("clock hi")
  84. #define BUS_IDLE  \
  85.  COMMAND(ctl_val=PBUS_IN|CLOCK_LOW|DE_SELECT);
  86.  /* BUS_IDLE: clock should always be left in the
  87.  "low" state.  PBUS should always be left
  88.  tri-stated (input).  Chip select should always
  89.  be high (not selected) */
  90. #define CLEAR  BUS_IDLE; CLOCK_UP; CLOCK_DOWN;
  91. /* 'CLEAR' resets the tlc532's internal byte
  92. pointer so it points to the high-byte register.
  93. This occurs after the chip gets a full clock
  94. cycle when it is de-selected.  */
  95.  
  96. void reset_adc(void) {
  97. /* call this once on power-up */
  98.     int i;
  99.     DATA_OUT(0xff);  /* raise DATA lines */
  100.     /* lower reset line & enable PBUS output: */
  101.     COMMAND (ctl_val = PBUS_OUT | CLOCK_LOW |
  102.              DE_SELECT); DEBUG("reset low");
  103.     /* now we have to raise and lower the clock
  104.     3 times (kind of like a magic spell) */
  105.     for (i = 0; i < 3; i++)
  106.         { CLOCK_UP; CLOCK_DOWN; }
  107.     /* Now return the bus to the idle state */
  108.     BUS_IDLE;
  109. }
  110.  
  111. unsigned int digital_values() {
  112. /* read the values from the digital pins */
  113.     unsigned int result;
  114.     COMMAND (ctl_val = PBUS_IN | READ | CLOCK_LOW
  115.              | REGISTER_1 | SELECT );
  116.     /* raise the clock & read the high byte */
  117.     CLOCK_UP;
  118.     /* shift it into the high byte: */
  119.     result = DATA_IN << 8; DEBUG("data in");
  120.     /* cycle the clock and read the low byte */
  121.     CLOCK_DOWN; CLOCK_UP;
  122.    /* don't disturb the high byte: */
  123.     result |= DATA_IN;
  124.     /* reset the bus & internal byte pointer */
  125.     CLEAR;
  126.     return result;
  127. }
  128.  
  129. unsigned int conversion(int input_line) {
  130.     unsigned int result;
  131.     /* number of clocks for a conversion: */
  132.     int clock_counts = 28;
  133.     COMMAND (ctl_val = PBUS_OUT | WRITE |
  134.              CLOCK_LOW | REGISTER_1 | SELECT );
  135.     CLOCK_UP;
  136.     /* set bit 0 of high byte for conversion: */
  137.     DATA_OUT(1);
  138.     /* Clock in the high byte: */
  139.     CLOCK_DOWN; CLOCK_UP;
  140.     /* select pin to convert: */
  141.     DATA_OUT(input_line & 0xf);
  142.     BUS_IDLE;
  143.     /* So byte pointer is reset during
  144.     conversion. Clock is low */
  145. /*  If you don't own MASM, replace the inline
  146.     code with the following: */
  147. /*  while (clock_counts--)
  148.           { CLOCK_UP; CLOCK_DOWN; }  */
  149. /*  Here's the in-line assembly to speed up the
  150.     clocking.  If you want the conversion()
  151.     function to run as fast as possible, write
  152.     the whole thing in assembly. */
  153.     /* register DX holds the port number: */
  154.     _DX = CONTROL;
  155.     /* register AL holds the output data: */
  156.     _AL = ctl_val;
  157.     /* register CX for decrement/looping */
  158.     _CX = clock_counts;
  159. do_clocks:
  160.      /* raise the clock line (bit 1):*/
  161.     asm   or    al, 10b;
  162.     /* output the control value:*/
  163.     asm   out   dx,al;
  164.     /* lower the clock line: */
  165.     asm   and   al, 11111101b;
  166.     /* output the control value: */
  167.     asm   out   dx,al;
  168.      /* decrement cx and loop if not zero: */
  169.     asm   loop  do_clocks
  170.  
  171.     COMMAND (ctl_val = PBUS_IN | READ | CLOCK_LOW
  172.              | REGISTER_0 | SELECT );
  173.     /* raise the clock line & read high byte */
  174.     CLOCK_UP;
  175.     /* shift it into the high byte: */
  176.     result = DATA_IN << 8; DEBUG("hi data in");
  177.     /* cycle the clock and read the low byte */
  178.     CLOCK_DOWN; CLOCK_UP;
  179.     result |= DATA_IN;  DEBUG("low data in");
  180.      /* reset bus and internal byte pointer */
  181.     CLEAR;
  182.     return result;
  183. }
  184.  
  185.  
  186.  
  187.  
  188.  
  189.  
  190. /*** Listing 2 *****/
  191.  
  192. /* Driver for the TI TLC532AIN A/D converter functions from issue #42.
  193. Digital inputs are constantly displayed.  Pressing the desired channel
  194. number (0-5, a-f) displays that channel. */
  195.  
  196. #include <dos.h>
  197. #define VIDEO 0x10              /* video interrupt */
  198. #define VC peek(0x40, 0x63)     /* 6845 video controller base register */
  199. #define BIT(x)  (1 << x)        /* macro to put a '1' in a binary position */
  200.  
  201. /* Declarations for TLC532AIN functions in file ADC.C */
  202. void reset_adc(void);   /* call this once on power-up */
  203. unsigned int digital_values(void); /* read the values from the digital pins */
  204. unsigned int conversion(int input_line); /* perform a conversion on a pin */
  205.  
  206. /* Move cursor to a place on the screen */
  207. void gotoxy(int x, int y) { /* from Turbo C reference manual */
  208.     union REGS regs;
  209.     regs.h.ah = 2;  /* set cursor position */
  210.     regs.h.dh = y;
  211.     regs.h.dl = x;
  212.     regs.h.bh = 0;  /* video page 0 */
  213.     int86(VIDEO, ®s, ®s);
  214. }
  215.  
  216. /* To understand these, see Fogg's 6845 article in issue #???? */
  217. /* Turn cursor flashing off and on */
  218. void cursor_off() { outportb(VC,10); outportb(VC+1,14); }
  219. void cursor_on() { outportb(VC,10); outportb(VC+1,11); }
  220.  
  221. main()
  222. {
  223.    int i = 1;  /* sample channel 1 first -- it's the power supply */
  224.    unsigned int digital, analog;
  225.    reset_adc();
  226.    cursor_off();  /* makes the display much easier to look at */
  227.    while (1){
  228.         gotoxy(0,1);
  229.         printf ("sampling channel %d  ",i); conversion(i);
  230.         while (!kbhit()) {
  231.             gotoxy(0,2);
  232.             printf("address: %d  \n", (digital_values() >> 6) & 0xf);
  233.             analog = conversion(i);
  234.             printf(" EOC = %d  ", analog & BIT(15) ? 1:0); /* not necessary */
  235.             printf(" conversion = 0x%x\n", analog & 0xff);
  236.             printf("digital values: ");
  237.             print_binary( digital_values() >> 10);
  238.         }
  239.     i = getch();                                /* get the character from kbhit() */
  240.     if (i == 27) break;                         /* quit on an ESC */
  241.     if (i <= '9' && i >= '0') i -= '0';         /* convert ascii */
  242.     if (i <= 'f' && i >= 'a') i -= 'a' - 10;    /* a to f becomes 10 - 15 */
  243.     i &= 0xf;                                   /* other chars get masked */
  244.     }
  245.     cursor_on();  /* turn cursor back on when program is finished */
  246. }
  247.  
  248.  
  249.  
  250.  
  251.  
  252. /**** listing 3 *****/
  253.  
  254. # A makefile for use with Turbo C's "make" program.  This will compile
  255. #   the code (which includes in-line assembly) from Bruce Eckel's A/D
  256. #   converter article in issue #42 of Micro Cornucopia.
  257. # All you have to do is type "make".
  258.  
  259. # where to look for the library files:
  260. TCLIB = c:\turboc
  261.  
  262. # The target file is adc.exe, and it depends on the .obj files.
  263. # The second line tells how to make the target from the .obj files
  264.  
  265. adc.exe : adc.obj driver.obj
  266.         tcc -oadc.exe -L$(TCLIB) adc.obj driver.obj
  267.  
  268. # The .obj files depend on the .c files.  In the case of adc.c, we
  269. # need to invoke the compiler with the in-line assembly option
  270.  
  271. adc.obj : adc.c
  272.         tcc -c -B adc.c
  273.  
  274. driver.obj : driver.c
  275.         tcc -c -I$(TCLIB) driver.c
  276.  
  277.