/**
 *******************************************************************************
 * @file    dpm32m0xx_uart.c
 *
 * @brief   Source file for UART firmware driver.
 *          This file provides firmware functions to manage the following
 *          functionalities of the Universal asynchronous receiver transmitter
 *          (UART) peripheral :
 *           + Initialization and Configuration function
 *           + Data transfers
 *           + Half-duplex mode
 *           + DMA transfers management
 *           + Interrupts and flags management
 *
 * @author  DPM
 *
 * @version V1.0.0
 *
 * @date    2023-11-01
 *
 * @verbatim
 ===============================================================================
                       ##### How to use this driver #####
 ===============================================================================
    [..]
      (#) Enable peripheral clock using the following functions
          RCC_APBPeriphClockCmd(RCC_APB_PERIPH_UARTx, ENABLE).

      (#) According to the UART mode, enable the GPIO clocks using
          RCC_AHBPeriphClockCmd(RCC_AHB_PERIPH_GPIO, ENABLE) function.
          (The I/O can be TX, RX).

      (#) Peripheral's alternate function:
        (+) Connect the pin to the desired peripherals' Alternate
            Function (AF) using GPIO_AltFuncConfig() function.
        (+) Configure the desired pin in alternate function by:
            GPIO_InitStruct->GPIO_Mode = GPIO_MODE_ALT_FUNC.
        (+) Select the type, pull-up/pull-down and output speed via
            GPIO_PuPd, GPIO_OType and GPIO_Speed members.
        (+) Call GPIO_Init() function.

      (#) Program the Baud Rate, Word Length, Stop Bit, Parity, and
          Mode(Receiver/Transmitter) using the UART_Init() function.

      (#) Enable the NVIC and the corresponding interrupt using
         UART_IntCmd() function if you need to use interrupt mode.

      (#) Enable the half duplex mode using UART_HalfDuplexCmd() function
          if you need to use half duplex mode.

      (#) Program the first data using UART_DataFirstConfig() function.

      (#) When using the DMA mode.
        (+) Configure the DMA using DMA_Init() function.
        (+) Active the needed channel Request using UART_DMACmd() function.

      (#) Enable the UART using the UART_Cmd() function.

      (#) Enable the DMA using the DMA_Cmd() function, when using DMA mode.

      (#) Data transfers using the UART_SendData() and UART_ReceiveData()
          function.

      (#) UART interrupt configuration:
        (+) To activate the UART interrupt, using UART_IntCmd() function.
        (+) Check on UART interrupt enable flags using UART_GetIntCmdStatus()
            function.
        (+) Check on UART interrupt occur flags using UART_GetIntFlagStatus()
            function.
        (+) Clear UART interrupt flags using UART_ClearIntFlag() function.

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

#include "dpm32m0xx_uart.h"
#include "dpm32m0xx_rcc.h"

/**
 *******************************************************************************
 * @brief   Deinitializes the UARTx peripheral registers to their default reset values.
 * @param   [in]  UARTx: Where x can be (0, 1, 2) to select the UART peripheral.
 * @retval  None.
 ******************************************************************************/
void UART_DeInit(UART_Type *UARTx)
{
  /* Reset UARTx Configuration,disable all UARTx interrupt. */
  UARTx->CR = (uint32_t)0x00000010;

  /* Set baud rate register as default value. */
  UARTx->BRR = (uint32_t)0x00000341;

  /* Set transmit register as default value. */
  UARTx->TDR = (uint16_t)0x0000;

  /* Clear all interrupt flag. */
  UARTx->ISR = (uint32_t)0x0000007F;
}

/**
 *******************************************************************************
 * @brief   Get the baud rate fractional value to bit.
 * @param   [in]  value: Baud rate fractional.
 * @retval  uint8_t: fractional to bit.
 ******************************************************************************/
static uint8_t UART_GetBaudRateFraDiv(double value)
{
  uint8_t data = 0, i = 0;

  /*  Convert 4 bits. */
  for(i = 0; i < 4; i++)
  {
    data <<= 1;

    /* If the current bit is greater than 0.5, the corresponding position 1. */
    if((value * 2) >= 1)
    {
      value = value * 2 - 1;
      data |= 1;
    }
    else
    {
      value = value * 2;
    }
  }

  return data & 0xf;
}

/**
 *******************************************************************************
 * @brief   Initializes the UARTx peripheral according to the specified parameters in the UART_InitType.
 * @param   [in]  UARTx: Where x can be (0, 1, 2) to select the UART peripheral.
 * @param   [in]  UART_InitType: Structure pointer of UART configuration.
 * @retval  None.
 ******************************************************************************/
void UART_Init(UART_Type *UARTx, UART_InitTypeStruct *UART_InitType)
{
  uint32_t tmpReg = 0UL;
  double brr = 0;
  /* Parameters check. */
  PARAM_ASSERT(IS_UART_PERIPH(UARTx));
  PARAM_ASSERT(IS_UART_BAUDRATE(UART_InitType->UART_BaudRate));
  PARAM_ASSERT(IS_UART_DATALEN(UART_InitType->UART_DataLen));
  PARAM_ASSERT(IS_UART_MODE(UART_InitType->UART_Mode));
  PARAM_ASSERT(IS_UART_PARITY(UART_InitType->UART_Parity));
  PARAM_ASSERT(IS_UART_STOPBITs(UART_InitType->UART_StopBits));

  /*---------------------------- UART CR Configuration -----------------------*/
  tmpReg = UARTx->CR;

  /* Clear DATA_LEN[1:0],STOP_LEN,PARITY_EN,PARITY_SEL,RX_EN,TX_EN bits. */
  tmpReg &= ~(UART_CR_DATA_LEN_Msk | UART_CR_STOP_LEN_Msk \
              | UART_CR_PARITY_SEL_Msk | UART_CR_PARITY_EN_Msk \
              | UART_CR_RX_EN_Msk | UART_CR_TX_EN_Msk);

  /* Set DATA_LEN[1:0] bits with UART_InitType->UART_DataLen param. */
  tmpReg |= (UART_InitType->UART_DataLen);

  /* Set STOP_LEN bit with UART_InitType->UART_StopBits param. */
  tmpReg |= (UART_InitType->UART_StopBits);

  /* Set PARITY_EN and PARITY_SEL bits with UART_InitType->UART_Parity param. */
  tmpReg |= (UART_InitType->UART_Parity);

  /* Set RX_EN and TX_EN bits with UART_InitType->UART_Mode param. */
  tmpReg |= (UART_InitType->UART_Mode);

  /* Store the new value. */
  UARTx->CR = tmpReg;

  /*---------------------------- UART BRR Configuration -----------------------*/
  tmpReg = UARTx->BRR;

  /* Clear DIV_INT[15:0] and DIV_FRAC[3:0] bits. */
  tmpReg &= ~(UART_BRR_DIV_INT_Msk | UART_BRR_DIV_FRAC_Msk);

  /* Get UART baud rate division value. */
  brr = (RCC_GetPCLKFrequency() / (double)UART_InitType->UART_BaudRate);

  /* Set DIV_INT[15:0] bits. */
  tmpReg |= (((uint32_t)(int)brr) << UART_BRR_DIV_INT_Pos);

  /* Set DIV_FRAC[3:0] bits. */
  tmpReg |= ((uint32_t)UART_GetBaudRateFraDiv(brr - (int)brr) << UART_BRR_DIV_FRAC_Pos);

  /* Store the new value. */
  UARTx->BRR = tmpReg;
}

/**
 *******************************************************************************
 * @brief   Initialize the UART_InitType with default parameters.
 * @param   [out]  UART_InitType: Pointer to a UART_InitTypeStruct structure
 *                 which will be initialized.
 * @retval  None.
 ******************************************************************************/
void UART_StructInit(UART_InitTypeStruct *UART_InitType)
{
  UART_InitType->UART_BaudRate = 115200;
  UART_InitType->UART_DataLen = UART_DATA_LEN_8;
  UART_InitType->UART_StopBits = UART_STOP_BITS_1;
  UART_InitType->UART_Parity = UART_PARITY_NO;
  UART_InitType->UART_Mode = UART_MODE_RX_TX;
}

/**
 *******************************************************************************
 * @brief   Enables or disables the specified UART peripheral.
 * @param   [in]  UARTx: Where x can be (0, 1, 2) to select the UART peripheral.
 * @param   [in]  NewState: New state of the UART peripheral.
 *                This parameter can be: ENABLE or DISABLE..
 * @retval  None.
 ******************************************************************************/
void UART_Cmd(UART_Type *UARTx, FunctionalState NewState)
{
  if(DISABLE != NewState)
  {
    /* Enable UART peripheral. */
    UARTx->CR |= UART_CR_EN_Msk;
  }
  else
  {
    /* Disable UART peripheral. */
    UARTx->CR &= ~UART_CR_EN_Msk;
  }
}

/**
 *******************************************************************************
 * @brief   Transmits single data through the UARTx peripheral.
 * @param   [in]  UARTx: Where x can be (0, 1, 2) to select the UART peripheral.
 * @param   [in]  UART_Data: The data to transmit.
 * @retval  None.
 ******************************************************************************/
void UART_SendData(UART_Type *UARTx, uint16_t UART_Data)
{
  /* Transmit Data */
  UARTx->TDR = (UART_Data & (uint16_t)0x01FF);
}

/**
 *******************************************************************************
 * @brief   Returns the most recent received data by the UARTx peripheral.
 * @param   [in]  UARTx: Where x can be (0, 1, 2) to select the UART peripheral.
 * @retval  uint16_t: The received data..
 ******************************************************************************/
uint16_t UART_ReceiveData(UART_Type *UARTx)
{
  /* Receive Data */
  return (uint16_t)(UARTx->RDR & (uint16_t)0x01FF);
}

/**
 *******************************************************************************
 * @brief   Enables or disables the UART Half Duplex communication.
 * @param   [in]  UARTx: Where x can be (0, 1, 2) to select the UART peripheral.
 * @param   [in]  NewState: New state of the UART peripheral.
 *                This parameter can be: ENABLE or DISABLE.
 * @retval  None.
 ******************************************************************************/
void UART_HalfDuplexCmd(UART_Type *UARTx, FunctionalState NewState)
{
  if(DISABLE != NewState)
  {
    /* Enable UART Half-Duplex mode. */
    UARTx->CR |= UART_CR_HALF_DUP_Msk;
  }
  else
  {
    /* Disable UART Half-Duplex mode. */
    UARTx->CR &= ~UART_CR_HALF_DUP_Msk;
  }
}

/**
 *******************************************************************************
 * @brief   Data is transmitted/received with the LSB/MSB first configuration.
 * @param   [in]  UARTx: Where x can be (0, 1, 2) to select the UART peripheral.
 * @param   [in]  UART_FirstData: Data first configuration.
 *                This parameter can be a value of @ref UART_FirstDataEnum.
 *                  @arg UART_DATA_LSB: LSB first.
 *                  @arg UART_DATA_MSB: MSB first.
 * @retval  None.
 ******************************************************************************/
void UART_DataFirstConfig(UART_Type *UARTx, UART_FirstDataEnum UART_FirstData)
{
  if(UART_DATA_LSB != UART_FirstData)
  {
    /* Data is transmitted/received with the MSB first. */
    UARTx->CR |= UART_CR_MSB_FIRST_Msk;
  }
  else
  {
    /* Data is transmitted/received with the LSB first. */
    UARTx->CR &= ~UART_CR_MSB_FIRST_Msk;
  }
}

#if defined (DPM32M08x) || defined(DPM32M05x)
/**
 *******************************************************************************
 * @brief   Enable or disable specifies DMA request.
 * @param   [in]  UARTx: Where x can be (0, 1, 2) to select the UART peripheral.
 * @param   [in]  UART_DMAReq: Specifies the DMA request.
 *                This parameter can be a value of @ref UART_DMAReqEnum.
 *                  @arg UART_DMA_REQ_RX: Rx DMA request.
 *                  @arg UART_DMA_REQ_TX: Tx DMA request.
 * @param   [in]  NewState: New state of the DMA request.
 *                This parameter can be: ENABLE or DISABLE.
 * @retval  None.
 ******************************************************************************/
void UART_DMACmd(UART_Type *UARTx, UART_DMAReqEnum UART_DMAReq, FunctionalState NewState)
{
  if(DISABLE != NewState)
  {
    /* Enable the DMA transfer for selected requests. */
    UARTx->CR |= (uint32_t)UART_DMAReq;
  }
  else
  {
    /* Disable the DMA transfer for selected requests. */
    UARTx->CR &= ~(uint32_t)UART_DMAReq;
  }
}

#endif  /* DPM32M08x || DPM32M05x */

/**
 *******************************************************************************
 * @brief   Enables or disables the specified UART interrupts.
 * @param   [in]  UARTx: Where x can be (0, 1, 2) to select the UART peripheral.
 * @param   [in]  UART_IntEn: Specifies the UART interrupt.
 *                This parameter can be any combination of @ref UART_IntTypeEnum.
 *                  @arg UART_INT_RXNE_IE: reception buffer interrupt.
 *                  @arg UART_INT_TXC_IE: transmission complete interrupt.
 *                  @arg UART_INT_TXE_IE: transmission buffer empty interrupt.
 *                  @arg UART_INT_ERR_IE: error interrupt,(Error includes PE, FE and ORE).
 *                  @arg UART_INT_IDLE_IE: idle frame interrupt.
 * @param   [in]  NewState: New state of the UART interrupts.
 *                This parameter can be: ENABLE or DISABLE.
 * @retval  None.
 ******************************************************************************/
void UART_IntCmd(UART_Type *UARTx, uint16_t UART_IntType, FunctionalState NewState)
{
  if(DISABLE != NewState)
  {
    /* Enable the interrupt for selected. */
    UARTx->CR |= (uint32_t)UART_IntType;
  }
  else
  {
    /* Disable the interrupt for selected. */
    UARTx->CR &= ~(uint32_t)UART_IntType;
  }
}

/**
 *******************************************************************************
 * @brief   Get the specified UART interrupt has enable or not.
 * @param   [in]  UARTx: Where x can be (0, 1, 2) to select the UART peripheral.
 * @param   [in]  UART_IntType: Specifies the UART interrupt.
 *                This parameter can be a value of @ref UART_IntTypeEnum.
 *                  @arg UART_INT_RXNE_IE: reception buffer interrupt.
 *                  @arg UART_INT_TXC_IE: transmission complete interrupt.
 *                  @arg UART_INT_TXE_IE: transmission buffer empty interrupt.
 *                  @arg UART_INT_ERR_IE: error interrupt,(Error includes PE, FE and ORE).
 *                  @arg UART_INT_IDLE_IE: idle frame interrupt.
 * @retval  FunctionalState: The UART interrupt state(ENABLE or DISABLE).
 ******************************************************************************/
FunctionalState UART_GetIntCmdStatus(UART_Type *UARTx, UART_IntTypeEnum UART_IntType)
{
  FunctionalState state = DISABLE;

  if(RESET != (UARTx->CR & (UART_IntType)))
  {
    /* Interrupt is enable. */
    state = ENABLE;
  }
  else
  {
    /* Interrupt is disbale. */
    state = DISABLE;
  }

  /* Return the status of the interrupt enable bit. */
  return state;
}

/**
 *******************************************************************************
 * @brief   Get the specified UART flag is set or not.
 * @param   [in]  UARTx: Where x can be (0, 1, 2) to select the UART peripheral.
 * @param   [in]  UART_IntFlag: Specifies the UART interrupt flag.
 *                This parameter can be a value of @ref UART_IntFlagEnum.
 *                  @arg UART_INT_FLAG_RXNE: reception buffer not empty flag.
 *                  @arg UART_INT_FLAG_TXC: transmission complete flag.
 *                  @arg UART_INT_FLAG_TXE: transmission buffer empty flag.
 *                  @arg UART_INT_FLAG_PARITY_ERR: parity error flag.
 *                  @arg UART_INT_FLAG_FRAME_ERR: framing error flag.
 *                  @arg UART_INT_FLAG_OVERR_ERR: over run error flag.
 *                  @arg UART_INT_FLAG_IDLE: idle idle frame flag.
 * @retval  FlagState: The state of UART_IntFlag (SET or RESET).
 ******************************************************************************/
FlagState UART_GetIntFlagStatus(UART_Type *UARTx, UART_IntFlagEnum UART_IntFlag)
{
  FlagState state = RESET;

  /* Get the status of the Interrupt */
  if(RESET != (UARTx->ISR & ((uint32_t)UART_IntFlag)))
  {
    state = SET;
  }
  else
  {
    state = RESET;
  }

  /* Return the status of the interrupt flag bit. */
  return state;
}

/**
 *******************************************************************************
 * @brief   Clears the UARTx interrupt flags.
 * @param   [in]  UARTx: Where x can be (0, 1, 2) to select the UART peripheral.
 * @param   [in]  UART_IntFlag: Specifies the UART interrupt flag.
 *                This parameter can be a value of @ref UART_IntFlagEnum.
 *                  @arg UART_INT_FLAG_RXNE: reception buffer not empty flag.
 *                  @arg UART_INT_FLAG_TXC: transmission complete flag.
 *                  @arg UART_INT_FLAG_TXE: transmission buffer empty flag.
 *                  @arg UART_INT_FLAG_PARITY_ERR: parity error flag.
 *                  @arg UART_INT_FLAG_FRAME_ERR: framing error flag.
 *                  @arg UART_INT_FLAG_OVERR_ERR: over run error flag.
 *                  @arg UART_INT_FLAG_IDLE: idle idle frame flag.
 * @retval  None.
 ******************************************************************************/
void UART_ClearIntFlag(UART_Type *UARTx, UART_IntFlagEnum UART_IntFlag)
{
  /* Clear interrupt flags.*/
  UARTx->ISR = ((uint32_t)UART_IntFlag);
}
