// From STM 2 Board UART with DMA example

#include <stm32h7xx_hal.h>
#include <stm32_hal_legacy.h>

#define BSP_ERROR_NONE                    0
#define BSP_ERROR_NO_INIT                -1
#define BSP_ERROR_WRONG_PARAM            -2
#define BSP_ERROR_BUSY                   -3
#define BSP_ERROR_PERIPH_FAILURE         -4
#define BSP_ERROR_COMPONENT_FAILURE      -5
#define BSP_ERROR_UNKNOWN_FAILURE        -6
#define BSP_ERROR_UNKNOWN_COMPONENT      -7 
#define BSP_ERROR_BUS_FAILURE            -8 
#define BSP_ERROR_CLOCK_FAILURE          -9  
#define BSP_ERROR_MSP_FAILURE            -10  
#define BSP_ERROR_FEATURE_NOT_SUPPORTED  -11



extern "C" void SysTick_Handler(void)
{
	HAL_IncTick();
	HAL_SYSTICK_IRQHandler();
}

UART_HandleTypeDef UartHandle;

static DMA_HandleTypeDef hdma_tx;
static DMA_HandleTypeDef hdma_rx;

static void Error_Handler(void);

extern "C" void DMA1_Stream1_IRQHandler()
{
	__NOP();
}

extern "C" void DMA1_Stream7_IRQHandler()
{
	HAL_DMA_IRQHandler(&hdma_rx);
}

extern "C" void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UartHandle)
{
	__NOP();
}

extern "C" void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
	__NOP();
}

extern "C" void HAL_UART_ErrorCallback(UART_HandleTypeDef *UartHandle)
{
	Error_Handler();
}

typedef enum
{
	greenLED = 0,
	yellowLED = 1,
	redLED = 2,
	numLEDs
}LEDName;

static GPIO_TypeDef* LED_PORT[numLEDs] = {
	GPIOB,
	GPIOE,
	GPIOB
};

static const uint16_t LED_PIN[numLEDs] = {
	GPIO_PIN_0,
	GPIO_PIN_1,
	GPIO_PIN_14
};

//__IO ITStatus UartReady = RESET;
//__IO uint32_t UserButtonStatus = 0; /* set to 1 after User Button interrupt  */

ALIGN_32BYTES(uint8_t txBuffer[]) = " *****UART_TwoBoards communication based on DMA*****  *****UART_TwoBoards communication based on DMA*****   *****UA\0RT_TwoBoards communication based on DMA***** ";
ALIGN_32BYTES(uint8_t rxBuffer[sizeof(txBuffer)]);

ALIGN_32BYTES(uint8_t pubx00Cmd[]) = "$PUBX,00*33\r\n";
//ALIGN_32BYTES(uint8_t gpsRXBuffer[sizeof(txBuffer)]);
ALIGN_32BYTES(uint8_t gpsRXBuffer[256]);

static void SystemClock_Config(void);
//static uint16_t Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength);
static void CPU_CACHE_Enable(void);
void initLEDs();
int32_t  turnLEDOn(LEDName Led);
int32_t  turnLEDOff(LEDName Led);
static void initUSART2(UART_HandleTypeDef *huart);

void usart2CharMatchCallback(UART_HandleTypeDef *huart);

extern "C" void USART2_IRQHandler(void)
{
	HAL_UART_IRQHandler(&UartHandle);

	if (__HAL_UART_GET_FLAG(&UartHandle, UART_FLAG_CMF))
	{
		usart2CharMatchCallback(&UartHandle);
		__HAL_UART_CLEAR_FLAG(&UartHandle, USART_ICR_CMCF);
	}
}

void usart2CharMatchCallback(UART_HandleTypeDef *huart)
{
	HAL_Delay(1);
	//printf("hit HAL_UART_CharMatchIdleCallback\n");
}

int main(void)
{
	CPU_CACHE_Enable();
	HAL_Init();

	initLEDs();
	turnLEDOff(greenLED);
	turnLEDOff(yellowLED);
	turnLEDOff(redLED);

	for (int i = 0; i < 5; i++)
	{
		turnLEDOn(greenLED);
		HAL_Delay(250);
		turnLEDOn(yellowLED);
		HAL_Delay(250);
		turnLEDOn(redLED);
		HAL_Delay(250);
		turnLEDOff(greenLED);
		turnLEDOff(yellowLED);
		turnLEDOff(redLED);
		HAL_Delay(250);
	}
	
	UartHandle.Instance        = USART2;
	UartHandle.Init.BaudRate   = 9600;
	UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
	UartHandle.Init.StopBits   = UART_STOPBITS_1;
	UartHandle.Init.Parity     = UART_PARITY_NONE;
	UartHandle.Init.HwFlowCtl  = UART_HWCONTROL_NONE;
	UartHandle.Init.Mode       = UART_MODE_TX_RX;
	UartHandle.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
	if (HAL_UART_DeInit(&UartHandle) != HAL_OK)
	{
		Error_Handler();
	}  
	if (HAL_UART_Init(&UartHandle) != HAL_OK)
	{
		Error_Handler();
	}
	
	__HAL_UART_ENABLE_IT(&UartHandle, UART_IT_CM);
	uint32_t cr2Value = 0;
	cr2Value = USART2->CR2;
	uint32_t clearMask = 0xFF000000;
	uint32_t setMask = 0x0D;
	MODIFY_REG(USART2->CR2, clearMask, setMask);

	for (int i = 0; i < 5; i++)
	{
		//printf("Starting receive DMA\n");
		//	if (HAL_UART_Receive_DMA(&UartHandle, (uint8_t *)rxBuffer, RXBUFFERSIZE) != HAL_OK)
			if(HAL_UART_Receive_DMA(&UartHandle, (uint8_t *)gpsRXBuffer, sizeof(gpsRXBuffer)) != HAL_OK)
		{
			Error_Handler();
		}

		//printf("Starting transmit DMA\n");
		//	if (HAL_UART_Transmit_DMA(&UartHandle, (uint8_t*)txBuffer, TXBUFFERSIZE) != HAL_OK)
			if(HAL_UART_Transmit_DMA(&UartHandle, (uint8_t*)pubx00Cmd, sizeof(pubx00Cmd)) != HAL_OK)
		{
			Error_Handler();
		}
		
		
	}
}


static void Error_Handler(void)
{
	/* Turn LED3 on */
	//BSP_LED_On(redLED);

	while (1)
	{
	}  
}

static void CPU_CACHE_Enable(void)
{
	/* Enable I-Cache */
	SCB_EnableICache();

	/* Enable D-Cache */
	SCB_EnableDCache();
}

void initLEDs()
{
	GPIO_InitTypeDef  gpio_init_structure;

	/* Enable the GPIO green LED Clock */
	__HAL_RCC_GPIOB_CLK_ENABLE();

	/* Enable the GPIO yellow LED Clock */
	__HAL_RCC_GPIOE_CLK_ENABLE();

	/* Enable the GPIO green LED Clock */
	__HAL_RCC_GPIOB_CLK_ENABLE();


	/* Configure the green LED pin */
	gpio_init_structure.Pin = GPIO_PIN_0;
	gpio_init_structure.Mode = GPIO_MODE_OUTPUT_PP;
	gpio_init_structure.Pull = GPIO_NOPULL;
	gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	HAL_GPIO_Init(GPIOB, &gpio_init_structure);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);

	/* Configure the yellow LED pin */
	gpio_init_structure.Pin = GPIO_PIN_1;
	gpio_init_structure.Mode = GPIO_MODE_OUTPUT_PP;
	gpio_init_structure.Pull = GPIO_NOPULL;
	gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	HAL_GPIO_Init(GPIOE, &gpio_init_structure);
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_RESET);

	/* Configure the red LED pin */
	gpio_init_structure.Pin = GPIO_PIN_14;
	gpio_init_structure.Mode = GPIO_MODE_OUTPUT_PP;
	gpio_init_structure.Pull = GPIO_NOPULL;
	gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	HAL_GPIO_Init(GPIOB, &gpio_init_structure);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_RESET);

}

int32_t turnLEDOff(LEDName Led)
{
	int32_t ret = BSP_ERROR_NONE;

	if ((Led != greenLED) && (Led != yellowLED) && (Led != redLED))
	{
		ret = BSP_ERROR_WRONG_PARAM;
	}
	else
	{
		HAL_GPIO_WritePin(LED_PORT[Led], LED_PIN[Led], GPIO_PIN_RESET);
	}

	return ret;
}

int32_t turnLEDOn(LEDName Led)
{
	int32_t ret = BSP_ERROR_NONE;

	if ((Led != greenLED) && (Led != yellowLED) && (Led != redLED))
	{
		ret = BSP_ERROR_WRONG_PARAM;
	}
	else
	{
		HAL_GPIO_WritePin(LED_PORT[Led], LED_PIN[Led], GPIO_PIN_SET);
	}

	return ret;
}

void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
	if (huart->Instance == USART2)
		//mainInitUSART2(huart);
		initUSART2(huart);
	
//	if (0)
//	{
//		static DMA_HandleTypeDef hdma_tx;
//		static DMA_HandleTypeDef hdma_rx;
//  
//		GPIO_InitTypeDef  GPIO_InitStruct;
//  
//		/*##-1- Enable peripherals and GPIO Clocks #################################*/
//		/* Enable GPIO TX/RX clock */
//		USARTx_TX_GPIO_CLK_ENABLE();
//		USARTx_RX_GPIO_CLK_ENABLE();
//
//		/* Enable USARTx clock */
//		USARTx_CLK_ENABLE();
//
//		/* Enable DMA clock */
//		DMAx_CLK_ENABLE();
//  
//		/*##-2- Configure peripheral GPIO ##########################################*/  
//		/* UART TX GPIO pin configuration  */
//		GPIO_InitStruct.Pin       = USARTx_TX_PIN;
//		GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
//		GPIO_InitStruct.Pull      = GPIO_PULLUP;
//		GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;
//		GPIO_InitStruct.Alternate = USARTx_TX_AF;
//
//		HAL_GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStruct);
//
//		/* UART RX GPIO pin configuration  */
//		GPIO_InitStruct.Pin = USARTx_RX_PIN;
//		GPIO_InitStruct.Alternate = USARTx_RX_AF;
//
//		HAL_GPIO_Init(USARTx_RX_GPIO_PORT, &GPIO_InitStruct);
//
//		/*##-3- Configure the DMA ##################################################*/
//		/* Configure the DMA handler for Transmission process */
//		hdma_tx.Instance                 = USARTx_TX_DMA_STREAM;
//		hdma_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;
//		hdma_tx.Init.PeriphInc           = DMA_PINC_DISABLE;
//		hdma_tx.Init.MemInc              = DMA_MINC_ENABLE;
//		hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
//		hdma_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
//		hdma_tx.Init.Mode                = DMA_NORMAL;
//		hdma_tx.Init.Priority            = DMA_PRIORITY_LOW;
//		hdma_tx.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
//		hdma_tx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
//		hdma_tx.Init.MemBurst            = DMA_MBURST_INC4;
//		hdma_tx.Init.PeriphBurst         = DMA_PBURST_INC4;
//		hdma_tx.Init.Request             = USARTx_TX_DMA_REQUEST;
//
//		HAL_DMA_Init(&hdma_tx);
//
//		/* Associate the initialized DMA handle to the UART handle */
//		__HAL_LINKDMA(huart, hdmatx, hdma_tx);
//
//		/* Configure the DMA handler for reception process */
//		hdma_rx.Instance                 = USARTx_RX_DMA_STREAM;
//		hdma_rx.Init.Direction           = DMA_PERIPH_TO_MEMORY;
//		hdma_rx.Init.PeriphInc           = DMA_PINC_DISABLE;
//		hdma_rx.Init.MemInc              = DMA_MINC_ENABLE;
//		hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
//		hdma_rx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
//		hdma_rx.Init.Mode                = DMA_NORMAL;
//		hdma_rx.Init.Priority            = DMA_PRIORITY_HIGH;
//		hdma_rx.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
//		hdma_rx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
//		hdma_rx.Init.MemBurst            = DMA_MBURST_INC4;
//		hdma_rx.Init.PeriphBurst         = DMA_PBURST_INC4;
//		hdma_rx.Init.Request             = USARTx_RX_DMA_REQUEST;
//
//		HAL_DMA_Init(&hdma_rx);
//
//		/* Associate the initialized DMA handle to the the UART handle */
//		__HAL_LINKDMA(huart, hdmarx, hdma_rx);
//    
//		/*##-4- Configure the NVIC for DMA #########################################*/
//		/* NVIC configuration for DMA transfer complete interrupt (USART2_TX) */
//		HAL_NVIC_SetPriority(USARTx_DMA_TX_IRQn, 0, 1);
//		HAL_NVIC_EnableIRQ(USARTx_DMA_TX_IRQn);
//    
//		/* NVIC configuration for DMA transfer complete interrupt (USART2_RX) */
//		HAL_NVIC_SetPriority(USARTx_DMA_RX_IRQn, 0, 0);
//		HAL_NVIC_EnableIRQ(USARTx_DMA_RX_IRQn);
//  
//		/* NVIC for USART, to catch the TX complete */
//		HAL_NVIC_SetPriority(USARTx_IRQn, 0, 1);
//		HAL_NVIC_EnableIRQ(USARTx_IRQn);
//	}
}

static void initUSART2(UART_HandleTypeDef *huart)
{
	
	GPIO_InitTypeDef  GPIO_InitStruct;
  
	/*##-1- Enable peripherals and GPIO Clocks #################################*/
	/* Enable GPIO TX/RX clock */
	__HAL_RCC_GPIOD_CLK_ENABLE();

	/* Enable USARTx clock */
	__HAL_RCC_USART2_CLK_ENABLE();

	/* Enable DMA clock */
	__HAL_RCC_DMA1_CLK_ENABLE();
  
	/*##-2- Configure peripheral GPIO ##########################################*/  
	/* UART TX GPIO pin configuration  */
	GPIO_InitStruct.Pin       = GPIO_PIN_5;
	GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull      = GPIO_PULLUP;
	GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF7_USART2;

	HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

	/* UART RX GPIO pin configuration  */
	GPIO_InitStruct.Pin = GPIO_PIN_6;
	GPIO_InitStruct.Alternate = GPIO_AF7_USART2;

	HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

	/*##-3- Configure the DMA ##################################################*/
	/* Configure the DMA handler for Transmission process */
	hdma_tx.Instance                 = DMA1_Stream7;
	hdma_tx.Init.Direction           = DMA_MEMORY_TO_PERIPH;
	hdma_tx.Init.PeriphInc           = DMA_PINC_DISABLE;
	hdma_tx.Init.MemInc              = DMA_MINC_ENABLE;
	hdma_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
	hdma_tx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
	hdma_tx.Init.Mode                = DMA_NORMAL;
	hdma_tx.Init.Priority            = DMA_PRIORITY_LOW;
	hdma_tx.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
	hdma_tx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
	hdma_tx.Init.MemBurst            = DMA_MBURST_INC4;
	hdma_tx.Init.PeriphBurst         = DMA_PBURST_INC4;
	hdma_tx.Init.Request             = DMA_REQUEST_USART2_TX;

	HAL_DMA_Init(&hdma_tx);

	/* Associate the initialized DMA handle to the UART handle */
	__HAL_LINKDMA(huart, hdmatx, hdma_tx);

	/* Configure the DMA handler for reception process */
	hdma_rx.Instance                 = DMA1_Stream1;
	hdma_rx.Init.Direction           = DMA_PERIPH_TO_MEMORY;
	hdma_rx.Init.PeriphInc           = DMA_PINC_DISABLE;
	hdma_rx.Init.MemInc              = DMA_MINC_ENABLE;
	hdma_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
	hdma_rx.Init.MemDataAlignment    = DMA_MDATAALIGN_BYTE;
	hdma_rx.Init.Mode                = DMA_NORMAL;
	hdma_rx.Init.Priority            = DMA_PRIORITY_HIGH;
	hdma_rx.Init.FIFOMode            = DMA_FIFOMODE_DISABLE;
	hdma_rx.Init.FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL;
	hdma_rx.Init.MemBurst            = DMA_MBURST_INC4;
	hdma_rx.Init.PeriphBurst         = DMA_PBURST_INC4;
	hdma_rx.Init.Request             = DMA_REQUEST_USART2_RX;

	HAL_DMA_Init(&hdma_rx);

	/* Associate the initialized DMA handle to the the UART handle */
	__HAL_LINKDMA(huart, hdmarx, hdma_rx);
    
	/*##-4- Configure the NVIC for DMA #########################################*/
	/* NVIC configuration for DMA transfer complete interrupt (USART2_TX) */
	HAL_NVIC_SetPriority(DMA1_Stream7_IRQn, 0, 1);
	HAL_NVIC_EnableIRQ(DMA1_Stream7_IRQn);
    
	/* NVIC configuration for DMA transfer complete interrupt (USART2_RX) */
	HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
	HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
  
	/* NVIC for USART, to catch the TX complete */
	HAL_NVIC_SetPriority(USART2_IRQn, 0, 1);
	HAL_NVIC_EnableIRQ(USART2_IRQn);
}
