Автоматизированы функции

Доделаны(?) 4 базовые самостоятельные функции для общения с флеш: Read, Write, Program, Erase.
This commit is contained in:
alexey
2024-08-13 10:35:16 +03:00
parent 8b91736e1b
commit b6e8e69ab3
2 changed files with 150 additions and 76 deletions

View File

@@ -2,16 +2,17 @@
Данный файл содержит базовые функции для общения с памятью FLASH по SPI.
//-------------------Функции-------------------//
@func users
- W25_FLASH_Read Считывание FLASH
- W25_FLASH_Write_Area Запись данных в заданный участок FLASH (с потерей данных в выбраном сектора за пределами этого участка)
- W25_FLASH_Erase_Sector Очистка сектора FLASH
- W25_FLASH_Program_Area Программирование FLASH
- 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_Program_Page Программирование страницы. *есть более общая функция W25_FLASH_Program_Area, которая программирует участки больше страницы
- W25_FLASH_Erase_Sector Очистка сектора FLASH. *есть более общая функция W25_FLASH_Erase, которая может ощичать несколько секторов
- W25_FLASH_Program_Page Программирование страницы. *есть более общая функция W25_FLASH_Program, которая программирует участки больше страницы
- W25_WriteEnablingUntilTimeout Разрешение записи, пока не будет ответа или не истек таймаут
- W25_WaitOnFlagUntilTimeout Ожидание флага пока не истек таймаута
@@ -109,7 +110,7 @@ void W25_Base_Init(W25_HandleTypeDef *hw25)
}
/**
* @brief Read data from external FLASH.
* @brief Read external FLASH.
* @param hw25 - указатель на хендл flash.
* @param FLASH_Address - адресс откуда начинать считывание.
* @param pBuff - куда записывать данные из FLASH.
@@ -130,7 +131,7 @@ HAL_StatusTypeDef W25_FLASH_Read(W25_HandleTypeDef *hw25, uint32_t FLASH_Address
return HAL_OK;
}
/**
* @brief Write data to area in external FLASH.
* @brief Write external FLASH.
* @param hw25 - указатель на хендл flash.
* @param WriteInit - указатель на структуру, определяющую участок памяти для записи.
* @param Timeout - время, за которое должно быть осуществлено чтение.
@@ -138,123 +139,168 @@ HAL_StatusTypeDef W25_FLASH_Read(W25_HandleTypeDef *hw25, uint32_t FLASH_Address
* @note Позволяет перепрограммировать участок памяти. Можно записывать несколько страниц.
* Данные в сектора участка, но за пределами участка не сохраняются.
*/
HAL_StatusTypeDef W25_FLASH_Write_Area(W25_HandleTypeDef *hw25, W25_WriteInitTypeDef *WriteInit, uint32_t Timeout)
HAL_StatusTypeDef W25_FLASH_Write(W25_HandleTypeDef *hw25, W25_WriteInitTypeDef *WriteInit, uint32_t Timeout)
{
uint8_t sector_buff[256];
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
// SAVE USER "SECTOR" FROM FLASH
// ERASE FLASH
timeoutcnt = HAL_GetTick() - tickstart; // update timeout
Timeout -= timeoutcnt;
tickstart += timeoutcnt;
// store data from flash
W25_Status = W25_FLASH_Read(hw25, WriteInit->Sector_Address, sector_buff, WriteInit->Sector_Size, Timeout);
if(W25_Status != HAL_OK)
return W25_Status;
// ERASE SECTOR
timeoutcnt = HAL_GetTick() - tickstart; // update timeout
Timeout -= timeoutcnt;
tickstart += timeoutcnt;
// erase flash
W25_Status = W25_FLASH_Erase_Sector(hw25, WriteInit->Sector_Address, Timeout);
W25_Status = W25_FLASH_Erase(hw25, WriteInit->Sector_Address, 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++)
// WRITE FLASH WITH SAVING PREVIOUS DATA
if(WriteInit->fSavePrevoisData)
{
sector_buff[addr_shift+i] = WriteInit->pDataPtr[addr_shift+i];
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;
}
// RESTORE UPDATED DATA TO FLASH
timeoutcnt = HAL_GetTick() - tickstart; // update timeout
Timeout -= timeoutcnt;
tickstart += timeoutcnt;
// restore data to flash
W25_Status = W25_FLASH_Program_Area(hw25, WriteInit->Sector_Address, sector_buff, WriteInit->Sector_Size, Timeout);
if(W25_Status != HAL_OK)
return W25_Status;
return HAL_OK;
}
/**
* @brief Program area in external FLASH.
* @brief Program external FLASH.
* @param hw25 - указатель на хендл flash.
* @param FLASH_Address - адресс куда начинать записывать.
* @param pData - откуда брать данные для записи в FLASH.
* @param Size - сколько байтов записать.
* @param Timeout - время, за которое должно быть осуществлено чтение.
* @return HAL status.
* @note Позволяет перепрограммировать участок памяти. Можно записывать несколько страниц.
* Данные в сектора участка, но за пределами участка не сохраняются.
* @note Программирование участка памяти, без ограничений на кол-во байт
*/
HAL_StatusTypeDef W25_FLASH_Program_Area(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout)
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 firstpage_size = 0;
uint16_t currentpage_size = 0;
uint16_t lastpage_size = Size;
uint16_t firstpage = (FLASH_Address/256);
uint16_t lastpage = ((FLASH_Address+Size-1)/256);
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
{
firstpage_size = (firstpage+1)*256 - FLASH_Address; // set size of data at first page
lastpage_size = (FLASH_Address+Size) - lastpage*256; // set size of data at last page
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, FLASH_Address, &pData[bytecnt], firstpage_size, Timeout, tickstart); // programm page
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 += firstpage_size;
FLASH_Address += firstpage_size;
// and set start size as page size. because next pages will be fully programmed
firstpage_size = W25_PAGE_SIZE;
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, FLASH_Address, &pData[bytecnt], lastpage_size, Timeout, tickstart); // programm 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 = HAL_GetTick();
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
// programm page (instruction)
// erase sector (instruction)
W25_CMD_Erase_Sector(hw25, FLASH_Address);
// waiting for ending of erasing
@@ -262,7 +308,7 @@ HAL_StatusTypeDef W25_FLASH_Erase_Sector(W25_HandleTypeDef *hw25, uint32_t FLASH
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
}
@@ -276,21 +322,30 @@ HAL_StatusTypeDef W25_FLASH_Erase_Sector(W25_HandleTypeDef *hw25, uint32_t FLASH
* @param tickstart - время, относительно которого надо отсчитывать таймаут.
* @return HAL status.
* @note Позволяет перепрограммировать только байты в прелелах одной страницы.
Для более гибкого программирования есть функция W25_FLASH_Program_Area, которая программирует участки любой длины (в теории).
Для более гибкого программирования есть функция 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;
}
/**
@@ -304,6 +359,7 @@ HAL_StatusTypeDef W25_FLASH_Program_Page(W25_HandleTypeDef *hw25, uint32_t FLASH
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)
{
@@ -486,7 +542,7 @@ void W25_CMD_Fast_Read(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t
void W25_CMD_Page_Program(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size)
{
// 1 command byte + 3 address bytes + 256 data bytes
uint8_t command[1+3+256];
uint8_t command[1+3+W25_PAGE_SIZE];
FLASH_Address = FLASH_Address & 0xFFFFFF;
command[0] = W25_PAGE_PROGRAM;
@@ -494,8 +550,9 @@ void W25_CMD_Page_Program(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8
command[2] = FLASH_Address >> 8 & 0xFF;
command[3] = FLASH_Address & 0xFF;
if((FLASH_Address/256) != ((FLASH_Address+Size-1)/256))
return;
// 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
@@ -582,7 +639,7 @@ uint64_t W25_CMD_Read_Device_ID(W25_HandleTypeDef *hw25)
*/
void W25_SPI_Transmit (W25_HandleTypeDef *hw25, uint8_t *data, uint16_t size)
{
HAL_SPI_Transmit (&hw25->hspi, data, size, 500);
HAL_SPI_Transmit (&hw25->hspi, data, size, HAL_MAX_DELAY);
}
/**
@@ -595,6 +652,6 @@ void W25_SPI_Transmit (W25_HandleTypeDef *hw25, uint8_t *data, uint16_t size)
*/
void W25_SPI_Receive (W25_HandleTypeDef *hw25, uint8_t *data, uint16_t size)
{
HAL_SPI_Receive (&hw25->hspi, data, size, 500);
HAL_SPI_Receive (&hw25->hspi, data, size, HAL_MAX_DELAY);
}
//-------------------------------------------------------------

View File

@@ -78,6 +78,7 @@ typedef struct
uint32_t Sector_Address;
uint32_t Sector_Size;
unsigned fSavePrevoisData:1;
}W25_WriteInitTypeDef;
typedef struct
@@ -99,6 +100,9 @@ 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;
@@ -113,7 +117,7 @@ extern W25_HandleTypeDef hw25;
void W25_Base_Init(W25_HandleTypeDef *hw25);
/**
* @brief Read data from external FLASH.
* @brief Read external FLASH.
* @param hw25 - указатель на хендл flash.
* @param FLASH_Address - адресс откуда начинать считывание.
* @param pBuff - куда записывать данные из FLASH.
@@ -125,7 +129,7 @@ void W25_Base_Init(W25_HandleTypeDef *hw25);
HAL_StatusTypeDef W25_FLASH_Read(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout);
/**
* @brief Write data to area in FLASH.
* @brief Write external FLASH.
* @param hw25 - указатель на хендл flash.
* @param WriteInit - указатель на структуру, определяющую участок памяти для записи.
* @param Timeout - время, за которое должно быть осуществлено чтение.
@@ -133,34 +137,46 @@ HAL_StatusTypeDef W25_FLASH_Read(W25_HandleTypeDef *hw25, uint32_t FLASH_Address
* @note Позволяет перепрограммировать участок памяти. Можно записывать несколько страниц.
* Данные в сектора участка, но за пределами участка не сохраняются.
*/
HAL_StatusTypeDef W25_FLASH_Write_Area(W25_HandleTypeDef *hw25, W25_WriteInitTypeDef *WriteInit, uint32_t Timeout);
HAL_StatusTypeDef W25_FLASH_Write(W25_HandleTypeDef *hw25, W25_WriteInitTypeDef *WriteInit, uint32_t Timeout);
/**
* @brief Program area in FLASH.
* @brief Program external FLASH.
* @param hw25 - указатель на хендл flash.
* @param FLASH_Address - адресс куда начинать записывать.
* @param pData - откуда брать данные для записи в FLASH.
* @param Size - сколько байтов записать.
* @param Timeout - время, за которое должно быть осуществлено чтение.
* @return HAL status.
* @note Позволяет перепрограммировать участок памяти. Можно записывать несколько страниц.
* Данные в сектора участка, но за пределами участка не сохраняются.
* @note Программирование участка памяти, без ограничений на кол-во байт
*/
HAL_StatusTypeDef W25_FLASH_Program_Area(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef W25_FLASH_Program(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout);
/**
* @brief Erase FLASH Sector.
* @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);
HAL_StatusTypeDef W25_FLASH_Erase_Sector(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint32_t Timeout, uint32_t tickstart);
/**
* @brief Program page in FLASH.
* @brief Program page in external FLASH.
* @param hw25 - указатель на хендл flash.
* @param FLASH_Address - адресс куда начинать записывать.
* @param pData - откуда брать данные для записи в FLASH.
@@ -169,9 +185,10 @@ HAL_StatusTypeDef W25_FLASH_Erase_Sector(W25_HandleTypeDef *hw25, uint32_t FLASH
* @param tickstart - время, относительно которого надо отсчитывать таймаут.
* @return HAL status.
* @note Позволяет перепрограммировать только байты в прелелах одной страницы.
Для более гибкого программирования есть функция W25_FLASH_Program_Area, которая программирует участки любой длины (в теории).
Для более гибкого программирования есть функция 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.