/**
  ******************************************************************************
  * @file    uart.c
  * @author  Разваляев Алексей
  * @brief   Драйвер UART на основе PLIB035.
  *          Этот файл содержит:
  *           + Инициализацию UART0/UART1
  *           + Управление FIFO
  *           + Передачу и приём данных в blocking и interrupt режимах
  *           + Общий обработчик прерываний UART
  *           + Функции настройки GPIO для UART
  *           + Очередь передачи для предотвращения потери данных
  *
  ******************************************************************************
  * @attention
  * 
  * Использование этого драйвера предполагает наличие корректных настроек:
  *  - Определены конфигурационные структуры uartx_config в periph_config.h
  *  - Определены пины TX/RX для UART0/UART1
  *
  ******************************************************************************
  * @verbatim
  ==============================================================================
                        ##### Как использовать этот драйвер #####
  ==============================================================================
  
  1. Настройка в periph_config.h:
      (+) Определите uart0_config, uart1_config для нужных UART
      (+) Используйте макросы для направления:
          • UART_Direction_None - прием и передача отключены
          • UART_Direction_RxTx - полный дуплекс
          • UART_Direction_Tx   - только передача
          • UART_Direction_Rx   - только прием

  2. Инициализация:
      (+) uart_init_first() - настройка GPIO, тактирования и прерываний
      (+) uart_init(&huart, &config) - инициализация конкретного UART

  3. Callback-функции:
      (+) UART_Set_Callback(&huart, UART_Callback_Rx, func)    - при завершении приема
      (+) UART_Set_Callback(&huart, UART_Callback_Tx, func)    - при завершении передачи
      (+) UART_Set_Callback(&huart, UART_Callback_Idle, func)  - при обнаружении IDLE
      (+) UART_Set_Callback(&huart, UART_Callback_Error, func) - при ошибках

  4. Запуск UART:
      (+) UART_Start(&huart, TxFifoLevel, RxFifoLevel) - включение UART и настройка FIFO

  5. Передача и приём данных:
      - Режим Polling:
          (+) UART_Transmit(&huart, buf, size, timeout) - блокирующая передача
          (+) UART_Receive(&huart, buf, size, timeout)  - блокирующий прием
      - Режим Interrupt:
          (+) UART_Transmit_IT(&huart, buf, size) - неблокирующая передача
          (+) UART_Receive_IT(&huart, buf, size)  - неблокирующий прием

  6. Обработка прерываний:
      (+) Прерывания обрабатывают TX FIFO, RX FIFO, ошибки и состояние IDLE

  7. GPIO для UART:
      (+) Пины настраиваются автоматически при вызове uart_init_first()
      (+) При необходимости можно вызвать uart0_gpio_deinit() для восстановления

  ==============================================================================
                          ##### Особенности работы #####
  ==============================================================================

  - Очередь передачи (USE_TX_QUEUE):
      - Циклический буфер на 32 сообщения (TX_QUEUE_SIZE)
      - Предотвращает потерю данных при частой отправке
      - Включается автоматически при USE_TX_QUEUE=1 в uart.h
      - Сообщения обрабатываются последовательно по завершении предыдущей передачи

  - FIFO и прерывания:
      - Аппаратный FIFO 16 байт для приема и передачи
      - Уровни прерываний настраиваются через TxFifoLevel и RxFifoLevel
      - Прерывание TX FIFO генерируется когда FIFO не полон
      - Прерывание RX FIFO генерируется когда FIFO не пуст

  - Состояние IDLE:
      - Обнаруживается по таймауту приема (32 бит-времени)
      - Генерирует прерывание RecieveTimeout
      - Полезно для определения конца пакета переменной длины

  - Обработка ошибок:
      - Обрабатываются все типы ошибок UART: фрейм, паритет, переполнение, break
      - При ошибке вызывается ErrCallback и сбрасываются флаги ошибок
      - Ошибки не останавливают работу UART

  - GPIO автоматическая настройка:
      - Пины TX/RX настраиваются в AltFunc режим при инициализации
      - Поддерживаются стандартные пины (PB10/PB11 для UART0, PB8/PB9 для UART1)

  - Режимы работы:
      - Polling: простой, но блокирующий, подходит для инициализации и отладки
      - Interrupt: неблокирующий, требует прерываний и поддерживает callback-функций
      - Queue: расширенный interrupt режим с буферизацией сообщений

  @endverbatim
  ******************************************************************************
  */



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

UART_HandleTypeDef huart0; /*!< Хендл UART0 */
UART_HandleTypeDef huart1; /*!< Хендл UART1 */


//-- Private function prototypes -----------------------------------------------
static int __uart_fifo_receive(UART_HandleTypeDef *huart, uint8_t it_mode);
static int __uart_fifo_transmit(UART_HandleTypeDef *huart, uint8_t it_mode);
#if USE_TX_QUEUE==1
static void __uart_tx_queue_process(UART_HandleTypeDef *huart);
static OperationStatus __uart_tx_queue_push(UART_HandleTypeDef *huart, const uint8_t *buf, uint16_t size);
static OperationStatus __uart_tx_queue_pop(UART_HandleTypeDef *huart);
#endif
//-- Defines -------------------------------------------------------------------




//-- UART Init functions -------------------------------------------------------

/**
  * @brief  Первичная инициализация UART (UART0 / UART1)
  * @details Настройка UART и хендлов: GPIO, тактирование и прерывания
  */
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.Instance = 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.Instance = 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_ExtInit_TypeDef *NewConfig)
{
  if(!huart || !huart->Instance)
    return ERROR;
  
  if(NewConfig != NULL)
  {
    huart->Config = NewConfig;
  }
  if(huart->Config == NULL)
  {
    return ERROR;
  }
  
  UART_Init(huart->Instance, &huart->Config->UART_Init);
  
  return OK;
}



/**
  * @brief  Установка коллбека UART
  * @param  htmr указатель на хендл UART
  * @param  CallbackType Тип коллбека
  * @param  Callback Функция коллбека
  * @retval void
  */
OperationStatus UART_Set_Callback(UART_HandleTypeDef* huart, UART_CallbackTypeDef CallbackType, void (*Callback)(void))
{
  if (!huart || !huart->Instance || !huart->Config)
    return ERROR;
  
  switch(CallbackType)
  {
    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;
}


//-- UART API functions --------------------------------------------------------

/**
  * @brief  Запуск UART и инициализация FIFO
  * @param  huart указатель на хендл UART
  * @param  TxFifoLevel уровень прерывания TX FIFO
  * @param  RxFifoLevel уровень прерывания RX FIFO
  * @retval OperationStatus OK - если успешно, ERROR - при ошибке
  */
OperationStatus UART_Start(UART_HandleTypeDef *huart, UART_FIFOLevel_TypeDef TxFifoLevel, UART_FIFOLevel_TypeDef RxFifoLevel)
{
  if (!huart)
    return ERROR;
    
  UART_ITFIFOLevelRxConfig(huart->Instance, RxFifoLevel);
  UART_ITFIFOLevelTxConfig(huart->Instance, 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;
  
#if USE_TX_QUEUE==1
  huart->TxQueue.QueueHead = 0;
  huart->TxQueue.QueueTail = 0;
  huart->TxQueue.QueueCount = 0;
  huart->TxQueue.QueueEmpty = 1;
  huart->TxQueue.QueueFull = 0;
  
  for (uint8_t i = 0; i < TX_QUEUE_SIZE; i++)
  {
    huart->TxQueue.Queue[i].Buf = NULL;
    huart->TxQueue.Queue[i].Size = 0;
    huart->TxQueue.Queue[i].InUse = 0;
  }
#endif
  
  
  
  UART_Cmd(huart->Instance, ENABLE);
  
  return OK;
}
/**
  * @brief  Передача данных по UART (блокирующий режим)
  * @param  huart указатель на хендл UART
  * @param  buf указатель на буфер данных
  * @param  size размер данных в байтах
  * @param  timeout таймаут ожидания (мс)
  * @retval OperationStatus OK - если успешно, ERROR - при ошибке или таймауте
  */
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;
}
/**
  * @brief  Приём данных по UART (блокирующий режим)
  * @param  huart указатель на хендл UART
  * @param  buf указатель на буфер приёма
  * @param  size количество принимаемых байт
  * @param  timeout таймаут ожидания (мс)
  * @retval OperationStatus OK - если успешно, ERROR - при ошибке или таймауте
  */
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;
}

/**
  * @brief  Передача данных по UART (прерывания)
  * @param  huart указатель на хендл UART
  * @param  buf указатель на буфер данных
  * @param  size размер данных в байтах
  * @retval OperationStatus OK - если успешно, ERROR - если передача уже идёт
  */
OperationStatus UART_Transmit_IT(UART_HandleTypeDef *huart,
                                uint8_t *buf,
                                uint16_t size)
{
  if (!huart || !buf || size == 0)
    return ERROR;

#if USE_TX_QUEUE==1
  // Автоматически используем очередь
  if (huart->TxQueue.QueueFull)
    return ERROR;
    
  OperationStatus status = __uart_tx_queue_push(huart, buf, size);
  if (status != OK)
    return ERROR;
    
  if (!huart->TxBusy)
  {
    __uart_tx_queue_process(huart);
  }
  
  return OK;
#else
  // Без очереди
  // Если TX занят, возвращаем ERROR
  if (huart->TxBusy)
    return ERROR;

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

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

  return OK;
#endif
}

/**
  * @brief  Приём данных по UART (прерывания)
  * @param  huart указатель на хендл UART
  * @param  buf указатель на буфер приёма
  * @param  size количество принимаемых байт
  * @retval OperationStatus OK - если успешно, ERROR - если приём уже идёт
  */
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->Instance,
             UART_ITSource_RxFIFOLevel |
             UART_ITSource_RecieveTimeout |
             UART_ITSource_ErrorFrame |
             UART_ITSource_ErrorParity |
             UART_ITSource_ErrorOverflow |
             UART_ITSource_ErrorBreak,
             ENABLE);

  return OK;
}

//-- UART Handler functions ---------------------------------------------------
/**
  * @brief  Общий обработчик прерываний UART
  * @param  huart указатель на хендл UART
  * @details   Обрабатывает RX/TX FIFO, ошибки и события Idle
  * @retval void
  */
void uart_irq_handler(UART_HandleTypeDef *huart)
{
  if (!huart || !huart->Instance || !huart->Config)
    return;

//  GPIO_SetBits(GPIOA, GPIO_Pin_7);
  
  UART_TypeDef *uart = huart->Instance;
  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();
    
#if USE_TX_QUEUE==1
    // Если есть очередь, обрабатываем следующий пакет
    __uart_tx_queue_process(huart);
#endif
    
  }
//  GPIO_ClearBits(GPIOA, GPIO_Pin_7);
}



//-- UART GPIO functions -------------------------------------------------------

/**
  * @brief  Инициализация GPIO для UART0
  */
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
}
/**
  * @brief  Деинициализация GPIO для UART0
  */
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
}
/**
  * @brief  Инициализация GPIO для UART1
  */
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
}
/**
  * @brief  Деинициализация GPIO для UART1
  */
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
}

//-- UART private functions ----------------------------------------------------
/**
  * @brief  Приём данных из RX FIFO
  * @param  huart указатель на хендл UART
  * @param  it_mode режим работы (0 — polling, 1 — прерывания)
  * @retval int 1 — приём завершён, 0 — данные ещё принимаются
  */
static int __uart_fifo_receive(UART_HandleTypeDef *huart, uint8_t it_mode)
{
  while (!UART_FlagStatus(huart->Instance, UART_Flag_RxFIFOEmpty) &&
         huart->RxCount < huart->RxSize)
  {
    huart->RxBufPtr[huart->RxCount++] = UART_RecieveData(huart->Instance);

    if (huart->RxCount == huart->RxSize)
    {
      huart->RxBusy = 0;
      
      if(it_mode)
      {
        // Выключаем RX прерывания
        UART_ITCmd(huart->Instance,
          UART_ITSource_RxFIFOLevel |
          UART_ITSource_RecieveTimeout,
          DISABLE);
      }
        
      if (huart->Config->RxCallback)
        huart->Config->RxCallback();
      return 1;
    }
  }
  return 0;
}
/**
  * @brief  Передача данных в TX FIFO
  * @param  huart указатель на хендл UART
  * @param  it_mode режим работы (0 — polling, 1 — прерывания)
  * @retval int 1 — передача завершена, 0 — данные ещё передаются
  */
static int __uart_fifo_transmit(UART_HandleTypeDef *huart, uint8_t it_mode)
{
  while (!UART_FlagStatus(huart->Instance, UART_Flag_TxFIFOFull) &&
         huart->TxCount < huart->TxSize)
  {
    UART_SendData(huart->Instance, huart->TxBufPtr[huart->TxCount++]);
  }

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

#if USE_TX_QUEUE==1
static void __uart_tx_queue_process(UART_HandleTypeDef *huart)
{
  if (!huart->TxBusy && !huart->TxQueue.QueueEmpty)
  {
    __uart_tx_queue_pop(huart);
  }
}

static OperationStatus __uart_tx_queue_push(UART_HandleTypeDef *huart, const uint8_t *buf, uint16_t size)
{
  if (!huart || !buf || size == 0 || huart->TxQueue.QueueFull)
    return ERROR;
    
  huart->TxQueue.Queue[huart->TxQueue.QueueHead].Buf = buf;
  huart->TxQueue.Queue[huart->TxQueue.QueueHead].Size = size;
  huart->TxQueue.Queue[huart->TxQueue.QueueHead].InUse = 1;
  
  huart->TxQueue.QueueHead = (huart->TxQueue.QueueHead + 1) % TX_QUEUE_SIZE;
  huart->TxQueue.QueueCount++;
  
  huart->TxQueue.QueueEmpty = 0;
  if (huart->TxQueue.QueueCount == TX_QUEUE_SIZE)
    huart->TxQueue.QueueFull = 1;
    
  return OK;
}
static OperationStatus __uart_tx_queue_pop(UART_HandleTypeDef *huart)
{
  if (!huart || huart->TxQueue.QueueEmpty)
    return ERROR;
    
  const uint8_t *buf = huart->TxQueue.Queue[huart->TxQueue.QueueTail].Buf;
  uint16_t size = huart->TxQueue.Queue[huart->TxQueue.QueueTail].Size;
  
  if (huart->TxBusy)
    return ERROR;

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

  UART_ITCmd(huart->Instance, UART_ITSource_TxFIFOLevel, ENABLE);
  __uart_fifo_transmit(huart, 1);
  
  huart->TxQueue.Queue[huart->TxQueue.QueueTail].Buf = NULL;
  huart->TxQueue.Queue[huart->TxQueue.QueueTail].Size = 0;
  huart->TxQueue.Queue[huart->TxQueue.QueueTail].InUse = 0;
  
  huart->TxQueue.QueueTail = (huart->TxQueue.QueueTail + 1) % TX_QUEUE_SIZE;
  huart->TxQueue.QueueCount--;
  
  huart->TxQueue.QueueFull = 0;
  if (huart->TxQueue.QueueCount == 0)
    huart->TxQueue.QueueEmpty = 1;
  
  return OK;
}
#endif
