Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

SST26VF032B operation problem

Status
Not open for further replies.

bremenpl

Member level 3
Joined
Jan 5, 2013
Messages
63
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,286
Activity points
1,827
Hello there,
I am writing a library at the moment to interface my STM32 mcu with SST26VF032B memory. I have a problem that I cannot solve for over 2 days now...
I am talking to the chip using regular SPI, not quad SPI. I am able to read JEDEC-ID as well as RDSR register. This ensures me in the fact that the communication layer works correctly. The problem is I am not able to read status register (RDSR command) and configuration register (RDCR command). The chip simply does not answer (0xFF's on the MISO line), I see this on the scope. I dont understand why doesnt he want to answer me with status register. This is my initialization procedure:

Code:
RSTEN (reset enable)
RST (reset)
WREN (write enable)
ULBPR (Global Block Protection Unlock)
SFDP (signature is read correctly)
RSDR (cannot read the status register)

Because I cannot read status register, I am not able to move forward with erasing and page programming.
I am really out of ideas now. I have found this topic where one user posted his library cod for this chip and mine is very simmilar, should work the same.
https://www.edaboard.com/threads/361867/
The #WP and #HOLD pins in the chip are tird to VCC all the time.

I have found in the web that many people had the problem that they could read JEDEC-ID but couldnt read status register. In those topics however there was no resolution posted, or the resolution was to clear the block protection bits. I am executing this command and it does not help me.

I would really appreciate all help regarding this issue.
 

If you referencing to my code, better ask in the same topic. No point to create a new one.
 

The topic is about something else really, I just wanted to point out that the configuration is alright. My problem is that The only instructions that work for me are the Identification instructions. I cannot read status register, config register or block protection register. I think I tried everything now, I even replaced the flash chip out of desperation, obviously didnt help...
 

I use my own code. I have only found the topic with your code and verified that I do everything correctly.
In my situation the SST26VF032B chip doesnt output anything on MISO line after asking him for status register etc. He only answers for identification commands.
 

I can post the code, it is not a problem. The thing is some of the commands work, so I am assuming the code works and I am doing something wrong with the configuration.

This is the function that should do the initialization of the chip communication:
Code:
/*
 * @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;

}

In this code I do the following:
Code:
RSTEN 
RST 
WREN 
ULBPR 
SFDP 
RSDR

The last command which is read status registers doesnt work, the memory chip does not answer. SFDP works, the rest I cannot confirm as it is a write without any ACK.
Here is the code for used relevant functions:
header:
Code:
/*
 * 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_ */

c file:
Code:
/*
 * 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;
}

At the lowest layer I am using the STM32 HAL library for SPI communication. I am not using interrupts nor DMA, I am sending/ reading in blocking mode.
Also here is the SPI initialization code generated with mxCUBE:
Code:
/* 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();
  }

}

I am using 8 bit wide communication, MSB first, CLK line idle state = 0, reading data on CLK rising edge.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top