/**********************************SPI**************************************
Данный файл содержит базовые функции для инициализации SPI.
//-------------------Функции-------------------//
@func users
	- SPI_Base_Init							Инициализация SPI 

@func spi initialize
	- SPI_GPIO_Init							Инициализация GPIO для SPI
	- SPI_DMA_Init							Инициализация DMA для SPI
	- SPI_MspInit								Аналог HAL_MspInit для SPI
	- SPI_MspDeInit							Аналог HAL_MspDeInit для SPI
***************************************************************************/
#include "spi_general.h"

//-------------------------------------------------------------------
//------------------------SPI INIT FUNCTIONS------------------------
/**  
	* @brief 	Initialize SPI with SPI_SettingsTypeDef structure.
	* @param 	sspi - указатель на структуру с настройками SPI.
  * @return HAL status.
	* @note 	SPI_SettingsTypeDef структура содержит хендл SPI и настройки перефирии (GPIO)
	*/
HAL_StatusTypeDef SPI_Base_Init(SPI_SettingsTypeDef *sspi)
{ // function takes setting structure for init
	
	// check is settings are valid
	if(Check_SPI_Init_Struct(sspi) != HAL_OK)
		return HAL_ERROR;
	
	SPI_MspInit(&sspi->hspi);	
	
	if (HAL_SPI_Init(&sspi->hspi) != HAL_OK)
  {
		ERROR_HANDLER_NAME();
		return HAL_ERROR;
  }
	
	// init gpio from SPISettings structure
	SPI_GPIO_Init(sspi);
	
//	// init dma from SPISettings structure if need
//	if (sspi->DMAChannel != 0)
//		SPI_DMA_Init(&sspi->hspi, sspi->hspi.hdmarx, sspi->DMAChannel, sspi->DMA_CHANNEL_X);
		
	return HAL_OK;
}


/**  
	* @brief 	Initialize GPIO for SPI.
	* @param 	GPIOx 			- порт для настройки.
	* @param 	GPIO_PIN_RX - пин для настройки на прием.
	* @param 	GPIO_PIN_TX - пин для настройки на передачу.
	*/
void SPI_GPIO_Init(SPI_SettingsTypeDef *sspi)
{ 	
	GPIO_InitTypeDef GPIO_InitStruct = {0};	
	// GPIO INIT	
	GPIO_Clock_Enable(sspi->CLK_GPIOx);
	GPIO_Clock_Enable(sspi->MISO_GPIOx);
	GPIO_Clock_Enable(sspi->MOSI_GPIOx);	
		// CLK PIN INIT
	GPIO_InitStruct.Pin = sspi->CLK_PIN;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
	HAL_GPIO_Init(sspi->CLK_GPIOx, &GPIO_InitStruct);
	// MISO PIN INIT
	GPIO_InitStruct.Pin = sspi->MISO_PIN;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
	HAL_GPIO_Init(sspi->MISO_GPIOx, &GPIO_InitStruct);
	// MOSI PIN INIT
	GPIO_InitStruct.Pin = sspi->MOSI_PIN;
	GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
	HAL_GPIO_Init(sspi->MOSI_GPIOx, &GPIO_InitStruct);
}

/**  
	* @brief 	Initialize DMA for SPI.
	* @param 	hspi 				- указатель на хендл SPI для настройки DMA.
	* @param 	hdma_rx 			- указатель на хендл DMA для линии приема SPI.
	* @param 	DMAChannel 		- указатель на канал DMA/поток DMA в STM32F407.
	* @param 	DMA_CHANNEL_X - канал DMA.
	*/
void SPI_DMA_Init(SPI_HandleTypeDef *hspi, DMA_HandleTypeDef *hdma_rx, DMA_Stream_TypeDef *DMAChannel, uint32_t DMA_CHANNEL_X)
{ // function takes spi and dma handlers and dmachannel for spi
//	// for now only dma rx is supported, tx maybe later if needed
//		// calc defines on boot_project_setup.h

//	/* SPI3 DMA Init */
//	/* SPI3_RX Init */
//	
//	hdma_rx->Instance = DMAChannel;
//#if defined(STM32F407xx) // dma channel choose for 407
//	hdma_rx->Init.Channel = DMA_CHANNEL_X;
//#endif
//	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_CIRCULAR;
//	hdma_rx->Init.Priority = DMA_PRIORITY_LOW;
//	if (HAL_DMA_Init(hdma_rx) != HAL_OK)
//	{
//		ERROR_HANDLER_NAME();
//	}

//	__USER_LINKDMA(hspi,hdmarx,hdma_rx); 	
//	

//	// __USER_LINKDMA is need because __HAL_LINKDMA is written for global defined hdma_rx
//	// so you get error because hal uses . insted of ->
}

/**  
	* @brief 	Initialize SPI & DMA clock and interrupt.
	* @param 	hspi - указатель на хендл SPI для инициализации.
	* @note 	Чтобы не генерировать функцию с иницилизацией неиспользуемых SPI,
						дефайнами в spi_general.h определяются используемые SPI.
	*/
void SPI_MspInit(SPI_HandleTypeDef *hspi) // analog for hal function
{ 	
	// rcc, dma and interrupt init for SPIs
	// GPIO init was moved to own functions SPI_GPIO_Init  
	if(0);
#ifdef USE_SPI1
	else if(hspi->Instance==SPI1)
  {
	
//  /* DMA2 clock enable */
//	__HAL_RCC_DMA2_CLK_ENABLE();
//  /* DMA interrupt init */
//  HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0, 0);
//  HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);
	
	/* SPI1 clock enable */
	__HAL_RCC_SPI1_CLK_ENABLE();
	
	/* SPI1 interrupt Init */
	HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
	HAL_NVIC_EnableIRQ(SPI1_IRQn);
  }
#endif // USE_SPI1
#ifdef USE_SPI2
	else if(hspi->Instance==SPI2)
  {
//  /* DMA1 clock enable */
//	__HAL_RCC_DMA1_CLK_ENABLE();
//  /* DMA interrupt init */
//  HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 0, 0);
//  HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
	
	/* SPI2 clock enable */
	__HAL_RCC_SPI2_CLK_ENABLE();
	
	/* SPI2 interrupt Init */
	HAL_NVIC_SetPriority(SPI2_IRQn, 0, 0);
	HAL_NVIC_EnableIRQ(SPI2_IRQn);
  }
#endif // USE_SPI2
#ifdef USE_SPI3
	else if(hspi->Instance==SPI3)
  {			
//  /* DMA1 clock enable */
//	__HAL_RCC_DMA1_CLK_ENABLE();
//  /* DMA interrupt init */
//  HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
//  HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
	
	/* SPI3 clock enable */
	__HAL_RCC_SPI3_CLK_ENABLE();	
	/* SPI3 interrupt Init */
	HAL_NVIC_SetPriority(SPI3_IRQn, 0, 0);
	HAL_NVIC_EnableIRQ(SPI3_IRQn);
  }
#endif // USE_SPI3
}

/**  
	* @brief 	Deinitialize SPI & DMA clock and interrupt.
	* @param 	hspi - указатель на хендл SPI для деинициализации.
	* @note 	Чтобы не генерировать функцию с деиницилизацией неиспользуемых SPI,
						дефайнами определяются используемые SPI.
	*/
void SPI_MspDeInit(SPI_HandleTypeDef *hspi) // analog for hal function
{ 	
	// rcc, dma and interrupt init for SPIs
	// GPIO init was moved to own functions SPI_GPIO_Init  
	if(0);
#ifdef USE_SPI1
	else if(hspi->Instance==SPI1)
  {
	
//  /* DMA2 clock enable */
//	__HAL_RCC_DMA2_CLK_ENABLE();
//  /* DMA interrupt init */
//  HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0, 0);
//  HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);
	
	/* SPI1 clock reset */
	__HAL_RCC_SPI1_FORCE_RESET();
	__HAL_RCC_SPI1_RELEASE_RESET();
  }
#endif // USE_SPI1
#ifdef USE_SPI2
	else if(hspi->Instance==SPI2)
  {
//  /* DMA1 clock enable */
//	__HAL_RCC_DMA1_CLK_ENABLE();
//  /* DMA interrupt init */
//  HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 0, 0);
//  HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
	
	/* SPI2 clock reset */
	__HAL_RCC_SPI2_FORCE_RESET();
	__HAL_RCC_SPI2_RELEASE_RESET();
  }
#endif // USE_SPI2
#ifdef USE_SPI3
	else if(hspi->Instance==SPI3)
  {			
//  /* DMA1 clock enable */
//	__HAL_RCC_DMA1_CLK_ENABLE();
//  /* DMA interrupt init */
//  HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0);
//  HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
	
	/* SPI3 clock reset */
	__HAL_RCC_SPI3_FORCE_RESET();
	__HAL_RCC_SPI3_RELEASE_RESET();
  }
#endif // USE_SPI3
}

/**  
	* @brief 	Check that spi init structure have correct values.
	* @param 	sspi - указатель на структуру с настройками SPI.
  * @return HAL status.
	*/
HAL_StatusTypeDef Check_SPI_Init_Struct(SPI_SettingsTypeDef *sspi)
{
	// check is settings are valid
	if (!IS_SPI_ALL_INSTANCE(sspi->hspi.Instance))
		return HAL_ERROR;
	
	// check init settings
	if (!IS_SPI_MODE(sspi->hspi.Init.Mode))
		return HAL_ERROR;	
	if (!IS_SPI_DIRECTION(sspi->hspi.Init.Direction))
		return HAL_ERROR;	
	if (!IS_SPI_DATASIZE(sspi->hspi.Init.DataSize))
		return HAL_ERROR;	
	if (!IS_SPI_BAUDRATE_PRESCALER(sspi->hspi.Init.BaudRatePrescaler))
		return HAL_ERROR;	
	if (!IS_SPI_CPOL(sspi->hspi.Init.CLKPolarity))
		return HAL_ERROR;	
	if (!IS_SPI_CPHA(sspi->hspi.Init.CLKPhase))
		return HAL_ERROR;	
	if (!IS_SPI_NSS(sspi->hspi.Init.NSS))
		return HAL_ERROR;		
	if (!IS_SPI_FIRST_BIT(sspi->hspi.Init.FirstBit))
		return HAL_ERROR;	
	if (!IS_SPI_CRC_CALCULATION(sspi->hspi.Init.CRCCalculation))
		return HAL_ERROR;	
	if (!IS_SPI_CRC_POLYNOMIAL(sspi->hspi.Init.NSS) && 
		(sspi->hspi.Init.CRCCalculation != SPI_CRCCALCULATION_DISABLE))
			return HAL_ERROR;	
	if (!IS_SPI_TIMODE(sspi->hspi.Init.TIMode))
		return HAL_ERROR;
	
	// check gpio
	if (!IS_GPIO_ALL_INSTANCE(sspi->CLK_GPIOx) || !IS_GPIO_ALL_INSTANCE(sspi->MISO_GPIOx) || !IS_GPIO_ALL_INSTANCE(sspi->MOSI_GPIOx))
		return HAL_ERROR;	
	if (!IS_GPIO_PIN(sspi->CLK_PIN) && !IS_GPIO_PIN(sspi->MISO_PIN) && !IS_GPIO_PIN(sspi->MOSI_PIN)) // if both pins arent set up
		return HAL_ERROR;
	
	return HAL_OK;
}

//------------------------SPI INIT FUNCTIONS------------------------
//-------------------------------------------------------------------