/*!
 * @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 "g32a10xx_dma.h"
#include "user_dma_config.h"

/** @addtogroup Examples
  @{
  */

/** @addtogroup DMA_Examples
  @{
  */

/** @addtogroup DMA_SPI_Master
  @{
  */

/** @defgroup DMA_SPI_Master_Macros Macros
  @{
*/

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

/**@} end of group DMA_SPI_Master_Macros */

/** @defgroup DMA_SPI_Master_Functions Functions
  @{
  */

/* Delay */
static void Delay(uint16_t Count);
/* DMA Init */
static void G32aEval_DMAInit(void);
/* Buffer Compare*/
static BOOL BufferCompare(const uint8_t* Buf1Ptr, const uint8_t* Buf2Ptr, uint8_t Size);
static void G32aEval_SpiGpioInit(Rcm_AhbPeriphType AhbPeriph, GPIO_T* PortPtr, Gpio_PinSourceType PinSource, 
    Gpio_AfType AfPin, const Gpio_ConfigType *GpioConfigPtr);
static void G32aEval_SpiInit(const Spi_ConfigType *SpiConfigPtr);


/*!
 * @brief       Main program
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
int main(void)
{
    uint32_t i = 0U;

    Board_LedInit(LED2);
    Board_LedInit(LED3);
    Board_ComInit(COM1);
    G32aEval_SpiGpioInit(SPI_NSS_GPIO_CLK, SPI_NSS_GPIO_PORT, SPI_NSS_PIN_SOURCE, SPI_NSS_AF, &g_spiNssConfig);
    G32aEval_SpiGpioInit(SPI_SCK_GPIO_CLK, SPI_SCK_GPIO_PORT, SPI_SCK_PIN_SOURCE, SPI_SCK_AF, &g_spiSckConfig);
    G32aEval_SpiGpioInit(SPI_MISO_GPIO_CLK, SPI_MISO_GPIO_PORT, SPI_MISO_PIN_SOURCE, SPI_MISO_AF, &g_spiMisoConfig);
    G32aEval_SpiGpioInit(SPI_MOSI_GPIO_CLK, SPI_MOSI_GPIO_PORT, SPI_MOSI_PIN_SOURCE, SPI_MOSI_AF, &g_spiMosiConfig);
    G32aEval_SpiInit(&g_spiConfig);
    G32aEval_DMAInit();
    printf("I am Master\r\n");

    while ((uint8_t)SET == Spi_ReadStatusFlag(SPI, SPI_FLAG_BUSY))
    {
        /* do nothing */
    }

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

    /* Compare Buffer*/
    if (BufferCompare(g_spiBufferTx, g_spiBufferRx, BUFF_SIZE) == TRUE)
    {
        Board_LedOn(LED2);
    }
    else
    {
        /* nothing */
    }
    /* SPI RESET*/
    Spi_Reset(SPI);

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

/*!
 * @brief       SPI GPIO Init
 *
 * @param       AhbPeriph: The peripneral type
 * 
 * @param        PortPtr: The port pointer
 * 
 * @param        PinSource: The pin source
 * 
 * @param        AfPin: The af pin
 * 
 * @param        GpioConfigPtr: The gpio configuration pointer
 *
 * @retval      None
 *
 * @note
 */
static void G32aEval_SpiGpioInit(Rcm_AhbPeriphType AhbPeriph, GPIO_T* PortPtr, Gpio_PinSourceType PinSource, 
    Gpio_AfType AfPin, const Gpio_ConfigType *GpioConfigPtr)
{
    /* Enable related clock*/
    Rcm_EnableAhbPeriphClock(AhbPeriph);
    Rcm_EnableApb2PeriphClock(RCM_APB2_PERIPH_SYSCFG);

    if (GpioConfigPtr->mode == GPIO_MODE_AF)
    {
        /* Config alter function*/
        Gpio_ConfigPinAF(PortPtr, PinSource, AfPin);
    }
    else if (GpioConfigPtr->mode == GPIO_MODE_OUT)
    {
        if ((PortPtr == SPI_NSS_GPIO_PORT) && (GpioConfigPtr->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, GpioConfigPtr);
}
/*!
 * @brief       SPI Init
 *
 * @param       SpiConfigPtr: The spi configuration pointer
 *
 * @retval      None
 *
 * @note
 */
static void G32aEval_SpiInit(const Spi_ConfigType *SpiConfigPtr)
{
    /* 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, SpiConfigPtr);

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

/*!
 * @brief       DMA Init
 *
 * @param       None
 *
 * @retval      None
 *
 * @note
 */
static void G32aEval_DMAInit(void)
{
    /* Enable DMA clock */
    Rcm_EnableAhbPeriphClock(RCM_AHB_PERIPH_DMA1);

    /* DMA Configure */
    Dma_Config(DMA1_CHANNEL_SPI_TX, &g_dmaSpiTxConfig);
    /* Clear DMA TF flag*/
    Dma_ClearIntFlag((uint32_t)DMA1_INT_FLAG_TF_SPI_TX);

    /* DMA Configure */
    Dma_Config(DMA1_CHANNEL_SPI_RX, &g_dmaSpiRxConfig);
    /* Clear DMA TF flag*/
    Dma_ClearIntFlag((uint32_t)DMA1_INT_FLAG_TF_SPI_RX);

    Spi_EnableDmaRxBuffer(SPI);
    Spi_EnableDmaTxBuffer(SPI);

    Dma_Enable(DMA1_CHANNEL_SPI_RX);
    Dma_Enable(DMA1_CHANNEL_SPI_TX);
}

/*!
 * @brief       Delay
 *
 * @param       Count: The delay time
 *
 * @retval      None
 *
 * @note
 */
static void Delay(uint16_t Count)
{
    volatile uint32_t delay = Count;

    while (delay > 0U)
    {
        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
 */
static BOOL BufferCompare(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 = 0U; 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)
    {
        /* do 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);

    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 DMA_SPI_Master_Functions */
/**@} end of group DMA_SPI_Master */
/**@} end of group DMA_Examples */
/**@} end of group Examples */
