/////////////////////////////////////////////////////////////////////////
////                            EX_CRC.C                             ////
////                                                                 ////
////  This example program shows how to send messages between two    ////
////  PICs using CRC error checking.  Pushing a button on the        ////
////  prototype card sends a message from that PIC to any other PICs ////
////  that are connected.  The receiving PIC then sends back an ACK  ////
////  after it correctly receives the message.                       ////
////                                                                 ////
////  Two seperate PICs are needed for this example.  Compile the    ////
////  code as is and program the first PIC.  Then switch the         ////
////  MACHINE_ADDRESS and SEND_ADDRESS and program the second PIC.   ////
////                                                                 ////
////                                                                 ////
////  Packet Protocol:                                               ////
////  +------------+--------------+---------+--------+------+-----+  ////
////  | Address TO | Address FROM | Control | Length | Data | CRC |  ////
////  +------------+--------------+---------+--------+------+-----+  ////
////                                                                 ////
////  Address TO:    1 byte         Address of sending PIC           ////
////  Address FROM:  1 byte         Address of receiving PIC         ////
////  Control:       1 byte         Used for ACK and NACK            ////
////  Length:        2 bytes        Number of bytes in Data field    ////
////  Data:          0 to N bytes   Data being sent                  ////
////  CRC:           2 bytes        16 Bit CRC                       ////
////                                                                 ////
////  Configure the CCS prototype card as follows:                   ////
////     Jumper from PIC 1 pin B0 to PIC 2 pin B1                    ////
////     Jumper from PIC 1 pin B1 to PIC 2 pin B0                    ////
////     Jumper from PIC 1 GND to PIC 2 GND                          ////
////     Jumper from PIC 1 Switch to PIC 1 pin B2                    ////
////     Jumper from PIC 2 Switch to PIC 2 pin B2                    ////
////     See additional connections below.                           ////
////                                                                 ////
////  This example will work with the PCM and PCH compilers.  The    ////
////  following conditional compilation lines are used to include a  ////
////  valid device for each compiler.  Change the device, clock and  ////
////  RS232 pins for your hardware if needed.                        ////
////                                                                 ////
/////////////////////////////////////////////////////////////////////////
////        (C) Copyright 1996,2003 Custom Computer Services         ////
//// This source code may only be used by licensed users of the CCS  ////
//// C compiler.  This source code may only be distributed to other  ////
//// licensed users of the CCS C compiler.  No other use,            ////
//// reproduction or distribution is permitted without written       ////
//// permission.  Derivative programs created using this software    ////
//// in object code form are not restricted in any way.              ////
/////////////////////////////////////////////////////////////////////////


#if defined(__PCM__)
#include <16F877.H>
#device *=16
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_B1, rcv=PIN_B0)

#elif defined(__PCH__)
#include <18F452.H>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_B1, rcv=PIN_B0)
#endif

#include <crc.c>

// CONSTANTS
#define MACHINE_ADDRESS 0x01
#define SEND_ADDRESS    0x02
#define ACK             0x01
#define NACK            0xFF
#define BUFFER_SIZE     64

// GLOBAL VARIABLES
int packet_buffer[BUFFER_SIZE];
int ext_buffer[BUFFER_SIZE];
int ext_buffer_next_in;
int ext_buffer_next_out;

#define MESSAGE_SEND    (!input(PIN_B2))
#define DATA_IN         (ext_buffer_next_in != ext_buffer_next_out)


// EXTERNAL INTERRUPT
// function for reading in bytes from other PIC
#INT_EXT
void ext_isr()
{
   ext_buffer[ext_buffer_next_in] = getc();     // get a byte, put it in buffer

   if(++ext_buffer_next_in == BUFFER_SIZE)      // increment counter
      ext_buffer_next_in = 0;
}

// GET_BUFF_INT
// function to extract bytes from the buffer
int get_buff_int()
{
   int retval;

   while(!DATA_IN);                             // wait until data available

   retval = ext_buffer[ext_buffer_next_out];    // get the byte
   if(++ext_buffer_next_out == BUFFER_SIZE)     // increment counter
      ext_buffer_next_out = 0;

   return retval;
}

// SEND_PACKET
// function to send a packet of data to another PIC
void send_packet(int* packet_ptr, int16 packet_length)
{
   int *ptr;
   int16 CRC,i;

   ptr = packet_ptr;                            // set pointer

   CRC = generate_16bit_crc(ptr, packet_length, CRC_CCITT);
                                                // make CRC
   for(i=0; i<packet_length; i++)               // send packet
      putc(packet_ptr[i]);

   putc((int)(CRC>>8));                         // send CRC
   putc((int)(CRC));
}

// GET_PACKET
// function to get a packet from the buffer and read the data
short get_packet(int* packet_ptr)
{
   short retval;
   int16 length;
   int16 CRC;
   int16 i;

   retval = TRUE;

   packet_ptr[0] = get_buff_int();              // get the address of send to
   packet_ptr[1] = get_buff_int();              // get the address of send from

   if(packet_ptr[0] != MACHINE_ADDRESS)
      retval = FALSE;

   packet_ptr[2] = get_buff_int();              // get the control byte
   if(packet_ptr[2] == NACK)
      retval = FALSE;

   packet_ptr[3] = get_buff_int();              // get the length of the data
   packet_ptr[4] = get_buff_int();

   length = (int16)(packet_ptr[3])<<8;
   length += packet_ptr[4];

   for(i=5; i<(length+5); i++)                  // get the data
      packet_ptr[i] = get_buff_int();

   packet_ptr[length+5] = get_buff_int();       // get the CRC
   packet_ptr[length+6] = get_buff_int();

   CRC = (int16)(packet_ptr[length+5])<<8;
   CRC += packet_ptr[length+6];

   if(CRC != generate_16bit_crc(packet_ptr, length+5, CRC_CCITT))
      retval = FALSE;

   return retval;
}

// Change RS-232 IO pins
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)  // Jumpers: 8 to 11, 6 to 14

void main()   {

   ext_buffer_next_in = 0;                      // init variables
   ext_buffer_next_out = 0;

   ext_int_edge(H_TO_L);                        // init interrupts
   enable_interrupts(INT_EXT);
   enable_interrupts(GLOBAL);

   while(TRUE)                                  // loop always
   {
      if(MESSAGE_SEND)                          // if button pushed
      {
         packet_buffer[0] = SEND_ADDRESS;
         packet_buffer[1] = MACHINE_ADDRESS;
         packet_buffer[2] = 0;
         packet_buffer[3] = 0;
         packet_buffer[4] = 9;
         packet_buffer[5] = 'H';
         packet_buffer[6] = 'i';
         packet_buffer[7] = ' ';
         packet_buffer[8] = 't';
         packet_buffer[9] = 'h';
         packet_buffer[10] = 'e';
         packet_buffer[11] = 'r';
         packet_buffer[12] = 'e';
         packet_buffer[13] = '!';
         send_packet(packet_buffer, 14);        // send message
      }

      delay_ms(100);

      if(!DATA_IN)                              // if no data in
         continue;                              // loop back

      if(get_packet(packet_buffer))             // if valid packet
      {
         int16 length,i;
         // int16 CRC;

         printf("Message from unit# %U\r\n",packet_buffer[1]);

         length = ((int16)(packet_buffer[3]<<8)) + packet_buffer[4];

         if(packet_buffer[2] == ACK)
            printf("Previous message sent was received by unit.\r\n");

         if(length)                             // display message
         {
            printf("Message is:\r\n\n");
            for(i=0; i<length; ++i)
               putc(packet_buffer[i+5]);
         }
         printf("\r\n\n... Message End ...\r\n\n\n");

         if(length)
         {
            packet_buffer[0] = packet_buffer[1];//send an ACK
            packet_buffer[1] = MACHINE_ADDRESS;
            packet_buffer[2] = ACK;
            packet_buffer[3] = 0;
            packet_buffer[4] = 0;
            send_packet(packet_buffer, 5);
         }
      }
      else                                      // if not valid packet
      {
         if(packet_buffer[0] != MACHINE_ADDRESS)
            break;                              // message is not for this PIC
         else if(packet_buffer[2] == NACK)      // tried to send and failed error
            printf("Previous message sent was not received by unit.\r\n");
         else
         {                                      // tried to receive and failed error
            printf("Message received was corrupted.\r\n");

            packet_buffer[0] = packet_buffer[1];//send a NACK
            packet_buffer[1] = MACHINE_ADDRESS;
            packet_buffer[2] = NACK;
            packet_buffer[3] = 0;
            packet_buffer[4] = 0;
            send_packet(packet_buffer, 5);
         }
      }
   }
}
