/*!
 * @file        main.c
 *
 * @brief       Main program body
 *
 * @version     V1.0.0
 *
 * @date        2026-02-25
 *
 * @attention
 *
 *  Copyright (C) 2026 Geehy Semiconductor
 *
 *  You may not use this file except in compliance with the
 *  GEEHY COPYRIGHT NOTICE (Geehy Semiconductor Software License Agreement).
 *
 *  The program is only for reference, which is distributed in the hope
 *  that it will be useful and instructional for customers to develop
 *  their software. Unless required by applicable law or agreed to in
 *  writing, the program is distributed on an "AS IS" BASIS, WITHOUT
 *  ANY WARRANTY OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the Geehy Semiconductor Software License Agreement for the governing permissions
 *  and limitations under the License.
 */

/* Includes */
#include "Board.h"
#include "stdio.h"
#include "g32a10xx_gpio.h"
#include "g32a10xx_misc.h"
#include "g32a10xx_eint.h"
#include "g32a10xx_syscfg.h"
#include "user_spi_config.h"

/** @addtogroup Examples
  @{
*/

/** @addtogroup SPI_Soft_Slave
  @{
*/

/** @defgroup SPI_Soft_Slave_Macros Macros
  @{
*/

/* printf function configs to USART1 */
#define DEBUG_USART  USART1

/**@} end of group SPI_Soft_Slave_Macros */


/** @defgroup SPI_Soft_Slave_Variables Variables
  @{
*/
static SoftSpi_SlaveCtxType g_spi_slave_ctx = {0};

/**@} end of group SPI_Soft_Slave_Variables */

/** @defgroup SPI_Soft_Slave_Functions Functions
  @{
  */

/* Spi_Delay */
void Spi_Delay(uint16_t Count);
/* Buffer Compare*/
BOOL Spi_DemoBufferCompare(const uint8_t* Buf1Ptr, const uint8_t* Buf2Ptr, uint8_t Size);
void SysTick_Start(void);
void SysTick_Stop(void);
void SoftSpi_GpioInit(Rcm_AhbPeriphType AhbPeriph, GPIO_T* PortPtr, const Gpio_ConfigType * GpioConfig);
void SoftSpi_EintInit(Syscfg_PortTypes Port, Syscfg_PinTypes Pin, Eint_ConfigType EintConfig);
void SoftSpi_EintDeInit(Syscfg_PinTypes Pin);
void SoftSpi_CheckCsState(void);

/*!
 * @brief       Main program
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
int main(void)
{
    /* index of Tx data */
    volatile uint8_t TxIdx = 0;
    /* index of Rx data */
    volatile uint8_t RxIdx = 0;
    uint8_t rx_byte_val = g_spi_slave_ctx.rx_byte;
    uint32_t i = 0;

    Board_LedInit(LED3);
    Board_LedInit(LED2);
    Board_ComInit(COM1);
    /* Configure the SysTick to generate a time base equal to 1 us */
    if (0u != SysTick_Config(SystemCoreClock / 1000000u))
    {
        while (1)
        {
            /* nothing */
        }
    }
    else
    {
        /* nothing */ 
    }
    
    
    SysTick_Stop();
    /* Initialize Software SPI in Slave Mode */
    SoftSpi_GpioInit(SPI_NSS_GPIO_CLK, SPI_NSS_GPIO_PORT, &g_spiNssConfig);
    SoftSpi_GpioInit(SPI_SCK_GPIO_CLK, SPI_SCK_GPIO_PORT, &g_spiSckConfig);
    SoftSpi_GpioInit(SPI_MISO_GPIO_CLK, SPI_MISO_GPIO_PORT, &g_spiMisoConfig);
    SoftSpi_GpioInit(SPI_MOSI_GPIO_CLK, SPI_MOSI_GPIO_PORT, &g_spiMosiConfig);
    SoftSpi_EintInit(SPI_SCK_SYSCFG_PORT, SPI_SCK_SYSCFG_PIN, g_spiSckEintConfig);
    SoftSpi_Init(&g_spi_slave_cfg);
    SoftSpi_SlaveInit(&g_spi_slave_cfg, &g_spi_slave_ctx);

    printf("I am Slave\r\n");

    g_spi_slave_ctx.next_tx_byte = g_spiBufferTx[TxIdx];
    while (TxIdx < (uint8_t)BUFF_SIZE)
    {
        SoftSpi_CheckCsState();
        if (0u != g_spi_slave_ctx.rx_ready)
        {
            rx_byte_val = g_spi_slave_ctx.rx_byte;
            g_spiBufferRx[RxIdx] = rx_byte_val;
            RxIdx = RxIdx + 1u;
            g_spi_slave_ctx.next_tx_byte = g_spiBufferTx[RxIdx % (uint8_t)BUFF_SIZE];
            TxIdx = TxIdx + 1u;
            g_spi_slave_ctx.rx_ready = 0u;
        }
        else
        {
            /* nothing */ 
        }

    }
    printf("\r\nSPI received data:");
    for (i = 0; i < (uint32_t)BUFF_SIZE; i++)
    {
        if ((i % 7u) == 0u)
        {
            printf("\r\n");
        }
        else
        {
            /* nothing */ 
        }
        printf(" %02x ,", g_spiBufferRx[i]);
    }
    printf("\r\n");

    /* Compare Buffer */
    if (Spi_DemoBufferCompare(g_spiBufferTx, g_spiBufferRx, BUFF_SIZE) == TRUE)
    {
        Board_LedOn(LED2);
    }
    else
    {
        /* nothing */ 
    }
    Nvic_DisableIrqRequest(EINT4_15_IRQn);
    SoftSpi_EintDeInit(SPI_SCK_SYSCFG_PIN);

    for (;;)
    {
        Spi_Delay(0xFFFF);
        Spi_Delay(0xFFFF);
        Board_LedToggle(LED3);
    }
}

/*!
 * @brief       Set Miso
 *
 * @param       Val
 *
 * @retval      None
 *
 * @note
 */
void Spi_SetMiso(uint8_t Val)
{
    if (0u == Val)
    {
        Gpio_WriteBitValue(SPI_MISO_GPIO_PORT, (uint16_t)SPI_MISO_PIN, Bit_RESET);
    }
    else
    {
        Gpio_WriteBitValue(SPI_MISO_GPIO_PORT, (uint16_t)SPI_MISO_PIN, Bit_SET);
    }
}

/*!
 * @brief       Set Miso Output Type
 *
 * @param       Val
 *
 * @retval      None
 *
 * @note
 */
void Spi_SetMisoOutputType(uint8_t Val)
{
    if (0u == Val)
    {
        Gpio_SetOutputType(SPI_MISO_GPIO_PORT, SPI_MISO_PIN, GPIO_OUT_TYPE_PP);
    }
    else
    {
        Gpio_SetOutputType(SPI_MISO_GPIO_PORT, SPI_MISO_PIN, GPIO_OUT_TYPE_OD);
    }
}

/*!
 * @brief       Read Mosi
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
uint8_t Spi_ReadMosi(void)
{
    return Gpio_ReadInputBit(SPI_MOSI_GPIO_PORT, (uint16_t)SPI_MOSI_PIN);
}

/*!
 * @brief       Set Sck
 *
 * @param       Val
 *
 * @retval      None
 *
 * @note
 */
void Spi_SetSck(uint8_t Val)
{
    if (0u == Val)
    {
        Gpio_WriteBitValue(SPI_SCK_GPIO_PORT, (uint16_t)SPI_SCK_PIN, Bit_RESET);
    }
    else
    {
        Gpio_WriteBitValue(SPI_SCK_GPIO_PORT, (uint16_t)SPI_SCK_PIN, Bit_SET);
    }
}

/*!
 * @brief       Read Sck
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
uint8_t Spi_ReadSck(void)
{
    return Gpio_ReadInputBit(SPI_SCK_GPIO_PORT, (uint16_t)SPI_SCK_PIN);
}

/*!
 * @brief       Read Cs
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
uint8_t Spi_ReadCs(void)
{
    return Gpio_ReadInputBit(SPI_NSS_GPIO_PORT, (uint16_t)SPI_NSS_PIN);
}

/*!
 * @brief       G32aEval SPI SCK Isr
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
void Spi_EnitSckIsr(void)
{
    if (Eint_ReadStatusFlag((uint32_t) SPI_SCK_EXTI_LINE) == (uint32_t)SET)
    {
        /* The interrupt flag bit must be cleared first. */
        Eint_ClearStatusFlag((uint32_t) SPI_SCK_EXTI_LINE);
        
        SoftSpi_SlaveSckIsr(&g_spi_slave_cfg, &g_spi_slave_ctx);
    }
    else
    {
         /*  nothing */
    }
}

/*!
 * @brief       Check Cs State
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
void SoftSpi_CheckCsState(void)
{
    uint8_t cs = g_spi_slave_cfg.read_cs();
    if ((cs == 0u) && (g_spi_slave_ctx.cs_active == 0u))
    {
        g_spi_slave_ctx.cs_active = 1u;
        Spi_SetMisoOutputType(0);
    }
    else if ((cs == 1u) && (g_spi_slave_ctx.cs_active != 0u))
    {
        g_spi_slave_ctx.cs_active = 0u;
        Spi_SetMisoOutputType(1);
    }
    else
    {
        /* do nothing */
    }
}

/*!
 * @brief       SPI GPIO Init
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
void SoftSpi_GpioInit(Rcm_AhbPeriphType AhbPeriph, GPIO_T* PortPtr, const Gpio_ConfigType * GpioConfig)
{
    /* Enable related clock*/
    Rcm_EnableAhbPeriphClock(AhbPeriph);
    Rcm_EnableApb2PeriphClock(RCM_APB2_PERIPH_SYSCFG);

    if (GpioConfig->mode == GPIO_MODE_OUT)
    {
        if ((PortPtr == SPI_NSS_GPIO_PORT) && (GpioConfig->pin == (uint16_t)SPI_NSS_PIN))
        {
            Gpio_SetBit(PortPtr, (uint16_t)SPI_NSS_PIN);
        }
        else
        {
            /* do nothing */
        }
    }
    else
    {
        /* do nothing */
    }

    /* config GPIO*/
    Gpio_Config(PortPtr, GpioConfig);
}

/*!
 * @brief       SPI EINT DeInit
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
void SoftSpi_EintDeInit(Syscfg_PinTypes Pin)
{
    /* Enable and set EINTx Interrupt to the Highest priority */
    switch (Pin)
    {
        case SYSCFG_PIN_0:
        case SYSCFG_PIN_1:
           Nvic_DisableIrqRequest(EINT0_1_IRQn);
            break;
        case SYSCFG_PIN_2:
        case SYSCFG_PIN_3:
           Nvic_DisableIrqRequest(EINT2_3_IRQn);
            break;
        case SYSCFG_PIN_4:
        case SYSCFG_PIN_5:
        case SYSCFG_PIN_6:
        case SYSCFG_PIN_7:
        case SYSCFG_PIN_8:
        case SYSCFG_PIN_9:
        case SYSCFG_PIN_10:
        case SYSCFG_PIN_11:
        case SYSCFG_PIN_12:
        case SYSCFG_PIN_13:
        case SYSCFG_PIN_14:
        case SYSCFG_PIN_15:
            Nvic_DisableIrqRequest(EINT4_15_IRQn);
            break;
        default:
            /* do nothing */
            break;
    }
}

/*!
 * @brief       SPI EINT Init
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
void SoftSpi_EintInit(Syscfg_PortTypes Port, Syscfg_PinTypes Pin, Eint_ConfigType EintConfig)
{
    static Eint_ConfigType staticConfig; 
    staticConfig = EintConfig;          
    /* Configure GPIO pin used as EINT Line */
    Syscfg_EintLine(Port, Pin);
    Eint_Config(&staticConfig);
    /* Enable and set EINTx Interrupt to the Highest priority */
    switch (Pin)
    {
        case SYSCFG_PIN_0:
        case SYSCFG_PIN_1:
           Nvic_EnableIrqRequest(EINT0_1_IRQn, 0x01);
            break;
        case SYSCFG_PIN_2:
        case SYSCFG_PIN_3:
           Nvic_EnableIrqRequest(EINT2_3_IRQn, 0x01);
            break;
        case SYSCFG_PIN_4:
        case SYSCFG_PIN_5:
        case SYSCFG_PIN_6:
        case SYSCFG_PIN_7:
        case SYSCFG_PIN_8:
        case SYSCFG_PIN_9:
        case SYSCFG_PIN_10:
        case SYSCFG_PIN_11:
        case SYSCFG_PIN_12:
        case SYSCFG_PIN_13:
        case SYSCFG_PIN_14:
        case SYSCFG_PIN_15:
            Nvic_EnableIrqRequest(EINT4_15_IRQn, 0x01);
            break;
        default:
            /* do nothing */
            break;
    }
}

/*!
 * @brief       Enables the SysTick timer and clears its interrupt.
 *
 * @param       None.
 *
 * @retval      None.
 *
 * @note        Start the counter, disables the interrupt, and clears any
 *              remaining pending state in the NVIC.
 */
void SysTick_Start(void)
{
    /* Disable counter and interrupt */
    SysTick->CTRL |= (SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk);
}

/*!
 * @brief       Disables the SysTick timer and clears its interrupt.
 *
 * @param       None.
 *
 * @retval      None.
 *
 * @note        Stops the counter, disables the interrupt, and clears any
 *              remaining pending state in the NVIC.
 */
void SysTick_Stop(void)
{
    /* Disable counter and interrupt */
    SysTick->CTRL &= ~(SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk);
    
    /* Clear residual pending interrupt */
    NVIC_ClearPendingIRQ(SysTick_IRQn);
}

/*!
 * @brief       Spi_Delay
 *
 * @param       Count: count
 *
 * @retval      None
 *
 * @note
 */
void Spi_Delay(uint16_t Count)
{
    volatile uint32_t delay = Count;

    while (0u != delay)
    {
        delay = delay - 1u;
    }
}

/*!
 * @brief       Compares two buffers
 *
 * @param       Buf1Ptr:    First buffer to be compared
 *
 * @param       Buf2Ptr:    Second buffer to be compared
 *
 * @param       Size:       Buffer size
 *
 * @retval      Return TRUE if Buf1Ptr = Buf2Ptr. If not then return FALSE
 *
 * @note
 */
BOOL Spi_DemoBufferCompare(const uint8_t* Buf1Ptr, const uint8_t* Buf2Ptr, uint8_t Size)
{
    uint8_t i;
    BOOL isEqual = TRUE;

    if ((Buf1Ptr == NULL) || (Buf2Ptr == NULL))
    {
        isEqual = FALSE;
    }
    else
    {
        for (i = 0; i < Size; i++)
        {
            if (Buf1Ptr[i] != Buf2Ptr[i])
            {
                isEqual = FALSE;
                break;
            }
            else
            {
                /* nothing */
            }
        }
    }

    return isEqual;
}

#if defined (__CC_ARM) || defined (__ICCARM__) || (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))

/*!
* @brief       Redirect C Library function printf to serial port.
*              After Redirection, you can use printf function.
*
* @param       ch:  The characters that need to be send.
*
* @param       *f:  pointer to a FILE that can recording all information
*              needed to control a stream
*
* @retval      The characters that need to be send.
*
* @note
*/
int fputc(int ch, FILE* f)
{
    /* send a byte of data to the serial port */
    Usart_TxData(DEBUG_USART, (uint8_t)ch);

    /* wait for the data to be send  */
    while (Usart_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == (uint8_t)RESET)
    {
        /* nothing */
    }

    return (ch);
}

#elif defined (__GNUC__)

/*!
* @brief       Redirect C Library function printf to serial port.
*              After Redirection, you can use printf function.
*
* @param       ch:  The characters that need to be send.
*
* @retval      The characters that need to be send.
*
* @note
*/
int __io_putchar(int ch)
{
    /* send a byte of data to the serial port */
    Usart_TxData(DEBUG_USART, ch);

    /* wait for the data to be send  */
    while (Usart_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET)
    {
        /* nothing */
    }

    return ch;
}

/*!
* @brief       Redirect C Library function printf to serial port.
*              After Redirection, you can use printf function.
*
* @param       file:  Meaningless in this function.
*
* @param       *ptr:  Buffer pointer for data to be sent.
*
* @param       len:  Length of data to be sent.
*
* @retval      The characters that need to be send.
*
* @note
*/
int _write(int file, char* ptr, int len)
{
    int i;
    for (i = 0; i < len; i++)
    {
        __io_putchar(*ptr++);
    }

    return len;
}

#else
#pragma message("Not supported compiler type")
#endif

/**@} end of group SPI_Soft_Slave_Functions */
/**@} end of group SPI_Soft_Slave */
/**@} end of group Examples */
