/********************************W25 FLASH**********************************
Данный файл содержит базовые функции для общения с памятью FLASH по SPI.
//-------------------Функции-------------------//
@func users
	- W25_FLASH_Read								Считывание FLASH
	- W25_FLASH_Write_Area					Запись данных в заданный участок FLASH (с потерей данных в выбраном сектора за пределами этого участка)
	- W25_FLASH_Erase_Sector				Очистка сектора FLASH
	- W25_FLASH_Program_Area				Программирование FLASH
	
@func initialization
	- W25_Base_Init									Инициализация SPI и GPIO для FLASH

@func process interaction with flash
	- W25_FLASH_Program_Page				Программирование страницы. *есть более общая функция W25_FLASH_Program_Area, которая программирует участки больше страницы
	- W25_WriteEnablingUntilTimeout	Разрешение записи, пока не будет ответа или не истек таймаут
	- W25_WaitOnFlagUntilTimeout		Ожидание флага пока не истек таймаута

@func cmd functions
	- W25_CMD_Read_Status_Register	Отправка комманд 	Read Status Register 1 / Read Status Register 1 (0x05h / 0x35h)
	- W25_CMD_Write_Status_Register	Отправка комманды Write Status Register (0x01h)
	- W25_CMD_Write_Enable					Отправка комманды Write Enable 					(0x06h)
	- W25_CMD_Write_Disable					Отправка комманды Write Disable 				(0x04h)
	- W25_CMD_Read_Data							Отправка комманды Read Data 						(0x03h)
	- W25_CMD_Fast_Read							Отправка комманды Fast Read							(0x0Bh)
	- W25_CMD_Page_Program					Отправка комманды Page Program					(0x02h)
	- W25_CMD_Erase_Sector					Отправка комманды Erase Sector					(0x20h)
	- W25_CMD_Read_JEDEC_ID					Отправка комманды Read JEDEC ID					(0x4Bh)
	- W25_CMD_Read_Device_ID				Отправка комманды Read Manufacture / Device Id (0x90)
	
@func SPI functions
	- W25_SPI_Transmit 							Функция отправки по SPI (содержит только HAL_SPI_Transmit)
	- W25_SPI_Receive								Функция приема по SPI (содержит только HAL_SPI_Receive)
***************************************************************************/
#include "spi_flash.h"
uint8_t sector_buff[W25_SECTOR_SIZE];

/* USER CODE BEGIN PV */
char str1[30];

//-------------------------------------------------------------
//--------------------------FOR USER---------------------------
/**  
	* @brief 	Initialize SPI and GPIO for W25 FLASH.
	* @param 	hw25 - указатель на структуру с настройками SPI и GPIO портов.
	*/
void W25_Base_Init(W25_HandleTypeDef *hw25)
{	
	
	// SPI PERIPH INIT
	if(hw25->hspi.Instance == NULL)
		hw25->hspi.Instance = SPI1;
	
  hw25->hspi.Init.Mode = SPI_MODE_MASTER;
  hw25->hspi.Init.Direction = SPI_DIRECTION_2LINES;
  hw25->hspi.Init.DataSize = SPI_DATASIZE_8BIT;
  hw25->hspi.Init.CLKPolarity = SPI_POLARITY_LOW;
  hw25->hspi.Init.CLKPhase = SPI_PHASE_1EDGE;
  hw25->hspi.Init.NSS = SPI_NSS_SOFT;
  hw25->hspi.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hw25->hspi.Init.TIMode = SPI_TIMODE_DISABLE;
  hw25->hspi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hw25->hspi.Init.CRCPolynomial = 10;

	// CLOCK
	if(hw25->hspi.Instance == SPI1)
		__HAL_RCC_SPI2_CLK_ENABLE();	
	else if (hw25->hspi.Instance == SPI2)
		__HAL_RCC_SPI2_CLK_ENABLE();	
	else if (hw25->hspi.Instance == SPI3)
		__HAL_RCC_SPI3_CLK_ENABLE();	
	
	// SPI INIT
	HAL_SPI_Init(&hw25->hspi);
	
	// GPIO INIT	
	GPIO_Clock_Enable(hw25->GPIOs.CS_GPIOx);
	GPIO_Clock_Enable(hw25->GPIOs.CLK_GPIOx);
	GPIO_Clock_Enable(hw25->GPIOs.MISO_GPIOx);
	GPIO_Clock_Enable(hw25->GPIOs.MOSI_GPIOx);	
	// CHIP SELECT PIN INIT
	GPIO_InitTypeDef GPIO_InitStruct = {0};	
  GPIO_InitStruct.Pin = hw25->GPIOs.CS_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(hw25->GPIOs.CS_GPIOx, &GPIO_InitStruct);
	// CLK PIN INIT
	GPIO_InitStruct.Pin = hw25->GPIOs.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(hw25->GPIOs.CLK_GPIOx, &GPIO_InitStruct);
	// MISO PIN INIT
	GPIO_InitStruct.Pin = hw25->GPIOs.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(hw25->GPIOs.MISO_GPIOx, &GPIO_InitStruct);
	// MOSI PIN INIT
	GPIO_InitStruct.Pin = hw25->GPIOs.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(hw25->GPIOs.MOSI_GPIOx, &GPIO_InitStruct);
	
}

/** 
	* @brief 	Read data from external FLASH.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	FLASH_Address - адресс откуда начинать считывание.
	* @param 	pBuff	 				- куда записывать данные из FLASH.
	* @param 	Size	 				- сколько байтов считывать.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @return HAL status.
	* @note		Включает в себя проверку на доступность памяти (флаг BUSY)
	*/
HAL_StatusTypeDef W25_FLASH_Read(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout)
{
	uint32_t tickstart = HAL_GetTick();
	
	// wait for unbusy
	if(W25_WaitOnFlagUntilTimeout(hw25, W25_SR_BUSY, 0, Timeout, tickstart) != HAL_OK) 	// if its unbusy for timeout
		return HAL_TIMEOUT;																																// return timeout error
	
	W25_CMD_Read_Data(hw25, FLASH_Address, pBuff, Size);
	return HAL_OK;
}
/** 
	* @brief 	Write data to area in external FLASH.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	WriteInit 		- указатель на структуру, определяющую участок памяти для записи.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @return HAL status.
	* @note		Позволяет перепрограммировать участок памяти. Можно записывать несколько страниц.
	*					Данные в сектора участка, но за пределами участка не сохраняются.
	*/
HAL_StatusTypeDef W25_FLASH_Write_Area(W25_HandleTypeDef *hw25, W25_WriteInitTypeDef *WriteInit, uint32_t Timeout)
{
	uint8_t sector_buff[256];
	uint32_t tickstart = HAL_GetTick();	
	uint32_t timeoutcnt = Timeout;	
	HAL_StatusTypeDef W25_Status;
	
	// WAIT FOR UNBUSY
	if(W25_WaitOnFlagUntilTimeout(hw25, W25_SR_BUSY, 0, Timeout, tickstart) != HAL_OK) 	// if its unbusy for timeout
		return HAL_TIMEOUT;																																// return timeout error
	
	// SAVE USER "SECTOR" FROM FLASH
	timeoutcnt = HAL_GetTick() - tickstart; // update timeout
	Timeout -= timeoutcnt;
	tickstart += timeoutcnt;	
	// store data from flash
	W25_Status = W25_FLASH_Read(hw25, WriteInit->Sector_Address, sector_buff, WriteInit->Sector_Size, Timeout);
	if(W25_Status != HAL_OK)
		return W25_Status;
	
	// ERASE SECTOR
	timeoutcnt = HAL_GetTick() - tickstart; // update timeout
	Timeout -= timeoutcnt;
	tickstart += timeoutcnt;
	// erase flash
	W25_Status = W25_FLASH_Erase_Sector(hw25, WriteInit->Sector_Address, Timeout);
	if(W25_Status != HAL_OK)
		return W25_Status;

	// CHANGE DATA IN USER "SECTOR"
	uint32_t addr_shift = WriteInit->Data_Address - WriteInit->Sector_Address;
	for(int i = 0; i < WriteInit->Data_Size; i++)
	{
		sector_buff[addr_shift+i] = WriteInit->pDataPtr[addr_shift+i];
	}
	
	// RESTORE UPDATED DATA TO FLASH
	timeoutcnt = HAL_GetTick() - tickstart; // update timeout
	Timeout -= timeoutcnt;
	tickstart += timeoutcnt;
	// restore data to flash
	W25_Status = W25_FLASH_Program_Area(hw25, WriteInit->Sector_Address, sector_buff, WriteInit->Sector_Size, Timeout);
	if(W25_Status != HAL_OK)
		return W25_Status;
	
	return HAL_OK;
}

/** 
	* @brief 	Program area in external FLASH.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	FLASH_Address - адресс куда начинать записывать.
	* @param 	pData	 				- откуда брать данные для записи в FLASH.
	* @param 	Size	 				- сколько байтов записать.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @return HAL status.
	* @note		Позволяет перепрограммировать участок памяти. Можно записывать несколько страниц.
	*					Данные в сектора участка, но за пределами участка не сохраняются.
	*/
HAL_StatusTypeDef W25_FLASH_Program_Area(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
	uint32_t tickstart = HAL_GetTick();	
	HAL_StatusTypeDef W25_Status;
	
	// CALC AREA TO PROGRAM
	uint16_t bytecnt = 0;
	uint16_t firstpage_size = 0;
	uint16_t lastpage_size = Size;
	uint16_t firstpage = (FLASH_Address/256);
	uint16_t lastpage = ((FLASH_Address+Size-1)/256);
	if(firstpage != lastpage) // if area is on several pages
	{
		firstpage_size = (firstpage+1)*256 - FLASH_Address; 	// set size of data at first page
		lastpage_size = (FLASH_Address+Size) - lastpage*256;		// set size of data at last page
	}
	
	// PROGRAM PAGES: FROM FIRST NO THE PREVIOUS TO THE LAST
	for(int i = 0; i < lastpage - firstpage; i++)
	{
		W25_Status = W25_FLASH_Program_Page(hw25, FLASH_Address, &pData[bytecnt], firstpage_size, Timeout, tickstart);	// programm page
		if(W25_Status != HAL_OK)
			return W25_Status;
		
		// note for multiple page program: first we program rest of the first page,
		// then we shift byte count to data, that shoud be on the next page
		bytecnt += firstpage_size;
		FLASH_Address += firstpage_size;
		// and set start size as page size. because next pages will be fully programmed
		firstpage_size = W25_PAGE_SIZE;
	}
	
	// PROGRAM LAST PAGE	
	W25_Status = W25_FLASH_Program_Page(hw25, FLASH_Address, &pData[bytecnt], lastpage_size, Timeout, tickstart);	// programm page
	if(W25_Status != HAL_OK)
		return W25_Status;
	
	return HAL_OK; // if all ok return HAL_OK
}

/** 
	* @brief 	Erase external FLASH Sector.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	FLASH_Address - адресс где надо данные стереть.
	* @param 	Timeout	 			- время, за которое должно быть осуществлена очистка.
	* @return HAL status.
	* @note		При Timeout = 0, функция не будет ожидать окончания очистки (выставления в 0 флагов BUSY и WEL)
	* @note		Микросхема вроде сама высчитывает какой сектор ерейзнуть, в соответствии с заданным адресом.
	*/
HAL_StatusTypeDef W25_FLASH_Erase_Sector(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint32_t Timeout)
{
	uint32_t tickstart = HAL_GetTick();
	
	// enable writting and waiting for unbusy
	if(W25_WriteEnablingUntilTimeout(hw25, Timeout, tickstart) != HAL_OK) 	// if writting isnt enable
		return HAL_TIMEOUT;																										// return timeout
	
	// programm page (instruction)
	W25_CMD_Erase_Sector(hw25, FLASH_Address);
	
	// waiting for ending of erasing	
	if(Timeout) // if timeout isn zero - wait it.
		if(W25_WaitOnFlagUntilTimeout(hw25, W25_SR_WEL|W25_SR_BUSY, 0, Timeout, tickstart) != HAL_OK)	// if erase isnt done (W25 busy and WEL bit isnt in reset state)
			return HAL_TIMEOUT;																																					// return timeout because erasing instruction accepted, but arent done
	// note: if timeout == 0, erasing wouldnt be checking for ending (check busy flag)
		
	return HAL_OK; // if all ok return HAL_OK
}

/** 
	* @brief 	Program page in external FLASH.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	FLASH_Address - адресс куда начинать записывать.
	* @param 	pData	 				- откуда брать данные для записи в FLASH.
	* @param 	Size	 				- сколько байтов записать.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @param 	tickstart	 		- время, относительно которого надо отсчитывать таймаут.
	* @return HAL status.
	* @note		Позволяет перепрограммировать только байты в прелелах одной страницы.
						Для более гибкого программирования есть функция W25_FLASH_Program_Area, которая программирует участки любой длины (в теории).
	*/
HAL_StatusTypeDef W25_FLASH_Program_Page(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout, uint32_t tickstart)
{
	// enable writting and waiting for unbusy
	if(W25_WriteEnablingUntilTimeout(hw25, Timeout, tickstart) != HAL_OK) 	// if writting isnt enable
		return HAL_TIMEOUT;																										// return timeout

	// programm page (instruction)
	W25_CMD_Page_Program(hw25, FLASH_Address, pData, Size);
		
	// waiting for ending of writting	
	if(W25_WaitOnFlagUntilTimeout(hw25, W25_SR_WEL|W25_SR_BUSY, 0, Timeout, tickstart) != HAL_OK)	// if writting isnt done (W25 busy and WEL bit isnt in reset state)
		return HAL_TIMEOUT;
	
	return HAL_OK;
}
/** 
	* @brief 	Setting WEL bit until it setted or until timeout.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @param 	tickstart	 		- время, относительно которого надо отсчитывать таймаут.
	* @return HAL status.
	* @note		Подает команду на разрешение записи до тех пор, пока она запись не разрешиться или до тех пор, пока таймаут не истечет.
	*/
HAL_StatusTypeDef W25_WriteEnablingUntilTimeout(W25_HandleTypeDef *hw25, uint32_t Timeout, uint32_t tickstart)
{
	// enable writting
	W25_CMD_Read_Status_Register(hw25, W25_SR_WEL|W25_SR_BUSY);
	while((hw25->SR&W25_SR_WEL) != W25_SR_WEL)
	{
		// if flash isnt busy - set WEL flag
		if((hw25->SR&W25_SR_BUSY) == 0)	
			W25_CMD_Write_Enable(hw25);
		
		W25_CMD_Read_Status_Register(hw25, W25_SR_WEL);
		if((HAL_GetTick() - tickstart) >=  Timeout) // if time is out 
			return HAL_TIMEOUT;												// set timeout
	}
	return HAL_OK; // if all ok return HAL_OK
}

/** 
	* @brief 	Wait for flag until timeout.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	FlagMask	 		- маска для флагов, какие флаги считывать.
	* @param 	FlagStatus	 	- какое состояние должно быть у выбранных флагов.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @param 	tickstart	 		- время, относительно которого надо отсчитывать таймаут.
	* @return HAL status.
	* @note		Считывает флаги до тех пор, пока они не будут в состоянии FlagStatus или до тех пор, пока таймаут не истечет.
	*/
HAL_StatusTypeDef W25_WaitOnFlagUntilTimeout(W25_HandleTypeDef *hw25, uint16_t FlagMask, uint16_t FlagStatus, uint32_t Timeout, uint32_t tickstart)
{
	// enable writting
	W25_CMD_Read_Status_Register(hw25, FlagMask);
	while((hw25->SR&FlagMask) != FlagStatus)
	{
		W25_CMD_Read_Status_Register(hw25, FlagMask);
		if((HAL_GetTick() - tickstart) >=  Timeout) // if time is out 
			return HAL_TIMEOUT;												// set timeout
	}
	return HAL_OK; // if all ok return HAL_OK
}



//-------------------------------------------------------------
//----------------------COMMAND FUNCTIONS----------------------
/** 
	* @brief 	Send command to read Status Register.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	RequestedBits - какие биты запросить.
	* @return Заполняет Status Register в hw25.
	* @note		Всего есть две комманды: на запрос верхнего или нижниго байта. 
	*					Функция в соответствии с RequestedBits определяет какой байт запросить, или два байта сразу.
	*/
void W25_CMD_Read_Status_Register(W25_HandleTypeDef *hw25, uint16_t RequestedBits)
{
	uint8_t command[2];
	uint8_t *pSRPtr = 0;
	uint8_t size = 1;
	
	if(RequestedBits >> 8) 	// if its high byte of status register
	{		
		command[0] = W25_READ_STATUS_REG_2;
		pSRPtr = (uint8_t *)(&hw25->SR) + 1; // set pointer to HI byte of SR register
		size = 1;
		if(RequestedBits & 0xFF) // if low byte also requester
		{
			size = 2;	// set size to 2 bytes
			command[1] = W25_READ_STATUS_REG_1;
		}
	}
	else										// of its low byte of status register
	{
		command[0] = W25_READ_STATUS_REG_1;
		pSRPtr = (uint8_t *)(&hw25->SR); // set pointer to LO byte of SR register
		size = 1;
	}
	 
	W25_Select(hw25);
  W25_SPI_Transmit(hw25, command, 1); 	// send insctruction to read SR
  W25_SPI_Receive(hw25, pSRPtr, 1);			// receive response
  W25_Deselect(hw25);
	if(size > 1) // if 2 bytes are requested
	{		
		W25_Select(hw25);
		W25_SPI_Transmit(hw25, command+1, 1); 	// send insctruction to read SR
		W25_SPI_Receive(hw25, pSRPtr-1, 1);			// receive response
		W25_Deselect(hw25);
	}
}

/** 
	* @brief 	Send command to write bits in Status Register.
	* @param 	hw25 				- указатель на хендл flash.
	* @param 	WrittenBits - какие биты запросить.
	* @note		Данная команда посылает биты, как сдвинутые на 2 вправо. Т.е. 0-й бит в комманде - 2-й бит BP0.
						Но биты указываются в также как они расположены и регистре. Функция сама выполняет сдвиг. 
	*/
void W25_CMD_Write_Status_Register(W25_HandleTypeDef *hw25, uint16_t WrittenBits)
{
	uint8_t command[2];
	
  command[0] = W25_WRITE_STATUS_REG;
	command[1] = WrittenBits >> 2;

  W25_Select(hw25);
  W25_SPI_Transmit(hw25, command, 1);
  W25_Deselect(hw25);
}

/** 
	* @brief 	Send command to set Write Enable Latch (WEL) in Status Register.
	* @param 	hw25 - указатель на хендл flash.
	* @note 	Разрешает запись в FLASH, путем высталения WEL в Status Register
	*/
void W25_CMD_Write_Enable(W25_HandleTypeDef *hw25)
{
  uint8_t command[1];
  command[0] = W25_WRITE_ENABLE;

  W25_Select(hw25);
  W25_SPI_Transmit(hw25, command, 1);
  W25_Deselect(hw25);
}


/** 
	* @brief 	Send command to read data from FLASH.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	FLASH_Address - адресс откуда начинать считывание.
	* @param 	pBuff	 				- куда записывать данные из FLASH.
	* @param 	Size	 				- сколько байтов считывать.
	* @return pBuff.
	*/

void W25_CMD_Read_Data(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size)
{
	uint8_t command[4];
	uint8_t response[2] = {0};
	
  command[0] = W25_READ_DATA;
  command[1] = FLASH_Address >> 16 	& 0xFF;
  command[2] = FLASH_Address >> 8 	& 0xFF;
  command[3] = FLASH_Address 				& 0xFF;
	
  W25_Select(hw25);
  W25_SPI_Transmit(hw25, command, 4);
  W25_SPI_Receive(hw25, pBuff, Size);	
  W25_Deselect(hw25);
}
/** 
	* @brief 	Send command to fast page program.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	FLASH_Address - адресс куда начинать записывать.
	* @param 	pData	 				- откуда брать данные для записи в FLASH.
	* @param 	Size	 				- сколько байтов записать.
	* @note		Программирование FLASH только в пределах одной страницы. 
	*					Т.е. если запись с 0x0, то не больше 256 байт. Если с 0ч40, то не больше 192 байт.
	*/
void W25_CMD_Fast_Read(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size)
{
	uint8_t command[5] = {0};
	uint8_t response[2] = {0};
	
  command[0] = W25_READ_DATA;
  command[1] = FLASH_Address >> 16 	& 0xFF;
  command[2] = FLASH_Address >> 8 	& 0xFF;
  command[3] = FLASH_Address 				& 0xFF;
  command[4] = 0xFF;
	
  W25_Select(hw25);
  W25_SPI_Transmit(hw25, command, 5);
  W25_SPI_Receive(hw25, pBuff, Size);	
  W25_Deselect(hw25);
}
/** 
	* @brief 	Send command to fast page program.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	FLASH_Address - адресс куда начинать записывать.
	* @param 	pData	 				- откуда брать данные для записи в FLASH.
	* @param 	Size	 				- сколько байтов записать.
	* @note		Программирование FLASH только в пределах одной страницы. 
	*					Т.е. если запись с 0x0, то не больше 256 байт. Если с 0ч40, то не больше 192 байт.
	*/
void W25_CMD_Page_Program(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size)
{
	// 1 command byte + 3 address bytes + 256 data bytes
	uint8_t command[1+3+256];
	FLASH_Address = FLASH_Address & 0xFFFFFF;
	
  command[0] = W25_PAGE_PROGRAM;
	command[1] = FLASH_Address >> 16 	& 0xFF;
	command[2] = FLASH_Address >> 8 	& 0xFF;
	command[3] = FLASH_Address 				& 0xFF;
	
	if((FLASH_Address/256) != ((FLASH_Address+Size-1)/256))
		return;
	
  W25_Select(hw25);
  W25_SPI_Transmit(hw25, command, 4); 	// send insctruction to write
  W25_SPI_Transmit(hw25, pData, Size);	// send data to write
  W25_Deselect(hw25);
}

/** 
	* @brief 	Send command to erase sector.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	FLASH_Address - адресс где надо данные стереть.
	* @note		Микросхема вроде сама высчитывает какой сектор ерейзнуть, в соответствии с заданным адресом.
	*/
void W25_CMD_Erase_Sector(W25_HandleTypeDef *hw25, uint32_t FLASH_Address)
{
	uint8_t command[4];
  uint8_t response[8];
	FLASH_Address = FLASH_Address & 0xFFFFFF;
	
  command[0] = W25_ERASE_SECTOR;
	command[1] = FLASH_Address >> 16;
	command[2] = FLASH_Address >> 8;
	command[3] = FLASH_Address;
	
  W25_Select(hw25);
  W25_SPI_Transmit(hw25, command, 4);
  W25_Deselect(hw25);
}


/** 
	* @brief 	Send command to read JEDEC ID.
	* @param 	hw25 					- указатель на хендл flash.
	* @return	JEDEC ID микросхемы.
	*/
uint32_t W25_CMD_Read_JEDEC_ID(W25_HandleTypeDef *hw25)
{
  uint8_t command[1] = {0};
  uint8_t receive[4] = {0};
	uint32_t return_val;
	
  command[0] = W25_READ_JEDEC_ID;
  W25_Select(hw25);
  W25_SPI_Transmit(hw25, command, 1);
  W25_SPI_Receive(hw25, &receive[1], 3);
  W25_Deselect(hw25);
	
	return_val = (*(uint64_t *)receive);
  return __REV(return_val) & 0xFFFFFF;
}

/** 
	* @brief 	Send command to read JEDEC ID.
	* @param 	hw25 					- указатель на хендл flash.
	* @return	Device ID микросхемы.
	*/
uint64_t W25_CMD_Read_Device_ID(W25_HandleTypeDef *hw25)
{
  uint8_t command[1] = {0};
  uint8_t receive[8] = {0};
	uint64_t return_val_LO;
	uint64_t return_val_HI;
  command[0] = W25_READ_UNIQUE_ID;
  W25_Select(hw25);
  W25_SPI_Transmit(hw25, command, 1);
  W25_SPI_Receive(hw25, receive, 8);
  W25_Deselect(hw25);
	
	return_val_LO = (*(uint64_t *)receive)  >> 32;
	return_val_HI = (*(uint64_t *)receive) & 0xFFFFFFFF;
  return ((uint64_t)__REV(return_val_HI) << 32) | __REV(return_val_LO);
}
//-------------------------------------------------------------

//-------------------------------------------------------------
//---------------------PERIPTH FUNCTIONS-----------------------
/** 
	* @brief 	SPI Transmit.
	* @param 	hw25 - указатель на хендл flash.
	* @param 	data - указатель на данные для отправки.
	* @param 	size - размер данных для отправки.
	* @return	Device ID микросхемы.
	* @note 	Здесь вызывается только функция HAL, и ничего больше.
	*/
void W25_SPI_Transmit (W25_HandleTypeDef *hw25, uint8_t *data, uint16_t size)
{
  HAL_SPI_Transmit (&hw25->hspi, data, size, 500);
 
}
/** 
	* @brief 	SPI Receive.
	* @param 	hw25 - указатель на хендл flash.
	* @param 	data - указатель на буффер для прниема данных.
	* @param 	size - размер данных для приема.
	* @return	Device ID микросхемы.
	* @note 	Здесь вызывается только функция HAL, и ничего больше.
	*/
void W25_SPI_Receive (W25_HandleTypeDef *hw25, uint8_t *data, uint16_t size)
{
  HAL_SPI_Receive (&hw25->hspi, data, size, 500);
}
//-------------------------------------------------------------
