531 lines
28 KiB
C
531 lines
28 KiB
C
/**
|
||
******************************************************************************
|
||
* @file memspi.c
|
||
* @brief Реализация работы с внешней памятью
|
||
******************************************************************************
|
||
* @details
|
||
Модуль реализует функции для удобной работы с FLASH/EEPROM по SPI:
|
||
- MEMSPI_Read_Memory Считывание внешней FLASH/EEPROM
|
||
- MEMSPI_EEPROM_Write Запись данных в внешнюю EEPROM
|
||
- MEMSPI_FLASH_Write Запись данных в внешнюю FLASH (функция сама очищает нужные сектора, и если надо сохраняет выбранные данные)
|
||
- MEMSPI_FLASH_Program Программирование внешней FLASH (выбранный участок FLASH должен быть очищен)
|
||
- MEMSPI_FLASH_Erase Очистка внешней FLASH
|
||
|
||
А также ряд вспомогательных функций:
|
||
- MEMSPI_EEPROM_Write_Page Запись страницы в EEPROM *есть более общая функция MEMSPI_EEPROM_Write, которая записывает участки больше страницы
|
||
- MEMSPI_FLASH_Erase_Sector Очистка сектора FLASH. *есть более общая функция MEMSPI_FLASH_Erase, которая может ощичать несколько секторов
|
||
- MEMSPI_FLASH_Program_Page Программирование страницы. *есть более общая функция MEMSPI_FLASH_Program, которая программирует участки больше страницы
|
||
- MEMSPI_WriteEnablingUntilTimeout Разрешение записи, пока не будет ответа или не истек таймаут
|
||
- MEMSPI_WaitOnFlagsUntilTimeout Ожидание флага пока не истек таймаута
|
||
******************************************************************************/
|
||
#include "memspi.h"
|
||
uint8_t sector_buff[MEMSPI_SECTOR_SIZE];
|
||
|
||
//-------------------------------------------------------------
|
||
//--------------------------FOR USER---------------------------
|
||
/**
|
||
* @brief Initialize SPI and GPIO for MEMSPI FLASH.
|
||
* @param hmemspi Указатель на структуру с настройками SPI и GPIO портов.
|
||
* @param hspi Указатель на хендл SPI.
|
||
*/
|
||
void MEMSPI_Base_Init(MEMSPI_HandleTypeDef *hmemspi, SPI_HandleTypeDef *hspi)
|
||
{
|
||
|
||
// SPI PERIPH INIT
|
||
if(hspi == NULL)
|
||
return;
|
||
|
||
hmemspi->hspi = hspi;
|
||
|
||
// GPIO INIT
|
||
// CHIP SELECT PIN INIT
|
||
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
||
GPIO_InitStruct.Pin = hmemspi->CS_PIN;
|
||
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
|
||
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
|
||
#ifdef GPIO_SPEED_FREQ_VERY_HIGH
|
||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
|
||
#else
|
||
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
|
||
#endif
|
||
HAL_GPIO_Init(hmemspi->CS_GPIOx, &GPIO_InitStruct);
|
||
MEMSPI_Deselect(hmemspi);
|
||
|
||
}
|
||
|
||
/**
|
||
* @brief Read external FLASH/EEPROM.
|
||
* @param hmemspi Указатель на хендл внешней памяти.
|
||
* @param FLASH_Address Адресс откуда начинать считывание.
|
||
* @param pBuff Куда записывать данные из FLASH.
|
||
* @param Size Вколько байтов считывать.
|
||
* @param Timeout Время, за которое должно быть осуществлено чтение.
|
||
* @return HAL status.
|
||
* @note Включает в себя проверку на доступность памяти (флаг BUSY)
|
||
*/
|
||
HAL_StatusTypeDef MEMSPI_Read_Memory(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pBuff, uint16_t Size, uint32_t Timeout)
|
||
{
|
||
HAL_StatusTypeDef MEMSPI_Status;
|
||
uint32_t tickstart = local_time();
|
||
|
||
// wait for unbusy
|
||
if(MEMSPI_WaitOnFlagsUntilTimeout(hmemspi, MEMSPI_SR_BUSY, 0, &Timeout, &tickstart) != HAL_OK) // if its unbusy for timeout
|
||
return HAL_TIMEOUT; // return timeout error
|
||
|
||
MEMSPI_Status = MEMSPI_CMD_Read_Data(hmemspi, FLASH_Address, pBuff, Size, Timeout);
|
||
if(MEMSPI_Status != HAL_OK)
|
||
return MEMSPI_Status;
|
||
|
||
return HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief Write external EEPROM.
|
||
* @param hmemspi Указатель на хендл внешней памяти.
|
||
* @param FLASH_Address Адресс куда начинать записывать.
|
||
* @param pData Откуда брать данные для записи в EEPROM.
|
||
* @param Size Вколько байтов записать.
|
||
* @param Timeout Время, за которое должна быть осуществлена запись.
|
||
* @param WaitForEnd Ожидание, пока память не выполненит операцию.
|
||
* @return HAL status.
|
||
* @note Позволяет записать участок памяти. Можно записывать несколько страниц.
|
||
*/
|
||
HAL_StatusTypeDef MEMSPI_EEPROM_Write(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout, uint8_t WaitForEnd)
|
||
{
|
||
uint32_t tickstart = local_time();
|
||
HAL_StatusTypeDef MEMSPI_Status;
|
||
|
||
// CALC AREA TO WRITE
|
||
uint16_t bytecnt = 0;
|
||
uint16_t currentpage_size = 0;
|
||
uint16_t lastpage_size = Size;
|
||
uint16_t firstpage = (FLASH_Address/MEMSPI_PAGE_SIZE);
|
||
uint16_t lastpage = ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE);
|
||
if(firstpage != lastpage) // if area is on several pages
|
||
{
|
||
currentpage_size = (firstpage+1)*MEMSPI_PAGE_SIZE - FLASH_Address; // set size of data on first page
|
||
lastpage_size = (FLASH_Address+Size) - lastpage*MEMSPI_PAGE_SIZE; // set size of data on last page
|
||
}
|
||
|
||
// PROGRAM PAGES: FROM FIRST NO THE PREVIOUS TO THE LAST
|
||
hmemspi->hNextAddr = FLASH_Address; // address would automatically increase in this variable
|
||
for(int i = 0; i < lastpage - firstpage; i++)
|
||
{
|
||
MEMSPI_Status = MEMSPI_EEPROM_Write_Page(hmemspi, hmemspi->hNextAddr, &pData[bytecnt], currentpage_size, &Timeout, &tickstart, 0); // programm page
|
||
if(MEMSPI_Status != HAL_OK) // note: no need waiting for end: the next call will wait for unbusy
|
||
return MEMSPI_Status;
|
||
|
||
// note for multiple page program: first we program rest of the first page,
|
||
// then we shift byte count to data, that shoud be on the next page
|
||
bytecnt += currentpage_size;
|
||
// and set current size as page size. because next pages will be fully programmed
|
||
currentpage_size = MEMSPI_PAGE_SIZE;
|
||
}
|
||
|
||
// PROGRAM LAST PAGE
|
||
MEMSPI_Status = MEMSPI_FLASH_Program_Page(hmemspi, hmemspi->hNextAddr, &pData[bytecnt], lastpage_size, &Timeout, &tickstart, WaitForEnd); // programm page
|
||
if(MEMSPI_Status != HAL_OK)
|
||
return MEMSPI_Status;
|
||
|
||
return HAL_OK;
|
||
}
|
||
/**
|
||
* @brief Write external FLASH.
|
||
* @param hmemspi Указатель на хендл внешней памяти.
|
||
* @param WriteInit Указатель на структуру, определяющую участок памяти для записи.
|
||
* @param Timeout Время, за которое должно быть осуществлено чтение.
|
||
* @param WaitForEnd Ожидание, пока память не выполненит операцию.
|
||
* @return HAL status.
|
||
* @note Позволяет перепрограммировать участок памяти. Можно записывать несколько страниц.
|
||
* Данные в сектора участка, но за пределами участка не сохраняются.
|
||
*/
|
||
HAL_StatusTypeDef MEMSPI_FLASH_Write(MEMSPI_HandleTypeDef *hmemspi, MEMSPI_WriteInitTypeDef *WriteInit, uint32_t Timeout, uint8_t WaitForEnd)
|
||
{
|
||
uint32_t tickstart = local_time();
|
||
uint32_t timeoutcnt = Timeout;
|
||
uint8_t *writebuff = WriteInit->pDataPtr;
|
||
HAL_StatusTypeDef MEMSPI_Status;
|
||
|
||
// CHECK FOR UNDEFINED STRUCTURE
|
||
if (WriteInit->Sector_Size == 0) // if sector undefined
|
||
{ // fill it with data init
|
||
WriteInit->Sector_Address = WriteInit->Data_Address;
|
||
WriteInit->Sector_Size = WriteInit->Data_Size;
|
||
}
|
||
else if (WriteInit->Data_Size == 0) // if data undefined
|
||
{ // fill it with sector init
|
||
WriteInit->Data_Address = WriteInit->Sector_Address;
|
||
WriteInit->Data_Size = WriteInit->Sector_Size;
|
||
} // if both undefined - return HAL ERR
|
||
else if ((WriteInit->Sector_Size == 0) && (WriteInit->Data_Size == 0))
|
||
{
|
||
return HAL_ERROR;
|
||
}
|
||
// CHECK FOR CORRECT STRUCTURE
|
||
// if data is beyound sector
|
||
if((WriteInit->Data_Address < WriteInit->Sector_Address) ||
|
||
((WriteInit->Data_Address + WriteInit->Data_Size) > (WriteInit->Sector_Address + WriteInit->Sector_Size)))
|
||
return HAL_ERROR;
|
||
|
||
// WRITE FLASH WITH SAVING PREVIOUS DATA
|
||
if(WriteInit->fSavePrevoisData)
|
||
{
|
||
// uint8_t sector_buff[WriteInit->Sector_Size];
|
||
// store data from flash
|
||
MEMSPI_Status = MEMSPI_Read_Memory(hmemspi, WriteInit->Sector_Address, sector_buff, WriteInit->Sector_Size, Timeout);
|
||
if(MEMSPI_Status != HAL_OK)
|
||
return MEMSPI_Status;
|
||
|
||
// CHANGE DATA IN USER "SECTOR"
|
||
uint32_t addr_shift = WriteInit->Data_Address - WriteInit->Sector_Address;
|
||
for(int i = 0; i < WriteInit->Data_Size; i++)
|
||
{
|
||
sector_buff[addr_shift+i] = WriteInit->pDataPtr[addr_shift+i];
|
||
}
|
||
|
||
// CALC AREA TO REPROGRAM
|
||
uint16_t lastsector_size = WriteInit->Sector_Size;
|
||
uint16_t firstsector = (WriteInit->Sector_Address/MEMSPI_SECTOR_SIZE);
|
||
uint16_t lastsector = ((WriteInit->Sector_Address+WriteInit->Sector_Size-1)/MEMSPI_SECTOR_SIZE);
|
||
if(firstsector != lastsector) // if area is on several pages
|
||
{
|
||
lastsector_size = (WriteInit->Sector_Address+WriteInit->Sector_Size) - lastsector*MEMSPI_SECTOR_SIZE; // set size of data on last page
|
||
}
|
||
// REPROGRAM SECTORS: FROM FIRST SECTOR NO THE PREVIOUS TO THE LAST
|
||
hmemspi->hNextAddr = WriteInit->Sector_Address; // address would automatically increase in this variable
|
||
hmemspi->hNextSector = firstsector+1;
|
||
uint16_t bytescnt = 0;
|
||
uint16_t bytes_to_next_sector = 0;
|
||
for(int i = 0; i < (lastsector - firstsector); i++)
|
||
{
|
||
// ERASE FLASH
|
||
MEMSPI_Status = MEMSPI_FLASH_Erase(hmemspi, hmemspi->hNextAddr, 1, Timeout, 0);
|
||
if(MEMSPI_Status != HAL_OK) // note: no need waiting for end: the next call will wait for unbusy
|
||
return MEMSPI_Status;
|
||
|
||
// PROGRAM FLASH WITH NEW DATA
|
||
// calc bytes to next sector
|
||
bytes_to_next_sector = hmemspi->hNextSector*MEMSPI_SECTOR_SIZE - hmemspi->hNextAddr;
|
||
// program data to flash
|
||
MEMSPI_Status = MEMSPI_FLASH_Program(hmemspi, hmemspi->hNextAddr, §or_buff[bytescnt], bytes_to_next_sector, Timeout, 0);
|
||
if(MEMSPI_Status != HAL_OK)
|
||
return MEMSPI_Status;
|
||
// shift bytes count to data, that shoud be on the next page
|
||
bytescnt += bytes_to_next_sector;
|
||
}
|
||
|
||
// ERASE FLASH
|
||
MEMSPI_Status = MEMSPI_FLASH_Erase(hmemspi, hmemspi->hNextAddr, 1, Timeout, 0);
|
||
if(MEMSPI_Status != HAL_OK) // note: no need waiting for end: the next call will wait for unbusy
|
||
return MEMSPI_Status;
|
||
|
||
// PROGRAM LAST SECTOR
|
||
MEMSPI_Status = MEMSPI_FLASH_Program(hmemspi, hmemspi->hNextAddr, §or_buff[bytescnt], lastsector_size, Timeout, WaitForEnd);
|
||
if(MEMSPI_Status != HAL_OK)
|
||
return MEMSPI_Status;
|
||
}
|
||
// WRITE FLASH WITHOUT SAVING PREVIOUS DATA
|
||
else
|
||
{
|
||
// ERASE FLASH
|
||
MEMSPI_Status = MEMSPI_FLASH_Erase(hmemspi, WriteInit->Sector_Address, WriteInit->Sector_Size, Timeout, 0);
|
||
if(MEMSPI_Status != HAL_OK) // note: no need waiting for end: the next call will wait for unbusy
|
||
return MEMSPI_Status;
|
||
|
||
// PROGRAM FLASH WITH NEW DATA
|
||
// program data to flash
|
||
MEMSPI_Status = MEMSPI_FLASH_Program(hmemspi, WriteInit->Sector_Address, WriteInit->pDataPtr, WriteInit->Sector_Size, Timeout, WaitForEnd);
|
||
if(MEMSPI_Status != HAL_OK)
|
||
return MEMSPI_Status;
|
||
}
|
||
|
||
return HAL_OK;
|
||
}
|
||
|
||
/**
|
||
* @brief Program external FLASH.
|
||
* @param hmemspi Указатель на хендл внешней памяти.
|
||
* @param FLASH_Address Адресс куда начинать записывать.
|
||
* @param pData Откуда брать данные для записи в FLASH.
|
||
* @param Size Вколько байтов записать.
|
||
* @param Timeout Время, за которое должно быть осуществлено чтение.
|
||
* @param WaitForEnd Ожидание, пока память не выполненит операцию.
|
||
* @return HAL status.
|
||
* @note Программирование участка памяти, без ограничений на кол-во байт
|
||
*/
|
||
HAL_StatusTypeDef MEMSPI_FLASH_Program(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t Timeout, uint8_t WaitForEnd)
|
||
{
|
||
uint32_t tickstart = local_time();
|
||
HAL_StatusTypeDef MEMSPI_Status;
|
||
|
||
// CALC AREA TO PROGRAM
|
||
uint16_t lastpage_size = Size;
|
||
uint16_t firstpage = (FLASH_Address/MEMSPI_PAGE_SIZE);
|
||
uint16_t lastpage = ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE);
|
||
if(firstpage != lastpage) // if area is on several pages
|
||
{
|
||
lastpage_size = (FLASH_Address+Size) - lastpage*MEMSPI_PAGE_SIZE; // set size of data on last page
|
||
}
|
||
|
||
// PROGRAM PAGES: FROM FIRST NO THE PREVIOUS TO THE LAST
|
||
hmemspi->hNextAddr = FLASH_Address; // address would automatically increase in this variable
|
||
hmemspi->hNextPage = firstpage+1; // address would automatically increase in this variable
|
||
uint16_t bytecnt = 0;
|
||
uint16_t bytes_to_next_page = 0;
|
||
for(int i = 0; i < lastpage - firstpage; i++)
|
||
{
|
||
// calc bytes to next sector
|
||
bytes_to_next_page = hmemspi->hNextPage*MEMSPI_PAGE_SIZE - hmemspi->hNextAddr;
|
||
|
||
MEMSPI_Status = MEMSPI_FLASH_Program_Page(hmemspi, hmemspi->hNextAddr, &pData[bytecnt], bytes_to_next_page, &Timeout, &tickstart, 0); // programm page
|
||
if(MEMSPI_Status != HAL_OK) // note: no need waiting for end: the next call will wait for unbusy
|
||
return MEMSPI_Status;
|
||
|
||
// then we shift byte count to data, that shoud be on the next page
|
||
bytecnt += bytes_to_next_page;
|
||
}
|
||
|
||
// PROGRAM LAST PAGE
|
||
MEMSPI_Status = MEMSPI_FLASH_Program_Page(hmemspi, hmemspi->hNextAddr, &pData[bytecnt], lastpage_size, &Timeout, &tickstart, WaitForEnd); // programm page
|
||
if(MEMSPI_Status != HAL_OK)
|
||
return MEMSPI_Status;
|
||
|
||
return HAL_OK; // if all ok return HAL_OK
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief Erase external FLASH.
|
||
* @param hmemspi Указатель на хендл внешней памяти.
|
||
* @param FLASH_Address Адресс где надо данные стереть.
|
||
* @param Size Вколько байтов стереть.
|
||
* @param Timeout Время, за которое должно быть осуществлена очистка.
|
||
* @param WaitForEnd Ожидание, пока память не выполненит операцию.
|
||
* @return HAL status.
|
||
* @note Т.к. очитска происходит по секторам, Size нужен, чтобы определить сколько секторов очистить
|
||
* И если начальны адресс будет на Sector 0, а последний байт на Sector 1, то произойдет очистка Sector 0 и Sector 1
|
||
*/
|
||
HAL_StatusTypeDef MEMSPI_FLASH_Erase(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint16_t Size, uint32_t Timeout, uint8_t WaitForEnd)
|
||
{
|
||
uint32_t tickstart = local_time();
|
||
HAL_StatusTypeDef MEMSPI_Status;
|
||
|
||
// CALC AREA TO ERASE
|
||
uint16_t bytecnt = 0;
|
||
uint16_t firstsector = (FLASH_Address/MEMSPI_SECTOR_SIZE);
|
||
uint16_t lastsector = ((FLASH_Address+Size-1)/MEMSPI_SECTOR_SIZE);
|
||
|
||
for(int i = 0; i <= (lastsector - firstsector); i++)
|
||
{
|
||
MEMSPI_Status = MEMSPI_FLASH_Erase_Sector(hmemspi, FLASH_Address, &Timeout, &tickstart, WaitForEnd); // programm page
|
||
if(MEMSPI_Status != HAL_OK)
|
||
return MEMSPI_Status;
|
||
FLASH_Address += MEMSPI_SECTOR_SIZE;
|
||
}
|
||
return HAL_OK;
|
||
}
|
||
|
||
|
||
//-------------------------------------------------------------
|
||
//----------------------SERVICE FUNCTIONS----------------------
|
||
/**
|
||
* @brief Write page in external EEPROM.
|
||
* @param hmemspi Указатель на хендл внешней памяти.
|
||
* @param FLASH_Address Адресс куда начинать записывать.
|
||
* @param pData Откуда брать данные для записи в EEPROM.
|
||
* @param Size Вколько байтов записать.
|
||
* @param Timeout Время, за которое должно быть осуществлено чтение.
|
||
* @param tickstart Начальное количество тиков.
|
||
* @param WaitForEnd Ожидание, пока память не выполненит операцию.
|
||
* @return HAL status.
|
||
* @note Позволяет записывать только байты в пределах одной страницы.
|
||
Для более гибкой записи есть функция MEMSPI_EEPROM_Write, которая программирует участки любой длины (в теории).
|
||
*/
|
||
HAL_StatusTypeDef MEMSPI_EEPROM_Write_Page(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd)
|
||
{
|
||
HAL_StatusTypeDef MEMSPI_Status;
|
||
// enable writting and waiting for unbusy
|
||
if(MEMSPI_WriteEnablingUntilTimeout(hmemspi, Timeout, tickstart) != HAL_OK) // if writting isnt enable
|
||
return HAL_TIMEOUT; // return timeout
|
||
|
||
MEMSPI_Status = MEMSPI_CMD_EEPROM_Write(hmemspi, FLASH_Address, pData, Size, *Timeout);
|
||
if(MEMSPI_Status != HAL_OK)
|
||
return MEMSPI_Status;
|
||
|
||
// waiting for ending of writting if need
|
||
if(WaitForEnd)
|
||
if(MEMSPI_WaitOnFlagsUntilTimeout(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 0, Timeout, tickstart) != HAL_OK) // if writting isnt done (MEMSPI busy and WEL bit isnt in reset state)
|
||
return HAL_TIMEOUT;
|
||
|
||
// update handle variables
|
||
hmemspi->hNextAddr = (FLASH_Address+Size);
|
||
hmemspi->hNextPage = (FLASH_Address+Size)/MEMSPI_PAGE_SIZE;
|
||
hmemspi->hNextSector = (FLASH_Address+Size)/MEMSPI_SECTOR_SIZE;
|
||
return HAL_OK;
|
||
}
|
||
/**
|
||
* @brief Erase external FLASH Sector.
|
||
* @param hmemspi Указатель на хендл внешней памяти.
|
||
* @param FLASH_Address Адресс где надо данные стереть.
|
||
* @param Timeout Время, за которое должно быть осуществлена очистка.
|
||
* @param tickstart Время, относительно которого надо отсчитывать таймаут.
|
||
* @param WaitForEnd Ожидание, пока память не выполненит операцию.
|
||
* @return HAL status.
|
||
* @note При Timeout = 0, функция не будет ожидать окончания очистки (выставления в 0 флагов BUSY и WEL)
|
||
* @note Микросхема вроде сама высчитывает какой сектор ерейзнуть, в соответствии с заданным адресом.
|
||
*/
|
||
HAL_StatusTypeDef MEMSPI_FLASH_Erase_Sector(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd)
|
||
{
|
||
HAL_StatusTypeDef MEMSPI_Status;
|
||
// enable writting and waiting for unbusy
|
||
if(MEMSPI_WriteEnablingUntilTimeout(hmemspi, Timeout, tickstart) != HAL_OK) // if writting isnt enable
|
||
return HAL_TIMEOUT; // return timeout
|
||
|
||
// erase sector (instruction)
|
||
MEMSPI_Status = MEMSPI_CMD_FLASH_Erase_Sector(hmemspi, FLASH_Address, *Timeout);
|
||
if(MEMSPI_Status != HAL_OK)
|
||
return MEMSPI_Status;
|
||
|
||
// waiting for ending of erasing if need
|
||
if(WaitForEnd)
|
||
if(MEMSPI_WaitOnFlagsUntilTimeout(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 0, Timeout, tickstart) != HAL_OK) // if erase isnt done (MEMSPI busy and WEL bit isnt in reset state)
|
||
return HAL_TIMEOUT; // return timeout because erasing instruction accepted, but arent done
|
||
// note: if timeout == 0, erasing wouldnt be checking for ending (check busy flag)
|
||
|
||
return HAL_OK; // if all ok return HAL_OK
|
||
}
|
||
|
||
/**
|
||
* @brief Program page in external FLASH.
|
||
* @param hmemspi Указатель на хендл внешней памяти.
|
||
* @param FLASH_Address Адресс куда начинать записывать.
|
||
* @param pData Откуда брать данные для записи в FLASH.
|
||
* @param Size Вколько байтов записать.
|
||
* @param Timeout Время, за которое должно быть осуществлено чтение.
|
||
* @param tickstart Время, относительно которого надо отсчитывать таймаут.
|
||
* @param WaitForEnd Ожидание, пока память не выполненит операцию.
|
||
* @return HAL status.
|
||
* @note Позволяет перепрограммировать только байты в прелелах одной страницы.
|
||
Для более гибкого программирования есть функция MEMSPI_FLASH_Program, которая программирует участки любой длины (в теории).
|
||
*/
|
||
HAL_StatusTypeDef MEMSPI_FLASH_Program_Page(MEMSPI_HandleTypeDef *hmemspi, uint32_t FLASH_Address, uint8_t *pData, uint16_t Size, uint32_t *Timeout, uint32_t *tickstart, uint8_t WaitForEnd)
|
||
{
|
||
HAL_StatusTypeDef MEMSPI_Status;
|
||
// enable writting and waiting for unbusy
|
||
if(MEMSPI_WriteEnablingUntilTimeout(hmemspi, Timeout, tickstart) != HAL_OK) // if writting isnt enable
|
||
return HAL_TIMEOUT; // return timeout
|
||
|
||
// check if flash range is placed at one page
|
||
if((FLASH_Address/MEMSPI_PAGE_SIZE) != ((FLASH_Address+Size-1)/MEMSPI_PAGE_SIZE)) // if page of first byte isnt equal page of last byte
|
||
return HAL_ERROR; // return error
|
||
|
||
// programm page (instruction)
|
||
MEMSPI_Status = MEMSPI_CMD_FLASH_Page_Program(hmemspi, FLASH_Address, pData, Size, *Timeout);
|
||
if(MEMSPI_Status != HAL_OK)
|
||
return MEMSPI_Status;
|
||
|
||
// waiting for ending of writting if need
|
||
if(WaitForEnd)
|
||
if(MEMSPI_WaitOnFlagsUntilTimeout(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 0, Timeout, tickstart) != HAL_OK) // if writting isnt done (MEMSPI busy and WEL bit isnt in reset state)
|
||
return HAL_TIMEOUT;
|
||
|
||
// update handle variables
|
||
hmemspi->hNextAddr = (FLASH_Address+Size);
|
||
hmemspi->hNextPage = (hmemspi->hNextAddr+Size)/MEMSPI_PAGE_SIZE;
|
||
hmemspi->hNextSector = (hmemspi->hNextAddr+Size)/MEMSPI_SECTOR_SIZE;
|
||
|
||
return HAL_OK;
|
||
}
|
||
/**
|
||
* @brief Setting WEL bit until it setted or until timeout.
|
||
* @param hmemspi Указатель на хендл внешней памяти.
|
||
* @param Timeout Время, за которое должно быть осуществлено чтение.
|
||
* @param tickstart Время, относительно которого надо отсчитывать таймаут.
|
||
* @return HAL status.
|
||
* @note Подает команду на разрешение записи до тех пор, пока она запись не разрешиться или до тех пор, пока таймаут не истечет.
|
||
*/
|
||
HAL_StatusTypeDef MEMSPI_WriteEnablingUntilTimeout(MEMSPI_HandleTypeDef *hmemspi, uint32_t *Timeout, uint32_t *tickstart)
|
||
{
|
||
HAL_StatusTypeDef MEMSPI_Status;
|
||
|
||
// wait for unbusy
|
||
if(MEMSPI_WaitOnFlagsUntilTimeout(hmemspi, MEMSPI_SR_BUSY, 0, Timeout, tickstart) != HAL_OK) // if its unbusy for timeout
|
||
return HAL_TIMEOUT;
|
||
|
||
// enable writing
|
||
while((hmemspi->SR&MEMSPI_SR_WEL) != MEMSPI_SR_WEL)
|
||
{
|
||
// if flash isnt busy - set WEL flag
|
||
if((hmemspi->SR&MEMSPI_SR_BUSY) == 0)
|
||
MEMSPI_CMD_Write_Enable(hmemspi, *Timeout);
|
||
|
||
// check is writting enabled
|
||
MEMSPI_Status = MEMSPI_CMD_Read_Status_Register(hmemspi, MEMSPI_SR_WEL|MEMSPI_SR_BUSY, 1, *Timeout);
|
||
if(MEMSPI_Status != HAL_OK)
|
||
{
|
||
MEMSPI_Deselect(hmemspi);
|
||
return MEMSPI_Status;
|
||
}
|
||
|
||
if((local_time() - *tickstart) >= *Timeout) // if time is out
|
||
{
|
||
MEMSPI_Deselect(hmemspi);
|
||
return HAL_TIMEOUT; // set timeout
|
||
}
|
||
}
|
||
MEMSPI_Deselect(hmemspi);
|
||
MEMSPI_Update_Timeout_Variables(Timeout, tickstart);
|
||
return HAL_OK; // if all ok return HAL_OK
|
||
}
|
||
|
||
/**
|
||
* @brief Wait for flag until timeout.
|
||
* @param hmemspi Указатель на хендл внешней памяти.
|
||
* @param FlagMask Маска для флагов, какие флаги считывать.
|
||
* @param FlagStatus Какое состояние должно быть у выбранных флагов.
|
||
* @param Timeout Время, за которое должно быть осуществлено чтение.
|
||
* @param tickstart Время, относительно которого надо отсчитывать таймаут.
|
||
* @return HAL status.
|
||
* @note Считывает флаги до тех пор, пока они не будут в состоянии FlagStatus или до тех пор, пока таймаут не истечет.
|
||
*/
|
||
HAL_StatusTypeDef MEMSPI_WaitOnFlagsUntilTimeout(MEMSPI_HandleTypeDef *hmemspi, uint16_t FlagMask, uint16_t FlagStatus, uint32_t *Timeout, uint32_t *tickstart)
|
||
{
|
||
HAL_StatusTypeDef MEMSPI_Status;
|
||
// check flags
|
||
MEMSPI_Status = MEMSPI_CMD_Read_Status_Register(hmemspi, FlagMask, 0, *Timeout);
|
||
if(MEMSPI_Status != HAL_OK)
|
||
return MEMSPI_Status;
|
||
while((hmemspi->SR&FlagMask) != FlagStatus)
|
||
{
|
||
// check flags
|
||
// MEMSPI_Status = MEMSPI_CMD_Read_Status_Register(hmemspi, FlagMask, *Timeout);
|
||
MEMSPI_Status = MEMSPI_SPI_Receive(hmemspi, (uint8_t *)&hmemspi->SR, 1, *Timeout); // receive response
|
||
if(MEMSPI_Status != HAL_OK)
|
||
{
|
||
MEMSPI_Deselect(hmemspi);
|
||
return MEMSPI_Status;
|
||
}
|
||
|
||
if((local_time() - *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 = local_time() - *tickstart; // update timeout
|
||
*Timeout -= timeoutcnt;
|
||
*tickstart += timeoutcnt;
|
||
}
|