// DS18B20_1.C - Sourceboost C
//
// copyright, P H Anderson, July 6, '09

#include <system.h>
#include <icd2.h>
#include <string.h>
#include <stdio.h>

#pragma DATA 0x2007, 0x28e4
#pragma DATA 0x2008, 0x3eff
#pragma CLOCK_FREQ 4000000 

#define NEGATIVE 1
#define POSITIVE 0

#define TRUE !0
#define FALSE 0

typedef unsigned char byte;

// 1-wire prototypes
byte _1w_init(void);
byte _1w_read_bit(void);
void _1w_write_bit(byte d);
byte _1w_in_byte(void);
void _1w_out_byte(byte d, byte strong);
void _1w_pin_hi(void);
void _1w_pin_low(void);

byte calc_crc(byte *buff, byte num_vals);

void print_str(char *s);
void print_float(byte sign_bit, byte whole, byte fract);
void asynch_setup(void);

rom  byte _0_mask[8] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};
rom  byte _1_mask[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};

byte channel;

void main()
{
    unsigned int T_reading;
    int Tc_100;
    byte a, n, whole, fract,sign_bit, crc, d[9];
    char s[25];

    osccon = 0x61;
    anselh = 0x00; // turn off highest six A/Ds

    trisb = 0xff;
    portb = 0x00;

    asynch_setup();

    while(1)
    {
        channel = 1; // globally defined to avoid having to pass
                     // to every function
        _1w_init();
        _1w_out_byte(0xcc, 0); // skip ROM
        _1w_out_byte(0x44, 1); // perform temperature meas
                               // followed by strong pullup

        delay_s(1);

        _1w_init();
        _1w_out_byte(0xcc, 0); // skip ROM
        _1w_out_byte(0xbe, 0); // send temperature data

        for (n=0; n<9; n++)
        {
            d[n] = _1w_in_byte();
        }

        crc =  calc_crc(d, 9);   // check CRC

        if (crc != 0) // failure
        {
            strcpy(s, "-88.88?n");
            print_str(s);
        }

        else
        {

           T_reading = d[1]; // high byte
           T_reading = (T_reading << 8) | d[0]; // a[0] is low byte

           if (T_reading & 0x8000)
           {
               sign_bit = NEGATIVE;
               T_reading = (T_reading ^ 0xffff) + 1; // 2s comp
           }
           else
           {
               sign_bit = POSITIVE;
           }

           Tc_100 = (6 * T_reading) + (T_reading / 4); // multiple by 6.25

           whole = Tc_100 / 100;
           fract = Tc_100 % 100;

           print_float(sign_bit, whole, fract);
       }
       delay_s(2);
    }
}

byte calc_crc(byte *buff, byte num_vals)
// see http://www.phanderson.com/PIC for an explanation
{
   byte shift_reg=0, data_bit, sr_lsb, fb_bit, i, j, sum=0;

   for (i=0; i<num_vals; i++)
   {
      sum = sum + buff[i];
   }

   if (sum == 0)
   {
      return(1);
   }

   for (i=0; i<num_vals; i++) // for each byte
   {
      for(j=0; j<8; j++)   // for each bit
      {
         data_bit = (buff[i]>>j)&0x01;
         sr_lsb = shift_reg & 0x01;
         fb_bit = (data_bit ^ sr_lsb) & 0x01;
         shift_reg = shift_reg >> 1;
         if (fb_bit)
         {
            shift_reg = shift_reg ^ 0x8c;
         }
      }
   }
   return(shift_reg);
}

void asynch_setup(void)
{   // for UART, see Manual pages beginning at page 151
    trisc.7 = 1;
    trisc.6 = 1;    // TX
    rcsta.SPEN = 1; // enable serial port
    txsta.TXEN = 1; // enable transmitter
    txsta.SYNC = 0; // asynchronous
    txsta.BRGH = 1;

    spbrg = 25; // 9600 baud at 4.0 MHz
}

void print_float(byte sign_bit, byte whole, byte fract)
{
    char s[20];
    if (sign_bit == NEGATIVE)
    {
        strcpy(s, "-");
        print_str(s);
    }

    sprintf(s, "%u", whole);
    print_str(s);
    strcpy(s, ".");
    print_str(s);
    sprintf(s, "%02u", fract);
    print_str(s);
    strcpy(s, "?n");
    print_str(s);
}

void print_str(char *s)
{
    while(*s != '\0')
    {
        txreg = *s;
        while(txsta.TRMT == 0)
        {
        }
        ++s;
    }
}


// The following are standard 1-Wire routines.
byte _1w_init(void)
{
   int n = 500;
   asm
   {
      CLRWDT
   }
   _1w_pin_hi();
   _1w_pin_low();
   delay_10us(50);

   _1w_pin_hi();
   while(--n)
   {
      if (((portb >> channel) &0x01) == 0)
      {
         delay_10us(50);
         return(TRUE);
      }
   }
   delay_10us(50);
   return(FALSE);
}

byte _1w_read_bit(void)
{
   byte temp, _0, _1, n;
   n = 3; // possibly adjust this
   _0 = _0_mask[channel];
   _1 = _1_mask[channel];

   portb &= _0;   // bring bit to zero
   trisb &= _0;   // output
   asm
   {
       NOP
       NOP
   }
   trisb |= _1;   // back to one
   while(--n)    ;   // loop delay
   temp = portb;  // read state 10 usec later
   delay_10us(6);
   return((temp >> channel)&0x01);
}

void _1w_write_bit(byte d)
{
   byte temp, a, _0, _1;

   _0 = _0_mask[channel];
   _1 = _1_mask[channel];

   if (d&0x01)    // wink low and then back to high Z
   {
        portb &= _0;
        trisb &= _0;
        asm
        {
           NOP
           NOP
           NOP
           NOP
           NOP
        }
        // asm delay_2us(2);
        trisb |= _1;
        delay_10us(6);

   }
   else        // bring low for 60 usecs
   {
        portb &= _0;
        trisb &= _0;
        delay_10us(6);
        trisb |= _1;
   }
}

byte _1w_in_byte(void)  // read a byte from DS18B20
{
   byte n, i_byte = 0x00, b;

   for (n=0; n<8; n++)
   {
      b = _1w_read_bit();
      if (b & 0x01)
      {
        i_byte=(i_byte>>1) | 0x80;  // least sig bit first
      }
      else
      {
        i_byte=i_byte >> 1;
      }
      delay_10us(6);
   }
   return(i_byte);
}

void _1w_out_byte(byte d, byte strong)  // output a byte to DS1820
{
   byte a, b, n, _0, _1;

   _0 = _0_mask[channel];
   _1 = _1_mask[channel];

   for(n=0; n<8; n++)
   {
      _1w_write_bit(d&0x01);

      d=d>>1;
   }
   if (strong)
   {
       porttb |= _1;
       trisb &= _0;
       delay_s(1);
       trisb |= _1;
       portb &= _0;
   }
}


void _1w_pin_hi(void)     // high Z
{
   byte _1 = _1_mask[channel];
   trisb |= _1;
}

void _1w_pin_low(void)    // hard logic zero
{
   byte _0;
   _0 = _0_mask[channel];
   portb &=  _0;
   trisb &=  _0;
}