/*
  ******************************************************************************
  * @file    app.c
  * @author  Chris_Kyle
  * @version V1.0.0
  * @date    2020
  * @brief   DMA demo source code.
  ******************************************************************************
*/
#include "app.h"


/** @addtogroup fxx_StdPeriph_Examples
  * @{
  */

/** @addtogroup DMA_M2M
  * @{
  */ 

/* Private typedef -----------------------------------------------------------*/
typedef enum {FAILED = 0, PASSED = !FAILED} TestStatus;

/* Private define ------------------------------------------------------------*/
#define BUFFER_LENGTH    (256)
#define USERKEY_PORT	GPIOB
#define USERKEY_PIN		GPIO_PIN_9

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
DMA_InitTypeDef  DMA_InitStructure;
GPIO_InitTypeDef    KEY_Handle;

__IO uint32_t gu32_Refresh_Flag;
__IO uint32_t gu32_ITC_Conunt = 0;    // transfer complete interrupt count
__IO uint32_t gu32_IE_Conunt = 0;     // transfer error interrupt count

uint8_t gu8_Buffer_A[BUFFER_LENGTH];
uint8_t gu8_Buffer_B[BUFFER_LENGTH];

/* Private function prototypes -----------------------------------------------*/
void RCC_Configuration(void);
void NVIC_Configuration(void);
    
/* Private functions ---------------------------------------------------------*/


/*********************************************************************************
* Function    : UserKEY_Init
* Description : User key initialize
* Input       : 
* Output      : 
**********************************************************************************/
void UserKEY_Init(void)
{
    KEY_Handle.Pin       = USERKEY_PIN;
    KEY_Handle.Mode      = GPIO_MODE_INPUT;
    KEY_Handle.Pull      = GPIO_PULLUP;
    KEY_Handle.Alternate = GPIO_FUNCTION_0;

    GPIO_Init(USERKEY_PORT, &KEY_Handle);
}

/*********************************************************************************
* Function    : UserKEY_Get
* Description : detection button is pressed
* Input       : 
* Output      : 
**********************************************************************************/
bool UserKEY_Get(void)
{
    if (Bit_RESET == GPIO_ReadInputDataBit(USERKEY_PORT, USERKEY_PIN)) 
    {
        System_Delay_MS(20);
        
        if (Bit_RESET == GPIO_ReadInputDataBit(USERKEY_PORT, USERKEY_PIN)) 
        {
            return true;
        }
    }

    return false;
}

/**
  * @brief  Configures the different system clocks.
  * @param  None
  * @retval None
  */
void RCC_Configuration(void)
{
    /* Enable DMA Module */
    System_Module_Enable(EN_DMA);
}

/**
  * @brief  Configure the nested vectored interrupt controller.
  * @param  None
  * @retval None
  */
void NVIC_Configuration(void)
{    
    /* Enable External Interrupt */
    NVIC_ClearPendingIRQ(DMA_IRQn);
    NVIC_EnableIRQ(DMA_IRQn);
}

/**
  * @brief  DMA_M2M_STANDARD_IT program
  * @param  None
  * @retval None
  */
void DMA_M2M_STANDARD_IT(void)
{     
    /* RCC configuration */    
    RCC_Configuration();
    /* NVIC configuration */
    NVIC_Configuration();
    /* DMA channel6 configuration */
    DMA_DeInit(DMA_Channel6);
    DMA_InitStructure.Request_ID = REG_M2M;
    DMA_InitStructure.SrcAddr = (uint32_t)gu8_Buffer_A;
    DMA_InitStructure.DstAddr = (uint32_t)gu8_Buffer_B;
    DMA_InitStructure.Data_Flow = DMA_DATA_FLOW_M2M;
    DMA_InitStructure.DMA_BufferSize = BUFFER_LENGTH;
    DMA_InitStructure.Source_Inc = DMA_SOURCE_ADDR_INCREASE_ENABLE;
    DMA_InitStructure.Desination_Inc = DMA_DST_ADDR_INCREASE_ENABLE;
    DMA_InitStructure.Source_Width = DMA_SRC_WIDTH_WORD;
    DMA_InitStructure.Desination_Width = DMA_DST_WIDTH_WORD;
    DMA_InitStructure.DMA_Mode = DMA_NORMAL;
    DMA_Init(DMA_Channel6, &DMA_InitStructure);

    /* Enable DMA Channel6 Transfer Complete interrupt */
    DMA_ITConfig(DMA_Channel6, DMA_CHANNEL_CONFIG_ITC, ENABLE);
    
    gu32_Refresh_Flag = 0;
    
    /* Enable DMA Channel6 transfer */
    DMA_Cmd(DMA_Channel6, ENABLE);
}

/**
  * @brief  DMA_M2M_CYCLE_IT program
  * @param  None
  * @retval None
  */
void DMA_M2M_CYCLE_IT(void)
{     
    /* RCC configuration */    
    RCC_Configuration();
    /* NVIC configuration */
    NVIC_Configuration();
    /* DMA channel6 configuration */
    DMA_DeInit(DMA_Channel6);
    DMA_InitStructure.Request_ID = REG_M2M;
    DMA_InitStructure.SrcAddr = (uint32_t)gu8_Buffer_A;
    DMA_InitStructure.DstAddr = (uint32_t)gu8_Buffer_B;
    DMA_InitStructure.Data_Flow = DMA_DATA_FLOW_M2M;
    DMA_InitStructure.DMA_BufferSize = BUFFER_LENGTH;
    DMA_InitStructure.Source_Inc = DMA_SOURCE_ADDR_INCREASE_ENABLE;
    DMA_InitStructure.Desination_Inc = DMA_DST_ADDR_INCREASE_ENABLE;
    DMA_InitStructure.Source_Width = DMA_SRC_WIDTH_WORD;
    DMA_InitStructure.Desination_Width = DMA_DST_WIDTH_WORD;
    DMA_InitStructure.DMA_Mode = DMA_CIRCULAR;
    DMA_Init(DMA_Channel6, &DMA_InitStructure);

    /* Enable DMA Channel6 Transfer Complete interrupt */
    DMA_ITConfig(DMA_Channel6, DMA_CHANNEL_CONFIG_ITC, ENABLE);
    
    gu32_Refresh_Flag = 0;
    
    /* Enable DMA Channel6 transfer */
    DMA_Cmd(DMA_Channel6, ENABLE);
}

/**
  * @brief  DMA_M2M_STANDARD_POLLING program
  * @param  None
  * @retval None
  */
void DMA_M2M_STANDARD_POLLING(void)
{     
    /* RCC configuration */    
    RCC_Configuration();
    /* NVIC configuration */
    NVIC_Configuration();
    /* DMA channel6 configuration */
    DMA_DeInit(DMA_Channel6);
    DMA_InitStructure.Request_ID = REG_M2M;
    DMA_InitStructure.SrcAddr = (uint32_t)gu8_Buffer_A;
    DMA_InitStructure.DstAddr = (uint32_t)gu8_Buffer_B;
    DMA_InitStructure.Data_Flow = DMA_DATA_FLOW_M2M;
    DMA_InitStructure.DMA_BufferSize = BUFFER_LENGTH;
    DMA_InitStructure.Source_Inc = DMA_SOURCE_ADDR_INCREASE_ENABLE;
    DMA_InitStructure.Desination_Inc = DMA_DST_ADDR_INCREASE_ENABLE;
    DMA_InitStructure.Source_Width = DMA_SRC_WIDTH_WORD;
    DMA_InitStructure.Desination_Width = DMA_DST_WIDTH_WORD;
    DMA_InitStructure.DMA_Mode = DMA_NORMAL;
    DMA_Init(DMA_Channel6, &DMA_InitStructure);
    
    /* Enable DMA Channel6 transfer */
    DMA_Cmd(DMA_Channel6, ENABLE);
}

/************************************************************************
 * function   : DMA_M2M_Test
 * Description: DMA memory to memory test. 
 ************************************************************************/ 
void DMA_M2M_Test(enum_TEST_MODE_t fe_Mode)
{
    uint32_t i;
    uint32_t lu32_COM_OK  = 0;
    uint32_t lu32_COM_Err = 0;
    
    printfS("DMA M2M test is Ready!!! \r\n");

    UserKEY_Init();
    
    printfS("---------- Please press the USR_PB button ----------\r\n");
    
    while(false == UserKEY_Get());
    System_Delay_MS(500);
    
    /* Clear Buffer */
    for (i = 0; i < BUFFER_LENGTH; i++)
    {
        gu8_Buffer_A[i] = 0;
        gu8_Buffer_B[i] = 0;
    }
    
    for (i = 0; i < BUFFER_LENGTH; i++)
    {
        gu8_Buffer_A[i] = i;
    }

    switch(fe_Mode)
    {
        case TEST_M2M_CYCLE:
        {
            DMA_M2M_CYCLE_IT();
            while (1) 
            {
                if (gu32_Refresh_Flag == 1) 
                {
                    gu32_Refresh_Flag = 0;
                    
                    printfS("DMA M2M test complete %d times, Error %d times!!! \r\n", gu32_ITC_Conunt, gu32_IE_Conunt);
                }
                
                if(true == UserKEY_Get())
                {
                    /* Disable DMA Channel6 transfer */
                    DMA_Cmd(DMA_Channel6, DISABLE);                    
                    break;
                }
            }
            break;
        }
        case TEST_M2M_STANDARD_IT:
        {
            DMA_M2M_STANDARD_IT();
            /* Wait Interrupt Handler Finish */
            while (gu32_Refresh_Flag != 1);

            printfS("DMA M2M test complete %d times, Error %d times!!! \r\n", gu32_ITC_Conunt, gu32_IE_Conunt);
            
            for (i = 0; i < BUFFER_LENGTH; i++)
            {
                if (gu8_Buffer_A[i] != gu8_Buffer_B[i]) 
                {
                    printfS("There is one mistake : gu8_TxBuffer[%d] != gu8_TxBuffer[%d] \r\n", i, i);
                    
                    lu32_COM_Err++;
                }
                else 
                {
                    lu32_COM_OK++;
                }
            }

            printfS("DMA M2M test OK count %d times \r\n",  lu32_COM_OK);
            printfS("DMA M2M test Err count %d times \r\n", lu32_COM_Err);
            break;
        }
 
        case TEST_M2M_STANDARD_POLLING:
        {
            DMA_M2M_STANDARD_POLLING();
            /* Wait Transfer Finish */
            while (RESET == DMA_GetFlagStatus(DMA_IT_FLAG_TC6));

            printfS("DMA M2M test complete!!! \r\n");
            
            for (i = 0; i < BUFFER_LENGTH; i++)
            {
                if (gu8_Buffer_A[i] != gu8_Buffer_B[i]) 
                {
                    printfS("There is one mistake : gu8_TxBuffer[%d] != gu8_TxBuffer[%d] \r\n", i, i);
                    
                    lu32_COM_Err++;
                }
                else 
                {
                    lu32_COM_OK++;
                }
            }

            printfS("DMA M2M test OK count %d times \r\n",  lu32_COM_OK);
            printfS("DMA M2M test Err count %d times \r\n", lu32_COM_Err);
            break;
        }
        
        default:
            break;
    }
    
    printfS("DMA M2M test End!!! \r\n");    
}
