/****************************************************************************
ADC3.C

ADC using PIC 16F873.  It's input range is 0-5V.

It is controlled by three bytes sent via RS232.
   * The first byte is '$'.
   * The second byte is the unit address and ADC channel select,
     configured as:    XX A4 A3 A2 A1 A0 C1 C0(lsb)
   * The third byte is the digital value to be sent out Port C,
     configured as:    D7 D6 D5 D4 D3 D2 D1 D0(lsb)
If the second and third bytes are not each received within one second,
the software resets to look for the '$' again.

Upon receiving the three bytes, the digital value is sent out Port C,
a conversion is done and the digital equivalent returned at 9600 baud
as one to four characters (0-1023) followed by a trailing CR/LF.  If
leading zeros are desired so that four characters are always received,
tie NO ZEROS high or let it float.

The LED flashes when the chip is selected.

The serial Tx output floats when high, and is 0V when low.  This allows
paralleling several of these units on the same Rx and Tx line on the TTL
side of the MAX235 RS232 converter. (The Tx line needs a pullup resistor.)

                   ---------
           +5--20-|Vdd      |
           +5---1-|Mclr   B6|-27----LED
          Gnd---8-|Vss      |
          Gnd--19-|Vss      |
         4MHz--10-|Xtal     |                          --------
             ---9-|Xtal     |                  Gnd-20-|EN      |
                  |         |                  Gnd-21-|SD      |
                  |         |                  Gnd-11-|Vss     |
                  | 16F873  |                   +5-12-|Vcc     |
                  |         |                         |        |
   ADC CH O ->--2-|A0     A4|-6---<----9600 baud----9-|OUT   IN|-10-<-Rx
   ADC CH 1 ->--3-|A1     A5|-7--->--*-9600 baud----7-|IN   OUT|-4-->-Tx
   ADC CH 2 ->--5-|A3       |        |                |        |
                  |         |   from other units      | MAX235 |
   DIGOUT 7 -<-18-|C7       |                          --------
   DIGOUT 6 -<-17-|C6       |         -----
   DIGOUT 5 -<-16-|C5     B4|-25--<--|     |
   DIGOUT 4 -<-15-|C4     B3|-24--<--| ADR |
   DIGOUT 3 -<-14-|C3     B2|-23--<--| DIP |--Gnd
   DIGOUT 2 -<-13-|C2     B1|-22--<--| SW. |
   DIGOUT 1 -<-12-|C1     B0|-21--<--|     |
   DIGOUT 0 -<-11-|C0       |        |     |
                  |         |         -----
                  |         |
  -NO ZEROS ->-26-|B5       |
                   ---------

External crystal oscillator = 4MHz
Cycle time = 1uS

Jon Fick  03/29/99

***************************************************************************/

#include < 16F873.h >
#list

/* Set configuration bits in the PIC processor */
#fuses XT, NOPROTECT, NOPUT, NOWDT, NOBROWNOUT, NOLVP, NOCPD, NOWRT

#use delay (clock=4000000)      /* sets appropriate compiler constants */
#use standard_io ( a )
#use standard_io ( b )
#use fast_io ( C )
#use rs232 ( baud=9600, XMIT=PIN_A5, RCV=PIN_A4, FLOAT_HIGH )

#define PORT_B          6
#define PORT_C          7
#define NO_ZEROS        PIN_B5
#define LED             PIN_B6
#define TIMEOUT_VALUE   15

#byte   DIGOUT = PORT_C
#byte   BIT_SWITCH = PORT_B

void BlinkLed ( void );

static char cTimeoutCounter, cError;

void main( void )
    {
    char cAdcVal, cDigVal, cChannel, cAddress;

    set_tris_c ( 0b00000000 );        /* make all outputs */
    setup_counters ( RTCC_INTERNAL, RTCC_DIV_256 );
    setup_adc_ports ( RA0_RA1_RA3_ANALOG );
    setup_adc ( ADC_CLOCK_INTERNAL );
    port_b_pullups ( TRUE );
    enable_interrupts ( INT_RTCC );                /* enable timer interrupt */
    enable_interrupts ( GLOBAL );                  /* enable global interrupts */
    cError = NO;
    BlinkLed();                                /* blink on initialization */

    while ( TRUE )                             /* do forever */
        {
        while ( getc() != '$' );        /* wait until '$' leader comes along */

        /* PREPARE FOR SECOND BYTE */
        cTimeoutCounter = 0;            /* restart timeout counter */
        cError = NO;                    /* default */
        while ( kbhit() == FALSE )      /* wait for start bit */
            {
            if ( cError == YES )        /* if timed out */
                {
                break;
                }
            }
        if ( cError == NO )             /* if not timed out yet */
            {
            cAdcVal = getc();           /* get second byte from serial */

            /* PREPARE FOR THIRD BYTE */
            cTimeoutCounter = 0;            /* restart timeout counter */
            cError = NO;                    /* default */
            while ( kbhit() == FALSE )      /* wait for start bit */
                {
                if ( cError == YES )        /* if timed out */
                    {
                    break;
                    }
                }
            if ( cError == NO )         /* if not timed out yet */
                {
                cDigVal = getc();       /* get third byte from serial */

                /* ALL THREE BYTES RECEIVED SUCCESSFULLY */
                cChannel = cAdcVal & 0x03;              /* get channel select bits 0 & 1 */
                cAddress = ( cAdcVal >> 2 ) & 0x1F;     /* get address bits 2-6 */

                if ( cAddress == ( ~BIT_SWITCH & 0x1F ) )    /* if address matches bit switches */
                    {
                    /* SET THE DIGITAL OUTS, CHANNEL, AND RETURN ADC MEASUREMENT */
                    DIGOUT = cDigVal;                /* send to Port C */
                    switch ( cChannel )              /* set ADC channel */
                        {
                        case 0x00:
                        case 0x03:
                            {
                            set_adc_channel ( 0 );
                            break;
                            }
                            case 0x01:
                            {
                            set_adc_channel ( 1 );
                            break;
                            }
                        case 0x02:
                            {
                            set_adc_channel ( 3 );
                            break;
                            }
                        }
                        delay_us ( 25 );         /* wait the "delay aquisition time" after setting channel (refer to data sheet) */
                    if ( input ( NO_ZEROS ) == HIGH )
                        {
                        printf ( "%04lu", read_adc() );    /* read the ADC and transmit value with leading zeros */
                        }
                    else
                        {
                        printf ( "%lu", read_adc() );    /* read the ADC and transmit value */
                        }
                    printf ( "\r\n" );            /* CR/LF */
                    BlinkLed();                   /* signal done */
                    }
                }
            }
        }
    }

void BlinkLed ( void )
    {
    output_high ( LED );
    delay_ms ( 10 );
    output_low ( LED );
    }

/*****************************************************************************************************/
#INT_RTCC void TIMER_INTERRUPT ( void )
    {
    /* This interrupt occurs every 65mS. */
    if ( cTimeoutCounter > TIMEOUT_VALUE )    /* has it counted to timeout yet? */
        {
        cError = YES;               /* timed out, signal the event */
        }
    else
        {
        cTimeoutCounter++;          /* if not, increment the counter */
        }
    }