   /*** FILEHEADER ****************************************************************
 *
 *    FILENAME:    ds1820.h
 *    DATE:        25.02.2005
 *    AUTHOR:      Christian Stadler
 *
 *    DESCRIPTION: Driver for DS1820 1-Wire Temperature sensor (Dallas)
 *
 ******************************************************************************/

/*** HISTORY OF CHANGE *********************************************************
 *
 *    $Log: /pic/_drv/ds1820.h $
 * 
 * 9     13.11.10 20:02 Stadler
 * - changed interrupt lock and delay functions to #defines to remove
 * function call overhead
 * 
 * 8     6.11.10 10:24 Stadler
 * - adjusted 1-wire timing
 * 
 * 7     5.11.10 22:55 Stadler
 * - changed driver API
 * 
 * 6     5.11.10 21:59 Stadler
 * - added DS18B20 support
 * - fixed problem with ROM search algorithm
 * 
 * 5     2.11.10 20:25 Stadler
 * - changed function DS1820_FindFirstDev to DS1820_FindFirstDevice
 * - changed function DS1820_FindNextDev to DS1820_FindNextDevice
 * - updated code style
 * 
 * 4     31.10.10 17:12 Stadler
 * - introduced DS1820_DelayUs
 * 
 * 3     25.10.10 13:09 Stadler
 * - added interrupt lock
 * 
 * 2     12.03.05 11:24 Stadler
 * - added EEPROM write function
 * - added "Search ROM Algorithm" to control multiple devices
 *
 * 1     26.02.05 18:18 Stadler
 * Driver for DS1820 1-Wire Temperature sensor (Dallas)
 *
 ******************************************************************************/

#ifndef _DS1820_H
#define _DS1820_H


/* check configuration of driver */
#ifndef DS1820_DATAPIN
#error DS1820 data pin not defined!
#endif


#define TEMP_RES              0x100 /* temperature resolution => 1/256°C = 0.0039°C */


/* -------------------------------------------------------------------------- */
/*                         DS1820 Timing Parameters                           */
/* -------------------------------------------------------------------------- */

#define DS1820_RST_PULSE       480   /* master reset pulse time in [us] */
#define DS1820_MSTR_BITSTART   2     /* delay time for bit start by master */
#define DS1820_PRESENCE_WAIT   40    /* delay after master reset pulse in [us] */
#define DS1820_PRESENCE_FIN    480   /* dealy after reading of presence pulse [us] */
#define DS1820_BITREAD_DLY     5     /* bit read delay */
#define DS1820_BITWRITE_DLY    100   /* bit write delay */


/* -------------------------------------------------------------------------- */
/*                            DS1820 Registers                                */
/* -------------------------------------------------------------------------- */

#define DS1820_REG_TEMPLSB    0
#define DS1820_REG_TEMPMSB    1
#define DS1820_REG_CNTREMAIN  6
#define DS1820_REG_CNTPERSEC  7
#define DS1820_SCRPADMEM_LEN  9     /* length of scratchpad memory */

#define DS1820_ADDR_LEN       8


/* -------------------------------------------------------------------------- */
/*                            DS1820 Commands                                 */
/* -------------------------------------------------------------------------- */

#define DS1820_CMD_SEARCHROM     0xF0
#define DS1820_CMD_READROM       0x33
#define DS1820_CMD_MATCHROM      0x55
#define DS1820_CMD_SKIPROM       0xCC
#define DS1820_CMD_ALARMSEARCH   0xEC
#define DS1820_CMD_CONVERTTEMP   0x44
#define DS1820_CMD_WRITESCRPAD   0x4E
#define DS1820_CMD_READSCRPAD    0xBE
#define DS1820_CMD_COPYSCRPAD    0x48
#define DS1820_CMD_RECALLEE      0xB8


#define DS1820_FAMILY_CODE_DS18B20      0x28
#define DS1820_FAMILY_CODE_DS18S20      0x10


/* -------------------------------------------------------------------------- */
/*                            static variables                                */
/* -------------------------------------------------------------------------- */

static bool bDoneFlag;
static uint8 nLastDiscrepancy_u8;
static uint8 nRomAddr_au8[DS1820_ADDR_LEN];



/* -------------------------------------------------------------------------- */
/*                           Low-Level Functions                              */
/* -------------------------------------------------------------------------- */


/*******************************************************************************
 * FUNCTION:   DS1820_DelayUs
 * PURPOSE:    Delay for the given number of micro seconds.
 *
 * INPUT:      dly_us      number of micro seconds to delay
 * OUTPUT:     -
 * RETURN:     -
 ******************************************************************************/
#define DS1820_DelayUs(dly_us)       delay_us(dly_us)


/*******************************************************************************
 * FUNCTION:   DS1820_DelayMs
 * PURPOSE:    Delay for the given number of milliseconds.
 *
 * INPUT:      dly_ms      number of milliseconds to delay
 * OUTPUT:     -
 * RETURN:     -
 ******************************************************************************/
#define DS1820_DelayMs(dly_ms)   delay_ms(dly_ms)


/*******************************************************************************
 * FUNCTION:   DS1820_DisableInterrupts
 * PURPOSE:    Disable interrupts
 *
 * INPUT:      -
 * OUTPUT:     -
 * RETURN:     -
 ******************************************************************************/
#ifdef DS1820_INTERRUPT_LOCK
#define DS1820_DisableInterrupts()  disable_interrupts(GLOBAL)
#else
#define DS1820_DisableInterrupts()
#endif



/*******************************************************************************
 * FUNCTION:   DS1820_EnableInterrupts
 * PURPOSE:    Enable interrupts
 *
 * INPUT:      -
 * OUTPUT:     -
 * RETURN:     -
 ******************************************************************************/
#ifdef DS1820_INTERRUPT_LOCK
#define DS1820_EnableInterrupts()   enable_interrupts(GLOBAL)
#else
#define DS1820_EnableInterrupts()
#endif


/*******************************************************************************
 * FUNCTION:   DS1820_Reset
 * PURPOSE:    Initializes the DS1820 device.
 *
 * INPUT:      -
 * OUTPUT:     -
 * RETURN:     FALSE if at least one device is on the 1-wire bus, TRUE otherwise
 ******************************************************************************/
bool DS1820_Reset(void)
{
   bool bPresPulse;

   DS1820_DisableInterrupts();
   
   /* reset pulse */
   output_low(DS1820_DATAPIN);
   DS1820_DelayUs(DS1820_RST_PULSE);
   output_high(DS1820_DATAPIN);

   /* wait until pullup pull 1-wire bus to high */
   DS1820_DelayUs(DS1820_PRESENCE_WAIT);

   /* get presence pulse */
   bPresPulse = input(DS1820_DATAPIN);

   DS1820_DelayUs(424);
   
   DS1820_EnableInterrupts();

   return bPresPulse;
}


/*******************************************************************************
 * FUNCTION:   DS1820_ReadBit
 * PURPOSE:    Reads a single bit from the DS1820 device.
 *
 * INPUT:      -
 * OUTPUT:     -
 * RETURN:     bool        value of the bit which as been read form the DS1820
 ******************************************************************************/
bool DS1820_ReadBit(void)
{
   bool bBit;

   DS1820_DisableInterrupts();
   
   output_low(DS1820_DATAPIN);
   DS1820_DelayUs(DS1820_MSTR_BITSTART);
   input(DS1820_DATAPIN);
   DS1820_DelayUs(DS1820_BITREAD_DLY);

   bBit = input(DS1820_DATAPIN);
   
   DS1820_EnableInterrupts();

   return (bBit);
}


/*******************************************************************************
 * FUNCTION:   DS1820_WriteBit
 * PURPOSE:    Writes a single bit to the DS1820 device.
 *
 * INPUT:      bBit        value of bit to be written
 * OUTPUT:     -
 * RETURN:     -
 ******************************************************************************/
void DS1820_WriteBit(bool bBit)
{
   DS1820_DisableInterrupts();
   
   output_low(DS1820_DATAPIN);
   DS1820_DelayUs(DS1820_MSTR_BITSTART);

   if (bBit != FALSE)
   {
      output_high(DS1820_DATAPIN);
   }

   DS1820_DelayUs(DS1820_BITWRITE_DLY);
   output_high(DS1820_DATAPIN);
   
   DS1820_EnableInterrupts();
}


/*******************************************************************************
 * FUNCTION:   DS1820_ReadByte
 * PURPOSE:    Reads a single byte from the DS1820 device.
 *
 * INPUT:      -
 * OUTPUT:     -
 * RETURN:     uint8          byte which has been read from the DS1820
 ******************************************************************************/
uint8 DS1820_ReadByte(void)
{
   uint8 i;
   uint8 value = 0;

   for (i=0 ; i < 8; i++)
   {
      if ( DS1820_ReadBit() )
      {
         value |= (1 << i);
      }
      DS1820_DelayUs(120);
   }
   return(value);
}


/*******************************************************************************
 * FUNCTION:   DS1820_WriteByte
 * PURPOSE:    Writes a single byte to the DS1820 device.
 *
 * INPUT:      val_u8         byte to be written
 * OUTPUT:     -
 * RETURN:     -
 ******************************************************************************/
void DS1820_WriteByte(uint8 val_u8)
{
   uint8 i;
   uint8 temp;

   for (i=0; i < 8; i++)      /* writes byte, one bit at a time */
   {
      temp = val_u8 >> i;     /* shifts val right 'i' spaces */
      temp &= 0x01;           /* copy that bit to temp */
      DS1820_WriteBit(temp);  /* write bit in temp into */
   }

   DS1820_DelayUs(105);
}



/* -------------------------------------------------------------------------- */
/*                             API Interface                                  */
/* -------------------------------------------------------------------------- */


/*******************************************************************************
 * FUNCTION:   DS1820_AddrDevice
 * PURPOSE:    Addresses a single or all devices on the 1-wire bus.
 *
 * INPUT:      nAddrMethod       use DS1820_CMD_MATCHROM to select a single
 *                               device or DS1820_CMD_SKIPROM to select all
 * OUTPUT:     -
 * RETURN:     -
 ******************************************************************************/
void DS1820_AddrDevice(uint8 nAddrMethod)
{
   uint8 i;
   
   if (nAddrMethod == DS1820_CMD_MATCHROM)
   {
      DS1820_WriteByte(DS1820_CMD_MATCHROM);     /* address single devices on bus */
      for (i = 0; i < DS1820_ADDR_LEN; i ++)
      {
         DS1820_WriteByte(nRomAddr_au8[i]);
      }
   }
   else
   {
      DS1820_WriteByte(DS1820_CMD_SKIPROM);     /* address all devices on bus */
   }
}


/*******************************************************************************
 * FUNCTION:   DS1820_FindNextDevice
 * PURPOSE:    Finds next device connected to the 1-wire bus.
 *
 * INPUT:      -
 * OUTPUT:     nRomAddr_au8[]       ROM code of the next device
 * RETURN:     bool                 TRUE if there are more devices on the 1-wire
 *                                  bus, FALSE otherwise
 ******************************************************************************/
bool DS1820_FindNextDevice(void)
{
    uint8 state_u8;
    uint8 byteidx_u8;
    uint8 mask_u8 = 1;
    uint8 bitpos_u8 = 1;
    uint8 nDiscrepancyMarker_u8 = 0;
    bool bit_b;
    bool bStatus;
    bool next_b = FALSE;

    /* init ROM address */
    for (byteidx_u8=0; byteidx_u8 < 8; byteidx_u8 ++)
    {
        nRomAddr_au8[byteidx_u8] = 0x00;
    }

    bStatus = DS1820_Reset();        /* reset the 1-wire */

    if (bStatus || bDoneFlag)        /* no device found */
    {
        nLastDiscrepancy_u8 = 0;     /* reset the search */
        return FALSE;
    }

    /* send search rom command */
    DS1820_WriteByte(DS1820_CMD_SEARCHROM);

    byteidx_u8 = 0;
    do
    {
        state_u8 = 0;

        /* read bit */
        if ( DS1820_ReadBit() != 0 )
        {
            state_u8 = 2;
        }
        DS1820_DelayUs(120);

        /* read bit complement */
        if ( DS1820_ReadBit() != 0 )
        {
            state_u8 |= 1;
        }
        DS1820_DelayUs(120);

        /* description for values of state_u8: */
        /* 00    There are devices connected to the bus which have conflicting */
        /*       bits in the current ROM code bit position. */
        /* 01    All devices connected to the bus have a 0 in this bit position. */
        /* 10    All devices connected to the bus have a 1 in this bit position. */
        /* 11    There are no devices connected to the 1-wire bus. */

        /* if there are no devices on the bus */
        if (state_u8 == 3)
        {
            break;
        }
        else
        {
            /* devices have the same logical value at this position */
            if (state_u8 > 0)
            {
                /* get bit value */
                bit_b = (bool)(state_u8 >> 1);
            }
            /* devices have confilcting bits in the current ROM code */
            else
            {
                /* if there was a conflict on the last iteration */
                if (bitpos_u8 < nLastDiscrepancy_u8)
                {
                    /* take same bit as in last iteration */
                    bit_b = ( (nRomAddr_au8[byteidx_u8] & mask_u8) > 0 );
                }
                else
                {
                    bit_b = (bitpos_u8 == nLastDiscrepancy_u8);
                }

                if (bit_b == 0)
                {
                    nDiscrepancyMarker_u8 = bitpos_u8;
                }
            }

            /* store bit in ROM address */
           if (bit_b != 0)
           {
               nRomAddr_au8[byteidx_u8] |= mask_u8;
           }
           else
           {
               nRomAddr_au8[byteidx_u8] &= ~mask_u8;
           }

           DS1820_WriteBit(bit_b);

           /* increment bit position */
           bitpos_u8 ++;

           /* calculate next mask value */
           mask_u8 = mask_u8 << 1;

           /* check if this byte has finished */
           if (mask_u8 == 0)
           {
               byteidx_u8 ++;  /* advance to next byte of ROM mask */
               mask_u8 = 1;    /* update mask */
           }
        }
    } while (byteidx_u8 < DS1820_ADDR_LEN);


    /* if search was unsuccessful then */
    if (bitpos_u8 < 65)
    {
        /* reset the last discrepancy to 0 */
        nLastDiscrepancy_u8 = 0;
    }
    else
    {
        /* search was successful */
        nLastDiscrepancy_u8 = nDiscrepancyMarker_u8;
        bDoneFlag = (nLastDiscrepancy_u8 == 0);

        /* indicates search is not complete yet, more parts remain */
        next_b = TRUE;
    }

    return next_b;
}


/*******************************************************************************
 * FUNCTION:   DS1820_FindFirstDevice
 * PURPOSE:    Starts the device search on the 1-wire bus.
 *
 * INPUT:      -
 * OUTPUT:     nRomAddr_au8[]       ROM code of the first device
 * RETURN:     bool                 TRUE if there are more devices on the 1-wire
 *                                  bus, FALSE otherwise
 ******************************************************************************/
bool DS1820_FindFirstDevice(void)
{
    nLastDiscrepancy_u8 = 0;
    bDoneFlag = FALSE;

    return ( DS1820_FindNextDevice() );
}


/*******************************************************************************
 * FUNCTION:   DS1820_WriteEEPROM
 * PURPOSE:    Writes to the DS1820 EEPROM memory (2 bytes available).
 *
 * INPUT:      nTHigh         high byte of EEPROM
 *             nTLow          low byte of EEPROM
 * OUTPUT:     -
 * RETURN:     -
 ******************************************************************************/
void DS1820_WriteEEPROM(uint8 nTHigh, uint8 nTLow)
{
    /* --- write to scratchpad ----------------------------------------------- */
    DS1820_Reset();
    DS1820_AddrDevice(DS1820_CMD_MATCHROM);
    DS1820_WriteByte(DS1820_CMD_WRITESCRPAD); /* start conversion */
    DS1820_WriteByte(nTHigh);
    DS1820_WriteByte(nTLow);

    DS1820_DelayUs(10);

    DS1820_Reset();
    DS1820_AddrDevice(DS1820_CMD_MATCHROM);
    DS1820_WriteByte(DS1820_CMD_COPYSCRPAD); /* start conversion */

    delay_ms(10);
}


/*******************************************************************************
 * FUNCTION:   DS1820_GetTempRaw
 * PURPOSE:    Get temperature raw value from single DS1820 device.
 *
 *             Scratchpad Memory Layout
 *             Byte  Register
 *             0     Temperature_LSB
 *             1     Temperature_MSB
 *             2     Temp Alarm High / User Byte 1
 *             3     Temp Alarm Low / User Byte 2
 *             4     Reserved
 *             5     Reserved
 *             6     Count_Remain
 *             7     Count_per_C
 *             8     CRC
 *
 *             Temperature calculation for DS18S20 (Family Code 0x10):
 *             =======================================================
 *                                             (Count_per_C - Count_Remain)
 *             Temperature = temp_raw - 0.25 + ----------------------------
 *                                                     Count_per_C
 *
 *             Where temp_raw is the value from the temp_MSB and temp_LSB with
 *             the least significant bit removed (the 0.5C bit).
 *
 *
 *             Temperature calculation for DS18B20 (Family Code 0x28):
 *             =======================================================
 *                      bit7   bit6   bit5   bit4   bit3   bit2   bit1   bit0
 *             LSB      2^3    2^2    2^1    2^0    2^-1   2^-2   2^-3   2^-4
 *                      bit15  bit14  bit13  bit12  bit3   bit2   bit1   bit0
 *             MSB      S      S      S      S      S      2^6    2^5    2^4
 *
 *             The temperature data is stored as a 16-bit sign-extended two’s
 *             complement number in the temperature register. The sign bits (S)
 *             indicate if the temperature is positive or negative: for
 *             positive numbers S = 0 and for negative numbers S = 1.
 *
 * RETURN:     sint16         raw temperature value with a resolution
 *                            of 1/256°C
 ******************************************************************************/
sint16 DS1820_GetTempRaw(void)
{
    uint8 i;
    uint16 temp_u16;
    uint16 highres_u16;
    uint8 scrpad[DS1820_SCRPADMEM_LEN];

    /* --- start temperature conversion -------------------------------------- */
    DS1820_Reset();
    DS1820_AddrDevice(DS1820_CMD_MATCHROM);     /* address the device */
    output_high(DS1820_DATAPIN);
    DS1820_WriteByte(DS1820_CMD_CONVERTTEMP);   /* start conversion */
    //DS1820_DelayMs(DS1820_TEMPCONVERT_DLY);   /* wait for temperature conversion */
    DS1820_DelayMs(750);


    /* --- read sratchpad ---------------------------------------------------- */
    DS1820_Reset();
    DS1820_AddrDevice(DS1820_CMD_MATCHROM);   /* address the device */
    DS1820_WriteByte(DS1820_CMD_READSCRPAD);  /* read scratch pad */

    /* read scratch pad data */
    for (i=0; i < DS1820_SCRPADMEM_LEN; i++)
    {
        scrpad[i] = DS1820_ReadByte();
    }


    /* --- calculate temperature --------------------------------------------- */
    /* Formular for temperature calculation: */
    /* Temp = Temp_read - 0.25 + ((Count_per_C - Count_Remain)/Count_per_C) */

    /* get raw value of temperature (0.5°C resolution) */
    temp_u16 = 0;
    temp_u16 = (uint16)((uint16)scrpad[DS1820_REG_TEMPMSB] << 8);
    temp_u16 |= (uint16)(scrpad[DS1820_REG_TEMPLSB]);

    if (nRomAddr_au8[0] == DS1820_FAMILY_CODE_DS18S20)
    {
        /* get temperature value in 1°C resolution */
        temp_u16 >>= 1;
    
        /* temperature resolution is TEMP_RES (0x100), so 1°C equals 0x100 */
        /* => convert to temperature to 1/256°C resolution */
        temp_u16 = ((uint16)temp_u16 << 8);
    
        /* now substract 0.25°C */
        temp_u16 -= ((uint16)TEMP_RES >> 2);
    
        /* now calculate high resolution */
        highres_u16 = scrpad[DS1820_REG_CNTPERSEC] - scrpad[DS1820_REG_CNTREMAIN];
        highres_u16 = ((uint16)highres_u16 << 8);
        if (scrpad[DS1820_REG_CNTPERSEC])
        {
            highres_u16 = highres_u16 / (uint16)scrpad[DS1820_REG_CNTPERSEC];
        }
    
        /* now calculate result */
        highres_u16 = highres_u16 + temp_u16;
    }
    else
    {
        /* 12 bit temperature value has 0.0625°C resolution */
        /* shift left by 4 to get 1/256°C resolution */
        highres_u16 = temp_u16;
        highres_u16 <<= 4;
    }

    return (highres_u16);
}


/*******************************************************************************
 * FUNCTION:   DS1820_GetTempFloat
 * PURPOSE:    Converts internal temperature value to string (physical value).
 *
 * INPUT:      none
 * OUTPUT:     none
 * RETURN:     float          temperature value with as float value
 ******************************************************************************/
float DS1820_GetTempFloat(void)
{
    return ((float)DS1820_GetTempRaw() / (float)TEMP_RES);
}


/*******************************************************************************
 * FUNCTION:   DS1820_GetTempString
 * PURPOSE:    Converts internal temperature value to string (physical value).
 *
 * INPUT:      tRaw_s16       internal temperature value
 * OUTPUT:     strTemp_pc     user string buffer to write temperature value
 * RETURN:     sint16         temperature value with an internal resolution
 *                            of TEMP_RES
 ******************************************************************************/
void DS1820_GetTempString(sint16 tRaw_s16, char *strTemp_pc)
{
    sint16 tPhyLow_s16;
    sint8 tPhy_s8;

    /* convert from raw value (1/256°C resolution) to physical value */
    tPhy_s8 = (sint8)(tRaw_s16/TEMP_RES);

    /* convert digits from raw value (1/256°C resolution) to physical value */
    /*tPhyLow_u16 = tInt_s16 % TEMP_RES;*/
    tPhyLow_s16 = tRaw_s16 & 0xFF;      /* this operation is the same as */
                                        /* but saves flash memory tInt_s16 % TEMP_RES */
    tPhyLow_s16 = tPhyLow_s16 * 100;
    tPhyLow_s16 = (uint16)tPhyLow_s16 / TEMP_RES;

    /* write physical temperature value to string */
    sprintf(strTemp_pc, "%d.%02d", tPhy_s8, (sint8)tPhyLow_s16);
}

#endif /* _DS1820_H */

