#ifndef __DEBUG_H__
#define __DEBUG_H__

/* Debug */
/* Provide printf subroutine and delay function */
#pragma once

#include <stdint.h>

#include "ch554.h"

// UART1 baud rates
//     Setting   Actual      % error
//Std     9600   9615.38     0.16%
//       14400   14492.75    0.64%
//Std    19200   19230.77    0.16%
//Std    38400   38461.54    0.16%
//Std    57600   58823.53    2.12%
//      100000   100000      0.00%
//Std   115200   111111.1   -3.55%
//      128000   142857.14   11.61%
//Std   230400   250000      8.51%
//      250000   250000      0%
//      256000   333333.33   30.21%
//Std   460800   500000      8.51%
//Std   500000   500000      0%
//Std   576000
//Std   921600
//Std  1000000   1000000     0.00%

#ifndef UART0_BAUD
//#define UART0_BAUD    115200
#define UART0_BAUD      1000000
#endif

#ifndef UART1_BAUD
#define UART1_BAUD      500000
//#define UART1_BAUD    1000000
#endif

void CfgFsys(void);        // CH554 clock selection and configuration
void mDelayuS(uint16_t n); // Delay in units of uS
void mDelaymS(uint16_t n); // Delay in mS

// Set pin p1.4 and p1.5 to GPIO output mode.
void gpio_init(void);
void gpio_set(uint8_t pin);
void gpio_unset(uint8_t pin);
uint8_t gpio_get(uint8_t pin);

void gpio_init_p1_4_in(void);
void gpio_init_p1_5_out(void);
uint8_t gpio_p1_4_get(void);
void gpio_p1_5_set(void);
void gpio_p1_5_unset(void);

/*******************************************************************************
 * Function Name  : CH554UART0Alter()
 * Description    : Set the alternate pin mappings for UART0 (RX on P1.2, TX on P1.3)
 *******************************************************************************/
inline void CH554UART0Alter()
{
    PIN_FUNC |= bUART0_PIN_X; // RX on P1.2, TX on P1.3
}

/*******************************************************************************
 * Function Name  : mInitSTDIO()
 * Description    : CH554 UART0 is initialized
 *                  T1 is used as the baud rate generator of UART0 by default
 *                  T2 can also be used as baud rate generator
 *                  RX on P3.0, TX on P3.1
 *******************************************************************************/
inline void mInitSTDIO( )
{
    uint32_t x;
    uint8_t x2;

    SM0 = 0;   // 8-bit data asynchronous communication
    SM1 = 1;   // Variable baud rate, which is generated by timer T1 or T2

    // With SM0=0 and SM1=1 we are now in UART0 Mode 1

    SM2 = 0;   // In Mode 1, SM2=0 gives that the Receive interrupt flag bit is set when receiving data and the reception is valid

    // Use Timer1 as a baud rate generator
    RCLK = 0;  // UART0 receive clock
    TCLK = 0;  // UART0 transmit clock

    PCON |= SMOD; // Set Fast mode for UART0 baud rate communication
    x = 10 * FREQ_SYS / UART0_BAUD / 16; // If you change the main frequency, be careful not to overflow the value of x
    x2 = x % 10;
    x /= 10;
    if (x2 >= 5) {
        x++;                // Rounding
    }
    TMOD = (TMOD & ~bT1_GATE & ~bT1_CT & ~MASK_T1_MOD) | bT1_M1; // Timer1 as 8-bit auto-reload timer
    T2MOD = T2MOD | bTMR_CLK | bT1_CLK; // Timer1 clock selection
    TH1 = 0-x;                          // 12MHz crystal oscillator, baud / 12 is the actual need to set the baud rate
    TR1 = 1;                            // Start timer 1
    TI = 1;                             // Enable transmit interrupt
    REN = 1;                            // UART0 receive enable
    ES = 1;                             // UART0 interrupt enable
}

/*******************************************************************************
 * Function Name  : CH554UART0RcvByte()
 * Description    : CH554UART0 receives a byte
 * Return         : SBUF
 *******************************************************************************/
inline uint8_t CH554UART0RcvByte()
{
    while (RI == 0)
        ; // Wait for uart rx interrupt flag
    RI = 0;
    return SBUF;
}

/*******************************************************************************
 * Function Name  : CH554UART0SendByte(uint8_t SendDat)
 * Description    : CH554UART0 sends a byte
 * Input          : uint8_t SendDat; the data to be sent
 *******************************************************************************/
inline void CH554UART0SendByte(uint8_t SendDat)
{
    SBUF = SendDat;
    while (TI == 0)
        ; // Wait for transmit to finish (TI == 1)
    TI = 0;
}

/*******************************************************************************
 * Function Name  : CH554UART1Alter()
 * Description    : Set the alternate pin mappings for UART1 (RX on P3.4, TX on P3.2)
 *******************************************************************************/
inline void CH554UART1Alter()
{
    PIN_FUNC |= bUART1_PIN_X; // RX on P3.4, TX on P3.2
}

/*******************************************************************************
 * Function Name  : UART1Setup()
 * Description    : CH554 serial port 1 initialization
 *                  RX on P1.6, TX on P1.7
 *
 *******************************************************************************/
inline void UART1Setup()
{
    U1SM0 = 0;    // UART1 selects 8-bit data bit
    U1SMOD = 1;   // Fast mode
    U1REN = 1;    // Enable receiving
                  // Should correct for rounding in SBAUD1 calculation
    SBAUD1 = 256 - FREQ_SYS/16/UART1_BAUD; // Calculation for Fast mode
    IE_UART1 = 1; // Enable UART1 interrupt
}

/*******************************************************************************
 * Function Name  : UART1Clean()
 * Description    : Read out spurious data
 *******************************************************************************/
inline void UART1Clean()
{
    uint8_t tmp;

    while (U1RI) {
        tmp = SBUF1;
        U1RI = 0;
    }
}

/*******************************************************************************
 * Function Name  : CH554UART1RcvByte()
 * Description    : CH554UART1 receives a byte
 * Return         : SBUF
 *******************************************************************************/
inline uint8_t  CH554UART1RcvByte( )
{
    while (U1RI == 0)  // Query reception, interrupt mode is not required
        ;
    U1RI = 0;
    return SBUF1;
}

/*******************************************************************************
 * Function Name  : CH554UART1SendByte(uint8_t SendDat)
 * Description    : CH554UART1 sends a byte
 * Input          : uint8_t SendDat; data to be sent
 *******************************************************************************/
inline void CH554UART1SendByte(uint8_t SendDat)
{
    SBUF1 = SendDat; // Query sending, the interrupt mode does not need the following two statements, but TI=0 is required before sending
    while (U1TI == 0)
        ;
    U1TI = 0;
}

/*******************************************************************************
 * Function Name  : CH554UART1SendBuffer(uint8_t SendDat)
 * Description    : CH554UART1 sends a complete buffer
 * Input          : uint8_t *Buf; Data to be sent
 * Input          : uint32_t Len; Length of data
 *******************************************************************************/
inline void CH554UART1SendBuffer(uint8_t *Buf, uint32_t Len)
{
    uint32_t Count = 0;
    while (Count < Len) {
        if (gpio_p1_4_get()) {
            SBUF1 = Buf[Count++];
            while (U1TI == 0)
                ;
            U1TI = 0;
        }
    }
}

#if SDCC < 370
void putchar(char c);
char getchar(void);
#else
int putchar(int c);
int getchar(void);
#endif

/*******************************************************************************
* Function Name  : CH554WDTModeSelect(uint8_t mode)
* Description    : CH554 watchdog mode selection
* Input          : uint8_t mode
                   0  timer
                   1  watchDog
* Output         : None
* Return         : None
*******************************************************************************/
inline void CH554WDTModeSelect(uint8_t mode)
{
    SAFE_MOD = 0x55;
    SAFE_MOD = 0xaa;             // Enter Safe Mode
    if (mode) {
        GLOBAL_CFG |= bWDOG_EN;  // Start watchdog reset
    } else {
        GLOBAL_CFG &= ~bWDOG_EN; //Start watchdog only as a timer
    }
    SAFE_MOD = 0x00;             // Exit safe Mode
    WDOG_COUNT = 0;              // Watchdog assignment initial value
}

/*******************************************************************************
* Function Name  : CH554WDTFeed(uint8_t tim)
* Description    : CH554 watchdog timer time setting
* Input          : uint8_t tim watchdog reset time setting

                   00H(6MHz)=2.8s
                   80H(6MHz)=1.4s
* Output         : None
* Return         : None
*******************************************************************************/
inline void CH554WDTFeed(uint8_t tim)
{
    WDOG_COUNT = tim; // Watchdog counter assignment
}

#endif