/*==============================================================================
 * Инициализация тактирования с использованием бибилотеки PLIB035
 *------------------------------------------------------------------------------
 * ЦНИИ СЭТ, Разваляев Алексей <wot890089@mail.ru>
 *==============================================================================
 *                                  ЦНИИ СЭТ
 *==============================================================================
 */

//-- Includes ------------------------------------------------------------------
#include "periph_config.h"

UART_HandleTypeDef huart0;
UART_HandleTypeDef huart1;



static int __uart_fifo_receive(UART_HandleTypeDef *huart, uint8_t it_mode);
static int __uart_fifo_transmit(UART_HandleTypeDef *huart, uint8_t it_mode);
//-- Defines -------------------------------------------------------------------




//-- Peripheral init functions -------------------------------------------------
void uart_init_first(void)
{
  
#if (USE_UART0==1)
  // Настройка пинов для UART0
  uart0_gpio_init();
  
  // Включаем тактирование UART0
  RCU_UARTClkConfig(UART0_Num, RCU_PeriphClk_PLLClk, 0, DISABLE);
  RCU_UARTClkCmd(UART0_Num, ENABLE);
  UART_DeInit(UART0);
  // Инициализируем UART0
  huart0.UART = UART0;
  uart_init(&huart0, &uart0_config);
  NVIC_EnableIRQ(UART0_TD_IRQn);
  NVIC_EnableIRQ(UART0_RX_IRQn);
  NVIC_EnableIRQ(UART0_TX_IRQn);
  NVIC_EnableIRQ(UART0_E_RT_IRQn);
#endif
  
  
#if (USE_UART1==1)
  // Настройка пинов для UART1
  uart1_gpio_init();
  
  // Включаем тактирование UART1
  RCU_UARTClkConfig(UART1_Num, RCU_PeriphClk_PLLClk, 0, DISABLE);
  RCU_UARTClkCmd(UART1_Num, ENABLE);
  UART_DeInit(UART1);  
  
  // Инициализируем UART1
  NVIC_EnableIRQ(UART1_TD_IRQn);
  NVIC_EnableIRQ(UART1_RX_IRQn);
  NVIC_EnableIRQ(UART1_TX_IRQn);
  NVIC_EnableIRQ(UART1_E_RT_IRQn);
  
  huart1.UART = UART1;
  uart_init(&huart1, &uart1_config);
  
#endif
}




/**
  * @brief  Инициализация UART
  * @param  htmr указатель на хендл UART
  * @param  NewConfig указатель на новую конфигурацию UART, иначе используется та, что в структуре
  * @retval OperationStatus OK - если всё хорошо, ERROR - если ошибка
  */
OperationStatus uart_init(UART_HandleTypeDef *huart, UART_ExtInitTypeDef *NewConfig)
{
  if(huart->UART == NULL)
  {
    return ERROR;
  }
  if(NewConfig != NULL)
  {
    huart->Config = NewConfig;
  }
  if(huart->Config == NULL)
  {
    return ERROR;
  }
  
  UART_Init(huart->UART, &huart->Config->UART_Init);
  
  return OK;
}

OperationStatus uart_start(UART_HandleTypeDef *huart, UART_FIFOLevel_TypeDef TxFifoLevel, UART_FIFOLevel_TypeDef RxFifoLevel)
{
  if (huart == NULL)
    return ERROR;
    
  UART_ITFIFOLevelRxConfig(huart->UART, RxFifoLevel);
  UART_ITFIFOLevelTxConfig(huart->UART, TxFifoLevel);

  
  huart->TxBufPtr = NULL;
  huart->TxCount  = 0;
  huart->TxSize   = 0;
  huart->TxBusy   = 0;

  huart->RxBufPtr = NULL;
  huart->RxCount  = 0;
  huart->RxSize   = 0;
  huart->RxBusy   = 0;
  
  UART_Cmd(huart->UART, ENABLE);
  
  return OK;
}

OperationStatus uart_transmit(UART_HandleTypeDef *huart,
                              uint8_t *buf,
                              uint16_t size,
                              uint32_t timeout)
{
  if (!huart || !buf || size == 0)
    return ERROR;

  // Если TX занят, возвращаем ERROR
  if (huart->TxBusy)
    return ERROR;

  huart->TxBufPtr = buf;
  huart->TxSize   = size;
  huart->TxCount  = 0;
  huart->TxBusy   = 1;

  uint32_t starttick = millis();
  // Отправляем пока всё не отправится
  while(__uart_fifo_transmit(huart, 0) == 0)
  {
    if(millis() - starttick > timeout)
      return ERROR;
  }
  
  return OK;
}

OperationStatus uart_receive(UART_HandleTypeDef *huart,
                              uint8_t *buf,
                              uint16_t size,
                              uint32_t timeout)
{
  if (!huart || !buf || size == 0)
    return ERROR;

  // Если RX занят, возвращаем ERROR
  if (huart->RxBusy)
    return ERROR;

  huart->RxBufPtr = buf;
  huart->RxSize   = size;
  huart->RxCount  = 0;
  huart->RxBusy   = 1;
  
  uint32_t starttick = millis();
  // Принимаем всё пока всё не примется
  while(__uart_fifo_transmit(huart, 0) == 0)
  {
    if(millis() - starttick > timeout)
      return ERROR;;
  }
  
  return OK;
}


OperationStatus uart_transmit_it(UART_HandleTypeDef *huart,
                                uint8_t *buf,
                                uint16_t size)
{
  if (!huart || !buf || size == 0)
    return ERROR;

  // Если TX занят, возвращаем ERROR
  if (huart->TxBusy)
    return ERROR;

  huart->TxBufPtr = buf;
  huart->TxSize   = size;
  huart->TxCount  = 0;
  huart->TxBusy   = 1;

  // Включаем прерывания по TX FIFO
  UART_ITCmd(huart->UART, UART_ITSource_TxFIFOLevel, ENABLE);
  
  __uart_fifo_transmit(huart, 1);

  return OK;
}



OperationStatus uart_receive_it(UART_HandleTypeDef *huart,
                               uint8_t *buf,
                               uint16_t size)
{
  if (!huart || !buf || size == 0)
    return ERROR;

  // Если RX занят, возвращаем ERROR
  if (huart->RxBusy)
    return ERROR;

  huart->RxBufPtr = buf;
  huart->RxSize   = size;
  huart->RxCount  = 0;
  huart->RxBusy   = 1;

  // Включаем только RX прерывания
  UART_ITCmd(huart->UART,
             UART_ITSource_RxFIFOLevel |
             UART_ITSource_RecieveTimeout |
             UART_ITSource_ErrorFrame |
             UART_ITSource_ErrorParity |
             UART_ITSource_ErrorOverflow |
             UART_ITSource_ErrorBreak,
             ENABLE);

  return OK;
}


void uart_handler(UART_HandleTypeDef *huart)
{
  if (!huart || !huart->UART || !huart->Config)
    return;

  UART_TypeDef *uart = huart->UART;
  uint32_t mis = uart->MIS;

  // Ошибки
  if (mis & (UART_ITSource_ErrorFrame |
             UART_ITSource_ErrorParity |
             UART_ITSource_ErrorOverflow |
             UART_ITSource_ErrorBreak))
  {
    if (huart->Config->ErrCallback)
      huart->Config->ErrCallback();

    UART_ErrorStatusClear(uart, UART_Error_All);
    UART_ITStatusClear(uart, mis);
  }

  // RX FIFO
  if (mis & UART_ITSource_RxFIFOLevel)
  {
    // Принимаем
    if(__uart_fifo_receive(huart, 1))
    { // Когда всё приняли, флаги сбросили внутри функции
    }
      
    UART_ITStatusClear(uart, UART_ITSource_RxFIFOLevel);
  }

  // IDLE / Receive Timeout
  if (mis & UART_ITSource_RecieveTimeout)
  {
    UART_ITStatusClear(uart, UART_ITSource_RecieveTimeout);

    huart->RxBusy = 0;
    // Выключаем RX прерывания до следующего вызова uart_receive_it
    UART_ITCmd(uart,
      UART_ITSource_RxFIFOLevel |
      UART_ITSource_RecieveTimeout,
      DISABLE);

    if (huart->Config->IdleCallback)
      huart->Config->IdleCallback();
  }

  // TX FIFO
  if (mis & UART_ITSource_TxFIFOLevel)
  {
    // Отправляем
    if(__uart_fifo_transmit(huart, 1))
      { // Когда всё отправили, флаги сбросили внутри функции
    }

    UART_ITStatusClear(uart, UART_ITSource_TxFIFOLevel);
  }
  // Конец передачи
  if (mis & UART_ITSource_TransmitDone)
  {
    UART_ITStatusClear(uart, UART_ITSource_TransmitDone);
    huart->TxBusy = 0;

    if (huart->Config->TxCallback)
      huart->Config->TxCallback();
  }
}




/**
  * @brief  Установка коллбека UART
  * @param  htmr указатель на хендл UART
  * @param  cb_type Тип коллбека
  * @param  Callback Функция коллбека
  * @retval void
  */
OperationStatus uart_set_callback(UART_HandleTypeDef* huart, UART_CallbackTypeDef cb_type, void (*Callback)(void))
{
  if((huart->UART == NULL) || (huart->Config == NULL))
  {
    return ERROR;
  }
  
  switch(cb_type)
  {
    case UART_Callback_Rx:
      huart->Config->RxCallback = Callback;
      break;
    case UART_Callback_Tx:
      huart->Config->TxCallback = Callback;
      break;
    case UART_Callback_Idle:
      huart->Config->IdleCallback = Callback;
      break;
    case UART_Callback_Error:
      huart->Config->ErrCallback = Callback;
      break;
    
    default:
      return ERROR;
  }
  return OK;
}


void uart0_gpio_init(void)
{
#if USE_UART0==1
  // Получаем структуры
  GPIO_Init_TypeDef *tx_config = gpio_get_init(UART0_GPIO_Port, UART0_Tx_Pin);
  GPIO_Init_TypeDef *rx_config = gpio_get_init(UART0_GPIO_Port, UART0_Rx_Pin);
  
  // TX пин
  if (uart0_config.UART_Init.Tx == ENABLE && tx_config != NULL)
  {
    GPIO_StructInit(tx_config);
    tx_config->AltFunc = ENABLE;
    tx_config->Digital = ENABLE;
    tx_config->Pin = UART0_Tx_Pin;
    GPIO_Init(UART0_GPIO_Port, tx_config);
  }
  
  // RX пин
  if (uart0_config.UART_Init.Rx == ENABLE && rx_config != NULL)
  {
    GPIO_StructInit(rx_config);
    rx_config->AltFunc = ENABLE;
    rx_config->Digital = ENABLE;
    rx_config->Pin = UART0_Rx_Pin;
    GPIO_Init(UART0_GPIO_Port, rx_config);
  }
#endif
}

void uart0_gpio_deinit(void)
{
#if USE_UART0==1
  // Получаем структуры
  GPIO_Init_TypeDef *tx_config = gpio_get_init(UART0_GPIO_Port, UART0_Tx_Pin);
  GPIO_Init_TypeDef *rx_config = gpio_get_init(UART0_GPIO_Port, UART0_Rx_Pin);
  
  // Восстанавливаем оригинальные настройки из таблицы
  if (tx_config != NULL)
  {
    GPIO_StructInit(rx_config);
    rx_config->Pin = UART0_Tx_Pin;
    GPIO_Init(UART0_GPIO_Port, tx_config);
  }
  
  if (rx_config != NULL)
  {
    GPIO_StructInit(rx_config);
    rx_config->Pin = UART0_Rx_Pin;
    GPIO_Init(UART0_GPIO_Port, rx_config);
  }
#endif
}

void uart1_gpio_init(void)
{  
#if USE_UART1==1
  // Получаем структуры
  GPIO_Init_TypeDef *tx_config = gpio_get_init(UART1_GPIO_Port, UART1_Tx_Pin);
  GPIO_Init_TypeDef *rx_config = gpio_get_init(UART1_GPIO_Port, UART1_Rx_Pin);
  
  // TX пин
  if (uart1_config.UART_Init.Tx == ENABLE && tx_config != NULL)
  {
    GPIO_StructInit(tx_config);
    tx_config->AltFunc = ENABLE;
    tx_config->Digital = ENABLE;
    tx_config->Pin = UART1_Tx_Pin;
    GPIO_Init(UART1_GPIO_Port, tx_config);
  }
  
  // RX пин
  if (uart1_config.UART_Init.Rx == ENABLE && rx_config != NULL)
  {
    ;
    GPIO_StructInit(rx_config);
    rx_config->AltFunc = ENABLE;
    rx_config->Digital = ENABLE;
    rx_config->Pin = UART1_Rx_Pin;
    GPIO_Init(UART1_GPIO_Port, rx_config);
  }
#endif
}

void uart1_gpio_deinit(void)
{
#if USE_UART1==1
  // Получаем структуры
  GPIO_Init_TypeDef *tx_config = gpio_get_init(UART1_GPIO_Port, UART1_Tx_Pin);
  GPIO_Init_TypeDef *rx_config = gpio_get_init(UART1_GPIO_Port, UART1_Rx_Pin);
  
  // Восстанавливаем оригинальные настройки из таблицы
  if (tx_config != NULL)
  {
    GPIO_StructInit(rx_config);
    rx_config->Pin = UART1_Tx_Pin;
    GPIO_Init(UART1_GPIO_Port, tx_config);
  }
  
  if (rx_config != NULL)
  {
    GPIO_StructInit(rx_config);
    rx_config->Pin = UART1_Rx_Pin;
    GPIO_Init(UART1_GPIO_Port, rx_config);
  }
#endif
}


static int __uart_fifo_receive(UART_HandleTypeDef *huart, uint8_t it_mode)
{
  while (!UART_FlagStatus(huart->UART, UART_Flag_RxFIFOEmpty) &&
         huart->RxCount < huart->RxSize)
  {
    huart->RxBufPtr[huart->RxCount++] = UART_RecieveData(huart->UART);

    if (huart->RxCount == huart->RxSize)
    {
      huart->RxBusy = 0;
      
      if(it_mode)
      {
        // Выключаем RX прерывания
        UART_ITCmd(huart->UART,
          UART_ITSource_RxFIFOLevel |
          UART_ITSource_RecieveTimeout,
          DISABLE);
      }
        
      if (huart->Config->RxCallback)
        huart->Config->RxCallback();
      return 1;
    }
  }
  return 0;
}
static int __uart_fifo_transmit(UART_HandleTypeDef *huart, uint8_t it_mode)
{
  while (!UART_FlagStatus(huart->UART, UART_Flag_TxFIFOFull) &&
         huart->TxCount < huart->TxSize)
  {
    UART_SendData(huart->UART, huart->TxBufPtr[huart->TxCount++]);
  }

  if (huart->TxCount == huart->TxSize)
  {
    if(it_mode)
    {
      // Выключаем FIFO прерывание
      UART_ITCmd(huart->UART, UART_ITSource_TxFIFOLevel, DISABLE);
      // Включаем TransmitDone прерывание, коллбек будет по нему
      UART_ITCmd(huart->UART, UART_ITSource_TransmitDone, ENABLE);
    }
    else
    {
      while(!UART_FlagStatus(huart->UART, UART_Flag_TxFIFOEmpty)); // ждем пока не опустошится буфер
      
      huart->TxBusy = 0;
      if (huart->Config->TxCallback)
        huart->Config->TxCallback();
    }
    
    return 1;
  }
  return 0;
}