RSTEN (reset enable)
RST (reset)
WREN (write enable)
ULBPR (Global Block Protection Unlock)
SFDP (signature is read correctly)
RSDR (cannot read the status register)
/*
* @brief Final initialization procedure. It should be called in a thread, thus delay and
* logging are allowed. It is used for finding the physical flash memory device on spi line.
*
* @return HAL_OK if flash memory is successfully identified.
*/
HAL_StatusTypeDef spifl_InitFinal()
{
if (spifl.state != e_spiflState_FirstInitDone)
{
log_PushLine(e_logLevel_Critical,
"Wrong spi module state (%u) in final init func!", spifl.state);
return HAL_ERROR;
}
osSemaphoreWait(spifl.sema, osWaitForever);
HAL_StatusTypeDef retVal = HAL_OK;
// reset the device
retVal += spifl_resetDevice();
// Unlock global block protection, WREN first
retVal += spifl_writeConfigByte(e_spiflCmd_WREN);
retVal += spifl_writeConfigByte(e_spiflCmd_ULBPR);
spifl.bufSend[0] = e_spiflCmd_WBPR;
memset(spifl.bufSend + 1, 0, 10);
retVal += spifl_sendReceive(spifl.bufSend, spifl.bufRecv, 11);
// now check either SST26VF032B is visible on spi line by reading 1st DWORD (32 bit) of SFDP header
spifl.bufSend[0] = e_spiflCmd_SFDP; // sfdp command
spifl.bufSend[1] = SPIFL_SFDP_SIGNATURE_ADDR >> 16; // MSB next 3 bytes are addr
spifl.bufSend[2] = SPIFL_SFDP_SIGNATURE_ADDR >> 8;
spifl.bufSend[3] = SPIFL_SFDP_SIGNATURE_ADDR; // LSB
retVal += spifl_sendReceive(spifl.bufSend, spifl.bufRecv, 9);
union32_t sfdpSig;
memcpy(sfdpSig.u8, &spifl.bufRecv[5], sizeof(uint32_t));
// compare the signature with datasheet one
if (sfdpSig.u32 != SPIFL_SFDP_SIGNATURE_VAL)
retVal++;
else
spifl.state = e_spiflState_FullyOperational;
uint8_t status;
retVal += spifl_readStausReg(&status);
osSemaphoreRelease(spifl.sema);
return retVal;
}
RSTEN
RST
WREN
ULBPR
SFDP
RSDR
/*
* u_spi_flash.h
*
* Created on: 24.05.2017
* Author: Lukasz
*/
#ifndef U_SPI_FLASH_H_
#define U_SPI_FLASH_H_
/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_hal.h"
#include "cmsis_os.h"
#include "stdint.h"
#include "u_types.h"
/* Defines and macros --------------------------------------------------------*/
#define SPIFL_SFDP_SIGNATURE_ADDR 0
#define SPIFL_SFDP_SIGNATURE_SIZE 4
#define SPIFL_SFDP_SIGNATURE_VAL 0x50444653
#define SPIFL_TIMEOUT_MS 100
#define SPIFL_BUFFER_SIZE 64
#define SPIFL_PAGE_SIZE 256
/* Enums and structs ---------------------------------------------------------*/
/*
* @brief Initialization status of the module
*/
typedef enum
{
e_spiflState_Uninitialized = 0,
e_spiflState_FirstInitDone = 1,
e_spiflState_FullyOperational = 2,
} spiflState_t;
/*
* @brief DEVICE OPERATION INSTRUCTIONS FOR SST26VF032B/032BA.
* For description see Microchip DS20005218C document on page 13 (SST26VF032B/032BA datasheet)
*/
typedef enum
{
e_spiflCmd_WRSR = 0x01,
e_spiflCmd_Read = 0x03,
e_spiflCmd_RDSR = 0x05,
e_spiflCmd_WREN = 0x06,
e_spiflCmd_WBPR = 0x42,
e_spiflCmd_RSTEN = 0x66,
e_spiflCmd_RST = 0x99,
e_spiflCmd_SFDP = 0x5A,
e_spiflCmd_RBPR = 0x72,
e_spiflCmd_ULBPR = 0x98,
e_spiflCmd_JEDECID = 0x9F,
} spiflCmd_t;
/*
* @brief Status register masks
*/
typedef enum
{
e_spiflStatusReg_BUSY = 1 << 0,
e_spiflStatusReg_WEL = 1 << 1,
e_spiflStatusReg_WSE = 1 << 2,
e_spiflStatusReg_WSP = 1 << 3,
e_spiflStatusReg_WPLD = 1 << 4,
e_spiflStatusReg_SEC = 1 << 5,
e_spiflStatusReg_RES = 1 << 6,
} spiflStatusReg_t;
typedef struct
{
SPI_HandleTypeDef* handle; /*!< Preconfigured SPI HAL structure pointer */
gpioPin_t* nCS; /*!< #CS pin gpio, preconfigured */
spiflState_t state; /*!< current module initialization state */
osSemaphoreId sema; /*!< Transmission semaphore */
uint8_t bufSend[SPIFL_BUFFER_SIZE]; /*!< Internal send buffer */
uint8_t bufRecv[SPIFL_BUFFER_SIZE]; /*!< Internal read buffer */
} spiflData_t;
/* Private variables ---------------------------------------------------------*/
/* Public variables ----------------------------------------------------------*/
/* Fuction prototypes --------------------------------------------------------*/
HAL_StatusTypeDef spifl_InitPreliminary(SPI_HandleTypeDef* spiHandle, gpioPin_t* gpio);
HAL_StatusTypeDef spifl_InitFinal();
HAL_StatusTypeDef spifl_read(uint32_t startAddres, uint8_t* readBuffer, uint32_t len);
HAL_StatusTypeDef spifl_write(uint32_t startAddres, uint8_t* writeBuffer, uint32_t len);
/* Function declarations -----------------------------------------------------*/
#endif /* U_SPI_FLASH_H_ */
/*
* u_spi_flash.c
*
* Created on: 24.05.2017
* Author: Lukasz
*/
/* Includes ------------------------------------------------------------------*/
#include "u_spi_flash.h"
#include "u_logger.h"
#include "u_types.h"
/* Defines and macros --------------------------------------------------------*/
/* Enums and structs ---------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static spiflData_t spifl;
/* Public variables ----------------------------------------------------------*/
/* Fuction prototypes --------------------------------------------------------*/
HAL_StatusTypeDef spifl_writeConfigByte(uint8_t cmd);
HAL_StatusTypeDef spifl_resetDevice();
HAL_StatusTypeDef spifl_readStausReg(uint8_t* reg);
HAL_StatusTypeDef spifl_eraseSector(uint32_t sectorAddress);
HAL_StatusTypeDef spifl_pageProgram(uint32_t startAddres, uint8_t* writeBuffer, uint32_t len);
HAL_StatusTypeDef spifl_pollForWrite();
/* Function declarations -----------------------------------------------------*/
/*
* @brief Sets the #CS pin low or high
* @param state: non zero for high, 0 for low
*/
static inline void spifl_nCsSet(uint32_t state)
{
if (spifl.state != e_spiflState_Uninitialized)
HAL_GPIO_WritePin(spifl.nCS->gpioGroup, spifl.nCS->gpioNr, state);
else
log_PushLine(e_logLevel_Critical, "Unable to set nCS pin, spi flash interface not initialized!");
}
/*
* @brief HAL_SPI_TransmitReceive wrapper with #CS line handling
*/
static inline HAL_StatusTypeDef spifl_sendReceive(uint8_t *pTxData,
uint8_t *pRxData,
uint16_t Size)
{
if (e_spiflState_Uninitialized == spifl.state)
return HAL_ERROR;
if (!Size)
return HAL_OK;
assert_param(pRxData);
assert_param(pTxData);
HAL_StatusTypeDef retVal;
spifl_nCsSet(0);
retVal = HAL_SPI_TransmitReceive(spifl.handle, pTxData, pRxData, Size, SPIFL_TIMEOUT_MS);
spifl_nCsSet(1);
return retVal;
}
/*
* @brief Executes the reset sequence (RSTEN followed by RST) on the SST26VF032B.
* Has to be called in a thread.
* @return HAL_OK if spi send was succesfull.
*/
HAL_StatusTypeDef spifl_resetDevice()
{
if (e_spiflState_Uninitialized == spifl.state)
return HAL_ERROR;
HAL_StatusTypeDef retVal = HAL_OK;
// send RSTEN
osDelay(1); // need to add minimal delay before RSTEN
retVal += spifl_writeConfigByte(e_spiflCmd_RSTEN);
// send RST
osDelay(1); // need to add minimal delay before RST
retVal += spifl_writeConfigByte(e_spiflCmd_RST);
// final delay after whole reset procedure so other commands can work properly
osDelay(1);
return retVal;
}
/*
* @brief Use this function to write single operation instructions
* @param cmd: operation command.
*
* @return HAL_OK if sent correctly.
*/
HAL_StatusTypeDef spifl_writeConfigByte(uint8_t cmd)
{
uint8_t dummy;
return spifl_sendReceive(&cmd, &dummy, 1);
}
/*
* @brief Initializes the SPI flash memory module (MCU is communicating with SST26VF032B device).
* Single SPI peripheral is used by both motor modules so mutual exception has to be
* provided when sending or reading.
* In order to fully work with spi flash memory (use the write and read functions)
* the second init function has to be called in an RTOS thread.
* @param spiHandle: pointer to the spi struct representing this peripheral.
* @param gpio: pointer to a gpio pin struct that will be used as software #CS signal.
*
* @return HAL_OK if spi peripheral configured properly
*/
HAL_StatusTypeDef spifl_InitPreliminary(SPI_HandleTypeDef* spiHandle, gpioPin_t* gpio)
{
assert_param(spiHandle);
assert_param(gpio->gpioGroup);
assert_param(gpio->gpioNr);
HAL_StatusTypeDef retVal = HAL_OK;
spifl.state = e_spiflState_Uninitialized;
// HAL startup code does the basic config, no need to overlap it here.
// assign handlers
if (spiHandle)
spifl.handle = spiHandle;
else
retVal++;
if (gpio && gpio->gpioGroup && gpio->gpioNr)
spifl.nCS = gpio;
else
retVal++;
// create transmission semaphore
osSemaphoreDef_t tempDef;
spifl.sema = osSemaphoreCreate(&tempDef, 1);
if (!spifl.sema)
retVal++;
// set initialization flag
if (!retVal)
spifl.state = e_spiflState_FirstInitDone;
// set the nCS pin high
spifl_nCsSet(1);
assert_param(!retVal);
return retVal;
}
/*
* @brief Final initialization procedure. It should be called in a thread, thus delay and
* logging are allowed. It is used for finding the physical flash memory device on spi line.
*
* @return HAL_OK if flash memory is successfully identified.
*/
HAL_StatusTypeDef spifl_InitFinal()
{
if (spifl.state != e_spiflState_FirstInitDone)
{
log_PushLine(e_logLevel_Critical,
"Wrong spi module state (%u) in final init func!", spifl.state);
return HAL_ERROR;
}
osSemaphoreWait(spifl.sema, osWaitForever);
HAL_StatusTypeDef retVal = HAL_OK;
// reset the device
retVal += spifl_resetDevice();
// Unlock global block protection, WREN first
retVal += spifl_writeConfigByte(e_spiflCmd_WREN);
retVal += spifl_writeConfigByte(e_spiflCmd_ULBPR);
spifl.bufSend[0] = e_spiflCmd_WBPR;
memset(spifl.bufSend + 1, 0, 10);
retVal += spifl_sendReceive(spifl.bufSend, spifl.bufRecv, 11);
uint8_t status;
retVal += spifl_readStausReg(&status)
// now check either SST26VF032B is visible on spi line by reading 1st DWORD (32 bit) of SFDP header
spifl.bufSend[0] = e_spiflCmd_SFDP; // sfdp command
spifl.bufSend[1] = SPIFL_SFDP_SIGNATURE_ADDR >> 16; // MSB next 3 bytes are addr
spifl.bufSend[2] = SPIFL_SFDP_SIGNATURE_ADDR >> 8;
spifl.bufSend[3] = SPIFL_SFDP_SIGNATURE_ADDR; // LSB
retVal += spifl_sendReceive(spifl.bufSend, spifl.bufRecv, 9);
union32_t sfdpSig;
memcpy(sfdpSig.u8, &spifl.bufRecv[5], sizeof(uint32_t));
// compare the signature with datasheet one
if (sfdpSig.u32 != SPIFL_SFDP_SIGNATURE_VAL)
retVal++;
else
spifl.state = e_spiflState_FullyOperational;
// TODO temp
//uint8_t dummy[6];
//retVal += spifl_pageProgram(0, dummy, 6);
osSemaphoreRelease(spifl.sema);
return retVal;
}
/*
* @brief reads data from spi memory.
* @param startAddres: address in the memory starting from which read will be executed.
* @param readBuffer: address under which read bytes will be saved (incrementing the pointer).
* @param len: amount of bytes to read and save under \ref readBuffer.
*
* @return HAL_OK if data successfully read.
*/
HAL_StatusTypeDef spifl_read(uint32_t startAddres, uint8_t* readBuffer, uint32_t len)
{
if (spifl.state != e_spiflState_FullyOperational)
return HAL_ERROR;
assert_param(readBuffer);
const uint32_t readOffset = 4; // for clarity: cmd + 3 address bytes
// check if not exceeding internal buffer
if ((len + readOffset) > SPIFL_BUFFER_SIZE)
return HAL_ERROR;
// check if size is not zero
if (!len)
return HAL_OK; // nothing to read
// entering critical region
osSemaphoreWait(spifl.sema, osWaitForever);
HAL_StatusTypeDef retVal;
// set the read parameters
spifl.bufSend[0] = e_spiflCmd_Read;
spifl.bufSend[1] = (uint8_t)(startAddres >> 16); // MSB
spifl.bufSend[2] = (uint8_t)(startAddres >> 8);
spifl.bufSend[3] = (uint8_t)startAddres; // LSB
retVal = spifl_sendReceive(spifl.bufSend, spifl.bufRecv, len + readOffset);
if (!retVal) // on success copy the read data to the param buffer
memcpy(readBuffer, spifl.bufRecv + readOffset, len);
// leaving critical region
osSemaphoreRelease(spifl.sema);
return retVal;
}
/*
* @brief Uses Page program command in order to save data to the flash memory. The function
* handles in between pages data saving.
* @param startAddress: address from which the write will start
* @param writeBuffer: data to save.
* @param len: amount of bytes to save.
*
* @return HAL_OK if save successful.
*/
/*HAL_StatusTypeDef spifl_write(uint32_t startAddres, uint8_t* writeBuffer, uint32_t len)
{
if (spifl.state != e_spiflState_FullyOperational)
return HAL_ERROR;
assert_param(writeBuffer);
const uint32_t writeOffset = 4; // for clarity: cmd + 3 address bytes
// check if not exceeding internal buffer
if ((len + writeOffset) > SPIFL_BUFFER_SIZE)
return HAL_ERROR;
// check if size is not zero
if (!len)
return HAL_OK; // nothing to read
}*/
/*
* @brief Reads the status register from flash memory and saves it under \ref reg param.
* @param reg: pointer under which register will be saved.
*
* @return HAL_OK if status register successfully read.
*/
HAL_StatusTypeDef spifl_readStausReg(uint8_t* reg)
{
if (spifl.state == e_spiflState_Uninitialized)
return HAL_ERROR;
assert_param(reg);
HAL_StatusTypeDef retVal = HAL_OK;
spifl.bufSend[0] = e_spiflCmd_RDSR;
retVal = spifl_sendReceive(spifl.bufSend, spifl.bufRecv, 2);
if (!retVal && reg)
*reg = spifl.bufRecv[1]; // save register
return retVal;
}
/*
* @brief Function reads the status flag and returns \ref HAL_OK if there are no write
* operations pending and the write enable latch flag is set.
* @return HAL_OK if flags correct, otherwise timeout
*/
HAL_StatusTypeDef spifl_pollForWrite()
{
uint8_t status;
bool notReady;
// BUSY = 0, WEL = 1
for (uint32_t i = 0; i < SPIFL_TIMEOUT_MS; i++)
{
notReady = false;
if (spifl_readStausReg(&status))
return HAL_ERROR;
// check flags (divided on more if's for clarity)
if (status & e_spiflStatusReg_BUSY) // has to be cleared
notReady = true;
if (!(status & e_spiflStatusReg_WEL)) // has to be set
notReady = true;
if (notReady) // add delay only if necessary
osDelay(1);
else
return HAL_OK;
}
// timeout
return HAL_ERROR;
}
/*
* @brief Erases the selected sector
*/
/*HAL_StatusTypeDef spifl_eraseSector(uint32_t sectorAddress)
{
}*/
/*
* @brief Programs a single page (up to 256 bytes) starting from \ref startAddr. The command
* cannot exceed above single page boundary. If it would exceed, the function returns
* \ref HAL_ERROR and doesnt program anything.
* @param startAddress: programming start address.
* @param writeBuffer: data to program.
* @param len: amount of data (256 bytes max) to program.
*
* @return HAL_OK if programming succesfull.
*/
HAL_StatusTypeDef spifl_pageProgram(uint32_t startAddres, uint8_t* writeBuffer, uint32_t len)
{
assert_param(writeBuffer);
// check if len is not too long or 0
if ((len > SPIFL_PAGE_SIZE) || (!len))
return HAL_ERROR;
// check if page program would not exceed the page boundary
if ((startAddres + len - 1) / SPIFL_PAGE_SIZE) // non zero means overflow
return HAL_ERROR;
HAL_StatusTypeDef retVal = HAL_OK;
// at this point it is known that the address and the length are ok
// set write enable
retVal += spifl_writeConfigByte(e_spiflCmd_WREN);
// wait until there are no internal write operations and device is write enabled
retVal += spifl_pollForWrite();
return retVal;
}
/* SPI2 init function */
void MX_SPI2_Init(void)
{
hspi2.Instance = SPI2;
hspi2.Init.Mode = SPI_MODE_MASTER;
hspi2.Init.Direction = SPI_DIRECTION_2LINES;
hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi2.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi2.Init.NSS = SPI_NSS_SOFT;
hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi2) != HAL_OK)
{
Error_Handler();
}
}
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?