Добавлена поддержка SPI EEPROM:

- Функция для записи в EEPROM MEMSPI_EEPROM_Write
- Библиотека переименована из flash в memory spi
- Функции для вызова SPI Transmit/Receive вынесены в дефайн
This commit is contained in:
alexey
2024-08-13 13:28:09 +03:00
parent 13ec5ac93e
commit b8bd4034d1
3 changed files with 937 additions and 777 deletions

775
memory_spi/memory_spi.c Normal file
View File

@@ -0,0 +1,775 @@
/********************************MEMSPI FLASH**********************************
Данный файл содержит базовые функции для общения с памятью FLASH по SPI.
//-------------------Функции-------------------//
@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 для FLASH
@func service
- MEMSPI_FLASH_Erase_Sector Очистка сектора FLASH. *есть более общая функция MEMSPI_FLASH_Erase, которая может ощичать несколько секторов
- MEMSPI_FLASH_Program_Page Программирование страницы. *есть более общая функция MEMSPI_FLASH_Program, которая программирует участки больше страницы
- MEMSPI_WriteEnablingUntilTimeout Разрешение записи, пока не будет ответа или не истек таймаут
- MEMSPI_WaitOnFlagUntilTimeout Ожидание флага пока не истек таймаута
@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_FLASH_Page_Program Отправка комманды Write (eeprom) (0x02h)
- MEMSPI_CMD_FLASH_Page_Program Отправка комманды Page Program (flash) (0x02h)
- MEMSPI_CMD_FLASH_Erase_Sector Отправка комманды Erase Sector (flash) (0x20h)
- MEMSPI_CMD_Fast_Read Отправка комманды Fast Read (0x0Bh)
- MEMSPI_CMD_Read_JEDEC_ID Отправка комманды Read JEDEC ID (0x4Bh)
- MEMSPI_CMD_Read_Device_ID Отправка комманды Read Manufacture / Device Id (0x90)
@func SPI functions
- SPI_RES = MEMSPI_SPI_Transmit Функция отправки по SPI (содержит только HAL_SPI_Transmit)
- SPI_RES = MEMSPI_SPI_Receive Функция приема по SPI (содержит только HAL_SPI_Receive)
***************************************************************************/
#include "memory_spi.h"
//-------------------------------------------------------------
//--------------------------FOR USER---------------------------
/**
* @brief Initialize SPI and GPIO for MEMSPI FLASH.
* @param hmemspi - указатель на структуру с настройками SPI и GPIO портов.
*/
void MEMSPI_Base_Init(MEMSPI_HandleTypeDef *hmemspi)
{
// SPI PERIPH INIT
if(hmemspi->hspi.Instance == NULL)
hmemspi->hspi.Instance = SPI1;
hmemspi->hspi.Init.Mode = SPI_MODE_MASTER;
hmemspi->hspi.Init.Direction = SPI_DIRECTION_2LINES;
hmemspi->hspi.Init.DataSize = SPI_DATASIZE_8BIT;
hmemspi->hspi.Init.CLKPolarity = SPI_POLARITY_LOW;
hmemspi->hspi.Init.CLKPhase = SPI_PHASE_1EDGE;
hmemspi->hspi.Init.NSS = SPI_NSS_SOFT;
hmemspi->hspi.Init.FirstBit = SPI_FIRSTBIT_MSB;
hmemspi->hspi.Init.TIMode = SPI_TIMODE_DISABLE;
hmemspi->hspi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hmemspi->hspi.Init.CRCPolynomial = 10;
// CLOCK
if(hmemspi->hspi.Instance == SPI1)
__HAL_RCC_SPI2_CLK_ENABLE();
else if (hmemspi->hspi.Instance == SPI2)
__HAL_RCC_SPI2_CLK_ENABLE();
else if (hmemspi->hspi.Instance == SPI3)
__HAL_RCC_SPI3_CLK_ENABLE();
// SPI INIT
HAL_SPI_Init(&hmemspi->hspi);
// GPIO INIT
GPIO_Clock_Enable(hmemspi->GPIOs.CS_GPIOx);
GPIO_Clock_Enable(hmemspi->GPIOs.CLK_GPIOx);
GPIO_Clock_Enable(hmemspi->GPIOs.MISO_GPIOx);
GPIO_Clock_Enable(hmemspi->GPIOs.MOSI_GPIOx);
// CHIP SELECT PIN INIT
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = hmemspi->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(hmemspi->GPIOs.CS_GPIOx, &GPIO_InitStruct);
// CLK PIN INIT
GPIO_InitStruct.Pin = hmemspi->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(hmemspi->GPIOs.CLK_GPIOx, &GPIO_InitStruct);
// MISO PIN INIT
GPIO_InitStruct.Pin = hmemspi->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(hmemspi->GPIOs.MISO_GPIOx, &GPIO_InitStruct);
// MOSI PIN INIT
GPIO_InitStruct.Pin = hmemspi->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(hmemspi->GPIOs.MOSI_GPIOx, &GPIO_InitStruct);
}
/**
* @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_WaitOnFlagUntilTimeout(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 Read external FLASH/EEPROM.
* @param hmemspi - указатель на хендл внешней памяти.
* @param FLASH_Address - адресс куда начинать записывать.
* @param pData - откуда брать данные для записи в EEPROM.
* @param Size - сколько байтов записать.
* @param Timeout - время, за которое должно быть осуществлено чтение.
* @return HAL status.
* @note Включает в себя проверку на доступность памяти (флаг BUSY)
*/
HAL_StatusTypeDef MEMSPI_EEPROM_Write(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
uint32_t tickstart = HAL_GetTick();
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(MEMSPI_WaitOnFlagUntilTimeout(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 Write external FLASH.
* @param hmemspi - указатель на хендл внешней памяти.
* @param WriteInit - указатель на структуру, определяющую участок памяти для записи.
* @param Timeout - время, за которое должно быть осуществлено чтение.
* @return HAL status.
* @note Позволяет перепрограммировать участок памяти. Можно записывать несколько страниц.
* Данные в сектора участка, но за пределами участка не сохраняются.
*/
HAL_StatusTypeDef MEMSPI_FLASH_Write(MEMSPI_HandleTypeDef *hmemspi, MEMSPI_WriteInitTypeDef *WriteInit, uint32_t Timeout)
{
uint32_t tickstart = HAL_GetTick();
uint32_t timeoutcnt = Timeout;
uint8_t *writebuff = WriteInit->pDataPtr;
HAL_StatusTypeDef MEMSPI_Status;
// WAIT FOR UNBUSY
if(MEMSPI_WaitOnFlagUntilTimeout(hmemspi, MEMSPI_SR_BUSY, 0, &Timeout, &tickstart) != HAL_OK) // if its unbusy for timeout
return HAL_TIMEOUT; // return timeout error
// ERASE FLASH
MEMSPI_Status = MEMSPI_FLASH_Erase(hmemspi, WriteInit->Sector_Address, WriteInit->Sector_Size, Timeout);
if(MEMSPI_Status != HAL_OK)
return MEMSPI_Status;
// 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];
}
writebuff = sector_buff; // set pointer to buffer that need to be restored
// PROGRAM FLASH WITH NEW DATA
// program data to flash
MEMSPI_Status = MEMSPI_FLASH_Program(hmemspi, WriteInit->Sector_Address, writebuff, WriteInit->Sector_Size, Timeout);
if(MEMSPI_Status != HAL_OK)
return MEMSPI_Status;
}
// WRITE FLASH WITHOUT SAVING PREVIOUS DATA
else
{
// PROGRAM FLASH WITH NEW DATA
timeoutcnt = HAL_GetTick() - tickstart; // update timeout
Timeout -= timeoutcnt;
tickstart += timeoutcnt;
// program data to flash
MEMSPI_Status = MEMSPI_FLASH_Program(hmemspi, WriteInit->Sector_Address, writebuff, WriteInit->Sector_Size, Timeout);
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 - время, за которое должно быть осуществлено чтение.
* @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)
{
uint32_t tickstart = HAL_GetTick();
HAL_StatusTypeDef MEMSPI_Status;
// CALC AREA TO PROGRAM
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 increase automatically in this variable
for(int i = 0; i < lastpage - firstpage; i++)
{
MEMSPI_Status = MEMSPI_FLASH_Program_Page(hmemspi, hmemspi->hNextAddr, &pData[bytecnt], currentpage_size, &Timeout, &tickstart); // programm page
if(MEMSPI_Status != HAL_OK)
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); // 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 - время, за которое должно быть осуществлена очистка.
* @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)
{
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); // programm page
if(MEMSPI_Status != HAL_OK)
return MEMSPI_Status;
FLASH_Address += MEMSPI_SECTOR_SIZE;
}
return HAL_OK;
}
/**
* @brief Erase external FLASH Sector.
* @param hmemspi - указатель на хендл внешней памяти.
* @param FLASH_Address - адресс где надо данные стереть.
* @param Timeout - время, за которое должно быть осуществлена очистка.
* @param tickstart - время, относительно которого надо отсчитывать таймаут.
* @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)
{
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_CMD_FLASH_Erase_Sector(hmemspi, FLASH_Address, *Timeout);
if(MEMSPI_Status != HAL_OK)
return MEMSPI_Status;
// waiting for ending of erasing
if(Timeout) // if timeout isn zero - wait it.
if(MEMSPI_WaitOnFlagUntilTimeout(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 - время, относительно которого надо отсчитывать таймаут.
* @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)
{
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(MEMSPI_WaitOnFlagUntilTimeout(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 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;
// enable writting
MEMSPI_Status = MEMSPI_CMD_Write_Enable(hmemspi, *Timeout);
if(MEMSPI_Status != HAL_OK)
return MEMSPI_Status;
// check is writting enabled
MEMSPI_Status = MEMSPI_CMD_Read_Status_Register(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 0, *Timeout);
if(MEMSPI_Status != HAL_OK)
return MEMSPI_Status;
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, *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 Wait for flag until timeout.
* @param hmemspi - указатель на хендл внешней памяти.
* @param FlagMask - маска для флагов, какие флаги считывать.
* @param FlagStatus - какое состояние должно быть у выбранных флагов.
* @param Timeout - время, за которое должно быть осуществлено чтение.
* @param tickstart - время, относительно которого надо отсчитывать таймаут.
* @return HAL status.
* @note Считывает флаги до тех пор, пока они не будут в состоянии FlagStatus или до тех пор, пока таймаут не истечет.
*/
HAL_StatusTypeDef MEMSPI_WaitOnFlagUntilTimeout(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;
#ifdef MEMSPI_READ_STATUS_REG_2
if(RequestedBits >> 8) // if its high byte of status register
{
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;
}
}
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);
}
#else
command[0] = MEMSPI_READ_STATUS_REG;
pSRPtr = (uint8_t *)(&hmemspi->SR); // set pointer to HI byte of SR register
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
#endif
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[2];
command[0] = MEMSPI_WRITE_STATUS_REG;
command[1] = WrittenBits >> 2;
MEMSPI_Select(hmemspi);
SPI_RES = MEMSPI_SPI_Transmit(hmemspi, command, 1, 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;
}
//-------------------------------------------------------------

View File

@@ -1,59 +1,85 @@
/********************************W25 FLASH**********************************
Данный файл содержит инклюды и дефайны для общения с памятью FLASH по SPI.
/********************************SPI MEMORY*********************************
Данный файл содержит инклюды и дефайны для общения с FLASH/EEPROM по SPI.
***************************************************************************/
#ifndef __SPI_FLASH_H_
#define __SPI_FLASH_H_
#ifndef __SPI_MEMORY_H_
#define __SPI_MEMORY_H_
#include "stm32f4xx_hal.h"
#include "gpio_general.h"
/////////////////////////---USER SETTINGS---/////////////////////////
//#define EXT_FLASH
#define EXT_EEPROM
#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
#if defined(EXT_FLASH) & defined(EXT_EEPROM)
#error Choose only one memory
#endif
//#define SEPARATED_STATUS_REGISTER
/////////////////////////////////////////////////////////////////////
////////////////////////////---DEFINES---////////////////////////////
/**
* @brief Defines for W25 chip.
/**
* @brief SPI Transmit.
* @param _hmemspi_ - указатель на хендл внешней памяти.
* @param _data_ - указатель на данные для отправки.
* @param _size_ - размер данных для отправки.
* @param _timeout_ - время, за которое должна быть осуществлена отправка.
* @note Здесь вызывается только функция HAL, и ничего больше.
*/
#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)
#define MEMSPI_SPI_Transmit(_hmemspi_, _data_, _size_, _timeout_) HAL_SPI_Transmit(&_hmemspi_->hspi, _data_, _size_, _timeout_)
/**
* @brief SPI Receive.
* @param _hmemspi_ - указатель на хендл внешней памяти.
* @param _data_ - указатель на буффер для прниема данных.
* @param _size_ - размер данных для приема.
* @param _timeout_ - время, за которое должен быть осуществлен прием.
* @note Здесь вызывается только функция HAL, и ничего больше.
*/
#define MEMSPI_SPI_Receive(_hmemspi_, _data_, _size_, _timeout_) HAL_SPI_Receive(&_hmemspi_->hspi, _data_, _size_, _timeout_)
/**
* @brief Defines for MEMORY chip.
*/
#define MEMSPI_Select(_hmemspi_) (_hmemspi_->GPIOs.CS_GPIOx->BSRR = _hmemspi_->GPIOs.CS_PIN << 16)
#define MEMSPI_Deselect(_hmemspi_) (_hmemspi_->GPIOs.CS_GPIOx->BSRR = _hmemspi_->GPIOs.CS_PIN)
#define MEMSPI_SECTOR_SIZE (0x1000)
#define MEMSPI_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 MEMSPI_READ_JEDEC_ID 0x9F
#define MEMSPI_READ_UNIQUE_ID 0x4B
#define MEMSPI_WRITE_ENABLE 0x06
#define MEMSPI_WRITE_DISABLE 0x04
#define MEMSPI_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
#define MEMSPI_ERASE_SECTOR 0x20
#define MEMSPI_PAGE_PROGRAM 0x02
#define MEMSPI_WRITE_EEPROM MEMSPI_PAGE_PROGRAM
#define MEMSPI_READ_STATUS_REG 0x05
#if defined(SEPARATED_STATUS_REGISTER)
#define MEMSPI_READ_STATUS_REG_2 0x35
#endif
#define MEMSPI_READ_DATA 0x03
/**
* @brief Defines for Status Register.
*/
#define W25_Get_Flag(_hw25_,_flag_) (((W25_StatusRegisterTypeDef)(_hw25_->SR))._flag_)
#define MEMSPI_Get_Flag(_hmemspi_,_flag_) (((MEMSPI_StatusRegisterTypeDef)(_hmemspi_->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)
#define MEMSPI_SR_SUS (1<<15)
#define MEMSPI_SR_QE (1<<9)
#define MEMSPI_SR_SRP1 (1<<8)
#define MEMSPI_SR_SRP0 (1<<7)
#define MEMSPI_SR_SEC (1<<8)
#define MEMSPI_SR_TB (1<<5)
#define MEMSPI_SR_BP2 (1<<4)
#define MEMSPI_SR_BP1 (1<<3)
#define MEMSPI_SR_BP0 (1<<2)
#define MEMSPI_SR_WEL (1<<1)
#define MEMSPI_SR_BUSY (1<<0)
/**
* @brief Calc dividing including remainder (divide and ceil)
@@ -79,7 +105,7 @@ typedef struct
uint32_t Sector_Address;
uint32_t Sector_Size;
unsigned fSavePrevoisData:1;
}W25_WriteInitTypeDef;
}MEMSPI_WriteInitTypeDef;
typedef struct
{
@@ -93,32 +119,32 @@ typedef struct
uint32_t MISO_PIN;
GPIO_TypeDef *MOSI_GPIOx;
uint32_t MOSI_PIN;
}W25_GPIOTypeDef;
}MEMSPI_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;
SPI_HandleTypeDef hspi;
MEMSPI_GPIOTypeDef GPIOs;
uint32_t hNextAddr;
uint16_t hNextPage;
uint16_t hNextSector;
}MEMSPI_HandleTypeDef;
extern MEMSPI_HandleTypeDef hmemspi;
///////////////////////---STRUCTURES & ENUMS---//////////////////////
/////////////////////////////////////////////////////////////////////
///////////////////////---FUNCTIONS FOR USER---//////////////////////
/**
* @brief Initialize SPI and GPIO for W25 FLASH.
* @param hw25 - указатель на структуру с настройками SPI и GPIO портов.
* @brief Initialize SPI and GPIO for MEMSPI FLASH.
* @param hmemspi - указатель на структуру с настройками SPI и GPIO портов.
*/
void W25_Base_Init(W25_HandleTypeDef *hw25);
void MEMSPI_Base_Init(MEMSPI_HandleTypeDef *hmemspi);
/**
* @brief Read external FLASH.
* @param hw25 - указатель на хендл flash.
* @param hmemspi - указатель на хендл внешней памяти.
* @param FLASH_Address - адресс откуда начинать считывание.
* @param pBuff - куда записывать данные из FLASH.
* @param Size - сколько байтов считывать.
@@ -126,22 +152,34 @@ void W25_Base_Init(W25_HandleTypeDef *hw25);
* @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);
HAL_StatusTypeDef MEMSPI_Read_Memory(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout);
/**
* @brief Read external FLASH/EEPROM.
* @param hmemspi - указатель на хендл внешней памяти.
* @param FLASH_Address - адресс куда начинать записывать.
* @param pData - откуда брать данные для записи в EEPROM.
* @param Size - сколько байтов записать.
* @param Timeout - время, за которое должно быть осуществлено чтение.
* @return HAL status.
* @note Включает в себя проверку на доступность памяти (флаг BUSY)
*/
HAL_StatusTypeDef MEMSPI_EEPROM_Write(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout);
/**
* @brief Write external FLASH.
* @param hw25 - указатель на хендл flash.
* @param hmemspi - указатель на хендл внешней памяти.
* @param WriteInit - указатель на структуру, определяющую участок памяти для записи.
* @param Timeout - время, за которое должно быть осуществлено чтение.
* @return HAL status.
* @note Позволяет перепрограммировать участок памяти. Можно записывать несколько страниц.
* Данные в сектора участка, но за пределами участка не сохраняются.
*/
HAL_StatusTypeDef W25_FLASH_Write(W25_HandleTypeDef *hw25, W25_WriteInitTypeDef *WriteInit, uint32_t Timeout);
HAL_StatusTypeDef MEMSPI_FLASH_Write(MEMSPI_HandleTypeDef *hmemspi, MEMSPI_WriteInitTypeDef *WriteInit, uint32_t Timeout);
/**
* @brief Program external FLASH.
* @param hw25 - указатель на хендл flash.
* @param hmemspi - указатель на хендл внешней памяти.
* @param FLASH_Address - адресс куда начинать записывать.
* @param pData - откуда брать данные для записи в FLASH.
* @param Size - сколько байтов записать.
@@ -149,11 +187,11 @@ HAL_StatusTypeDef W25_FLASH_Write(W25_HandleTypeDef *hw25, W25_WriteInitTypeDef
* @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);
HAL_StatusTypeDef MEMSPI_FLASH_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout);
/**
* @brief Erase external FLASH.
* @param hw25 - указатель на хендл flash.
* @param hmemspi - указатель на хендл внешней памяти.
* @param FLASH_Address - адресс где надо данные стереть.
* @param Size - сколько байтов стереть.
* @param Timeout - время, за которое должно быть осуществлена очистка.
@@ -161,11 +199,14 @@ HAL_StatusTypeDef W25_FLASH_Program(W25_HandleTypeDef *hw25, uint32_t FLASH_Addr
* @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);
HAL_StatusTypeDef MEMSPI_FLASH_Erase(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint16_t Size, uint32_t Timeout);
///////////////////////---FUNCTIONS FOR USER---//////////////////////
/////////////////////////////////////////////////////////////////////
////////////////////////---SERVICE FUNCTIONS---//////////////////////
/**
* @brief Erase external FLASH Sector.
* @param hw25 - указатель на хендл flash.
* @param hmemspi - указатель на хендл внешней памяти.
* @param FLASH_Address - адресс где надо данные стереть.
* @param Timeout - время, за которое должно быть осуществлена очистка.
* @param tickstart - время, относительно которого надо отсчитывать таймаут.
@@ -173,11 +214,11 @@ HAL_StatusTypeDef W25_FLASH_Erase(W25_HandleTypeDef *hw25, uint32_t FLASH_Addres
* @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_StatusTypeDef MEMSPI_FLASH_Erase_Sector(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t *Timeout, uint32_t *tickstart);
/**
* @brief Program page in external FLASH.
* @param hw25 - указатель на хендл flash.
* @param hmemspi - указатель на хендл внешней памяти.
* @param FLASH_Address - адресс куда начинать записывать.
* @param pData - откуда брать данные для записи в FLASH.
* @param Size - сколько байтов записать.
@@ -185,23 +226,23 @@ HAL_StatusTypeDef W25_FLASH_Erase_Sector(W25_HandleTypeDef *hw25, uint32_t FLASH
* @param tickstart - время, относительно которого надо отсчитывать таймаут.
* @return HAL status.
* @note Позволяет перепрограммировать только байты в прелелах одной страницы.
Для более гибкого программирования есть функция W25_FLASH_Program, которая программирует участки любой длины (в теории).
Для более гибкого программирования есть функция MEMSPI_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);
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);
/**
* @brief Setting WEL bit until it setted or until timeout.
* @param hw25 - указатель на хендл flash.
* @param hmemspi - указатель на хендл внешней памяти.
* @param Timeout - время, за которое должно быть осуществлено чтение.
* @param tickstart - время, относительно которого надо отсчитывать таймаут.
* @return HAL status.
* @note Подает команду на разрешение записи до тех пор, пока она запись не разрешиться или до тех пор, пока таймаут не истечет.
*/
HAL_StatusTypeDef W25_WriteEnablingUntilTimeout(W25_HandleTypeDef *hw25, uint32_t Timeout, uint32_t tickstart);
HAL_StatusTypeDef MEMSPI_WriteEnablingUntilTimeout(MEMSPI_HandleTypeDef *hmemspi, uint32_t *Timeout, uint32_t *tickstart);
/**
* @brief Wait for flag until timeout.
* @param hw25 - указатель на хендл flash.
* @param hmemspi - указатель на хендл внешней памяти.
* @param FlagMask - маска для флагов, какие флаги считывать.
* @param FlagStatus - какое состояние должно быть у выбранных флагов.
* @param Timeout - время, за которое должно быть осуществлено чтение.
@@ -209,113 +250,114 @@ HAL_StatusTypeDef W25_WriteEnablingUntilTimeout(W25_HandleTypeDef *hw25, uint32_
* @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---//////////////////////
HAL_StatusTypeDef MEMSPI_WaitOnFlagUntilTimeout(MEMSPI_HandleTypeDef *hmemspi, uint16_t FlagMask, uint16_t FlagStatus, uint32_t *Timeout, uint32_t *tickstart);
/**
* @brief Update Timeout variables.
* @param Timeout - указатель на переменную содержащию значение таймаута
* @param tickstart - указатель на переменную содержащию начальное кол-во тиков.
*/
void MEMSPI_Update_Timeout_Variables(uint32_t *Timeout, uint32_t *tickstart);
////////////////////////---SERVICE FUNCTIONS---//////////////////////
/////////////////////////////////////////////////////////////////////
//////////////////////---FUNCTION FOR COMMAND---/////////////////////
/**
* @brief Send command to read Status Register.
* @param hw25 - указатель на хендл flash.
* @param hmemspi - указатель на хендл внешней памяти.
* @param RequestedBits - какие биты запросить.
* @return Заполняет Status Register в hw25.
* @param Timeout - время, за которое должно быть осуществлено чтение.
* @return Заполняет Status Register в hmemspi.
* @note Всего есть две комманды: на запрос верхнего или нижниго байта.
* Функция в соответствии с RequestedBits определяет какой байт запросить, или два байта сразу.
*/
void W25_CMD_Read_Status_Register(W25_HandleTypeDef *hw25, uint16_t RequestedBits);
HAL_StatusTypeDef MEMSPI_CMD_Read_Status_Register(MEMSPI_HandleTypeDef *hmemspi, uint16_t RequestedBits, uint8_t EndCMD, uint32_t Timeout);
/**
* @brief Send command to write bits in Status Register.
* @param hw25 - указатель на хендл flash.
* @param hmemspi - указатель на хендл внешней памяти.
* @param WrittenBits - какие биты запросить.
* @param Timeout - время, за которое должна быть осуществлена запись.
* @note Данная команда посылает биты, как сдвинутые на 2 вправо. Т.е. 0-й бит в комманде - 2-й бит BP0.
Но биты указываются в также как они расположены и регистре. Функция сама выполняет сдвиг.
*/
void W25_CMD_Write_Status_Register(W25_HandleTypeDef *hw25, uint16_t WrittenBits);
HAL_StatusTypeDef MEMSPI_CMD_Write_Status_Register(MEMSPI_HandleTypeDef *hmemspi, uint16_t WrittenBits, uint32_t Timeout);
/**
* @brief Send command to set Write Enable Latch (WEL) in Status Register.
* @param hw25 - указатель на хендл flash.
* @param hmemspi - указатель на хендл внешней памяти.
* @param Timeout - время, за которое должна быть осуществлена запись.
* @note Разрешает запись в FLASH, путем высталения WEL в Status Register
*/
void W25_CMD_Write_Enable(W25_HandleTypeDef *hw25);
HAL_StatusTypeDef MEMSPI_CMD_Write_Enable(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeout);
/**
* @brief Send command to read data from FLASH.
* @param hw25 - указатель на хендл flash.
* @brief Send command to read data from FLASH/EEPROM.
* @param hmemspi - указатель на хендл внешней памяти.
* @param FLASH_Address - адресс откуда начинать считывание.
* @param pBuff - куда записывать данные из FLASH.
* @param Size - сколько байтов считывать.
* @param Timeout - время, за которое должно быть осуществлено чтение.
* @return pBuff.
*/
void W25_CMD_Read_Data(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size);
HAL_StatusTypeDef MEMSPI_CMD_Read_Data(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout);
/**
* @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.
* @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);
/**
* @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 байт.
*/
void W25_CMD_Page_Program(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size);
HAL_StatusTypeDef MEMSPI_CMD_FLASH_Page_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout);
/**
* @brief Send command to erase sector.
* @param hw25 - указатель на хендл flash.
* @brief Send command to erase sector of FLASH.
* @param hmemspi - указатель на хендл внешней памяти.
* @param FLASH_Address - адресс где надо данные стереть.
* @param Timeout - время, за которое должна быть осуществлена очистка.
* @note Микросхема вроде сама высчитывает какой сектор ерейзнуть, в соответствии с заданным адресом.
*/
void W25_CMD_Erase_Sector(W25_HandleTypeDef *hw25, uint32_t FLASH_Address);
HAL_StatusTypeDef MEMSPI_CMD_FLASH_Erase_Sector(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t Timeout);
/**
* @brief Send command to read JEDEC ID.
* @param hw25 - указатель на хендл flash.
* @param hmemspi - указатель на хендл внешней памяти.
* @param Timeout - время, за которое должно быть осуществлено чтение.
* @return JEDEC ID микросхемы.
*/
uint32_t W25_CMD_Read_JEDEC_ID(W25_HandleTypeDef *hw25);
uint32_t MEMSPI_CMD_Read_JEDEC_ID(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeout);
/**
* @brief Send command to read JEDEC ID.
* @param hw25 - указатель на хендл flash.
* @param hmemspi - указатель на хендл внешней памяти.
* @param Timeout - время, за которое должно быть осуществлено чтение.
* @return Device ID микросхемы.
*/
uint64_t W25_CMD_Read_Device_ID(W25_HandleTypeDef *hw25);
uint64_t MEMSPI_CMD_Read_Device_ID(MEMSPI_HandleTypeDef *hmemspi, uint32_t Timeout);
/**
* @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);
//////////////////////---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_
#endif // __SPI_MEMORY_H_

View File

@@ -1,657 +0,0 @@
/********************************W25 FLASH**********************************
Данный файл содержит базовые функции для общения с памятью FLASH по SPI.
//-------------------Функции-------------------//
@func users
- W25_FLASH_Read Считывание внешней FLASH
- W25_FLASH_Write Запись данных в внешнюю FLASH (функция сама очищает нужные сектора, и если надо сохраняет выбранные данные)
- W25_FLASH_Program Программирование внешней FLASH (выбранный участок FLASH должен быть очищен)
- W25_FLASH_Erase Очистка внешней FLASH
@func initialization
- W25_Base_Init Инициализация SPI и GPIO для FLASH
@func process interaction with flash
- W25_FLASH_Erase_Sector Очистка сектора FLASH. *есть более общая функция W25_FLASH_Erase, которая может ощичать несколько секторов
- W25_FLASH_Program_Page Программирование страницы. *есть более общая функция W25_FLASH_Program, которая программирует участки больше страницы
- 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 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 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)
{
uint32_t tickstart = HAL_GetTick();
uint32_t timeoutcnt = Timeout;
uint8_t *writebuff = WriteInit->pDataPtr;
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
// ERASE FLASH
timeoutcnt = HAL_GetTick() - tickstart; // update timeout
Timeout -= timeoutcnt;
tickstart += timeoutcnt;
W25_Status = W25_FLASH_Erase(hw25, WriteInit->Sector_Address, WriteInit->Sector_Size, Timeout);
if(W25_Status != HAL_OK)
return W25_Status;
// WRITE FLASH WITH SAVING PREVIOUS DATA
if(WriteInit->fSavePrevoisData)
{
uint8_t sector_buff[WriteInit->Sector_Size];
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;
// 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];
}
writebuff = sector_buff; // set pointer to buffer that need to be restored
// PROGRAM FLASH WITH NEW DATA
timeoutcnt = HAL_GetTick() - tickstart; // update timeout
Timeout -= timeoutcnt;
tickstart += timeoutcnt;
// program data to flash
W25_Status = W25_FLASH_Program(hw25, WriteInit->Sector_Address, writebuff, WriteInit->Sector_Size, Timeout);
if(W25_Status != HAL_OK)
return W25_Status;
}
// WRITE FLASH WITHOUT SAVING PREVIOUS DATA
else
{
// PROGRAM FLASH WITH NEW DATA
timeoutcnt = HAL_GetTick() - tickstart; // update timeout
Timeout -= timeoutcnt;
tickstart += timeoutcnt;
// program data to flash
W25_Status = W25_FLASH_Program(hw25, WriteInit->Sector_Address, writebuff, WriteInit->Sector_Size, Timeout);
if(W25_Status != HAL_OK)
return W25_Status;
}
return HAL_OK;
}
/**
* @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)
{
uint32_t tickstart = HAL_GetTick();
HAL_StatusTypeDef W25_Status;
// CALC AREA TO PROGRAM
uint16_t bytecnt = 0;
uint16_t currentpage_size = 0;
uint16_t lastpage_size = Size;
uint16_t firstpage = (FLASH_Address/W25_PAGE_SIZE);
uint16_t lastpage = ((FLASH_Address+Size-1)/W25_PAGE_SIZE);
if(firstpage != lastpage) // if area is on several pages
{
currentpage_size = (firstpage+1)*W25_PAGE_SIZE - FLASH_Address; // set size of data on first page
lastpage_size = (FLASH_Address+Size) - lastpage*W25_PAGE_SIZE; // set size of data on last page
}
// PROGRAM PAGES: FROM FIRST NO THE PREVIOUS TO THE LAST
hw25->hNextAddr = FLASH_Address; // address would increase automatically in this variable
for(int i = 0; i < lastpage - firstpage; i++)
{
W25_Status = W25_FLASH_Program_Page(hw25, hw25->hNextAddr, &pData[bytecnt], currentpage_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 += currentpage_size;
// and set current size as page size. because next pages will be fully programmed
currentpage_size = W25_PAGE_SIZE;
}
// PROGRAM LAST PAGE
W25_Status = W25_FLASH_Program_Page(hw25, hw25->hNextAddr, &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.
* @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)
{
uint32_t tickstart = HAL_GetTick();
HAL_StatusTypeDef W25_Status;
// CALC AREA TO ERASE
uint16_t bytecnt = 0;
uint16_t firstsector = (FLASH_Address/W25_SECTOR_SIZE);
uint16_t lastsector = ((FLASH_Address+Size-1)/W25_SECTOR_SIZE);
for(int i = 0; i <= (lastsector - firstsector); i++)
{
W25_Status = W25_FLASH_Erase_Sector(hw25, FLASH_Address, Timeout, tickstart); // programm page
if(W25_Status != HAL_OK)
return W25_Status;
FLASH_Address += W25_SECTOR_SIZE;
}
return HAL_OK;
}
/**
* @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)
{
// enable writting and waiting for unbusy
if(W25_WriteEnablingUntilTimeout(hw25, Timeout, tickstart) != HAL_OK) // if writting isnt enable
return HAL_TIMEOUT; // return timeout
// erase sector (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, которая программирует участки любой длины (в теории).
*/
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
// check if flash range is placed at one page
if((FLASH_Address/W25_PAGE_SIZE) != ((FLASH_Address+Size-1)/W25_PAGE_SIZE)) // if page of first byte isnt equal page of last byte
return HAL_ERROR; // return error
// 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;
// update handle variables
hw25->hNextAddr = (FLASH_Address+Size);
hw25->hNextPage = (FLASH_Address+Size)/W25_PAGE_SIZE;
hw25->hNextSector = (FLASH_Address+Size)/W25_SECTOR_SIZE;
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_Write_Enable(hw25);
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+W25_PAGE_SIZE];
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;
// check if flash range is placed at one page
if((FLASH_Address/W25_PAGE_SIZE) != ((FLASH_Address+Size-1)/W25_PAGE_SIZE)) // if page of first byte isnt equal page of last byte
return; // return error
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, HAL_MAX_DELAY);
}
/**
* @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, HAL_MAX_DELAY);
}
//-------------------------------------------------------------