/********************************W25 FLASH**********************************
Данный файл содержит инклюды и дефайны для общения с памятью FLASH по SPI.
***************************************************************************/
#ifndef __SPI_FLASH_H_
#define __SPI_FLASH_H_

#include "stm32f4xx_hal.h"
#include "gpio_general.h"

/////////////////////////---USER SETTINGS---/////////////////////////

#define 		W25_Internal_Flash_Buffer_For_reProgram_BankNumber		FLASH_BANK_1
#define 		W25_Internal_Flash_Buffer_For_reProgram_SectNumber		FLASH_SECTOR_11
#define 		W25_Internal_Flash_Buffer_For_reProgram_Address				0x08E000000



/////////////////////////////////////////////////////////////////////
////////////////////////////---DEFINES---////////////////////////////
/**  
	* @brief 	Defines for W25 chip.
	*/
#define 		W25_Select(_hw25_) 		(_hw25_->GPIOs.CS_GPIOx->BSRR = _hw25_->GPIOs.CS_PIN << 16)
#define 		W25_Deselect(_hw25_) 	(_hw25_->GPIOs.CS_GPIOx->BSRR = _hw25_->GPIOs.CS_PIN)
#define 		W25_SECTOR_SIZE					(0x1000)
#define 		W25_PAGE_SIZE						(0x100)
/**  
	* @brief 	Defines for CMD.
	*/
#define    	W25_READ_JEDEC_ID  			0x9F
#define    	W25_READ_UNIQUE_ID 			0x4B
#define    	W25_WRITE_ENABLE  			0x06
#define    	W25_WRITE_DISABLE  			0x04
#define    	W25_WRITE_STATUS_REG		0x01

#define    	W25_ERASE_SECTOR  			0x20
#define    	W25_PAGE_PROGRAM  			0x02
#define    	W25_READ_STATUS_REG_1		0x05
#define    	W25_READ_STATUS_REG_2		0x35
#define    	W25_READ_DATA  					0x03
/**  
	* @brief 	Defines for Status Register.
	*/
#define 		W25_Get_Flag(_hw25_,_flag_)		(((W25_StatusRegisterTypeDef)(_hw25_->SR))._flag_)

#define 		W25_SR_SUS							(1<<15)
#define 		W25_SR_QE								(1<<9)
#define 		W25_SR_SRP1							(1<<8)
#define 		W25_SR_SRP0							(1<<7)
#define 		W25_SR_SEC							(1<<8)
#define 		W25_SR_TB								(1<<5)
#define 		W25_SR_BP2							(1<<4)
#define 		W25_SR_BP1							(1<<3)
#define 		W25_SR_BP0							(1<<2)
#define 		W25_SR_WEL							(1<<1)
#define 		W25_SR_BUSY							(1<<0)

/**
	* @brief	Calc dividing including remainder (divide and ceil)
	*	@param 	_val_		- делимое.
	*	@param 	_div_		- делитель.
	*	@note 	Если результат деления без остатка: он возвращается как есть
	*					Если с остатком - округляется вверх
	*/
//#define Divide_Up(_val_, _div_)												(((_val_)%(_div_))? (_val_)/(_div_)+1 : (_val_)/_div_)	/* через тернарный оператор */
#define Divide_Up(_val_, _div_)												((_val_ - 1) / _div_) + 1																	/* через мат выражение */	

////////////////////////////---DEFINES---////////////////////////////


/////////////////////////////////////////////////////////////////////
///////////////////////---STRUCTURES & ENUMS---//////////////////////
typedef struct
{
	uint8_t 	*pDataPtr;
	uint32_t 	Data_Address;
	uint32_t 	Data_Size;
	
	uint32_t 	Sector_Address;
	uint32_t 	Sector_Size;
	unsigned 	fSavePrevoisData:1;
}W25_WriteInitTypeDef;

typedef struct
{
	uint16_t SR;
	SPI_HandleTypeDef hspi;
	GPIO_TypeDef 	*CS_GPIOx;
	uint32_t			CS_PIN;
	GPIO_TypeDef 	*CLK_GPIOx;
	uint32_t			CLK_PIN;
	GPIO_TypeDef 	*MISO_GPIOx;
	uint32_t			MISO_PIN;
	GPIO_TypeDef 	*MOSI_GPIOx;
	uint32_t			MOSI_PIN;
}W25_GPIOTypeDef;

typedef struct
{
	uint16_t SR;
	SPI_HandleTypeDef hspi;
	W25_GPIOTypeDef		GPIOs;
	uint32_t					hNextAddr;
	uint16_t					hNextPage;
	uint16_t					hNextSector;
}W25_HandleTypeDef;
extern W25_HandleTypeDef hw25;

///////////////////////---STRUCTURES & ENUMS---//////////////////////

/////////////////////////////////////////////////////////////////////
///////////////////////---FUNCTIONS FOR USER---//////////////////////
/**  
	* @brief 	Initialize SPI and GPIO for W25 FLASH.
	* @param 	hw25 - указатель на структуру с настройками SPI и GPIO портов.
	*/
void W25_Base_Init(W25_HandleTypeDef *hw25);

/** 
	* @brief 	Read 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);

/** 
	* @brief 	Write external FLASH.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	WriteInit 		- указатель на структуру, определяющую участок памяти для записи.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @return HAL status.
	* @note		Позволяет перепрограммировать участок памяти. Можно записывать несколько страниц.
	*					Данные в сектора участка, но за пределами участка не сохраняются.
	*/
HAL_StatusTypeDef W25_FLASH_Write(W25_HandleTypeDef *hw25, W25_WriteInitTypeDef *WriteInit, uint32_t Timeout);

/** 
	* @brief 	Program external FLASH.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	FLASH_Address - адресс куда начинать записывать.
	* @param 	pData	 				- откуда брать данные для записи в FLASH.
	* @param 	Size	 				- сколько байтов записать.
	* @param 	Timeout	 			- время, за которое должно быть осуществлено чтение.
	* @return HAL status.
	* @note		Программирование участка памяти, без ограничений на кол-во байт
	*/
HAL_StatusTypeDef W25_FLASH_Program(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout);

/** 
	* @brief 	Erase external FLASH.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	FLASH_Address - адресс где надо данные стереть.
	* @param 	Size	 				- сколько байтов стереть.
	* @param 	Timeout	 			- время, за которое должно быть осуществлена очистка.
	* @return HAL status.
	* @note		Т.к. очитска происходит по секторам, Size нужен, чтобы определить сколько секторов очистить
	*					И если начальны адресс будет на Sector 0, а последний байт на Sector 1, то произойдет очистка Sector 0 и Sector 1
	*/
HAL_StatusTypeDef W25_FLASH_Erase(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint16_t Size, uint32_t Timeout);

/** 
	* @brief 	Erase external FLASH Sector.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	FLASH_Address - адресс где надо данные стереть.
	* @param 	Timeout	 			- время, за которое должно быть осуществлена очистка.
	* @param 	tickstart	 		- время, относительно которого надо отсчитывать таймаут.
	* @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);

/** 
	* @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, которая программирует участки любой длины (в теории).
	*/
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);

/** 
	* @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);

/** 
	* @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);
///////////////////////---FUNCTIONS FOR USER---//////////////////////

/////////////////////////////////////////////////////////////////////
//////////////////////---FUNCTION FOR COMMAND---/////////////////////
/** 
	* @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);

/** 
	* @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);

/** 
	* @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);

/** 
	* @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);

/** 
	* @brief 	Send command to fast read data from FLASH.
	* @param 	hw25 					- указатель на хендл flash.
	* @param 	FLASH_Address - адресс откуда начинать считывание.
	* @param 	pBuff	 				- куда записывать данные из FLASH.
	* @param 	Size	 				- сколько байтов считывать.
	* @note		Данная функция предполагает отправку одного dummy байта после адресса, но у меня поч не работает пока :(
	*/
void W25_CMD_Fast_Read(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size);

/** 
	* @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);

/** 
	* @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);

/** 
	* @brief 	Send command to read JEDEC ID.
	* @param 	hw25 					- указатель на хендл flash.
	* @return	JEDEC ID микросхемы.
	*/
uint32_t W25_CMD_Read_JEDEC_ID(W25_HandleTypeDef *hw25);

/** 
	* @brief 	Send command to read JEDEC ID.
	* @param 	hw25 					- указатель на хендл flash.
	* @return	Device ID микросхемы.
	*/
uint64_t W25_CMD_Read_Device_ID(W25_HandleTypeDef *hw25);
//////////////////////---FUNCTION FOR COMMAND---/////////////////////

/////////////////////////////////////////////////////////////////////
//////////////////////---FUNCTION FOR PERIPTH---/////////////////////
/** 
	* @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);

/** 
	* @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);

//////////////////////---FUNCTION FOR PERIPTH---/////////////////////

#endif // __SPI_FLASH_H_