/************ SMB driver ************/

#define  SA          0x00     // Slave Address (0 for single slave / 0x5A<<1 default)
#define  RAM_Access  0x00     // RAM access command
#define  RAM_Tobj1   0x07     // To1 address in the RAM
#define  RAM_Tamb    0x06     // Ta address in the RAM


// High and Low level of clock
#define HIGHLEV 40      // max. 50us
#define LOWLEV     100    // max. 30ms
#define TBUF       20

// SMBus control signals
#define SCL    PIN_B0
#define SDA    PIN_B1

#define mSDA_HIGH()     output_float(SDA);   // SDA float
#define mSDA_LOW()   output_low(SDA);     // SDA low
#define mSCL_HIGH()     output_float(SCL);   // SCL float
#define mSCL_LOW()   output_low(SCL);     // SCL low

#define  ACK     0
#define NACK   1

//**********************************************************************************************
//                                                      START CONDITION ON SMBus
//**********************************************************************************************
//Name:                 START_bit
//Function:             Generate START condition on SMBus
//Parameters:   No
//Return:               No
//Comments:     Refer to "System Managment BUS(SMBus) specification Version 2.0"
//                              or AN"SMBus communication with MLX90614" on the website www.melexis.com
//**********************************************************************************************
void SMB_START_bit(void)
{
//   disable_interrupts(GLOBAL);
        mSDA_HIGH();                    // Set SDA line
        delay_us( TBUF );          // Wait a few microseconds
        mSCL_HIGH();                    // Set SCL line
        delay_us( TBUF );          // Generate bus free time between Stop
                                                           // and Start condition (Tbuf=4.7us min)
        mSDA_LOW();                        // Clear SDA line
        delay_us( TBUF );          // Hold time after (Repeated) Start Condition.
                                                           // After this period, the first clock is generated.
                                                           // (Thd:sta=4.0us min)
        mSCL_LOW();                             // Clear SCL line
//   enable_interrupts(GLOBAL);
        delay_us( TBUF );          // Wait a few microseconds
}

//*********************************************************************************************
//                                                              STOP CONDITION ON SMBus
//*********************************************************************************************
//Name:                 STOPbit
//Function:             Generate STOP condition on SMBus
//Parameters:   No
//Return:               No
//Comments:     Refer to "System Managment BUS(SMBus) specification Version 2.0"
//                      or AN"SMBus communication with MLX90614" on the website www.melexis.com
//*********************************************************************************************
void SMB_STOP_bit(void)
{
//   disable_interrupts(GLOBAL);
mSDA_HIGH();
        mSCL_LOW();                        // Clear SCL line
        delay_us( TBUF );          // Wait a few microseconds
        mSDA_LOW();                             // Clear SDA line
        delay_us( TBUF );          // Wait a few microseconds
        mSCL_HIGH();                    // Set SCL line
        delay_us( TBUF );          // Stop condition setup time(Tsu:sto=4.0us min)
        mSDA_HIGH();                    // Set SDA line
//   enable_interrupts(GLOBAL);
}


void SMB_send_bit(unsigned char bit_out)
{
//   disable_interrupts(GLOBAL);
        if(bit_out==0) {mSDA_LOW();}
        else               {mSDA_HIGH();}
   delay_us(3);
        mSCL_HIGH();                                    // Set SCL line
        delay_us( HIGHLEV );                    // High Level of Clock Pulse
        mSCL_LOW();                                             // Clear SCL line

toggle_dome();

        delay_us( LOWLEV );                     // Low Level of Clock Pulse
//      mSDA_HIGH();                                // Master release SDA line ,
//   enable_interrupts(GLOBAL);

toggle_dome();
        return;
}

unsigned char SMB_Receive_bit(void)
{
        unsigned char Ack_bit;

//   disable_interrupts(GLOBAL);
        mSDA_HIGH();  //_SDA_IO=1;              // SDA-input
        mSCL_HIGH();                                    // Set SCL line
        delay_us( HIGHLEV );                    // High Level of Clock Pulse
        if(input(SDA))  Ack_bit=1;      // \ Read acknowledgment bit, save it in Ack_bit
        else            Ack_bit=0;                      // /
        mSCL_LOW();                                             // Clear SCL line

toggle_dome();

        delay_us( LOWLEV );                     // Low Level of Clock Pulse
//   enable_interrupts(GLOBAL);

toggle_dome();
        return  Ack_bit;
}


//*********************************************************************************************
//                                                              TRANSMIT DATA ON SMBus
//*********************************************************************************************
//Name:                 TX_byte
//Function:             Send a byte on SMBus
//Parameters:   TX_buffer ( the byte which will be send on the SMBus )
//Return:               Ack_bit   ( acknowledgment bit )
//Comments:     Sends MSbit first
//*********************************************************************************************
unsigned char SMB_TX_byte(unsigned char Tx_buffer)
{
        unsigned char   Bit_counter;
        unsigned char   Ack_bit;
        unsigned char   bit_out;

        for(Bit_counter=8; Bit_counter; Bit_counter--)
        {
                if(Tx_buffer&0x80) bit_out=1; // If the current bit of Tx_buffer is 1 set bit_out
                else                       bit_out=0;     // else clear bit_out

                SMB_send_bit(bit_out);                  // Send the current bit on SDA
                Tx_buffer<<=1;                                // Get next bit for checking
        }

        Ack_bit=SMB_Receive_bit();                      // Get acknowledgment bit

        return  Ack_bit;
}

//*********************************************************************************************
//                                                                      RECEIVE DATA ON SMBus
//*********************************************************************************************
//Name:                 RX_byte
//Function:             Receive a byte on SMBus
//Parameters:   ack_nack (ackowlegment bit)
//Return:               RX_buffer(Received byte)
//Comments:     MSbit is received first
//*********************************************************************************************
unsigned char SMB_RX_byte(unsigned char ack_nack)
{
        unsigned char   RX_buffer;
        unsigned char   Bit_Counter;

        for(Bit_Counter=8; Bit_Counter; Bit_Counter--)
        {
                if(SMB_Receive_bit())                           // Get a bit from the SDA line
                {
                        RX_buffer <<= 1;                                        // If the bit is HIGH save 1  in RX_buffer
                        RX_buffer |=0b00000001;
                }
                else
                {
                        RX_buffer <<= 1;                                        // If the bit is LOW save 0 in RX_buffer
                        RX_buffer &=0b11111110;
                }
        }

        SMB_send_bit(ack_nack);                                 // Sends acknowledgment bit

        return RX_buffer;
}


unsigned char PEC_calculation(unsigned char pec[]) // CRC calculation
{
   unsigned char   crc[6];
   unsigned char   BitPosition=47;
   unsigned char   shift;
   unsigned char   i;
   unsigned char   j;
   unsigned char   temp;

   do
   {
      crc[5]=0;            /* Load CRC value 0x000000000107 */
      crc[4]=0;
      crc[3]=0;
      crc[2]=0;
      crc[1]=0x01;
      crc[0]=0x07;
      BitPosition=47;      /* Set maximum bit position at 47 */
      shift=0;

      //Find first 1 in the transmited message
      i=5;                 /* Set highest index */
      j=0;
      while((pec[i]&(0x80>>j))==0 && i>0)
      {
         BitPosition--;
         if(j<7)
         {
            j++;
         }
         else
         {
            j=0x00;
            i--;
         }
toggle_dome();
      }

      shift=BitPosition-8;   /*Get shift value for crc value*/


      //Shift crc value
      while(shift)
      {
         for(i=5; i<0xFF; i--)
         {
            if((crc[i-1]&0x80) && (i>0))
            {
               temp=1;
            }
            else
            {
               temp=0;
            }
            crc[i]<<=1;
            crc[i]+=temp;
         }
         shift--;
toggle_dome();
      }

      //Exclusive OR between pec and crc
      for(i=0; i<=5; i++)
      {
         pec[i] ^=crc[i];
      }
   } while(BitPosition>8);

   return pec[0];
}