/********************************MEMSPI FLASH**********************************
Данный файл содержит базовые функции для общения с памятью EEPROM/FLASH по SPI.

*********************************INIT EXAMPLE**********************************

	hmemspi.CS_GPIOx = GPIOC;
	hmemspi.CS_PIN = 	GPIO_PIN_4;
	MEMSPI_Base_Init(&hmemspi, &hspi3);
  
	MEMSPI_RES = MEMSPI_EEPROM_Write(&hmemspi, mem_adr, write_buff, buff_size, Timeout, 1);
	MEMSPI_RES = MEMSPI_Read_Memory(&hmemspi, mem_adr, read_buff, buff_size, Timeout);
  
***********************************FUNCTIONS***********************************
@func users
	- MEMSPI_Read_Memory								Считывание внешней FLASH/EEPROM
	- MEMSPI_EEPROM_Write								Запись данных в внешнюю EEPROM
	- MEMSPI_FLASH_Write								Запись данных в внешнюю FLASH		(функция сама очищает нужные сектора, и если надо сохраняет выбранные данные)
	- MEMSPI_FLASH_Program							Программирование внешней FLASH 	(выбранный участок FLASH должен быть очищен)
	- MEMSPI_FLASH_Erase								Очистка внешней FLASH
	
@func initialization
	- MEMSPI_Base_Init									Инициализация SPI и GPIO для внешней памяти

@func service
	- MEMSPI_EEPROM_Write_Page					Запись страницы в EEPROM 		*есть более общая функция MEMSPI_EEPROM_Write, которая записывает участки больше страницы
	- MEMSPI_FLASH_Erase_Sector					Очистка сектора FLASH. 			*есть более общая функция MEMSPI_FLASH_Erase, которая может ощичать несколько секторов
	- MEMSPI_FLASH_Program_Page					Программирование страницы. 	*есть более общая функция MEMSPI_FLASH_Program, которая программирует участки больше страницы
	- MEMSPI_WriteEnablingUntilTimeout	Разрешение записи, пока не будет ответа или не истек таймаут
	- MEMSPI_WaitOnFlagsUntilTimeout		Ожидание флага пока не истек таймаута

@func cmd functions
	- MEMSPI_CMD_Read_Status_Register		Отправка комманд 	Read Status Register / Read Status Register 2 (0x05h / 0x35h)
	- MEMSPI_CMD_Write_Status_Register	Отправка комманды Write Status Register (0x01h)
	- MEMSPI_CMD_Write_Enable						Отправка комманды Write Enable 					(0x06h)
	- MEMSPI_CMD_Write_Disable					Отправка комманды Write Disable 				(0x04h)
	- MEMSPI_CMD_Read_Data							Отправка комманды Read Data 						(0x03h)
	- MEMSPI_CMD_EEPROM_Write						Отправка комманды Write (eeprom)				(0x02h)
	- MEMSPI_CMD_FLASH_Page_Program			Отправка комманды Page Program (flash)	(0x02h)
	- MEMSPI_CMD_FLASH_Erase_Sector			Отправка комманды Erase Sector (flash)	(0x20h)
	- MEMSPI_CMD_Read_JEDEC_ID					Отправка комманды Read JEDEC ID					(0x4Bh)
	- MEMSPI_CMD_Read_Device_ID					Отправка комманды Read Manufacture / Device Id (0x90)
	
***************************************************************************/
#include "memory_spi.h"
uint8_t sector_buff[MEMSPI_SECTOR_SIZE];

//-------------------------------------------------------------
//--------------------------FOR USER---------------------------
/**  
	* @brief 	Initialize SPI and GPIO for MEMSPI FLASH.
	* @param 	hmemspi - указатель на структуру с настройками SPI и GPIO портов.
	*/
void MEMSPI_Base_Init(MEMSPI_HandleTypeDef *hmemspi, SPI_HandleTypeDef *hspi)
{	
  
	// SPI PERIPH INIT
	if(hspi == NULL)
    return;
    
  hmemspi->hspi = hspi;
		
	// GPIO INIT	
	// CHIP SELECT PIN INIT
	GPIO_InitTypeDef GPIO_InitStruct = {0};	
  GPIO_InitStruct.Pin = hmemspi->CS_PIN;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
#ifdef GPIO_SPEED_FREQ_VERY_HIGH
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
#else
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
#endif
  HAL_GPIO_Init(hmemspi->CS_GPIOx, &GPIO_InitStruct);
  MEMSPI_Deselect(hmemspi);
	
}

/** 
	* @brief 	Read external FLASH/EEPROM.
	* @param 	hmemspi 			- указатель на хендл внешней памяти.
	* @param 	FLASH_Address - адресс откуда начинать считывание.
	* @param 	pBuff	 				- куда записывать данные из FLASH.
	* @param 	Size	 				- сколько байтов считывать.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @return HAL status.
	* @note		Включает в себя проверку на доступность памяти (флаг BUSY)
	*/
HAL_StatusTypeDef MEMSPI_Read_Memory(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout)
{
	HAL_StatusTypeDef MEMSPI_Status;
	uint32_t tickstart = HAL_GetTick();
	
	// wait for unbusy
	if(MEMSPI_WaitOnFlagsUntilTimeout(hmemspi, MEMSPI_SR_BUSY, 0, &Timeout, &tickstart) != HAL_OK) 	// if its unbusy for timeout
		return HAL_TIMEOUT;																																// return timeout error
	
	MEMSPI_Status = MEMSPI_CMD_Read_Data(hmemspi, FLASH_Address, pBuff, Size, Timeout);
	if(MEMSPI_Status != HAL_OK)
		return MEMSPI_Status;
	
	return HAL_OK;
}

/** 
	* @brief 	Write external EEPROM.
	* @param 	hmemspi 			- указатель на хендл внешней памяти.
	* @param 	FLASH_Address - адресс куда начинать записывать.
	* @param 	pData	 				- откуда брать данные для записи в EEPROM.
	* @param 	Size	 				- сколько байтов записать.
	* @param 	Timeout	 			- время, за которое должна быть осуществлена запись.
	* @param 	WaitForEnd	 	- ожидание, пока память не выполненит операцию.
	* @return HAL status.
	* @note		Позволяет записать участок памяти. Можно записывать несколько страниц.
	*/
HAL_StatusTypeDef MEMSPI_EEPROM_Write(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout, uint8_t WaitForEnd)
{
	uint32_t tickstart = HAL_GetTick();
	HAL_StatusTypeDef MEMSPI_Status;
	
	// CALC AREA TO WRITE
	uint16_t bytecnt = 0;
	uint16_t currentpage_size = 0;
	uint16_t lastpage_size = Size;
	uint16_t firstpage = (FLASH_Address/MEMSPI_PAGE_SIZE);
	uint16_t lastpage = ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE);
	if(firstpage != lastpage) // if area is on several pages
	{
		currentpage_size = (firstpage+1)*MEMSPI_PAGE_SIZE - FLASH_Address; 	// set size of data on first page
		lastpage_size = (FLASH_Address+Size) - lastpage*MEMSPI_PAGE_SIZE;		// set size of data on last page
	}
	
	// PROGRAM PAGES: FROM FIRST NO THE PREVIOUS TO THE LAST
	hmemspi->hNextAddr = FLASH_Address; // address would automatically increase in this variable
	for(int i = 0; i < lastpage - firstpage; i++)
	{
		MEMSPI_Status = MEMSPI_EEPROM_Write_Page(hmemspi, hmemspi->hNextAddr, &pData[bytecnt], currentpage_size, &Timeout, &tickstart, 0);	// programm page
		if(MEMSPI_Status != HAL_OK)																																																	// note: no need waiting for end: the next call will wait for unbusy
			return MEMSPI_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 += currentpage_size;
		// and set current size as page size. because next pages will be fully programmed
		currentpage_size = MEMSPI_PAGE_SIZE;
	}
	
	// PROGRAM LAST PAGE
	MEMSPI_Status = MEMSPI_FLASH_Program_Page(hmemspi, hmemspi->hNextAddr, &pData[bytecnt], lastpage_size, &Timeout, &tickstart, WaitForEnd);	// programm page
	if(MEMSPI_Status != HAL_OK)
		return MEMSPI_Status;
	
	return HAL_OK;
}
/** 
	* @brief 	Write external FLASH.
	* @param 	hmemspi 			- указатель на хендл внешней памяти.
	* @param 	WriteInit 		- указатель на структуру, определяющую участок памяти для записи.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @param 	WaitForEnd	 	- ожидание, пока память не выполненит операцию.
	* @return HAL status.
	* @note		Позволяет перепрограммировать участок памяти. Можно записывать несколько страниц.
	*					Данные в сектора участка, но за пределами участка не сохраняются.
	*/
HAL_StatusTypeDef MEMSPI_FLASH_Write(MEMSPI_HandleTypeDef *hmemspi, MEMSPI_WriteInitTypeDef *WriteInit, uint32_t Timeout, uint8_t WaitForEnd)
{
	uint32_t tickstart = HAL_GetTick();	
	uint32_t timeoutcnt = Timeout;	
	uint8_t *writebuff = WriteInit->pDataPtr;	
	HAL_StatusTypeDef MEMSPI_Status;

	// CHECK FOR UNDEFINED STRUCTURE
	if (WriteInit->Sector_Size == 0) 		// if sector undefined
	{																		// fill it with data init
		WriteInit->Sector_Address = WriteInit->Data_Address;
		WriteInit->Sector_Size = WriteInit->Data_Size;
	}
	else if (WriteInit->Data_Size == 0) // if data undefined
	{																		// fill it with sector init
		WriteInit->Data_Address = WriteInit->Sector_Address;
		WriteInit->Data_Size = WriteInit->Sector_Size;
	}																		// if both undefined - return HAL ERR
	else if ((WriteInit->Sector_Size == 0) && (WriteInit->Data_Size == 0))
	{
		return HAL_ERROR;
	}
	// CHECK FOR CORRECT STRUCTURE
	// if data is beyound sector
	if((WriteInit->Data_Address < WriteInit->Sector_Address) || 
		((WriteInit->Data_Address + WriteInit->Data_Size) > (WriteInit->Sector_Address + WriteInit->Sector_Size)))
		return HAL_ERROR;
	
	// WRITE FLASH WITH SAVING PREVIOUS DATA
	if(WriteInit->fSavePrevoisData)
	{
//		uint8_t sector_buff[WriteInit->Sector_Size];
		// store data from flash
		MEMSPI_Status = MEMSPI_Read_Memory(hmemspi, WriteInit->Sector_Address, sector_buff, WriteInit->Sector_Size, Timeout);
		if(MEMSPI_Status != HAL_OK)
			return MEMSPI_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];
		}
		
		// CALC AREA TO REPROGRAM
		uint16_t lastsector_size = WriteInit->Sector_Size;
		uint16_t firstsector = (WriteInit->Sector_Address/MEMSPI_SECTOR_SIZE);
		uint16_t lastsector = ((WriteInit->Sector_Address+WriteInit->Sector_Size-1)/MEMSPI_SECTOR_SIZE);
		if(firstsector != lastsector) // if area is on several pages
		{
			lastsector_size = (WriteInit->Sector_Address+WriteInit->Sector_Size) - lastsector*MEMSPI_SECTOR_SIZE;		// set size of data on last page
		}
		// REPROGRAM SECTORS: FROM FIRST SECTOR NO THE PREVIOUS TO THE LAST
		hmemspi->hNextAddr = WriteInit->Sector_Address; // address would automatically increase in this variable
		hmemspi->hNextSector = firstsector+1;
		uint16_t bytescnt = 0;
		uint16_t bytes_to_next_sector = 0;
		for(int i = 0; i < (lastsector - firstsector); i++)
		{
			// ERASE FLASH
			MEMSPI_Status = MEMSPI_FLASH_Erase(hmemspi, hmemspi->hNextAddr, 1, Timeout, 0);
			if(MEMSPI_Status != HAL_OK)																																			// note: no need waiting for end: the next call will wait for unbusy
				return MEMSPI_Status;
			
			// PROGRAM FLASH WITH NEW DATA
			// calc bytes to next sector
			bytes_to_next_sector = hmemspi->hNextSector*MEMSPI_SECTOR_SIZE - hmemspi->hNextAddr;
			// program data to flash
			MEMSPI_Status = MEMSPI_FLASH_Program(hmemspi, hmemspi->hNextAddr, &sector_buff[bytescnt], bytes_to_next_sector, Timeout, 0);
			if(MEMSPI_Status != HAL_OK)
				return MEMSPI_Status;			
			// shift bytes count to data, that shoud be on the next page
			bytescnt += bytes_to_next_sector; 
		}
		
		// ERASE FLASH
		MEMSPI_Status = MEMSPI_FLASH_Erase(hmemspi, hmemspi->hNextAddr, 1, Timeout, 0);
		if(MEMSPI_Status != HAL_OK)																																			// note: no need waiting for end: the next call will wait for unbusy
			return MEMSPI_Status;
			
		// PROGRAM LAST SECTOR
		MEMSPI_Status = MEMSPI_FLASH_Program(hmemspi, hmemspi->hNextAddr, &sector_buff[bytescnt], lastsector_size, Timeout, WaitForEnd);
		if(MEMSPI_Status != HAL_OK)
			return MEMSPI_Status;					
	}
	// WRITE FLASH WITHOUT SAVING PREVIOUS DATA
	else
	{
		// ERASE FLASH
		MEMSPI_Status = MEMSPI_FLASH_Erase(hmemspi, WriteInit->Sector_Address, WriteInit->Sector_Size, Timeout, 0);
		if(MEMSPI_Status != HAL_OK)																																			// note: no need waiting for end: the next call will wait for unbusy
			return MEMSPI_Status;
		
		// PROGRAM FLASH WITH NEW DATA
		// program data to flash
		MEMSPI_Status = MEMSPI_FLASH_Program(hmemspi, WriteInit->Sector_Address, WriteInit->pDataPtr, WriteInit->Sector_Size, Timeout, WaitForEnd);
		if(MEMSPI_Status != HAL_OK)
			return MEMSPI_Status;
	}
	
	return HAL_OK;
}

/** 
	* @brief 	Program external FLASH.
	* @param 	hmemspi 			- указатель на хендл внешней памяти.
	* @param 	FLASH_Address - адресс куда начинать записывать.
	* @param 	pData	 				- откуда брать данные для записи в FLASH.
	* @param 	Size	 				- сколько байтов записать.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @param 	WaitForEnd	 	- ожидание, пока память не выполненит операцию.
	* @return HAL status.
	* @note		Программирование участка памяти, без ограничений на кол-во байт
	*/
HAL_StatusTypeDef MEMSPI_FLASH_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout, uint8_t WaitForEnd)
{
	uint32_t tickstart = HAL_GetTick();	
	HAL_StatusTypeDef MEMSPI_Status;
	
	// CALC AREA TO PROGRAM
	uint16_t lastpage_size = Size;
	uint16_t firstpage = (FLASH_Address/MEMSPI_PAGE_SIZE);
	uint16_t lastpage = ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE);
	if(firstpage != lastpage) // if area is on several pages
	{
		lastpage_size = (FLASH_Address+Size) - lastpage*MEMSPI_PAGE_SIZE;		// set size of data on last page
	}
	
	// PROGRAM PAGES: FROM FIRST NO THE PREVIOUS TO THE LAST
	hmemspi->hNextAddr = FLASH_Address; // address would automatically increase in this variable
	hmemspi->hNextPage = firstpage+1; // address would automatically increase in this variable
	uint16_t bytecnt = 0;
	uint16_t bytes_to_next_page = 0;
	for(int i = 0; i < lastpage - firstpage; i++)
	{
		// calc bytes to next sector
		bytes_to_next_page = hmemspi->hNextPage*MEMSPI_PAGE_SIZE - hmemspi->hNextAddr;
		
		MEMSPI_Status = MEMSPI_FLASH_Program_Page(hmemspi, hmemspi->hNextAddr, &pData[bytecnt], bytes_to_next_page, &Timeout, &tickstart, 0);	// programm page
		if(MEMSPI_Status != HAL_OK)																																																	// note: no need waiting for end: the next call will wait for unbusy
			return MEMSPI_Status;
				
		// then we shift byte count to data, that shoud be on the next page
		bytecnt += bytes_to_next_page;
	}
	
	// PROGRAM LAST PAGE
	MEMSPI_Status = MEMSPI_FLASH_Program_Page(hmemspi, hmemspi->hNextAddr, &pData[bytecnt], lastpage_size, &Timeout, &tickstart, WaitForEnd);	// programm page
	if(MEMSPI_Status != HAL_OK)
		return MEMSPI_Status;
	
	return HAL_OK; // if all ok return HAL_OK
}


/** 
	* @brief 	Erase external FLASH.
	* @param 	hmemspi 			- указатель на хендл внешней памяти.
	* @param 	FLASH_Address - адресс где надо данные стереть.
	* @param 	Size	 				- сколько байтов стереть.
	* @param 	Timeout	 			- время, за которое должно быть осуществлена очистка.
	* @param 	WaitForEnd	 	- ожидание, пока память не выполненит операцию.
	* @return HAL status.
	* @note		Т.к. очитска происходит по секторам, Size нужен, чтобы определить сколько секторов очистить
	*					И если начальны адресс будет на Sector 0, а последний байт на Sector 1, то произойдет очистка Sector 0 и Sector 1
	*/
HAL_StatusTypeDef MEMSPI_FLASH_Erase(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint16_t Size, uint32_t Timeout, uint8_t WaitForEnd)
{	
	uint32_t tickstart = HAL_GetTick();	
	HAL_StatusTypeDef MEMSPI_Status;
	
	// CALC AREA TO ERASE
	uint16_t bytecnt = 0;
	uint16_t firstsector = (FLASH_Address/MEMSPI_SECTOR_SIZE);
	uint16_t lastsector = ((FLASH_Address+Size-1)/MEMSPI_SECTOR_SIZE);
	
	for(int i = 0; i <= (lastsector - firstsector); i++)
	{
		MEMSPI_Status = MEMSPI_FLASH_Erase_Sector(hmemspi, FLASH_Address, &Timeout, &tickstart, WaitForEnd);	// programm page
		if(MEMSPI_Status != HAL_OK)
			return MEMSPI_Status;		
		FLASH_Address += MEMSPI_SECTOR_SIZE;
	}
	return HAL_OK;
}


//-------------------------------------------------------------
//----------------------SERVICE FUNCTIONS----------------------
/** 
	* @brief 	Write page in external EEPROM.
	* @param 	hmemspi 			- указатель на хендл внешней памяти.
	* @param 	FLASH_Address - адресс куда начинать записывать.
	* @param 	pData	 				- откуда брать данные для записи в EEPROM.
	* @param 	Size	 				- сколько байтов записать.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @param 	WaitForEnd	 	- ожидание, пока память не выполненит операцию.
	* @return HAL status.
	* @note		Позволяет записывать только байты в пределах одной страницы.
						Для более гибкой записи есть функция MEMSPI_EEPROM_Write, которая программирует участки любой длины (в теории).
	*/
HAL_StatusTypeDef MEMSPI_EEPROM_Write_Page(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd)
{
	HAL_StatusTypeDef MEMSPI_Status;	
	// enable writting and waiting for unbusy
	if(MEMSPI_WriteEnablingUntilTimeout(hmemspi, Timeout, tickstart) != HAL_OK) 	// if writting isnt enable
		return HAL_TIMEOUT;																										// return timeout
	
	MEMSPI_Status = MEMSPI_CMD_EEPROM_Write(hmemspi, FLASH_Address, pData, Size, *Timeout);
	if(MEMSPI_Status != HAL_OK)
		return MEMSPI_Status;
	
	// waiting for ending of writting	if need
	if(WaitForEnd)
		if(MEMSPI_WaitOnFlagsUntilTimeout(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 0, Timeout, tickstart) != HAL_OK)	// if writting isnt done (MEMSPI busy and WEL bit isnt in reset state)
			return HAL_TIMEOUT;
	
	// update handle variables
	hmemspi->hNextAddr = (FLASH_Address+Size);
	hmemspi->hNextPage = (FLASH_Address+Size)/MEMSPI_PAGE_SIZE;
	hmemspi->hNextSector = (FLASH_Address+Size)/MEMSPI_SECTOR_SIZE;
	return HAL_OK;
}
/** 
	* @brief 	Erase external FLASH Sector.
	* @param 	hmemspi 			- указатель на хендл внешней памяти.
	* @param 	FLASH_Address - адресс где надо данные стереть.
	* @param 	Timeout	 			- время, за которое должно быть осуществлена очистка.
	* @param 	tickstart	 		- время, относительно которого надо отсчитывать таймаут.
	* @param 	WaitForEnd	 	- ожидание, пока память не выполненит операцию.
	* @return HAL status.
	* @note		При Timeout = 0, функция не будет ожидать окончания очистки (выставления в 0 флагов BUSY и WEL)
	* @note		Микросхема вроде сама высчитывает какой сектор ерейзнуть, в соответствии с заданным адресом.
	*/
HAL_StatusTypeDef MEMSPI_FLASH_Erase_Sector(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd)
{	
	HAL_StatusTypeDef MEMSPI_Status;
	// enable writting and waiting for unbusy
	if(MEMSPI_WriteEnablingUntilTimeout(hmemspi, Timeout, tickstart) != HAL_OK) 	// if writting isnt enable
		return HAL_TIMEOUT;																										// return timeout
	
	// erase sector (instruction)
	MEMSPI_Status = MEMSPI_CMD_FLASH_Erase_Sector(hmemspi, FLASH_Address, *Timeout);
	if(MEMSPI_Status != HAL_OK)
		return MEMSPI_Status;
	
	// waiting for ending of erasing if need
	if(WaitForEnd)
		if(MEMSPI_WaitOnFlagsUntilTimeout(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 0, Timeout, tickstart) != HAL_OK)	// if erase isnt done (MEMSPI 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 	hmemspi 			- указатель на хендл внешней памяти.
	* @param 	FLASH_Address - адресс куда начинать записывать.
	* @param 	pData	 				- откуда брать данные для записи в FLASH.
	* @param 	Size	 				- сколько байтов записать.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @param 	tickstart	 		- время, относительно которого надо отсчитывать таймаут.
	* @param 	WaitForEnd	 	- ожидание, пока память не выполненит операцию.
	* @return HAL status.
	* @note		Позволяет перепрограммировать только байты в прелелах одной страницы.
						Для более гибкого программирования есть функция MEMSPI_FLASH_Program, которая программирует участки любой длины (в теории).
	*/
HAL_StatusTypeDef MEMSPI_FLASH_Program_Page(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd)
{
	HAL_StatusTypeDef MEMSPI_Status;
	// enable writting and waiting for unbusy
	if(MEMSPI_WriteEnablingUntilTimeout(hmemspi, Timeout, tickstart) != HAL_OK) 	// if writting isnt enable
		return HAL_TIMEOUT;																										// return timeout
	
	// check if flash range is placed at one page
	if((FLASH_Address/MEMSPI_PAGE_SIZE) != ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE)) // if page of first byte isnt equal page of last byte
		return HAL_ERROR;																													// return error
	
	// programm page (instruction)
	MEMSPI_Status = MEMSPI_CMD_FLASH_Page_Program(hmemspi, FLASH_Address, pData, Size, *Timeout);
	if(MEMSPI_Status != HAL_OK)
		return MEMSPI_Status;
	
	// waiting for ending of writting	 if need
	if(WaitForEnd)
		if(MEMSPI_WaitOnFlagsUntilTimeout(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 0, Timeout, tickstart) != HAL_OK)	// if writting isnt done (MEMSPI busy and WEL bit isnt in reset state)
			return HAL_TIMEOUT;
	
	// update handle variables
	hmemspi->hNextAddr = (FLASH_Address+Size);
	hmemspi->hNextPage = (hmemspi->hNextAddr+Size)/MEMSPI_PAGE_SIZE;
	hmemspi->hNextSector = (hmemspi->hNextAddr+Size)/MEMSPI_SECTOR_SIZE;
	
	return HAL_OK;
}
/** 
	* @brief 	Setting WEL bit until it setted or until timeout.
	* @param 	hmemspi 			- указатель на хендл внешней памяти.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @param 	tickstart	 		- время, относительно которого надо отсчитывать таймаут.
	* @return HAL status.
	* @note		Подает команду на разрешение записи до тех пор, пока она запись не разрешиться или до тех пор, пока таймаут не истечет.
	*/
HAL_StatusTypeDef MEMSPI_WriteEnablingUntilTimeout(MEMSPI_HandleTypeDef *hmemspi, uint32_t *Timeout, uint32_t *tickstart)
{
	HAL_StatusTypeDef MEMSPI_Status;
	
	// wait for unbusy
	if(MEMSPI_WaitOnFlagsUntilTimeout(hmemspi, MEMSPI_SR_BUSY, 0, Timeout, tickstart) != HAL_OK) 	// if its unbusy for timeout
		return HAL_TIMEOUT;		
	
	// enable writing
	while((hmemspi->SR&MEMSPI_SR_WEL) != MEMSPI_SR_WEL)
	{
		// if flash isnt busy - set WEL flag
		if((hmemspi->SR&MEMSPI_SR_BUSY) == 0)	
			MEMSPI_CMD_Write_Enable(hmemspi, *Timeout);
		
		// check is writting enabled
		MEMSPI_Status = MEMSPI_CMD_Read_Status_Register(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 1, *Timeout);
		if(MEMSPI_Status != HAL_OK)
		{
			MEMSPI_Deselect(hmemspi);
			return MEMSPI_Status;
		}
		
		if((HAL_GetTick() - *tickstart) >=  *Timeout) // if time is out 
		{
			MEMSPI_Deselect(hmemspi);
			return HAL_TIMEOUT;												// set timeout
		}
	}
  MEMSPI_Deselect(hmemspi);
	MEMSPI_Update_Timeout_Variables(Timeout, tickstart);
	return HAL_OK; // if all ok return HAL_OK
}

/** 
	* @brief 	Wait for flag until timeout.
	* @param 	hmemspi 			- указатель на хендл внешней памяти.
	* @param 	FlagMask	 		- маска для флагов, какие флаги считывать.
	* @param 	FlagStatus	 	- какое состояние должно быть у выбранных флагов.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @param 	tickstart	 		- время, относительно которого надо отсчитывать таймаут.
	* @return HAL status.
	* @note		Считывает флаги до тех пор, пока они не будут в состоянии FlagStatus или до тех пор, пока таймаут не истечет.
	*/
HAL_StatusTypeDef MEMSPI_WaitOnFlagsUntilTimeout(MEMSPI_HandleTypeDef *hmemspi, uint16_t FlagMask, uint16_t FlagStatus, uint32_t *Timeout, uint32_t *tickstart)
{
	HAL_StatusTypeDef MEMSPI_Status;
	// check flags
	MEMSPI_Status = MEMSPI_CMD_Read_Status_Register(hmemspi, FlagMask, 0, *Timeout);
	if(MEMSPI_Status != HAL_OK)
		return MEMSPI_Status;
	while((hmemspi->SR&FlagMask) != FlagStatus)
	{
		// check flags
//		MEMSPI_Status = MEMSPI_CMD_Read_Status_Register(hmemspi, FlagMask, *Timeout);
		MEMSPI_Status = MEMSPI_SPI_Receive(hmemspi, (uint8_t *)&hmemspi->SR, 1, *Timeout);	// receive response
		if(MEMSPI_Status != HAL_OK)
		{
			MEMSPI_Deselect(hmemspi);
			return MEMSPI_Status;
		}
		
		if((HAL_GetTick() - *tickstart) >=  *Timeout) // if time is out
		{			
			MEMSPI_Deselect(hmemspi);
			return HAL_TIMEOUT;												// set timeout
		}
	}
  MEMSPI_Deselect(hmemspi);
	MEMSPI_Update_Timeout_Variables(Timeout, tickstart);
	return HAL_OK; // if all ok return HAL_OK
}

/** 
	* @brief 	Update Timeout variables.
	* @param 	Timeout 		- указатель на переменную содержащию значение таймаута
	* @param 	tickstart 	- указатель на переменную содержащию начальное кол-во тиков.
	*/
void MEMSPI_Update_Timeout_Variables(uint32_t *Timeout, uint32_t *tickstart)
{
	uint32_t timeoutcnt = HAL_GetTick() - *tickstart; // update timeout
	*Timeout -= timeoutcnt;
	*tickstart += timeoutcnt;	
}
//-------------------------------------------------------------
//----------------------COMMAND FUNCTIONS----------------------
/** 
	* @brief 	Send command to read Status Register.
	* @param 	hmemspi 			- указатель на хендл внешней памяти.
	* @param 	RequestedBits - какие биты запросить.
	* @param 	EndCMD 				- завершать комманду или нет. (очистка Chip Selected пина)
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @return Заполняет Status Register в hmemspi.
	* @note		Всего есть две комманды: на запрос верхнего или нижниго байта. 
	*					Функция в соответствии с RequestedBits определяет какой байт запросить, или два байта сразу.
	*/
HAL_StatusTypeDef MEMSPI_CMD_Read_Status_Register(MEMSPI_HandleTypeDef *hmemspi, uint16_t RequestedBits, uint8_t EndCMD, uint32_t Timeout)
{
	HAL_StatusTypeDef SPI_RES;
	uint8_t command[2];
	uint8_t *pSRPtr = 0;
	uint8_t size = 1;
	
	if(RequestedBits >> 8) 	// if its high byte of status register
	{		
#ifdef MEMSPI_READ_STATUS_REG_2
		command[0] = MEMSPI_READ_STATUS_REG_2;
		pSRPtr = (uint8_t *)(&hmemspi->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] = MEMSPI_READ_STATUS_REG;
		}
#endif // MEMSPI_READ_STATUS_REG_2
	}
	else										// of its low byte of status register
	{
		command[0] = MEMSPI_READ_STATUS_REG;
		pSRPtr = (uint8_t *)(&hmemspi->SR); // set pointer to LO byte of SR register
		size = 1;
	}	 
	MEMSPI_Select(hmemspi);
  SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 1, Timeout); 	// send insctruction to read SR
  SPI_RES = MEMSPI_SPI_Receive(hmemspi, pSRPtr, 1, Timeout);			// receive response
	if(size > 1) // if 2 bytes are requested
	{		
		MEMSPI_Deselect(hmemspi);
		MEMSPI_Select(hmemspi);
		SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command+1, 1, Timeout); 	// send insctruction to read SR
		SPI_RES = MEMSPI_SPI_Receive(hmemspi, pSRPtr-1, 1, Timeout);			// receive response
		MEMSPI_Deselect(hmemspi);
	}
	if(EndCMD)
		MEMSPI_Deselect(hmemspi);
	return SPI_RES;
}

/** 
	* @brief 	Send command to write bits in Status Register.
	* @param 	hmemspi 		- указатель на хендл внешней памяти.
	* @param 	WrittenBits - какие биты запросить.
	* @note		Данная команда посылает биты, как сдвинутые на 2 вправо. Т.е. 0-й бит в комманде - 2-й бит BP0.
						Но биты указываются в также как они расположены и регистре. Функция сама выполняет сдвиг. 
	*/
HAL_StatusTypeDef MEMSPI_CMD_Write_Status_Register(MEMSPI_HandleTypeDef *hmemspi, uint16_t WrittenBits, uint32_t Timeout)
{
	HAL_StatusTypeDef SPI_RES;
	uint8_t command[3];
	uint8_t size;
	
  command[0] = MEMSPI_WRITE_STATUS_REG;
	command[1] = WrittenBits;
	command[2] = WrittenBits >> 8;
	
	size = 3;

  MEMSPI_Select(hmemspi);
  SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, size, Timeout);
  MEMSPI_Deselect(hmemspi);
	
	return SPI_RES;
}

/** 
	* @brief 	Send command to set Write Enable Latch (WEL) in Status Register.
	* @param 	hmemspi - указатель на хендл внешней памяти.
	* @note 	Разрешает запись в FLASH, путем высталения WEL в Status Register
	*/
HAL_StatusTypeDef MEMSPI_CMD_Write_Enable(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeout)
{
	HAL_StatusTypeDef SPI_RES;
  uint8_t command[1];
  command[0] = MEMSPI_WRITE_ENABLE;

  MEMSPI_Select(hmemspi);
  SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 1, Timeout);
  MEMSPI_Deselect(hmemspi);
	
	return SPI_RES;
}


/** 
	* @brief 	Send command to read data from FLASH/EEPROM.
	* @param 	hmemspi 			- указатель на хендл внешней памяти.
	* @param 	FLASH_Address - адресс откуда начинать считывание.
	* @param 	pBuff	 				- куда записывать данные из FLASH.
	* @param 	Size	 				- сколько байтов считывать.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @return pBuff.
	*/
HAL_StatusTypeDef MEMSPI_CMD_Read_Data(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout)
{
	HAL_StatusTypeDef SPI_RES;
	uint8_t command[4];
	uint8_t response[2] = {0};
	
  command[0] = MEMSPI_READ_DATA;
  command[1] = FLASH_Address >> 16 	& 0xFF;
  command[2] = FLASH_Address >> 8 	& 0xFF;
  command[3] = FLASH_Address 				& 0xFF;
	
  MEMSPI_Select(hmemspi);
  SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 4, Timeout);
  SPI_RES = MEMSPI_SPI_Receive(hmemspi, pBuff, Size, Timeout);	
  MEMSPI_Deselect(hmemspi);
	
	return SPI_RES;
}

/** 
	* @brief 	Send command to write eeprom.
	* @param 	hmemspi 			- указатель на хендл внешней памяти.
	* @param 	FLASH_Address - адресс куда начинать записывать.
	* @param 	pData	 				- откуда брать данные для записи в FLASH.
	* @param 	Size	 				- сколько байтов записать.
	* @param 	Timeout				- время, за которое должна быть осуществлена запись.
	* @note		Рзамер данных для записи в EEPROM без ограничений. 
	*/
HAL_StatusTypeDef MEMSPI_CMD_EEPROM_Write(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
	HAL_StatusTypeDef SPI_RES;
	// 1 command byte + 3 address bytes + 256 data bytes
	uint8_t command[1+3+MEMSPI_PAGE_SIZE];
	FLASH_Address = FLASH_Address & 0xFFFFFF;
	
  command[0] = MEMSPI_WRITE_EEPROM;
	command[1] = FLASH_Address >> 16 	& 0xFF;
	command[2] = FLASH_Address >> 8 	& 0xFF;
	command[3] = FLASH_Address 				& 0xFF;
	
  MEMSPI_Select(hmemspi);
  SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 4, Timeout); 	// send insctruction to write
  SPI_RES = MEMSPI_SPI_Transmit(hmemspi, pData, Size, Timeout);	// send data to write
  MEMSPI_Deselect(hmemspi);
	
	return SPI_RES;
}
/** 
	* @brief 	Send command to page program in FLASH.
	* @param 	hmemspi 			- указатель на хендл внешней памяти.
	* @param 	FLASH_Address - адресс куда начинать записывать.
	* @param 	pData	 				- откуда брать данные для записи в FLASH.
	* @param 	Size	 				- сколько байтов записать.
	* @param 	Timeout				- время, за которое должна быть осуществлена запись.
	* @note		Программирование FLASH только в пределах одной страницы. 
	*					Т.е. если запись с 0x0, то не больше 256 байт. Если с 0ч40, то не больше 192 байт.
	*/
HAL_StatusTypeDef MEMSPI_CMD_FLASH_Page_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
	HAL_StatusTypeDef SPI_RES;
	// 1 command byte + 3 address bytes + 256 data bytes
	uint8_t command[1+3+MEMSPI_PAGE_SIZE];
	FLASH_Address = FLASH_Address & 0xFFFFFF;
	
  command[0] = MEMSPI_PAGE_PROGRAM;
	command[1] = FLASH_Address >> 16 	& 0xFF;
	command[2] = FLASH_Address >> 8 	& 0xFF;
	command[3] = FLASH_Address 				& 0xFF;
	
	// check if flash range is placed at one page
	if((FLASH_Address/MEMSPI_PAGE_SIZE) != ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE)) // if page of first byte isnt equal page of last byte
		return HAL_ERROR;																																					// return error
	
  MEMSPI_Select(hmemspi);
  SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 4, Timeout); 	// send insctruction to write
  SPI_RES = MEMSPI_SPI_Transmit(hmemspi, pData, Size, Timeout);	// send data to write
  MEMSPI_Deselect(hmemspi);
	
	return SPI_RES;
}

/** 
	* @brief 	Send command to erase sector.
	* @param 	hmemspi				- указатель на хендл внешней памяти.
	* @param 	FLASH_Address - адресс где надо данные стереть.
	* @param 	Timeout				- время, за которое должна быть осуществлена очистка.
	* @note		Микросхема вроде сама высчитывает какой сектор ерейзнуть, в соответствии с заданным адресом.
	*/
HAL_StatusTypeDef MEMSPI_CMD_FLASH_Erase_Sector(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t Timeout)
{
	HAL_StatusTypeDef SPI_RES;
	uint8_t command[4];
  uint8_t response[8];
	FLASH_Address = FLASH_Address & 0xFFFFFF;
	
  command[0] = MEMSPI_ERASE_SECTOR;
	command[1] = FLASH_Address >> 16;
	command[2] = FLASH_Address >> 8;
	command[3] = FLASH_Address;
	
  MEMSPI_Select(hmemspi);
  SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 4, Timeout);
  MEMSPI_Deselect(hmemspi);
	
	return SPI_RES;
}


/** 
	* @brief 	Send command to read JEDEC ID.
	* @param 	hmemspi	- указатель на хендл внешней памяти.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @return	JEDEC ID микросхемы.
	*/
uint32_t MEMSPI_CMD_Read_JEDEC_ID(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeout)
{
	HAL_StatusTypeDef SPI_RES;
  uint8_t command[1] = {0};
  uint8_t receive[4] = {0};
	uint32_t return_val;
	
  command[0] = MEMSPI_READ_JEDEC_ID;
  MEMSPI_Select(hmemspi);
  SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 1, Timeout);
  SPI_RES = MEMSPI_SPI_Receive(hmemspi, &receive[1], 3, Timeout);
  MEMSPI_Deselect(hmemspi);
	
	return_val = (*(uint64_t *)receive);
  return __REV(return_val) & 0xFFFFFF;
}

/** 
	* @brief 	Send command to read JEDEC ID.
	* @param 	hmemspi	- указатель на хендл внешней памяти.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @return	Device ID микросхемы.
	*/
uint64_t MEMSPI_CMD_Read_Device_ID(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeout)
{
	HAL_StatusTypeDef SPI_RES;
  uint8_t command[1] = {0};
  uint8_t receive[8] = {0};
	uint64_t return_val_LO;
	uint64_t return_val_HI;
  command[0] = MEMSPI_READ_UNIQUE_ID;
  MEMSPI_Select(hmemspi);
  SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 1, Timeout);
  SPI_RES = MEMSPI_SPI_Receive(hmemspi, receive, 8, Timeout);
  MEMSPI_Deselect(hmemspi);
	
	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);
}
/** 
	* @brief 	Send command to fast read data from FLASH.
	* @param 	hmemspi 			- указатель на хендл внешней памяти.
	* @param 	FLASH_Address - адресс откуда начинать считывание.
	* @param 	pBuff	 				- куда записывать данные из FLASH.
	* @param 	Size	 				- сколько байтов считывать.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @note		Данная функция предполагает отправку одного dummy байта после адресса, но у меня поч не работает пока :(
	*/
HAL_StatusTypeDef MEMSPI_CMD_Fast_Read(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout)
{
	HAL_StatusTypeDef SPI_RES;
	uint8_t command[5] = {0};
	uint8_t response[2] = {0};
	
  command[0] = MEMSPI_READ_DATA;
  command[1] = FLASH_Address >> 16 	& 0xFF;
  command[2] = FLASH_Address >> 8 	& 0xFF;
  command[3] = FLASH_Address 				& 0xFF;
  command[4] = 0xFF;
	
  MEMSPI_Select(hmemspi);
  SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 5, Timeout);
  SPI_RES = MEMSPI_SPI_Receive(hmemspi, pBuff, Size, Timeout);	
  MEMSPI_Deselect(hmemspi);
	
	return SPI_RES;
}
//-------------------------------------------------------------

