///////////////////////////////////////////////////////////////////////////
////              Library for a DS1933 SRAM                            ////
////                                                                   ////
////   data = read_scratchpad(addr, ES)                                ////
////     Used to verify scratchpad data and target address             ////
////                                                                   ////
////   write_scratchpad(addr, data)                                    ////
////     Used to write data to scratchpad                              ////
////                                                                   ////
////   copy_scratchpad(addr, ES)                                       ////
////     Used to copy data from scratchpad to memory                   ////
////                                                                   ////
////   read_entire_memory(mem)                                         ////
////     used to read the entire 512 bytes of memory                   ////
////                                                                   ////
////   data = read_memory(addr)                                        ////
////     Used to read a place in memory                                ////
////                                                                   ////
////   block_fill(from, to, filler)                                    ////
////     Used to block fill a specified amount of memory               ////
////                                                                   ////
////   match_block_fill(from, to, filler)                              ////
////     Used to block fill a specified amount of memory               ////
////     from a specific ROM                                           ////
////                                                                   ////
////   Following functions are used to access the 64 bit lasered ROM   ////
////                                                                   ////
////   read_rom_specs(family, ID, CRC)                                 ////
////     Seperates different data from the rom                         ////
////                                                                   ////
////   read_rom(data)                                                  ////
////     Reads in the entire rom                                       ////
////                                                                   ////
////   search_rom()                                                    ////
////     Identify the connected device                                 ////
////                                                                   ////
////   match_rom(romdata)                                              ////
////     Device with correct 64-bit sequence will respond              ////
////                                                                   ////
////   skip_rom()                                                      ////
////     To save time on single drop bus system                        ////
////                                                                   ////
///////////////////////////////////////////////////////////////////////////
////        (C) Copyright 1996,2006 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.                               ////
///////////////////////////////////////////////////////////////////////////

#ifndef  DS1993_C
#define  DS1993_C

#ifndef TOUCH_PIN
   #define TOUCH_PIN  PIN_B0
#endif

#include <touch.c>


/////////////////////////////
////                     ////
//// Function Prototypes ////
////                     ////
/////////////////////////////

/*
BYTE read_scratchpad(int16 addr, BYTE* ES)
This will read scratchpad data from a specified address
PARAM addr: The address to read from
NOTE: In order to copy data from the scratchpad, the ES byte must be known
RETURNS: The data in the address of scratchpad
*/
BYTE read_scratchpad(int16 addr, BYTE* ES);

/*
void write_scratchpad(int16 addr, BYTE data)
This will write a byte of data to the scratchpad at a specified location
PARAM addr: The address to write to
PARAM data: The byte to write to the specified location
RETURNS: none
*/
void write_scratchpad(int16 addr, BYTE data);

/*
void copy_scratchpad(int16 addr, BYTE ES)
This will copy the contents of the scratchpad to the specified location
PARAM addr: The address to copy to
PARAM ES: The E/S byte to use
NOTE: All parameters passed in must match the DS1993's 3 address registers
*/
void copy_scratchpad(int16 addr, BYTE ES);

/*
void read_entire_memory(BYTE mem[])
This will read all 512 bytes of a DS1993 into a 512 byte array on the PIC
PARAM: A pointer a variable to put the data into
NOTE: mem must be pointing to the beginning of a 512 wide array
RETURNS: none
*/
void read_entire_memory(BYTE mem[]);

/*
BYTE read_memory(int16 addr)
This will read one byte from memory at a specified address
PARAM: The address to read from
RETURNS: The data at the address
*/
BYTE read_memory(int16 addr);

/*
void block_fill(int16 from, int16 to, BYTE filler)
This will block fill a chunk of memory
PARAM: Where to start the block fill
PARAM: Where to stop the block fill, including the address
PARAM: The byte to fill the block with
RETURNS: none
*/
void block_fill(int16 from, int16 to, BYTE filler);

/*
void match_block_fill(BYTE rom[], int16 from, int16 to, BYTE filler)
This will block fill a chunk of memory at a specific DS1993
PARAM: A pointer to the beginning of the rom data to test
PARAM: Where to start the block fill
PARAM: Where to stop the block fill, including the address
PARAM: The byte to fill the block with
NOTE: rom must be pointing to the beginning of a 8-wide array
RETURNS: none
*/
void match_block_fill(BYTE rom[], int16 from, int16 to, BYTE filler);

/*
void read_rom_specs(BYTE* family, BYTE ID[], BYTE* CRC)
This will take the 64-bit lasered ROM and put it into seperate variables.
  The DS1993 will then be ready to excecute a memory function.
PARAMS: Pointers to the variables to put the various data into
NOTE: ID must be pointing to the beginning a 6 wide array
RETURNS: none
*/
void read_rom_specs(BYTE* family, BYTE ID[], BYTE* CRC);

/*
void read_rom(BYTE data[])
This will take the 64-bit lasered ROM and put it into a variable. The DS1993
  will then be ready to excecute a memory function.
PARAM: A pointer to the variable to put the rom data into
NOTE: data must be pointing to the beginning of a 8-wide array.
NOTE: the LSB in the ROM (family) will start at index 0,
  the MSB in the rom (CRC) will be at index 7.
RETURNS: none
*/
void read_rom(BYTE data[]);

/*
void match_rom(BYTE romdata[])
This will send the match ROM command to the DS1993.
  If the rom data sent into the function equals the rom data on the
  DS1993, then the DS1993 will be ready for a memory function, otherwise
  the DS1993 will wait for a reset pulse to begin a rom function.
PARAM: A pointer to the beginning of the rom data to test
NOTE: romdata must be pointing to the beginning of a 8-wide array
RETURNS: none
*/
void match_rom(BYTE romdata[]);

/*
void search_rom()
This will print to the console all of the contents of all of the
  ROMs that are connected to the one-wire bus
PARAM: none
RETURNS: none
*/
void search_rom();

/*
void skip_rom()
This will save time on single drop bus system by bypassing any rom function
  and heading straight to a memory function
PARAMS: none
RETURNS: none
*/
void skip_rom();

//////////////////////////////////
////                          ////
//// Function Implementations ////
////                          ////
//////////////////////////////////

/*
BYTE read_scratchpad(int16 addr, BYTE* ES)
This will read scratchpad data from a specified address
PARAM addr: The address to read from
NOTE: In order to copy data from the scratchpad, the ES byte must be known
RETURNS: The data in the address of scratchpad
*/
BYTE read_scratchpad(int16 addr, BYTE* ES)
{
   touch_write_byte(0xAA);       // read scratchpad command
   touch_read_byte();            // read TA1
   touch_read_byte();            // read TA2
   *ES = touch_read_byte();      // read E/S
   return touch_read_byte();     // read the result
}

/*
void write_scratchpad(int16 addr, BYTE data)
This will write a byte of data to the scratchpad at a specified location
PARAM addr: The address to write to
PARAM data: The byte to write to the specified location
RETURNS: none
*/
void write_scratchpad(int16 addr, BYTE data)
{
   touch_write_byte(0x0F);       // write scratchpad command
   touch_write_byte(addr);       // write TA1
   touch_write_byte(addr >> 8);  // write TA2
   touch_write_byte(data);       // write data
}

/*
void copy_scratchpad(int16 addr, BYTE ES)
This will copy the contents of the scratchpad to the specified location
PARAM addr: The address to copy to
PARAM ES: The E/S byte to use
NOTE: All parameters passed in must match the DS1993's 3 address registers
*/
void copy_scratchpad(int16 addr, BYTE ES)
{
   touch_write_byte(0x55);       // copy scratchpad command
   touch_write_byte(addr);       // write TA1
   touch_write_byte(addr >> 8);  // write TA2
   touch_write_byte(ES);         // write E/S
}

/*
void read_entire_memory(BYTE mem[])
This will read all 512 bytes of a DS1993 into a 512 byte array on the PIC
PARAM: A pointer a variable to put the data into
NOTE: mem must be pointing to the beginning of a 512 wide array
RETURNS: none
*/
void read_entire_memory(BYTE mem[])
{
   int16 i;

   touch_write_byte(0xF0);       // read memory command
   touch_write_byte(0x00);       // start at 0x00...
   touch_write_byte(0x00);       // ... on page 0
   for(i = 0; i < 512; i++)
      mem[i] = touch_read_byte();
}

/*
BYTE read_memory(int16 addr)
This will read one byte from memory at a specified address
PARAM: The address to read from
RETURNS: The data at the address
*/
BYTE read_memory(int16 addr)
{
   touch_write_byte(0xF0);       // read memory command
   touch_write_byte(addr);       // write TA1
   touch_write_byte(addr >> 8);  // write TA2
   return touch_read_byte();     // read in result
}

/*
void block_fill(int16 from, int16 to, BYTE filler)
This will block fill a chunk of memory
PARAM: Where to start the block fill
PARAM: Where to stop the block fill, including the address
PARAM: The byte to fill the block with
RETURNS: none
*/
void block_fill(int16 from, int16 to, BYTE filler)
{
   int16 i;
   BYTE ES, scratchpad_data;

   if(to < from)
   {
      printf("\r\nError: End of block before beginning of block.\r\nStopping block fill...");
      return;
   }

   for(i = from; i <= to; i++)
   {
      // write some data to the scratchpad
      reset_pulse();
      skip_rom();
      write_scratchpad(i, filler);

      // read back that data
      reset_pulse();
      skip_rom();
      scratchpad_data = read_scratchpad(i, &ES);

      // copy that data
      reset_pulse();
      skip_rom();
      copy_scratchpad(i, ES);
   }
}

/*
void match_block_fill(BYTE rom[], int16 from, int16 to, BYTE filler)
This will block fill a chunk of memory at a specific DS1993
PARAM: A pointer to the beginning of the rom data to test
PARAM: Where to start the block fill
PARAM: Where to stop the block fill, including the address
PARAM: The byte to fill the block with
NOTE: rom must be pointing to the beginning of a 8-wide array
RETURNS: none
*/
void match_block_fill(BYTE rom[], int16 from, int16 to, BYTE filler)
{
   int16 i;
   BYTE ES, scratchpad_data;

   if(to < from)
   {
      printf("\r\nError: End of block before beginning of block.\r\nStopping block fill...");
      return;
   }

   for(i = from; i <= to; i++)
   {
      // write some data to the scratchpad
      reset_pulse();
      match_rom(rom);
      write_scratchpad(i, filler);

      // read back that data
      reset_pulse();
      match_rom(rom);
      scratchpad_data = read_scratchpad(i, &ES);

      // copy that data
      reset_pulse();
      match_rom(rom);
      copy_scratchpad(i, ES);
   }
}

/*
void read_rom_specs(BYTE* family, BYTE ID[], BYTE* CRC)
This will take the 64-bit lasered ROM and put it into seperate variables.
  The DS1993 will then be ready to excecute a memory function.
PARAMS: Pointers to the variables to put the various data into
NOTE: ID must be pointing to the beginning a 6 wide array
RETURNS: none
*/
void read_rom_specs(BYTE* family, BYTE ID[], BYTE* CRC)
{
   BYTE i;

   // give the command to read back the requested data
   touch_write_byte(0x33);

   // family is the first item in the array
   *family = touch_read_byte();

   // the ID is the next 6 bytes
   for(i = 0; i < 6; i++)
      ID[i] = touch_read_byte();

   // CRc is the last byte
   *CRC = touch_read_byte();
}

/*
void read_rom(BYTE data[])
This will take the 64-bit lasered ROM and put it into a variable. The DS1993
  will then be ready to excecute a memory function.
PARAM: A pointer to the variable to put the rom data into
NOTE: data must be pointing to the beginning of a 8-wide array.
NOTE: the LSB in the ROM (family) will start at index 0,
  the MSB in the rom (CRC) will be at index 7.
RETURNS: none
*/
void read_rom(BYTE data[])
{
   BYTE i;

   // give the command to read back the requested data
   touch_write_byte(0x33);

   // fill up data
   for(i = 0; i < 8; i++)
      data[i] = touch_read_byte();
}

/*
void match_rom(BYTE romdata[])
This will send the match ROM command to the DS1993.
  If the rom data sent into the function equals the rom data on the
  DS1993, then the DS1993 will be ready for a memory function, otherwise
  the DS1993 will wait for a reset pulse to begin a rom function.
PARAM: A pointer to the beginning of the rom data to test
NOTE: romdata must be pointing to the beginning of a 8-wide array
RETURNS: none
*/
void match_rom(BYTE romdata[])
{
   BYTE romdatacopy[8], i;

   // make a copy of romdata, we don't necessarily want to modify the original data
   for(i = 0; i < 8; i++)
      romdatacopy[i] = romdata[i];

   // give the command to match ROM
   touch_write_byte(0x55);

   // start trying to match
   for(i = 0; i < 64; i++)
      touch_write_bit(shift_right(romdatacopy, 8, 0));
}

/*
void search_rom()
This will print to the console all of the contents of all of the
ROMs that are connected to the one-wire bus
PARAM: none
RETURNS: none
*/
void search_rom()
{
   /*
   Please read p51-p54 in the "standard" data sheet for the DS1993 for a basic algorithm explanation.
   At the time of this writing, this data sheet could be found at
   www.maxim-ic.com/products/ibutton/ibuttons/standard.pdf
   */
   BYTE
      i,                               // used for loops
      last_desc_pos = 0,               // the position of the last discrepancy in the current iteration
      desc_marker = 0,                 // the position of the discrepancy of last iteration
      contents[8] = {0,0,0,0,0,0,0,0}, // the contents of the ROM in the current iteration
      firstromcont[8];                 // the contents of the first ROM read in

   int1
      bitA,                            // the bit read in from the current index of all of the rom(s)
      bitB,                            // the complement of the bit read in from the current index of all of the rom(s)
      last_desc = 0;                   // the bit to shift into contents[]

   BOOLEAN
      firstrom = true,
      done = false;

   // printf nice header
   printf("\r\n---ROMs found---\r\n");

   do
   {
      // set the last discrepancy position to the discrepancy marker from the last iteration
      last_desc_pos = desc_marker;

      // search rom command
      reset_pulse();
      touch_write_byte(0xF0);

      // read in one entire rom
      for(i = 0; i < 64; i++)
      {
         bitA = touch_read_bit(); // read one bit from rom(s)
         bitB = touch_read_bit(); // read complement of previous bit from rom(s)

         if((!bitA) && (!bitB))
         {
            // at this point we know that we don't know a bit
            // set the discrepancy marker to the current index
            desc_marker = i;

            // check to see if the current index is where the last discrepancy was
            if(i == last_desc_pos)
               // if so, flip the bit that gets shifted in
               last_desc = !last_desc;

            // shift in a bit
            shift_right(contents, 8, last_desc);
            touch_write_bit(last_desc);
         }

         else
         {
            // at this point we know that we know a bit
            // shift in bitA because that is the known bit
            shift_right(contents, 8, bitA);
            touch_write_bit(bitA);
         }
      }

      // because of this weird algorithm, we need to keep track of the first rom;
      //   eventually the algorithm will loop back to the first rom and we'll know we're done
      if(firstrom)
      {
         for(i = 0; i < 8; i++)
            firstromcont[i] = contents[i];
         firstrom = false;
      }

      else
      {
         // print off what we found
         for(i = 0; i < 8; i++)
            printf("%X", contents[7 - i]);
         printf("\r\n");

         done = true;
         for(i = 0; i < 8; i++)
            if(firstromcont[i] != contents[i])
               done = false;
      }
   } while(!done);
}

/*
void skip_rom()
This will save time on single drop bus system by bypassing any rom function
  and heading straight to a memory function
PARAMS: none
RETURNS: none
*/
void skip_rom()
{
   // give the command to skip ROM
   touch_write_byte(0xCC);
}

#endif
