#include <stm32h7xx_hal.h>
#include <stm32_hal_legacy.h>

#include <memory.h>
#include <stm32h7xx_hal_uart.h>
#include <stm32h7xx_hal_rcc.h>

#ifdef __cplusplus
extern "C"
#endif
	
void SysTick_Handler(void)
{
	HAL_IncTick();
	HAL_SYSTICK_IRQHandler();
}

void usart1CharMatchCallback(UART_HandleTypeDef *huart);

static UART_HandleTypeDef hUART1 = UART_HandleTypeDef();
static DMA_HandleTypeDef hdma_tx;
static DMA_HandleTypeDef hdma_rx;


bool msgReady = false;
uint8_t rxBuffer[128];
uint8_t txBuffer[] = "the quick brown fox jumped over the lazy dog\n";

extern "C" void USART1_IRQHandler()
{
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_SET);
	HAL_UART_IRQHandler(&hUART1);
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_RESET);
	
	if (__HAL_UART_GET_FLAG(&hUART1, UART_FLAG_CMF))
	{
		usart1CharMatchCallback(&hUART1);
		__HAL_UART_CLEAR_FLAG(&hUART1, USART_ICR_CMCF);
	}
}
 
void usart1CharMatchCallback(UART_HandleTypeDef *huart)
{
	msgReady = true;
	//printf("hit HAL_UART_CharMatchIdleCallback\n");
}

extern "C" void DMA1_Stream1_IRQHandler()
{
	__NOP();
}
extern "C" void DMA1_Stream0_IRQHandler()
{
	HAL_DMA_IRQHandler(&hdma_rx);
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_RESET);
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	__NOP();
}

int main(void)
{
	HAL_Init();

	__GPIOE_CLK_ENABLE();
	GPIO_InitTypeDef ledGPIO_InitStructure;

	ledGPIO_InitStructure.Pin = GPIO_PIN_1;

	ledGPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
	ledGPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
	ledGPIO_InitStructure.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(GPIOE, &ledGPIO_InitStructure);

	for (int i = 0; i < 5; i++)
	{
		HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_SET);
		HAL_Delay(500);
		HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_RESET);
		HAL_Delay(500);
	}
	
	__USART1_CLK_ENABLE();
	__GPIOB_CLK_ENABLE();
	
	GPIO_InitTypeDef uart1GPIO_InitStructure;

	uart1GPIO_InitStructure.Pin = GPIO_PIN_6;
	uart1GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
	uart1GPIO_InitStructure.Alternate = GPIO_AF7_USART1;
	uart1GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
	uart1GPIO_InitStructure.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(GPIOB, &uart1GPIO_InitStructure);
	
	uart1GPIO_InitStructure.Pin = GPIO_PIN_7;
	uart1GPIO_InitStructure.Mode = GPIO_MODE_AF_OD;
	HAL_GPIO_Init(GPIOB, &uart1GPIO_InitStructure);

	hUART1.Instance = USART1;
	hUART1.Init.BaudRate = 115200;
	hUART1.Init.WordLength = UART_WORDLENGTH_8B;
	hUART1.Init.StopBits = UART_STOPBITS_1;
	hUART1.Init.Parity = UART_PARITY_NONE;
	hUART1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	hUART1.Init.Mode = UART_MODE_TX_RX;
	
	if (HAL_UART_Init(&hUART1) != HAL_OK)
		asm("bkpt 255");
	
	NVIC_EnableIRQ(USART1_IRQn);
	
	//__HAL_RCC_DMA1_CLK_ENABLE;
	do 
	{
		__IO uint32_t tmpreg; 
		SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_DMA1EN); 
		/* Delay after an RCC peripheral clock enabling */ 
		tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_DMA1EN); 
		UNUSED(tmpreg); 
	} while (0);
		
	hdma_tx.Instance                 = DMA1_Stream1;
	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_USART1_TX;

	HAL_DMA_Init(&hdma_tx);

	/* Associate the initialized DMA handle to the UART handle */
	__HAL_LINKDMA(&hUART1, hdmatx, hdma_tx);

	/* Configure the DMA handler for reception process */
	hdma_rx.Instance                 = DMA1_Stream0;
	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_USART1_RX;

	HAL_DMA_Init(&hdma_rx);

	/* Associate the initialized DMA handle to the the UART handle */
	__HAL_LINKDMA(&hUART1, hdmarx, hdma_rx);
    
	/*##-4- Configure the NVIC for DMA #########################################*/
	/* NVIC configuration for DMA transfer complete interrupt (USART1_RX) */
	HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 1);
	HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
    
	/* NVIC configuration for DMA transfer complete interrupt (USART1_TX) */
	HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
	HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
	
	__HAL_UART_ENABLE_IT(&hUART1, UART_IT_CM);
	uint32_t cr2Value = 0;
	cr2Value = USART2->CR2;
	uint32_t clearMask = 0xFF000000;
	uint32_t setMask = 0x0D;
	MODIFY_REG(USART2->CR2, clearMask, setMask);
	
	memset(rxBuffer, 0, sizeof(rxBuffer));
	msgReady = false;
	
	HAL_UART_Receive_DMA(&hUART1, rxBuffer, sizeof(rxBuffer));
	HAL_UART_Transmit(&hUART1, txBuffer, sizeof(txBuffer), 10000);
	
	while (!msgReady)
	{
		HAL_Delay(10);
	}
	
	__NOP();
}

