/**
 * *************************************************************************************************************
 * @file Uart.c
 * @author BH_CodeGenerator
 * @version 0.1
 * @date 2022-04-01
 * @warning <!--auto generated by Tools, do not modify or add anything, otherwise, your change will be lost!!! -->
 * @brief all the functions prototypes for Uart firmware library
 * MCU / CFG Ver. :BH66F2475/1.7
 * Version = 1.0.2
 * * Uart 初始化配置函數
 * * Uart 使能和除能
 * * Uart 發送16進制buf (使用中断的方式)
 * * Uart 發送char 字符串 (使用中断的方式)
 * * Uart 接收 (使用中断的方式)
 * *************************************************************************************************************
 *  @attention
 *
 *  Firmware Disclaimer Information
 *
 *  1. The customer hereby acknowledges and agrees that the program technical documentation, including the
 *     code, which is supplied by BEST HEALTH ELECTRONIC Inc., (hereinafter referred to as BestHealth) is the
 *     proprietary and confidential intellectual property of BestHealth, and is protected by copyright law and
 *     other intellectual property laws.
 *
 *  2. The customer hereby acknowledges and agrees that the program technical documentation, including the
 *     code, is confidential information belonging to BestHealth, and must not be disclosed to any third parties
 *     other than BestHealth and the customer.
 *
 *  3. The program technical documentation, including the code, is provided and for customer reference
 *     only. After delivery by BestHealth, the customer shall use the program technical documentation, including
 *     the code, at their own risk. BestHealth disclaims any expressed, implied or statutory warranties, including
 *     the warranties of merchantability, satisfactory quality and fitness for a particular purpose.
 *
 *  <h2><center>Copyright (C) BEST HEALTH ELECTRONIC Inc. All rights reserved</center></h2>
 */
/* Define to prevent recursive inclusion -------------------------------------------------------------------*/
#include "Uart.h"
#include "..\board\BoardCfg.h"
#ifdef SUPPORT_ISP
    #include "..\application\Iap_Isp.h"
#endif

#ifndef FH_CLOCK_FREQ
    #define FH_CLOCK_FREQ 4000000    //! <Set system frequency, currently supported settings are 4000000,8000000,12000000
#endif
#ifndef UART_BAUD_RATE
    #define UART_BAUD_RATE 9600    //!< 300,600,1200,2400,4800,9600,14400,19200,38400,57600,115200,230400,250000
#endif

// clang-format off
#define SET_UARTn_TX_INTERRUPT_DISABLE()   { _tiie = 0; _teie = 0;}
#define SET_UARTn_TX_INTERRUPT_ENABLE()    { _tiie = 1; _teie = 1;}
#define SET_UARTn_Format_D8_NONE_S1()      { _ucr1 = 0x00; }// Transmit 8bit = (8bit-Data None-Parity None-Add)1bit-Stop
#define SET_UARTn_Format_D7_EVEN_S1()      { _ucr1 = 0x20; }// Transfer 8bit = (7bit-Data Even-Parity None-Add)1bit-Stop
#define SET_UARTn_Format_D7_ODD_S1()       { _ucr1 = 0x30; }// Transmit 8bit = (7bit-Data Odd -Parity None-Add)1bit-Stop
#define SET_UARTn_Format_D9_NONE_S1()      { _ucr1 = 0x40; }// Transfer 9bit = (9bit-Data None-Parity None-Add)1bit-Stop
#define SET_UARTn_Format_D8_EVEN_S1()      { _ucr1 = 0x60; }// Transfer 9bit = (8bit-Data Even-Parity None-Add)1bit-Stop
#define SET_UARTn_Format_D8_ODD_S1()       { _ucr1 = 0x70; }// Transfer 9bit = (8bit-Data Odd-Parity None-Add)1bit-Stop
#define SET_UARTn_Format_D8_NONE_S2()      { _ucr1 = 0x00; }// Transmit 8bit = (8bit-Data None-Parity None-Add)2bit-Stop
#define SET_UARTn_Format_D7_EVEN_S2()      { _ucr1 = 0x20; }// Transfer 8bit = (7bit-Data Even-Parity None-Add)2bit-Stop
#define SET_UARTn_Format_D7_ODD_S2()       { _ucr1 = 0x30; }// Transfer 8bit = (7bit-Data Odd -Parity None-Add)2bit-Stop
#define SET_UARTn_Format_D9_NONE_S2()      { _ucr1 = 0x40; }// Transfer 9bit = (9bit-Data None-Parity None-Add)2bit-Stop
#define SET_UARTn_Format_D8_EVEN_S2()      { _ucr1 = 0x60; }// Transfer 9bit = (8bit-Data Even-Parity None-Add)2bit-Stop
#define SET_UARTn_Format_D8_ODD_S2()       { _ucr1 = 0x70; }// Transfer 9bit = (8bit-Data Odd-Parity None-Add)2bit-Stop

//400kHz / 38400 = 10.41666 BRD=10 0.41666*8=3.33 UMOD=3 實際波特率=fh/(BRD+UMOD/8)=400kHz/(10+3/8)=38554.2 誤差=(35554.2-38400)/38400=0.4%
#define SET_UART_BAUDRATE_38400_400kHz()   { _ufcr = 0x28; _brdl = 0x0A; _brdh = 0x00;}

#if FH_CLOCK_FREQ == 4000000
    #define SET_UARTn_BAUDRATE_300()       { _ufcr = 0x18; _brdl = 0x15; _brdh = 0x34;}
    #define SET_UARTn_BAUDRATE_600()       { _ufcr = 0x28; _brdl = 0x0A; _brdh = 0x1A;}
    #define SET_UARTn_BAUDRATE_1200()      { _ufcr = 0x18; _brdl = 0x05; _brdh = 0x0D;}
    #define SET_UARTn_BAUDRATE_2400()      { _ufcr = 0x28; _brdl = 0x82; _brdh = 0x06;}
    #define SET_UARTn_BAUDRATE_4800()      { _ufcr = 0x18; _brdl = 0x41; _brdh = 0x03;}
    #define SET_UARTn_BAUDRATE_9600()      { _ufcr = 0x28; _brdl = 0xA0; _brdh = 0x01;}
    #define SET_UARTn_BAUDRATE_14400()     { _ufcr = 0x30; _brdl = 0x15; _brdh = 0x01;}
    #define SET_UARTn_BAUDRATE_19200()     { _ufcr = 0x18; _brdl = 0xD0; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_28800()     { _ufcr = 0x38; _brdl = 0x8A; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_38400()     { _ufcr = 0x08; _brdl = 0x68; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_56000()     { _ufcr = 0x18; _brdl = 0x47; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_57600()     { _ufcr = 0x20; _brdl = 0x45; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_115200()    { _ufcr = 0x30; _brdl = 0x22; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_230400()    { _ufcr = 0x18; _brdl = 0x11; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_250000()    { _ufcr = 0x00; _brdl = 0x10; _brdh = 0x00;}
#endif
#if FH_CLOCK_FREQ == 8000000
    #define SET_UARTn_BAUDRATE_300()       { _ufcr = 0x28; _brdl = 0x2A; _brdh = 0x68;}
    #define SET_UARTn_BAUDRATE_600()       { _ufcr = 0x18; _brdl = 0x15; _brdh = 0x34;}
    #define SET_UARTn_BAUDRATE_1200()      { _ufcr = 0x28; _brdl = 0x0A; _brdh = 0x1A;}
    #define SET_UARTn_BAUDRATE_2400()      { _ufcr = 0x18; _brdl = 0x05; _brdh = 0x0D;}
    #define SET_UARTn_BAUDRATE_4800()      { _ufcr = 0x28; _brdl = 0x82; _brdh = 0x06;}
    #define SET_UARTn_BAUDRATE_9600()      { _ufcr = 0x18; _brdl = 0x41; _brdh = 0x03;}
    #define SET_UARTn_BAUDRATE_14400()     { _ufcr = 0x20; _brdl = 0x2B; _brdh = 0x02;}
    #define SET_UARTn_BAUDRATE_19200()     { _ufcr = 0x28; _brdl = 0xA0; _brdh = 0x01;}
    #define SET_UARTn_BAUDRATE_28800()     { _ufcr = 0x30; _brdl = 0x15; _brdh = 0x01;}
    #define SET_UARTn_BAUDRATE_38400()     { _ufcr = 0x18; _brdl = 0xD0; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_56000()     { _ufcr = 0x38; _brdl = 0x8E; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_57600()     { _ufcr = 0x38; _brdl = 0x8A; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_115200()    { _ufcr = 0x20; _brdl = 0x45; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_230400()    { _ufcr = 0x30; _brdl = 0x22; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_250000()    { _ufcr = 0x00; _brdl = 0x20; _brdh = 0x00;}
#endif
#if FH_CLOCK_FREQ == 12000000
    #define SET_UARTn_BAUDRATE_300()       { _ufcr = 0x00; _brdl = 0x40; _brdh = 0x9C;}
    #define SET_UARTn_BAUDRATE_600()       { _ufcr = 0x00; _brdl = 0x20; _brdh = 0x4E;}
    #define SET_UARTn_BAUDRATE_1200()      { _ufcr = 0x00; _brdl = 0x10; _brdh = 0x27;}
    #define SET_UARTn_BAUDRATE_2400()      { _ufcr = 0x00; _brdl = 0x88; _brdh = 0x13;}
    #define SET_UARTn_BAUDRATE_4800()      { _ufcr = 0x00; _brdl = 0xC4; _brdh = 0x09;}
    #define SET_UARTn_BAUDRATE_9600()      { _ufcr = 0x00; _brdl = 0xE2; _brdh = 0x04;}
    #define SET_UARTn_BAUDRATE_14400()     { _ufcr = 0x18; _brdl = 0x41; _brdh = 0x03;}
    #define SET_UARTn_BAUDRATE_19200()     { _ufcr = 0x00; _brdl = 0x71; _brdh = 0x02;}
    #define SET_UARTn_BAUDRATE_28800()     { _ufcr = 0x28; _brdl = 0xA0; _brdh = 0x01;}
    #define SET_UARTn_BAUDRATE_38400()     { _ufcr = 0x20; _brdl = 0x38; _brdh = 0x01;}
    #define SET_UARTn_BAUDRATE_56000()     { _ufcr = 0x10; _brdl = 0xD6; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_57600()     { _ufcr = 0x18; _brdl = 0xD0; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_115200()    { _ufcr = 0x08; _brdl = 0x68; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_230400()    { _ufcr = 0x08; _brdl = 0x34; _brdh = 0x00;}
    #define SET_UARTn_BAUDRATE_250000()    { _ufcr = 0x00; _brdl = 0x30; _brdh = 0x00;}
#endif
// clang-format on

volatile Uart_Status_t uartStatus;
volatile uint8_t *queue_in;
volatile uint8_t *queue_out;

/*
 * @Brief configuration
 **
 * @Param cfg_t
 */
void Uart_Cfg(const Uart_Cfg_t *cfg_t)
{
#if defined(PIN_TX_PA6)
    PIN_PA6_TX();
#endif
#if defined(PIN_TX_PA0)
    PIN_PA0_TX();
#endif
#if defined(PIN_RX_PA7)
    PIN_PA7_RX();
#endif
#if defined(PIN_RX_PA2)
    PIN_PA2_RX();
#endif

    SET_UARTn_Format_D8_NONE_S1();    // Set the data transmission format.
// Baud rate setting

#if UART_BAUD_RATE == 300
    SET_UARTn_BAUDRATE_300();
#elif UART_BAUD_RATE == 600
    SET_UARTn_BAUDRATE_600();
#elif UART_BAUD_RATE == 1200
    SET_UARTn_BAUDRATE_1200();
#elif UART_BAUD_RATE == 2400
    SET_UARTn_BAUDRATE_2400();
#elif UART_BAUD_RATE == 4800
    SET_UARTn_BAUDRATE_4800();
#elif UART_BAUD_RATE == 9600
    SET_UARTn_BAUDRATE_9600();
#elif UART_BAUD_RATE == 14400
    SET_UARTn_BAUDRATE_14400();
#elif UART_BAUD_RATE == 19200
    SET_UARTn_BAUDRATE_19200();
#elif UART_BAUD_RATE == 28800
    SET_UARTn_BAUDRATE_28800();
#elif UART_BAUD_RATE == 38400
	if(_irc2 == 0) // 4MHz
	{
    	SET_UARTn_BAUDRATE_38400();
	}
	else // 400kHz
	{
		SET_UART_BAUDRATE_38400_400kHz();	
	}
#elif UART_BAUD_RATE == 57600
    SET_UARTn_BAUDRATE_57600();
#elif UART_BAUD_RATE == 115200
    SET_UARTn_BAUDRATE_115200();
#elif UART_BAUD_RATE == 230400
    SET_UARTn_BAUDRATE_230400();
#elif UART_BAUD_RATE == 250000
    SET_UARTn_BAUDRATE_250000();
#else
    #error "Please set the correct UART_BAUD_RATE and FH_CLOCK_FREQ"
#endif

    _ufcr &= 0xFC;    // default rx fifo 1byte trigger
    _ufcr |= 0x01;
    _adden = cfg_t->flag.b.addr;
    _wake  = cfg_t->flag.b.wakeUp;
    _rie   = 1;    // OERR (overflow)/RXIF (valid data) can be set when the interrupt flag bit is set.
                   // _Tiie = 1; // TIDLE (no data transmission) is set, and the interrupt flag bit can be set
                   // _Teie = 1; // When TXIF (data has been loaded into the shift register and TXR is empty) is set, the interrupt flag bit can be set
    uartStatus.txLen              = 0;
    uartStatus.txOffset           = 0;
    queue_in                      = (uint8_t *)uartStatus.rxBuf;
    queue_out                     = (uint8_t *)uartStatus.rxBuf;
    uartStatus.flag.b.queueIsFull = false;
}

/* *
 * @ Brief Send hexadecimal buf
 *
 * @ Param buf Sent buf
 * @ Param len length sent
 * @ Note uses interrupt sending. If. txLen = 0 is uartStatus, the data has been sent.
 * @ Return true Sent successfully
 * @ Return false Failed to send, the last data has not been sent yet
 */
bool Uart_TxByte_Interrupt(volatile uint8_t *buf, uint8_t len)
{
    if (uartStatus.txLen > 0)
    {
        return false;
    }
    else
    {
        // clang-format off
        uartStatus.txLen    = len - 1;
        uartStatus.txOffset = buf;
        _acc = _usr;
        _txr_rxr = *uartStatus.txOffset;
        uartStatus.flag.b.isBusy = true;
        SET_UARTn_TX_INTERRUPT_ENABLE();
        // clang-format on
        return true;
    }
}

/* *
 * @ Brief Send hexadecimal buf
 *
 * @ Param byte Sent byte
 * @Param len byte len
 * @ Return true Sent successfully
 * @ Return false Failed to send
 */
void Uart_TxByte_Polling(uint8_t *byte, uint8_t len)
{
    SET_UARTn_TX_INTERRUPT_DISABLE();
    uint8_t i;
    for (i = 0; i < len; i++)
    {
        // clang-format off
        _acc      = _usr;
        _txr_rxr = *byte;
        while (!_tidle)
            ;
        byte++;
        // clang-format on
    }
}

/* *
 * @ brief reads uart cache data
 * @ param * byte The first readable byte in the serial port cache. When there is no readable data, it returns 0, an integer type.
 * @ return true has readable bytes
 * @ return false unreadable bytes
 */
bool Uart_IsAvailable_ReadByte(uint8_t *byte)
{
    if ((queue_in != queue_out) || uartStatus.flag.b.queueIsFull)
    {
        if (queue_out >= (uint8_t *)(uartStatus.rxBuf + uartStatus.rxBufLen))
        {
            queue_out = (uint8_t *)(uartStatus.rxBuf);
        }
        *byte = *queue_out++;

        uartStatus.flag.b.queueIsFull = false;
        return true;
    }
    else
    {
        return false;
    }
}

#ifdef SUPPORT_ISP
// clang-format off
__attribute__((interrupt(0x2C), reg_acc(0x8A), isr_at(0x2C + PROM_AP_ADDRESS_START)))
void UART_ISR_Routine()
// clang-format on
#else
DEFINE_ISR(UART_ISR, 0x30)
#endif
{
    // Noise Interference Error | Frame Error | Overflow Error
    if (_nf | _ferr | _oerr)
    {
        _acc = _usr;
        _acc = _txr_rxr;
    }
    // Send data
    if (_txif)
    {
        if (uartStatus.txLen > 0)
        {
            uartStatus.txOffset++;
            _txr_rxr = *uartStatus.txOffset;
            uartStatus.txLen--;
        }
        else
        {
            if (_tidle)
            {
                uartStatus.flag.b.isBusy = false;
            }
        }
    }
    // Receive data
    if (_rxif)
    {
        if ((queue_in > queue_out) && ((queue_in - queue_out) >= uartStatus.rxBufLen))
        {
            // Queue is full: in is first byte and out is last byte
        }
        else if (uartStatus.flag.b.queueIsFull)
        {
            // Queue is full: in   == out
        }
        else
        {
            // Queue is not full
            if (queue_in >= (uint8_t *)(uartStatus.rxBuf + uartStatus.rxBufLen))
            {
                queue_in = (uint8_t *)(uartStatus.rxBuf);
            }
            *queue_in++ = _txr_rxr;
            if (queue_in == queue_out)
            {
                uartStatus.flag.b.queueIsFull = true;    // Queue is full: in   == out
            }
        }
    }
}

/* *
 * @ brief converts data to string
 *
 * @ param value The data to be converted.
 * @ param str string address
 * @ param size The length of the converted string (including the sign bit). If the length is not high, the high bits are discarded.
 */
void IntegerToString(int32_t value, uint8_t *str, uint8_t size)
{
    if (value < 0)
    {
        value  = -value;
        str[0] = '-';
    }
    else
    {
        str[0] = ' ';
    }
    volatile bit isNotFirst;
    isNotFirst = false;
    size--;
    do
    {
        str[size] = '0' + value % 10;
        if (isNotFirst && value == 0)
        {
            str[size] = ' ';
        }
        value /= 10;
        size--;
        isNotFirst = true;
    } while (size > 0);
}