/**
  ******************************************************************************
  * @file    ds18b20_driver.c
  * @brief   This file includes the HAL/LL driver for DS18B20 1-Wire Digital
  * 		 Thermometer
  ******************************************************************************
  */
#include "ds18b20_driver.h"

DS18B20_Drv_t DS;
OneWire_t OW;

/**
  * @brief  The function is used to check valid DS18B20 ROM
  * @retval Return in OK = 1, Failed = 0
  * @param  ROM		Pointer to ROM number
  */
HAL_StatusTypeDef DS18B20_IsValidAddress(uint8_t *ROM)
{
  if(ROM == NULL)
    return HAL_ERROR;
  
  uint8_t check_family = (*ROM == DS18B20_FAMILY_CODE);
	/* Calculate CRC */
	uint8_t crc = OneWire_CRC8(ROM, 7);
	uint8_t check_crc = (crc == ROM[7]);
	/* Checks if first byte is equal to DS18B20's family code */
  if(check_family && check_crc)
    return HAL_OK;
  else
    return HAL_ERROR;
}
/**
  * @brief  The function is used to check valid DS18B20 ROM
  * @retval Return in OK = 1, Failed = 0
  * @param  ROM		Pointer to ROM number
  */
HAL_StatusTypeDef DS18B20_IsValid(uint8_t *ROM)
{
  if(ROM == NULL)
    return HAL_ERROR;
  
  if(*ROM == DS18B20_FAMILY_CODE)
    return HAL_OK;
  else
    return HAL_ERROR;
}

/**
  * @brief  The function is used to get resolution
  * @retval Return value in 9 - 12
  * @param  OW		OneWire HandleTypedef
  * @param  ROM		Pointer to ROM number
  */
uint8_t DS18B20_GetResolution(OneWire_t* OW, uint8_t *ROM) {
	uint8_t conf;

  if(OW == NULL)
    return HAL_ERROR;
  if(ROM == NULL)
    return HAL_ERROR;
  
	/* Check valid ROM */
	if (DS18B20_IsValid(ROM) != HAL_OK) 
    return 0;

	/* Reset line */
	OneWire_Reset(OW);

	/* Select ROM number */
	OneWire_MatchROM(OW, ROM);

	/* Read scratchpad command by onewire protocol */
	OneWire_WriteByte(OW, DS18B20_CMD_READSCRATCHPAD);

	/* Ignore first 4 bytes */
	OneWire_ReadByte(OW);
	OneWire_ReadByte(OW);
	OneWire_ReadByte(OW);
	OneWire_ReadByte(OW);

	/* 5th byte of scratchpad is configuration register */
	conf = OneWire_ReadByte(OW);

	/* Return 9 - 12 value according to number of bits */
	return ((conf & 0x60) >> 5) + 9;
}

/**
  * @brief  The function is used as set resolution
  * @retval status in OK = 1, Failed = 0
  * @param  OW			OneWire HandleTypedef
  * @param  ROM			Pointer to ROM number
  * @param  Resolution	Resolution in 9 - 12
  */
HAL_StatusTypeDef DS18B20_SetResolution(OneWire_t* OW, uint8_t *ROM,
		DS18B20_Res_t Resolution)
{
  if(OW == NULL)
    return HAL_ERROR;
  if(ROM == NULL)
    return HAL_ERROR;
  
	uint8_t th, tl, conf;

	/* Check valid ROM */
	if (DS18B20_IsValid(ROM) != HAL_OK) 
    return HAL_ERROR;

	/* Reset line */
	OneWire_Reset(OW);

	/* Select ROM number */
	OneWire_MatchROM(OW, ROM);

	/* Read scratchpad command by onewire protocol */
	OneWire_WriteByte(OW, DS18B20_CMD_READSCRATCHPAD);

	/* Ignore first 2 bytes */
	OneWire_ReadByte(OW);
	OneWire_ReadByte(OW);

	th = OneWire_ReadByte(OW);
	tl = OneWire_ReadByte(OW);
	conf = OneWire_ReadByte(OW);
  
	/* Set choosed resolution */
  conf = Resolution;

	/* Reset line */
	OneWire_Reset(OW);

	/* Select ROM number */
	OneWire_MatchROM(OW, ROM);

	/* Write scratchpad command by onewire protocol, only th, tl and conf
	 * register can be written */
	OneWire_WriteByte(OW, DS18B20_CMD_WRITESCRATCHPAD);

	/* Write bytes */
	OneWire_WriteByte(OW, th);
	OneWire_WriteByte(OW, tl);
	OneWire_WriteByte(OW, conf);

	/* Reset line */
	OneWire_Reset(OW);

	/* Select ROM number */
	OneWire_MatchROM(OW, ROM);

	/* Copy scratchpad to EEPROM of DS18B20 */
	OneWire_WriteByte(OW, DS18B20_CMD_COPYSCRATCHPAD);

	return HAL_OK;
}

/**
  * @brief  The function is used as start selected ROM device
  * @retval status in OK = 1, Failed = 0
  * @param  OW			OneWire HandleTypedef
  * @param  ROM			Pointer to ROM number
  */
HAL_StatusTypeDef DS18B20_StartConvT(OneWire_t* OW, uint8_t *ROM)
{
  if(OW == NULL)
    return HAL_ERROR;
  if(ROM == NULL)
    return HAL_ERROR;
  
	/* Check if device is DS18B20 */
	if(DS18B20_IsValid(ROM) != HAL_OK)
    return HAL_ERROR;

	/* Reset line */
	OneWire_Reset(OW);

	/* Select ROM number */
	OneWire_MatchROM(OW, ROM);

	/* Start temperature conversion */
	OneWire_WriteByte(OW, DS18B20_CMD_CONVERT);

	return HAL_OK;
}
/**
  * @brief  The function is used as start all ROM device
  * @param  OW			OneWire HandleTypedef
  */
HAL_StatusTypeDef DS18B20_StartConvTAll(OneWire_t* OW)
{
  if(OW == NULL)
    return HAL_ERROR;
  
	/* Reset pulse */
	OneWire_Reset(OW);

	/* Skip rom */
	OneWire_WriteByte(OW, ONEWIRE_CMD_SKIPROM);

	/* Start conversion on all connected devices */
	OneWire_WriteByte(OW, DS18B20_CMD_CONVERT);
  
  return HAL_OK;
}

/**
  * @brief  The function is used as read temreature from device and store in selected
  * 		destination
  * @retval status in OK = 1, Failed = 0
  * @param  OW				OneWire HandleTypedef
  * @param  ROM				Pointer to ROM number
  * @param  Destination		Pointer to return value
  */
HAL_StatusTypeDef DS18B20_CalcTemperature(OneWire_t* OW, uint8_t *ROM, uint8_t *Scratchpad, float *Destination)
{
  if(OW == NULL)
    return HAL_ERROR;
  if(ROM == NULL)
    return HAL_ERROR;
  if(Scratchpad == NULL)
    return HAL_ERROR;
  if(Destination == NULL)
    return HAL_ERROR;
  
	uint16_t temperature;
	uint8_t resolution;
	int8_t digit, minus = 0;
	float decimal;
  
	/* Check if device is DS18B20 */
	if (DS18B20_IsValid(ROM) != HAL_OK)
    return HAL_ERROR;

	/* First two bytes of scratchpad are temperature values */
	temperature = Scratchpad[0] | (Scratchpad[1] << 8);

	/* Reset line */
	OneWire_Reset(OW);

	/* Check if temperature is negative */
	if (temperature & 0x8000) {
		/* Two's complement, temperature is negative */
		temperature = ~temperature + 1;
		minus = 1;
	}

	/* Get sensor resolution */
	resolution = Scratchpad[4];

	/* Store temperature integer digits and decimal digits */
	digit = temperature >> 4;
	digit |= ((temperature >> 8) & 0x7) << 4;

	/* Store decimal digits */
	switch (resolution) {
		case DS18B20_RESOLUTION_9BITS: {
			decimal = (temperature >> 3) & 0x01;
			decimal *= (float)DS18B20_DECIMAL_STEP_9BIT;
		} break;
		case DS18B20_RESOLUTION_10BITS: {
			decimal = (temperature >> 2) & 0x03;
			decimal *= (float)DS18B20_DECIMAL_STEP_10BIT;
		} break;
		case DS18B20_RESOLUTION_11BITS: {
			decimal = (temperature >> 1) & 0x07;
			decimal *= (float)DS18B20_DECIMAL_STEP_11BIT;
		} break;
		case DS18B20_RESOLUTION_12BITS: {
			decimal = temperature & 0x0F;
			decimal *= (float)DS18B20_DECIMAL_STEP_12BIT;
		} break;
		default: {
      *Destination = 0;
      return HAL_ERROR;
		}
	}

	/* Check for negative part */
	decimal = digit + decimal;
	if (minus) {
		decimal = 0 - decimal;
	}

	/* Set to pointer */
	*Destination = decimal;

	/* Return HAL_OK, temperature valid */
	return HAL_OK;
}


/**
  * @brief  The function is used as read scratchpad from device
  * @retval status in OK = 1, Failed = 0
  * @param  OW				OneWire HandleTypedef
  * @param  ROM				Pointer to ROM number
  * @param  Destination		Pointer to Scratchpad array
  */
HAL_StatusTypeDef DS18B20_ReadScratchpad(OneWire_t* OW, uint8_t *ROM, uint8_t *Scratchpad)
{
  if(OW == NULL)
    return HAL_ERROR;
  if(ROM == NULL)
    return HAL_ERROR;
  if(Scratchpad == NULL)
    return HAL_ERROR;
  
	/* Reset line */
	OneWire_Reset(OW);

	/* Select ROM number */
	OneWire_MatchROM(OW, ROM);

	/* Read scratchpad command by onewire protocol */
	OneWire_WriteByte(OW, DS18B20_CMD_READSCRATCHPAD);

	/* Get data */
	for (int i = 0; i < 9; i++) {
		/* Read byte by byte */
		Scratchpad[i] = OneWire_ReadByte(OW);
	}

	/* Calculate CRC */	
	uint8_t crc = OneWire_CRC8(Scratchpad, 8);

	/* Check if CRC is ok */
	if (crc != Scratchpad[8]) {
		/* CRC invalid */
		return HAL_ERROR;
	}
  return HAL_OK;
}


/**
  * @brief  The function is used to wait for end of convertion
  * @param  OW			OneWire HandleTypedef
  */
HAL_StatusTypeDef DS18B20_WaitForEndConvertion(OneWire_t* OW)
{
  if(OW == NULL)
    return HAL_ERROR;
  uint32_t tickstart = HAL_GetTick();
  
	/* Wait until line is released, then coversion is completed */
	while(OneWire_ReadBit(OW) == 0)
  {
    if(HAL_GetTick() - tickstart > DS18B20_DELAY_MS_MAX)
      return HAL_TIMEOUT; // end of convertion has not come
  }
  return HAL_OK; // convertion done
}


/**
  * @brief  The function is used to wait for end of convertion without blocking
  * @param  OW			OneWire HandleTypedef
  */
HAL_StatusTypeDef DS18B20_WaitForEndConvertion_NonBlocking(OneWire_t* OW)
{
  if(OW == NULL)
    return HAL_ERROR;
    
	/* If line is pull down - conversion is ongoing */
	if(OneWire_ReadBit(OW) == 0)
    return HAL_BUSY;
  else
    return HAL_OK; // convertion done
}


/**
  * @brief  The function is used as set temperature alarm range on
  * 		selected device
  * @retval status in OK = 1, Failed = 0
  * @param  OW		OneWire HandleTypedef
  * @param  ROM		Pointer to ROM number
  * @param  Low		Low temperature alarm, value > -55, 0 = reset
  * @param  High	High temperature alarm,, value < 125, 0 = reset
  */
HAL_StatusTypeDef DS18B20_SetTempAlarm(OneWire_t* OW, uint8_t *ROM, int8_t Low,
		int8_t High)
{
  if(OW == NULL)
    return HAL_ERROR;
  if(ROM == NULL)
    return HAL_ERROR;
  
	uint8_t tl, th, conf;

	/* Check if device is DS18B20 */
	if (DS18B20_IsValid(ROM) != HAL_OK)
    return HAL_ERROR;

	Low = ((Low < -55) || (Low == 0)) ? -55 : Low;
	High = ((High > 125) || (High == 0)) ? 125 : High;

	/* Reset line */
	OneWire_Reset(OW);

	/* Select ROM number */
	OneWire_MatchROM(OW, ROM);

	/* Read scratchpad command by onewire protocol */
	OneWire_WriteByte(OW, DS18B20_CMD_READSCRATCHPAD);

	/* Ignore first 2 bytes */
	OneWire_ReadByte(OW);
	OneWire_ReadByte(OW);

	th = OneWire_ReadByte(OW);
	tl = OneWire_ReadByte(OW);
	conf = OneWire_ReadByte(OW);

	th = (uint8_t)High;
	tl = (uint8_t)Low;

	/* Reset line */
	OneWire_Reset(OW);

	/* Select ROM number */
	OneWire_MatchROM(OW, ROM);

	/* Write scratchpad command by onewire protocol, only th, tl and conf
	 * register can be written */
	OneWire_WriteByte(OW, DS18B20_CMD_WRITESCRATCHPAD);

	/* Write bytes */
	OneWire_WriteByte(OW, th);
	OneWire_WriteByte(OW, tl);
	OneWire_WriteByte(OW, conf);

	/* Reset line */
	OneWire_Reset(OW);

	/* Select ROM number */
	OneWire_MatchROM(OW, ROM);

	/* Copy scratchpad to EEPROM of DS18B20 */
	OneWire_WriteByte(OW, DS18B20_CMD_COPYSCRATCHPAD);

	return HAL_OK;
}

/**
  * @brief  The function is used as set user bytes with mask
  * @retval status in OK = 1, Failed = 0
  * @param  OW		OneWire HandleTypedef
  * @param  ROM		Pointer to ROM number
  * @param  UserBytes12		First 2 User Bytes (tHigh and tLow)
  * @param  UserBytes34	  Second 2 User Bytes
  * @param  UserBytesMask Which User Bytes write, and which ignore
  */
HAL_StatusTypeDef DS18B20_WriteUserBytes(OneWire_t* OW, uint8_t *ROM, int16_t UserBytes12,
		int16_t UserBytes34, uint8_t UserBytesMask)
{
  if(OW == NULL)
    return HAL_ERROR;
  if(ROM == NULL)
    return HAL_ERROR;
  
	uint8_t ub1, ub2, conf, ub3, ub4;
  uint8_t UserByte1 = UserBytes12 & 0xFF;
  uint8_t UserByte2 = UserBytes12 >> 8;
  uint8_t UserByte3 = UserBytes34 & 0xFF;
  uint8_t UserByte4 = UserBytes34 >> 8;

	/* Check if device is DS18B20 */
	if (DS18B20_IsValid(ROM) != HAL_OK)
    return HAL_ERROR;

	/* Reset line */
	OneWire_Reset(OW);

	/* Select ROM number */
	OneWire_MatchROM(OW, ROM);

	/* Read scratchpad command by onewire protocol */
	OneWire_WriteByte(OW, DS18B20_CMD_READSCRATCHPAD);

	/* Ignore first 2 bytes */
	OneWire_ReadByte(OW);
	OneWire_ReadByte(OW);

	ub1 = OneWire_ReadByte(OW);
	ub2 = OneWire_ReadByte(OW);
	conf = OneWire_ReadByte(OW);
	OneWire_ReadByte(OW);
	ub3 = OneWire_ReadByte(OW);
	ub4 = OneWire_ReadByte(OW);

	/* If user bytes in mask */
  if(UserBytesMask & (1<<0))
  {
    ub1 = UserByte1;
  }
  if(UserBytesMask & (1<<1))
  {
    ub2 = UserByte2;
  }
  if(UserBytesMask & (1<<2))
  {
    ub3 = UserByte3;
  }
  if(UserBytesMask & (1<<3))
  {
    ub4 = UserByte4;
  }
  

	/* Reset line */
	OneWire_Reset(OW);

	/* Select ROM number */
	OneWire_MatchROM(OW, ROM);

	/* Write scratchpad command by onewire protocol, only th, tl and conf
	 * register can be written */
	OneWire_WriteByte(OW, DS18B20_CMD_WRITESCRATCHPAD);

	/* Write bytes */
	OneWire_WriteByte(OW, ub1);
	OneWire_WriteByte(OW, ub2);
	OneWire_WriteByte(OW, conf);
	OneWire_WriteByte(OW, ub3);
	OneWire_WriteByte(OW, ub4);

	/* Reset line */
	OneWire_Reset(OW);

	/* Select ROM number */
	OneWire_MatchROM(OW, ROM);

	/* Copy scratchpad to EEPROM of DS18B20 */
	OneWire_WriteByte(OW, DS18B20_CMD_COPYSCRATCHPAD);

	return HAL_OK;
}


///**
//  * @brief  The function is used as search device that had temperature alarm
//  * 		triggered and store it in DS18B20 alarm data structure
//  * @retval status of search, OK = 1, Failed = 0
//  * @param  DS		DS18B20 HandleTypedef
//  * @param  OW		OneWire HandleTypedef
//  */
//uint8_t DS18B20_AlarmSearch(DS18B20_Drv_t *DS, OneWire_t* OW)
//{
//	uint8_t t = 0;

//	/* Reset Alarm in DS */
//	for(uint8_t i = 0; i < OW->RomCnt; i++)
//	{
//		for(uint8_t j = 0; j < 8; j++)
//		{
//			DS->AlmAddr[i][j] = 0;
//		}
//	}

//	/* Start alarm search */
//	while (OneWire_Search(OW, DS18B20_CMD_ALARM_SEARCH))
//	{
//		/* Store ROM of device which has alarm flag set */
//		OneWire_GetDevRom(OW, DS->AlmAddr[t]);
//		t++;
//	}
//	return (t > 0) ? 1 : 0;
//}

/**
  * @brief  The function is used to initialize the DS18B20 sensor, and search
  * 		for all ROM along the line. Store in DS18B20 data structure
  * @retval Rom detect status, OK = 1, No Rom detected = 0
  * @param  DS			DS18B20 HandleTypedef
  * @param  OW			OneWire HandleTypedef
  */
HAL_StatusTypeDef DS18B20_Search(DS18B20_Drv_t *DS, OneWire_t *OW)
{
  if(OW == NULL)
    return HAL_ERROR;
  
  
  OW->RomCnt = 0;
	/* Search all OneWire devices ROM */
	while(1)
	{
		/* Start searching for OneWire devices along the line */
		if(OneWire_Search(OW, ONEWIRE_CMD_SEARCHROM) != 1) break;

		/* Get device ROM */
		OneWire_GetDevRom(OW, DS->DevAddr[OW->RomCnt]);

		OW->RomCnt++;
	}
  for(int i = OW->RomCnt; i < DS18B20_DEVICE_AMOUNT; i++)
  {
    for(int j = 0; j < 8; j++)
    DS->DevAddr[i][j] = 0;
  }
  

  if(OW->RomCnt > 0)
    return HAL_OK;
  else
    return HAL_BUSY;
}
