Files
STM32_MemorySPI/spi_flash/spi_flash.c
alexey b6e8e69ab3 Автоматизированы функции
Доделаны(?) 4 базовые самостоятельные функции для общения с флеш: Read, Write, Program, Erase.
2024-08-13 10:35:16 +03:00

658 lines
28 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/********************************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);
}
//-------------------------------------------------------------