/**
******************************************************************************
* @file    onewire.c
* @brief   This file includes the HAL/LL driver for OneWire devices
******************************************************************************
*/
#include "onewire.h"

/**
  * @brief  The internal function is used to write bit
  * @param  OW		OneWire HandleTypedef
  * @param  bit		bit in 0 or 1
  */
void OneWire_WriteBit(OneWire_t* OW, uint8_t bit)
{
  if(OW == NULL)
    return;
#ifndef ONEWIRE_UART_H
  __disable_irq();
	if(bit)
	{
		/* Set line low */
		OneWire_Pin_Level(OW, 0);
		OneWire_Pin_Mode(OW, Output);
    
    /* Forming pulse */
		OneWire_Delay_us(ONEWIRE_WRITE_1_US);

		/* Release line (pull up line) */
		OneWire_Pin_Mode(OW, Input);

		/* Wait for 55 us and release the line */
		OneWire_Delay_us(ONEWIRE_COMMAND_SLOT_US - ONEWIRE_WRITE_1_US);
		OneWire_Pin_Mode(OW, Input);
	}else{
		/* Set line low */
		OneWire_Pin_Level(OW, 0);
		OneWire_Pin_Mode(OW, Output);
    
    /* Forming pulse */
		OneWire_Delay_us(ONEWIRE_WRITE_0_US);

		/* Release line (pull up line) */
		OneWire_Pin_Mode(OW, Input);

		/* Wait for 5 us and release the line */
		OneWire_Delay_us(ONEWIRE_COMMAND_SLOT_US - ONEWIRE_WRITE_0_US);
		OneWire_Pin_Mode(OW, Input);
	}
  __enable_irq();
#else
  OneWireUART_ProcessBit(onewire_uart, bit);
#endif
}

/**
  * @brief  The function is used to read bit
  * @retval bit
  * @param  OW		OneWire HandleTypedef
  */
uint8_t OneWire_ReadBit(OneWire_t* OW)
{
  if(OW == NULL)
    return 0;
  
  __disable_irq();
	uint8_t bit = 0;
#ifndef ONEWIRE_UART_H
	/* Line low */
	OneWire_Pin_Level(OW, 0);
	OneWire_Pin_Mode(OW, Output);
	OneWire_Delay_us(ONEWIRE_READ_CMD_US);

	/* Release line */
	OneWire_Pin_Mode(OW, Input);
	OneWire_Delay_us(ONEWIRE_READ_DELAY_US);

	/* Read line value */
	bit = OneWire_Pin_Read(OW);

	/* Wait 50us to complete 60us period */
	OneWire_Delay_us(ONEWIRE_COMMAND_SLOT_US - ONEWIRE_READ_CMD_US - ONEWIRE_READ_DELAY_US);
  __enable_irq();
#else
  bit = OneWireUART_ProcessBit(onewire_uart, 1);
#endif
	/* Return bit value */
	return bit;
}

/**
  * @brief  The function is used to write byte
  * @param  OW		OneWire HandleTypedef
  * @param  byte	byte to write
  */
void OneWire_WriteByte(OneWire_t* OW, uint8_t byte)
{
  if(OW == NULL)
    return;
  
#ifndef ONEWIRE_UART_H
	uint8_t bit = 8;
	/* Write 8 bits */
	while (bit--) {
		/* LSB bit is first */
		OneWire_WriteBit(OW, byte & 0x01);
		byte >>= 1;
	}
#else
 OneWireUART_ProcessByte(onewire_uart, byte);
#endif
}

/**
  * @brief  The function is used to read byte
  * @retval byte from device
  * @param  OW		OneWire HandleTypedef
  */
uint8_t OneWire_ReadByte(OneWire_t* OW)
{
  if(OW == NULL)
    return 0;
  
	uint8_t byte = 0;
#ifndef ONEWIRE_UART_H
  uint8_t bit = 8;
	while (bit--) {
		byte >>= 1;
		byte |= (OneWire_ReadBit(OW) << 7);
	}
#else
  byte = OneWireUART_ProcessByte(onewire_uart, 0xFF);
#endif

	return byte;
}

/**
  * @brief  The function is used to reset device
  * @retval respond from device
  * @param  OW		OneWire HandleTypedef
  */
uint8_t OneWire_Reset(OneWire_t* OW)
{
  if(OW == NULL)
    return 1;
  
#ifndef ONEWIRE_UART_H
	/* Line low, and wait 480us */
	OneWire_Pin_Level(OW, 0);
	OneWire_Pin_Mode(OW, Output);
	OneWire_Delay_us(ONEWIRE_RESET_PULSE_US);

	/* Release line and wait for 70us */
	OneWire_Pin_Mode(OW, Input);
	OneWire_Delay_us(ONEWIRE_PRESENCE_WAIT_US);

	/* Check bit value */
	uint8_t rslt = OneWire_Pin_Read(OW);

	/* Delay for 410 us */
	OneWire_Delay_us(ONEWIRE_PRESENCE_DURATION_US);
#else
  
  uint8_t rslt = 0;
  if(OneWireUART_Reset(onewire_uart) == HAL_OK)
    rslt = 0;
  else
    rslt = 1;
#endif

	return rslt;
}

/**
  * @brief  The function is used to search device
  * @retval Search result
  * @param  OW		OneWire HandleTypedef
  */
uint8_t OneWire_Search(OneWire_t* OW, uint8_t Cmd)
{
  if(OW == NULL)
    return 0;
  
	uint8_t id_bit_number 	= 1;
	uint8_t last_zero 		= 0;
	uint8_t rom_byte_number = 0;
	uint8_t search_result 	= 0;
	uint8_t rom_byte_mask 	= 1;
	uint8_t id_bit, cmp_id_bit, search_direction;

	/* if the last call was not the last one */
	if (!OW->LastDeviceFlag)
	{
		if (OneWire_Reset(OW))
		{
			OW->LastDiscrepancy = 0;
			OW->LastDeviceFlag = 0;
			OW->LastFamilyDiscrepancy = 0;
			return 0;
		}

		// issue the search command
		OneWire_WriteByte(OW, Cmd);

		// loop to do the search
		do {
			// read a bit and its complement
			id_bit = OneWire_ReadBit(OW);
			cmp_id_bit = OneWire_ReadBit(OW);

			// check for no devices on 1-wire
			if ((id_bit == 1) && (cmp_id_bit == 1))
			{
				break;
			} else {
				// all devices coupled have 0 or 1
				if (id_bit != cmp_id_bit)
				{
					search_direction = id_bit;  // bit write value for search
				} else {
					/* if this discrepancy if before the Last Discrepancy
					 * on a previous next then pick the same as last time */
					if (id_bit_number < OW->LastDiscrepancy)
					{
						search_direction = ((OW->RomByte[rom_byte_number] & rom_byte_mask) > 0);
					} else {
						// if equal to last pick 1, if not then pick 0
						search_direction = (id_bit_number == OW->LastDiscrepancy);
					}

					// if 0 was picked then record its position in LastZero
					if (search_direction == 0)
					{
						last_zero = id_bit_number;

						// check for Last discrepancy in family
						if (last_zero < 9)
						{
							OW->LastFamilyDiscrepancy = last_zero;
						}
					}
				}

				/* set or clear the bit in the ROM byte rom_byte_number
				 * with mask rom_byte_mask */
				if (search_direction == 1)
				{
					OW->RomByte[rom_byte_number] |= rom_byte_mask;
				} else {
					OW->RomByte[rom_byte_number] &= ~rom_byte_mask;
				}

				// serial number search direction write bit
				OneWire_WriteBit(OW, search_direction);

				/* increment the byte counter id_bit_number and shift the
				 * mask rom_byte_mask */
				id_bit_number++;
				rom_byte_mask <<= 1;

				/* if the mask is 0 then go to new SerialNum byte
				 * rom_byte_number and reset mask */
				if (rom_byte_mask == 0)
				{
					rom_byte_number++;
					rom_byte_mask = 1;
				}
			}
		} while (rom_byte_number < 8);  /* loop until through all ROM bytes 0-7
		if the search was successful then */

		if (!(id_bit_number < 65))
		{
			/* search successful so set LastDiscrepancy, LastDeviceFlag,
			 * search_result */
			OW->LastDiscrepancy = last_zero;
			// check for last device
			if (OW->LastDiscrepancy == 0) {
				OW->LastDeviceFlag = 1;
			}
			search_result = 1;
		}
	}

	/* if no device found then reset counters so next 'search' will be like a
	 * first */
	if (!search_result || !OW->RomByte[0])
	{
		OW->LastDiscrepancy = 0;
		OW->LastDeviceFlag = 0;
		OW->LastFamilyDiscrepancy = 0;
		search_result = 0;
	}

	return search_result;
}

/**
  * @brief  The function is used get ROM full address
  * @param  OW		OneWire HandleTypedef
  * @param  ROM		Pointer to device ROM
  */
void OneWire_GetDevRom(OneWire_t* OW, uint8_t *ROM)
{
	for (uint8_t i = 0; i < 8; i++) {
		*(ROM + i) = OW->RomByte[i];
	}
}

/**
  * @brief  The function is used to initialize OneWire Communication
  * @param  OW		OneWire HandleTypedef
  */
void OneWire_Init(OneWire_t* OW)
{
	OneWire_Pin_Mode(OW, Output);
	OneWire_Pin_Level(OW, 1);
	OneWire_Delay_us(1000);
	OneWire_Pin_Level(OW, 0);
	OneWire_Delay_us(1000);
	OneWire_Pin_Level(OW, 1);
	OneWire_Delay_us(2000);

	/* Reset the search state */
	OW->LastDiscrepancy 		= 0;
	OW->LastDeviceFlag 			= 0;
	OW->LastFamilyDiscrepancy 	= 0;
	OW->RomCnt 					= 0;
}

/**
  * @brief  The function is used selected specific device ROM
  * @param  OW		OneWire HandleTypedef
  * @param  ROM		Pointer to device ROM
  */
void OneWire_MatchROM(OneWire_t* OW, uint8_t *ROM)
{
	OneWire_WriteByte(OW, ONEWIRE_CMD_MATCHROM);

	for (uint8_t i = 0; i < 8; i++)
	{
		OneWire_WriteByte(OW, *(ROM + i));
	}
}

/**
  * @brief  The function is used to access to all ROM
  * @param  OW		OneWire HandleTypedef
  */
void OneWire_Skip(OneWire_t* OW)
{
	OneWire_WriteByte(OW, ONEWIRE_CMD_SKIPROM);
}

/**
  * @brief  The function is used check CRC
  * @param  Addr	Pointer to address
  * @param  ROM		Number of byte
  */
uint8_t OneWire_CRC8(uint8_t *Addr, uint8_t Len)
{
	uint8_t crc = 0;
	uint8_t inbyte, i, mix;

	while (Len--)
	{
		inbyte = *Addr++;

		for (i = 8; i; i--)
		{
			mix = (crc ^ inbyte) & 0x01;
			crc >>= 1;
			crc ^= (mix) ? 0x8C : 0;
			inbyte >>= 1;
		}
	}
	return crc;
}
