/*!
 * @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_spi.h"
#include "user_spi_config.h"
#include "g32a10xx_int.h"
#include "string.h"

/** @addtogroup Examples
  @{
*/

/** @addtogroup SPI_Examples
  @{
*/

/** @addtogroup SPI_TwoBoards_Master
  @{
  */

/** @defgroup SPI_TwoBoards_Master_Macros Macros
  @{
*/

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

/**@} end of group SPI_TwoBoards_Master_Macros */

/** @defgroup SPI_TwoBoards_Master_Functions Functions
  @{
  */

/* Spi_Delay */
void Spi_Delay(uint16_t Count);
/* SPI GPIO Init */
void Spi_DemoGpioInit(Rcm_AhbPeriphType AhbPeriph, GPIO_T* PortPtr, Gpio_PinSourceType PinSource, Gpio_AfType AfPin, const Gpio_ConfigType * GpioConfig);
/* SPI Init */
void Spi_DemoInit(Spi_ConfigType SpiConfig);
/* Buffer Compare*/
BOOL Spi_DemoBufferCompare(const uint8_t* Buf1Ptr, const uint8_t* Buf2Ptr, uint8_t Size);

/*!
 * @brief       Main program
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
int main(void)
{
    uint32_t i = 0;
    /* index of TxData*/
    volatile uint8_t TxIdx = 0;
    /* index of RxData*/
    volatile uint8_t RxIdx = 0;

    Board_LedTypes tempLed = LED1;

    Board_LedInit(LED1);
    Board_LedInit(LED2);
    Board_LedInit(LED3);
    Board_LedInit(LED4);
    Board_ButtonInit(BUTTON_KEY1, BUTTON_MODE_EINT);
    Board_ButtonInit(BUTTON_KEY2, BUTTON_MODE_EINT);
    Board_ButtonInit(BUTTON_KEY3, BUTTON_MODE_EINT);
    Board_ButtonInit(BUTTON_KEY4, BUTTON_MODE_EINT);
    Board_ComInit(COM1);

    printf("I am Master\r\n");
    printf("First, establish the correct physical connection between master and slave devices.\r\n");
    printf("Then, the slave selects the target key, followed by the master selecting the same key.\r\n");
    printf("Press KEY1   : Enter SPI full duplex mode\r\n");
    printf("Press KEY2   : Enter SPI half duplex mode\r\n");
    printf("Press KEY3   : Enter SPI single wire mode master recieve slave send\r\n");
    printf("Press KEY4   : Enter SPI single wire mode master send slave receive\r\n");
    while (1)
    {
        switch (g_keyPress)
        {
            case (int8_t)BUTTON_KEY1:
                printf("\r\nEnter SPI full duplex mode");
                Spi_DemoGpioInit(SPI_NSS_GPIO_CLK, SPI_NSS_GPIO_PORT, SPI_NSS_PIN_SOURCE, SPI_NSS_AF, &g_spiNssConfig);
                Spi_DemoGpioInit(SPI_SCK_GPIO_CLK, SPI_SCK_GPIO_PORT, SPI_SCK_PIN_SOURCE, SPI_SCK_AF, &g_spiSckConfig);
                Spi_DemoGpioInit(SPI_MISO_GPIO_CLK, SPI_MISO_GPIO_PORT, SPI_MISO_PIN_SOURCE, SPI_MISO_AF, &g_spiMisoConfig);
                Spi_DemoGpioInit(SPI_MOSI_GPIO_CLK, SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN_SOURCE, SPI_MOSI_AF, &g_spiMosiConfig);
                Spi_DemoInit(g_spiFullDuplexConfig);
                memset(g_spiBufferRx, 0, sizeof(g_spiBufferRx));
                TxIdx = 0;
                RxIdx = 0;

                while (TxIdx < (uint8_t)BUFF_SIZE)
                {
                    while (Spi_ReadStatusFlag(SPI, SPI_FLAG_TXBE) == (uint8_t)RESET)
                    {
                        /* nothing */
                    }
                    Spi_TxData8(SPI, g_spiBufferTx[TxIdx]);
                    TxIdx = TxIdx + 1u;

                    while (Spi_ReadStatusFlag(SPI, SPI_FLAG_RXBNE) == (uint8_t)RESET)
                    {
                        /* nothing */
                    }
                    g_spiBufferRx[RxIdx] = Spi_RxData8(SPI);
                    RxIdx = RxIdx + 1u;
                }
                while (0u != Spi_ReadStatusFlag(SPI, SPI_FLAG_BUSY))
                {
                    /* nothing */
                }
                printf("\r\nSPI received data:");

                tempLed = LED1;
                break;
            case (int8_t)BUTTON_KEY2:
                printf("\r\nEnter SPI half duplex mode");
                Spi_DemoGpioInit(SPI_NSS_GPIO_CLK, SPI_NSS_GPIO_PORT, SPI_NSS_PIN_SOURCE, SPI_NSS_AF, &g_spiNssConfig);
                Spi_DemoGpioInit(SPI_SCK_GPIO_CLK, SPI_SCK_GPIO_PORT, SPI_SCK_PIN_SOURCE, SPI_SCK_AF, &g_spiSckConfig);
                Spi_DemoGpioInit(SPI_MOSI_GPIO_CLK, SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN_SOURCE, SPI_MOSI_AF, &g_spiMosiConfig);
                Spi_DemoInit(g_spiHalfDuplexConfig);
                memset(g_spiBufferRx, 0, sizeof(g_spiBufferRx));
                TxIdx = 0;
                RxIdx = 0;

                while (TxIdx < (uint8_t)BUFF_SIZE)
                {
                    while (Spi_ReadStatusFlag(SPI, SPI_FLAG_TXBE) == (uint8_t)RESET)
                    {
                        /* nothing */
                    }
                    Spi_TxData8(SPI, g_spiBufferTx[TxIdx]);
                    TxIdx = TxIdx + 1u;
                }
                while (0u != Spi_ReadStatusFlag(SPI, SPI_FLAG_BUSY))
                {
                    /* nothing */
                }
                Spi_DisableBidirectionalMode(SPI);
                Spi_DisableOutputDirection(SPI);
                TxIdx = 0;
                while (RxIdx < (uint8_t)BUFF_SIZE)
                {
                    while (Spi_ReadStatusFlag(SPI, SPI_FLAG_TXBE) == (uint8_t)RESET)
                    {
                        /* nothing */
                    }
                    Spi_TxData8(SPI, g_spiBufferTx[TxIdx]);
                    TxIdx = TxIdx + 1u;
                    while (Spi_ReadStatusFlag(SPI, SPI_FLAG_RXBNE) == (uint8_t)RESET)
                    {
                        /* nothing */
                    }
                    g_spiBufferRx[RxIdx] = Spi_RxData8(SPI);
                    RxIdx = RxIdx + 1u;
                }
                printf("\r\nSPI received data:");

                tempLed = LED2;
                break;
            case (int8_t)BUTTON_KEY3:
                printf("\r\nEnter SPI single wire mode master recieve slave send");
                Spi_DemoGpioInit(SPI_NSS_GPIO_CLK, SPI_NSS_GPIO_PORT, SPI_NSS_PIN_SOURCE, SPI_NSS_AF, &g_spiNssConfig);
                Spi_DemoGpioInit(SPI_SCK_GPIO_CLK, SPI_SCK_GPIO_PORT, SPI_SCK_PIN_SOURCE, SPI_SCK_AF, &g_spiSckConfig);
                Spi_DemoGpioInit(SPI_MISO_GPIO_CLK, SPI_MISO_GPIO_PORT, SPI_MISO_PIN_SOURCE, SPI_MISO_AF, &g_spiMisoConfig);
                Spi_DemoInit(g_spiSingleWireMrxStxConfig);
                memset(g_spiBufferRx, 0, sizeof(g_spiBufferRx));
                TxIdx = 0;
                RxIdx = 0;

                while (RxIdx < (uint8_t)BUFF_SIZE)
                {
                    while (Spi_ReadStatusFlag(SPI, SPI_FLAG_TXBE) == (uint8_t)RESET)
                    {
                        /* nothing */
                    }
                    Spi_TxData8(SPI, g_spiBufferTx[TxIdx]);
                    TxIdx = TxIdx + 1u;

                    while (Spi_ReadStatusFlag(SPI, SPI_FLAG_RXBNE) == (uint8_t)RESET)
                    {
                        /* nothing */
                    }
                    g_spiBufferRx[RxIdx] = Spi_RxData8(SPI);
                    RxIdx = RxIdx + 1u;

                }
                printf("\r\nSPI received data:");

                tempLed = LED3;
                break;
            case (int8_t)BUTTON_KEY4:
                printf("\r\nEnter SPI single wire mode master send slave receive");
                Spi_DemoGpioInit(SPI_NSS_GPIO_CLK, SPI_NSS_GPIO_PORT, SPI_NSS_PIN_SOURCE, SPI_NSS_AF, &g_spiNssConfig);
                Spi_DemoGpioInit(SPI_SCK_GPIO_CLK, SPI_SCK_GPIO_PORT, SPI_SCK_PIN_SOURCE, SPI_SCK_AF, &g_spiSckConfig);
                Spi_DemoGpioInit(SPI_MOSI_GPIO_CLK, SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN_SOURCE, SPI_MOSI_AF, &g_spiMosiConfig);
                Spi_DemoInit(g_spiSingleWireMtxSrxConfig);
                memset(g_spiBufferRx, 0, sizeof(g_spiBufferRx));
                TxIdx = 0;
                RxIdx = 0;

                while (TxIdx < (uint8_t)BUFF_SIZE)
                {
                    while (Spi_ReadStatusFlag(SPI, SPI_FLAG_TXBE) == (uint8_t)RESET)
                    {
                        /* nothing */
                    }
                    Spi_TxData8(SPI, g_spiBufferTx[TxIdx]);
                    TxIdx = TxIdx + 1u;
                }
                printf("\r\nSPI sent data:");

                tempLed = LED4;
                break;
            default:
                /* nothing */
                break;

        }
        if (g_keyPress != -1)
        {
            if (g_keyPress != (int8_t)BUTTON_KEY4)
            {
                for (i = 0; i < (uint32_t)BUFF_SIZE; i++)
                {
                    if ((i % 7u) == 0u)
                    {
                        printf("\r\n");
                    }
                    else
                    {
                        /* do nothing */
                    }
                    printf(" %02x ,", g_spiBufferRx[i]);
                }
                printf("\r\n");

              /* Compare Buffer*/
              if (Spi_DemoBufferCompare(g_spiBufferTx, g_spiBufferRx, BUFF_SIZE) == TRUE)
              {
                  Board_LedOn(tempLed);
              }
              else
              {
                  Board_LedOff(tempLed);
              }
            }
            else
            {
                for (i = 0; i < (uint32_t)BUFF_SIZE; i++)
                {
                    if ((i % 7u) == 0u)
                    {
                        printf("\r\n");
                    }
                    else
                    {
                        /* do nothing */
                    }
                    printf(" %02x ,", g_spiBufferTx[i]);
                }
                printf("\r\n");
                Board_LedOn(tempLed);
            }
            g_keyPress = -1;
            Spi_Reset(SPI);
            printf("\r\nI am Master\r\n");
            printf("First, establish the correct physical connection between master and slave devices.\r\n");
            printf("Then, the slave selects the target key, followed by the master selecting the same key.\r\n");
            printf("Press KEY1   : Enter SPI full duplex mode\r\n");
            printf("Press KEY2   : Enter SPI half duplex mode\r\n");
            printf("Press KEY3   : Enter SPI single wire mode master recieve slave send\r\n");
            printf("Press KEY4   : Enter SPI single wire mode master send slave receive\r\n");
        }
        else
        {
            while(g_keyPress == -1)
            {
                Spi_Delay(0xFFFF);
            }
        }
    }
}

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

    if (GpioConfig->mode == GPIO_MODE_AF)
    {
        /* Config alter function*/
        Gpio_ConfigPinAF(PortPtr, PinSource, AfPin);
    }
    else 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 Init
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
void Spi_DemoInit(Spi_ConfigType SpiConfig)
{
    Spi_ConfigType localSpiConfig;
    localSpiConfig = SpiConfig;

    /* Enable related clock*/
    Rcm_EnableApb2PeriphClock(RCM_APB2_PERIPH_SPI1);
    Rcm_EnableApb2PeriphClock(RCM_APB2_PERIPH_SYSCFG);

    /* SPI RESET*/
    Spi_Reset(SPI);

    /* SPI configuration*/
    Spi_Config(SPI, &localSpiConfig);

    /* SPI ENABLE*/
    Spi_Enable(SPI);
}

/*!
 * @brief       Spi_Delay
 *
 * @param       None
 *
 * @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_TwoBoards_Master_Functions */
/**@} end of group SPI_TwoBoards_Master */
/**@} end of group SPI_Examples */
/**@} end of group Examples */
