414 lines
13 KiB
C
414 lines
13 KiB
C
#include "spi_flash.h"
|
|
uint8_t rx_buf[1025];
|
|
uint8_t tx_buf[10];
|
|
uint8_t sector_buff[W25_SECTOR_SIZE];
|
|
|
|
/* USER CODE BEGIN PV */
|
|
char str1[30];
|
|
|
|
//----------------------FUNCTION FUNCTIONS---------------------
|
|
//-------------------------------------------------------------
|
|
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;
|
|
}
|
|
HAL_StatusTypeDef W25_FLASH_Write_Area(W25_HandleTypeDef *hw25, W25_reProgramInitTypeDef *WriteInit, uint32_t Timeout)
|
|
{
|
|
uint8_t sector_buff[256];
|
|
uint32_t tickstart = HAL_GetTick();
|
|
uint32_t timeoutcnt = Timeout;
|
|
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
|
|
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);
|
|
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];
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
HAL_StatusTypeDef W25_FLASH_Program_Area(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 lastpage_size = Size;
|
|
uint16_t firstpage = (FLASH_Address/256);
|
|
uint16_t lastpage = ((FLASH_Address+Size-1)/256);
|
|
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
|
|
}
|
|
|
|
// PROGRAM PAGES: FROM FIRST NO THE PREVIOUS TO THE LAST
|
|
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
|
|
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;
|
|
}
|
|
|
|
// PROGRAM LAST PAGE
|
|
W25_Status = W25_FLASH_Program_Page(hw25, FLASH_Address, &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
|
|
}
|
|
|
|
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
|
|
|
|
// 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;
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
HAL_StatusTypeDef W25_FLASH_Erase_Sector(W25_HandleTypeDef *hw25, uint32_t FLASH_Address, uint32_t Timeout)
|
|
{
|
|
uint32_t tickstart = HAL_GetTick();
|
|
|
|
// 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)
|
|
W25_CMD_Erase_Sector(hw25, FLASH_Address);
|
|
|
|
// waiting for ending of erasing
|
|
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
|
|
|
|
return HAL_OK; // if all ok return HAL_OK
|
|
}
|
|
|
|
HAL_StatusTypeDef W25_WriteEnablingUntilTimeout(W25_HandleTypeDef *hw25, uint32_t Timeout, uint32_t tickstart)
|
|
{
|
|
// enable writting
|
|
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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
|
|
//-----------------ELEMENTARY COMMAND FUNCTIONS----------------
|
|
//-------------------------------------------------------------
|
|
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_CS_Set(hw25);
|
|
W25_SPI_Transmit(hw25, command, 4);
|
|
W25_SPI_Receive(hw25, pBuff, Size);
|
|
W25_CS_Reset(hw25);
|
|
}
|
|
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_CS_Set(hw25);
|
|
W25_SPI_Transmit(hw25, command, 5);
|
|
W25_SPI_Receive(hw25, pBuff, Size);
|
|
W25_CS_Reset(hw25);
|
|
}
|
|
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];
|
|
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;
|
|
|
|
if((FLASH_Address/256) != ((FLASH_Address+Size-1)/256))
|
|
return;
|
|
|
|
W25_CS_Set(hw25);
|
|
W25_SPI_Transmit(hw25, command, 4); // send insctruction to write
|
|
W25_SPI_Transmit(hw25, pData, Size); // send data to write
|
|
W25_CS_Reset(hw25);
|
|
}
|
|
|
|
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_CS_Set(hw25);
|
|
W25_SPI_Transmit(hw25, command, 4);
|
|
W25_CS_Reset(hw25);
|
|
}
|
|
|
|
void W25_CMD_Write_Enable(W25_HandleTypeDef *hw25)
|
|
{
|
|
uint8_t command[1];
|
|
command[0] = W25_WRITE_ENABLE;
|
|
|
|
W25_CS_Set(hw25);
|
|
W25_SPI_Transmit(hw25, command, 1);
|
|
W25_CS_Reset(hw25);
|
|
}
|
|
|
|
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_CS_Set(hw25);
|
|
W25_SPI_Transmit(hw25, command, 1);
|
|
W25_CS_Reset(hw25);
|
|
}
|
|
|
|
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_CS_Set(hw25);
|
|
W25_SPI_Transmit(hw25, command, 1); // send insctruction to read SR
|
|
W25_SPI_Receive(hw25, pSRPtr, 1); // receive response
|
|
W25_CS_Reset(hw25);
|
|
if(size > 1) // if 2 bytes are requested
|
|
{
|
|
W25_CS_Set(hw25);
|
|
W25_SPI_Transmit(hw25, command+1, 1); // send insctruction to read SR
|
|
W25_SPI_Receive(hw25, pSRPtr-1, 1); // receive response
|
|
W25_CS_Reset(hw25);
|
|
}
|
|
}
|
|
uint32_t W25_CMD_Read_JEDEC_ID(W25_HandleTypeDef *hw25)
|
|
{
|
|
uint8_t dt[4] = {0};
|
|
uint32_t return_val;
|
|
|
|
tx_buf[0] = W25_READ_JEDEC_ID;
|
|
W25_CS_Set(hw25);
|
|
W25_SPI_Transmit(hw25, tx_buf, 1);
|
|
W25_SPI_Receive(hw25, &dt[1], 3);
|
|
W25_CS_Reset(hw25);
|
|
|
|
return_val = (*(uint64_t *)dt);
|
|
return __REV(return_val) & 0xFFFFFF;
|
|
}
|
|
|
|
uint64_t W25_CMD_Read_Device_ID(W25_HandleTypeDef *hw25)
|
|
{
|
|
uint8_t dt[8];
|
|
uint64_t return_val_LO;
|
|
uint64_t return_val_HI;
|
|
tx_buf[0] = W25_READ_UNIQUE_ID;
|
|
W25_CS_Set(hw25);
|
|
W25_SPI_Transmit(hw25, tx_buf, 1);
|
|
W25_SPI_Receive(hw25, dt, 8);
|
|
W25_CS_Reset(hw25);
|
|
|
|
return_val_LO = (*(uint64_t *)dt) >> 32;
|
|
return_val_HI = (*(uint64_t *)dt) & 0xFFFFFFFF;
|
|
return ((uint64_t)__REV(return_val_HI) << 32) | __REV(return_val_LO);
|
|
}
|
|
//-------------------------------------------------------------
|
|
|
|
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);
|
|
|
|
}
|
|
//-------------------------------------------------------------
|
|
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
void W25_SPI_Transmit (W25_HandleTypeDef *hw25, uint8_t *data, uint16_t size)
|
|
{
|
|
HAL_SPI_Transmit (&hw25->hspi, data, size, 500);
|
|
|
|
}
|
|
//-------------------------------------------------------------
|
|
void W25_SPI_Receive (W25_HandleTypeDef *hw25, uint8_t *data, uint16_t size)
|
|
{
|
|
HAL_SPI_Receive (&hw25->hspi, data, size, 500);
|
|
}
|
|
//-------------------------------------------------------------
|