/* Includes ------------------------------------------------------------------*/
#include "FE82160.h"

/* Private typedef -----------------------------------------------------------*/
typedef enum
{
  OWIRE_ERROR = 1,
  OWIRE_SUCCESS,
  OWIRE_TX,
  OWIRE_RX,
  OWIRE_TX_RX
} OWIRE_Status;

typedef struct
{
  const uint8_t *TxBuffer;
  uint32_t TxSize;
  uint32_t TxCount;
  uint8_t *RxBuffer;
  uint32_t RxSize;
  uint32_t RxCount;
  OWIRE_Status Status;
} OWIRE_IT_HandleTypeDef;

/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
OWIRE_IT_HandleTypeDef OWIRE_Handler = {0};
uint8_t ReceiveArea[3];

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

void delay(volatile uint32_t n)
{
  while (n--)
    ;
}

void OneWire_Init(void)
{
  OWIRE_InitTypeDef OWIRE_InitStruct;

  SystemCoreClockUpdate();

  RCC_APBPeriphClockCmd(RCC_APBPeriph_OWIRE, ENABLE);
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE);
  GPIO_Init(GPIOD, GPIO_Pin_2, GPIO_OTYPE_OD | GPIO_PUPD_UP | GPIO_AF7);

  OWIRE_DeInit();

  OWIRE_InitStruct.ClockDiv = OWIRE_CLOCK_DIVIDER_1;
  OWIRE_InitStruct.FirstBit = OWIRE_FIRSTBIT_LSB;
  OWIRE_InitStruct.DataSize = OWIRE_DATASIZE_8BIT;
  OWIRE_InitStruct.ReadMode = OWIRE_RDMODE_0;
  OWIRE_InitStruct.NoiseFilterEn = OWIRE_NOISE_FILTER_DISABLE;
  OWIRE_InitStruct.NoiseFilterClk = OWIRE_NOISE_FILTER_CLK_DIV_1;
  OWIRE_InitStruct.ResetCounter = 480;
  OWIRE_InitStruct.PresencePulseCounter = 60;
  OWIRE_InitStruct.BitRateCounter = 60;
  OWIRE_InitStruct.DriveCounter = 3;
  OWIRE_InitStruct.ReadSamplingCounter = 10;
  OWIRE_InitStruct.RecoverCounter = 13;
  OWIRE_Init(&OWIRE_InitStruct);

  NVIC_SetPriority(OWIRE_IRQn, 1);
  NVIC_EnableIRQ(OWIRE_IRQn);

  OWIRE_Cmd(ENABLE);
}

uint8_t OneWire_Receive_IT(uint8_t *RxData, uint32_t RxSize)
{
  OWIRE_Handler.TxBuffer = (void *)0;
  OWIRE_Handler.TxSize = 0;
  OWIRE_Handler.TxCount = 0;
  OWIRE_Handler.RxBuffer = RxData;
  OWIRE_Handler.RxSize = RxSize;
  OWIRE_Handler.RxCount = 0;
  OWIRE_Handler.Status = OWIRE_RX;

  OWIRE_ITConfig(OWIRE_IT_ACKERROR | OWIRE_IT_INIT | OWIRE_IT_RXDONE, ENABLE);
  OWIRE_WriteCommand(OWIRE_COMMAND_INIT); // INIT command

  while ((OWIRE_Handler.Status != OWIRE_SUCCESS) || (OWIRE_Handler.Status != OWIRE_ERROR))
    ;

  return (OWIRE_Handler.Status == OWIRE_SUCCESS);
}

void OWIRE_IRQHandler(void)
{
  uint8_t data;

  /* If error occurs */
  if (OWIRE_GetFlagStatus(OWIRE_FLAG_ACKERROR) != RESET)
  {
    OWIRE_Handler.Status = OWIRE_ERROR;
    OWIRE_ClearFlag(OWIRE_FLAG_ACKERROR);
  }

  /* Initialization done */
  if (OWIRE_GetFlagStatus(OWIRE_FLAG_INITDONE) != RESET)
  {
    switch (OWIRE_Handler.Status)
    {
      case OWIRE_TX_RX:
      case OWIRE_TX:
      {
        if (OWIRE_Handler.TxCount < OWIRE_Handler.TxSize)
        {
          OWIRE_WriteData(OWIRE_Handler.TxBuffer[OWIRE_Handler.TxCount++]);
          OWIRE_WriteCommand(OWIRE_COMMAND_TX);
        }
        else
        {
          OWIRE_Handler.Status = OWIRE_SUCCESS;
        }
      }
      break;
      case OWIRE_RX:
      {
        if (OWIRE_Handler.RxCount < OWIRE_Handler.RxSize)
        {
          OWIRE_WriteCommand(OWIRE_COMMAND_RX);
        }
        else
        {
          OWIRE_Handler.Status = OWIRE_SUCCESS;
        }
      }
      break;
    }

    OWIRE_ClearFlag(OWIRE_FLAG_INITDONE);
  }

  /* Tx done */
  if (OWIRE_GetFlagStatus(OWIRE_FLAG_TXDONE) != RESET)
  {
    if (OWIRE_Handler.TxCount < OWIRE_Handler.TxSize)
    {
      OWIRE_WriteData(OWIRE_Handler.TxBuffer[OWIRE_Handler.TxCount++]);
      OWIRE_WriteCommand(OWIRE_COMMAND_TX);
    }
    else
    {
      if (OWIRE_Handler.Status == OWIRE_TX)
      {
        OWIRE_Handler.Status = OWIRE_SUCCESS;
      }
      else if (OWIRE_Handler.Status == OWIRE_TX_RX)
      {
        OWIRE_WriteCommand(OWIRE_COMMAND_RX);
        OWIRE_Handler.Status = OWIRE_RX;
      }
      else
      {
        OWIRE_Handler.Status = OWIRE_ERROR;
      }
    }
    OWIRE_ClearFlag(OWIRE_FLAG_TXDONE);
  }

  /* Rx done */
  if (OWIRE_GetFlagStatus(OWIRE_FLAG_RXDONE) != RESET)
  {
    data = OWIRE_ReadData();
    if (OWIRE_Handler.RxCount < OWIRE_Handler.RxSize)
    {
      OWIRE_Handler.RxBuffer[OWIRE_Handler.RxCount++] = data;
      if (OWIRE_Handler.RxCount < OWIRE_Handler.RxSize)
      {
        OWIRE_WriteCommand(OWIRE_COMMAND_RX);
      }
    }
    else
    {
      if (OWIRE_Handler.Status == OWIRE_RX)
      {
        OWIRE_Handler.Status = OWIRE_SUCCESS;
      }
      else
      {
        OWIRE_Handler.Status = OWIRE_ERROR;
      }
    }
    OWIRE_ClearFlag(OWIRE_FLAG_RXDONE);
  }
}

int main(void)
{
  SystemCoreClockUpdate();

  // Enable GPIOD clock
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE);

  // Config GPIO
  GPIO_Init(GPIOD, GPIO_Pin_4, GPIO_MODE_OUT | GPIO_OTYPE_PP);
  GPIO_SetBits(GPIOD, GPIO_Pin_4);

  OneWire_Init();
  OneWire_Receive_IT(ReceiveArea, 3);

  /* Infinite loop */
  while (1)
  {
    delay(1000000);
    GPIO_ToggleBits(GPIOD, GPIO_Pin_4);
  }
}

#ifdef USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */

  /* Infinite loop */
  while (1)
  {
  }
}
#endif
