Skip to main content
Associate III
March 19, 2025
Solved

Interfacing Nucleo F767ZI Board with W25Q256JV using QSPI

  • March 19, 2025
  • 4 replies
  • 5304 views

Hi Everyone,

I interfaced Nucleo F767ZI with W25Q256JV using SPI and everything was working fine. But due to time and speed requirements, now I have to shift it to QSPI. I have used example code of STM Document named "QSPI_F769_discovery_flashloader" and developed a code for QSPI. But whenever I try to run the code and write/read the memory, nothing happens. I started to run code in debug on STMCube IDE and found that when try to send command to enable write and read status register, the register value doesn't change to write enable (0x02).

Always fail here and Error LED turns ON.

if (HAL_QSPI_AutoPolling(&hqspi, &sCommand, &sConfig, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
return QSPI_ERROR;
}

The Write Enable function is here.

uint8_t QSPI_WriteEnable(void) {

QSPI_CommandTypeDef sCommand;

QSPI_AutoPollingTypeDef sConfig;



memset(&sCommand, 0, sizeof(sCommand));

memset(&sConfig, 0, sizeof(sConfig));



/* Enable write operations ------------------------------------------ */

sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;

sCommand.Instruction = WRITE_ENABLE_CMD;

sCommand.AddressMode = QSPI_ADDRESS_NONE;

sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;

sCommand.DataMode = QSPI_DATA_NONE;

sCommand.DummyCycles = 0;

sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;

sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;

sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;



if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)

!= HAL_OK) {

return QSPI_ERROR;

}



/* Configure automatic polling mode to wait for write enabling ---- */

sConfig.Match = 0x02;

sConfig.Mask = 0x02;

sConfig.MatchMode = QSPI_MATCH_MODE_AND;

sConfig.StatusBytesSize = 1;

sConfig.Interval = 0x10;

sConfig.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE;

sCommand.Instruction = READ_STATUS_REG_CMD;

sCommand.DataMode = QSPI_DATA_1_LINE;



if (HAL_QSPI_AutoPolling(&hqspi, &sCommand, &sConfig,

HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {

HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);

return QSPI_ERROR;

}

return QSPI_OK;

}

Below is my attached qspi.h and qspi.c files and CubeMX configuration.

Capture.PNG

Kindly help me, what is the problem and if possible provide me a .c .h file for QSPI it's urgent.

Looking forward to suitable response, thanks in advance.

Best answer by jumman_JHINGA

hii @NajeebUllahKhan 

Try following files ... there you may need to change flash size or other some parameters.

To test your quad spi is working or not take reference of following code 


void testQSPI(){
	QSPI_CommandTypeDef s_command;
	uint8_t pData[3];
	uint8_t wData[0x100];
	uint8_t rData[0x100];
	uint32_t i;
	printf("***************QuadSPi Example*******************************\r\n");
	BSP_QSPI_Init();

	/*##-2-Read Device ID Test ###########################################*/
	/* Read Manufacture/Device ID */
	s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
 s_command.Instruction = READ_ID_CMD;
 s_command.AddressMode = QSPI_ADDRESS_1_LINE;
 s_command.AddressSize = QSPI_ADDRESS_24_BITS;
 s_command.Address = 0x000000;
 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
 s_command.DataMode = QSPI_DATA_1_LINE;
 s_command.DummyCycles = 0;
 s_command.NbData = 2;
 s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	
 if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
 {
		Error_Handler();
 }
	if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
 {
		Error_Handler();
 }
	printf("SPI I/0 Read Device ID : 0x%2X 0x%2X\r\n",pData[0],pData[1]);
	
	
	/* Read Manufacture/Device ID Dual I/O*/
	s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
 s_command.Instruction = DUAL_READ_ID_CMD;
 s_command.AddressMode = QSPI_ADDRESS_2_LINES;
 s_command.AddressSize = QSPI_ADDRESS_24_BITS;
 s_command.Address = 0x000000;
 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_2_LINES;
	s_command.AlternateBytesSize= QSPI_ALTERNATE_BYTES_8_BITS;
	s_command.AlternateBytes = 0;
 s_command.DataMode = QSPI_DATA_2_LINES;
 s_command.DummyCycles = 0;
 s_command.NbData = 2;
 s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	
 if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
 {
		Error_Handler();
 }
	if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
 {
		Error_Handler();
 }
	printf("Dual I/O Read Device ID : 0x%2X 0x%2X\r\n",pData[0],pData[1]);
	
	/* Read Manufacture/Device ID Quad I/O*/
	s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
 s_command.Instruction = QUAD_READ_ID_CMD;
 s_command.AddressMode = QSPI_ADDRESS_4_LINES;
 s_command.AddressSize = QSPI_ADDRESS_24_BITS;
 s_command.Address = 0x000000;
 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_4_LINES;
	s_command.AlternateBytesSize= QSPI_ALTERNATE_BYTES_8_BITS;
	s_command.AlternateBytes = 0x00;
 s_command.DataMode = QSPI_DATA_4_LINES;
 s_command.DummyCycles = 4;
 s_command.NbData = 2;
 s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	
 if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
 {
		Error_Handler();
 }
	if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
 {
		Error_Handler();
 }
	printf("Quad I/O Read Device ID : 0x%2X 0x%2X\r\n",pData[0],pData[1]);

	/* Read JEDEC ID */
	s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
 s_command.Instruction = READ_JEDEC_ID_CMD;
 s_command.AddressMode = QSPI_ADDRESS_NONE;
 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
 s_command.DataMode = QSPI_DATA_1_LINE;
 s_command.DummyCycles = 0;
 s_command.NbData = 3;
 s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	
 if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
 {
		Error_Handler();
 }
	if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
 {
		Error_Handler();
 }
	printf("Read JEDEC ID : 0x%2X 0x%2X 0x%2X\r\n\r\n",pData[0],pData[1],pData[2]);
	
	/*##-3-QSPI Erase/Write/Read Test ###########################################*/
	/* fill buffer */
	for(i =0;i<0x100;i ++)
	{
			wData[i] = i;
		 rData[i] = 0;
	}

	if(BSP_QSPI_Erase_Block(0) == QSPI_OK)
		printf(" QSPI Erase Block ok\r\n");
	else
		Error_Handler();
	
	if(BSP_QSPI_Write(wData,0x00,0x100)== QSPI_OK)
		printf(" QSPI Write ok\r\n");
	else
		Error_Handler();
	
	if(BSP_QSPI_Read(rData,0x00,0x100)== QSPI_OK)
		printf(" QSPI Read ok\r\n\r\n");
	else
		Error_Handler();
	
	printf("QSPI Read Data : \r\n");
	for(i =0;i<0x100;i++)
		printf("0x%02X ",rData[i]);
	printf("\r\n\r\n");
	
	for(i =0;i<0x100;i++)
		if(rData[i] != wData[i])printf("0x%02X 0x%02X ",wData[i],rData[i]);
	printf("\r\n\r\n");
	/* check date */
	if(memcmp(wData,rData,0x100) == 0 ) 
		printf(" W25Q128FV QuadSPI Test OK\r\n");
	else
		printf(" W25Q128FV QuadSPI Test False\r\n");

}

 

4 replies

jumman_JHINGA
jumman_JHINGABest answer
Senior III
March 19, 2025

hii @NajeebUllahKhan 

Try following files ... there you may need to change flash size or other some parameters.

To test your quad spi is working or not take reference of following code 


void testQSPI(){
	QSPI_CommandTypeDef s_command;
	uint8_t pData[3];
	uint8_t wData[0x100];
	uint8_t rData[0x100];
	uint32_t i;
	printf("***************QuadSPi Example*******************************\r\n");
	BSP_QSPI_Init();

	/*##-2-Read Device ID Test ###########################################*/
	/* Read Manufacture/Device ID */
	s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
 s_command.Instruction = READ_ID_CMD;
 s_command.AddressMode = QSPI_ADDRESS_1_LINE;
 s_command.AddressSize = QSPI_ADDRESS_24_BITS;
 s_command.Address = 0x000000;
 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
 s_command.DataMode = QSPI_DATA_1_LINE;
 s_command.DummyCycles = 0;
 s_command.NbData = 2;
 s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	
 if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
 {
		Error_Handler();
 }
	if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
 {
		Error_Handler();
 }
	printf("SPI I/0 Read Device ID : 0x%2X 0x%2X\r\n",pData[0],pData[1]);
	
	
	/* Read Manufacture/Device ID Dual I/O*/
	s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
 s_command.Instruction = DUAL_READ_ID_CMD;
 s_command.AddressMode = QSPI_ADDRESS_2_LINES;
 s_command.AddressSize = QSPI_ADDRESS_24_BITS;
 s_command.Address = 0x000000;
 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_2_LINES;
	s_command.AlternateBytesSize= QSPI_ALTERNATE_BYTES_8_BITS;
	s_command.AlternateBytes = 0;
 s_command.DataMode = QSPI_DATA_2_LINES;
 s_command.DummyCycles = 0;
 s_command.NbData = 2;
 s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	
 if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
 {
		Error_Handler();
 }
	if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
 {
		Error_Handler();
 }
	printf("Dual I/O Read Device ID : 0x%2X 0x%2X\r\n",pData[0],pData[1]);
	
	/* Read Manufacture/Device ID Quad I/O*/
	s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
 s_command.Instruction = QUAD_READ_ID_CMD;
 s_command.AddressMode = QSPI_ADDRESS_4_LINES;
 s_command.AddressSize = QSPI_ADDRESS_24_BITS;
 s_command.Address = 0x000000;
 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_4_LINES;
	s_command.AlternateBytesSize= QSPI_ALTERNATE_BYTES_8_BITS;
	s_command.AlternateBytes = 0x00;
 s_command.DataMode = QSPI_DATA_4_LINES;
 s_command.DummyCycles = 4;
 s_command.NbData = 2;
 s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	
 if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
 {
		Error_Handler();
 }
	if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
 {
		Error_Handler();
 }
	printf("Quad I/O Read Device ID : 0x%2X 0x%2X\r\n",pData[0],pData[1]);

	/* Read JEDEC ID */
	s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
 s_command.Instruction = READ_JEDEC_ID_CMD;
 s_command.AddressMode = QSPI_ADDRESS_NONE;
 s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
 s_command.DataMode = QSPI_DATA_1_LINE;
 s_command.DummyCycles = 0;
 s_command.NbData = 3;
 s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
 s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
 s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
	
 if (HAL_QSPI_Command(&hqspi, &s_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
 {
		Error_Handler();
 }
	if (HAL_QSPI_Receive(&hqspi, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
 {
		Error_Handler();
 }
	printf("Read JEDEC ID : 0x%2X 0x%2X 0x%2X\r\n\r\n",pData[0],pData[1],pData[2]);
	
	/*##-3-QSPI Erase/Write/Read Test ###########################################*/
	/* fill buffer */
	for(i =0;i<0x100;i ++)
	{
			wData[i] = i;
		 rData[i] = 0;
	}

	if(BSP_QSPI_Erase_Block(0) == QSPI_OK)
		printf(" QSPI Erase Block ok\r\n");
	else
		Error_Handler();
	
	if(BSP_QSPI_Write(wData,0x00,0x100)== QSPI_OK)
		printf(" QSPI Write ok\r\n");
	else
		Error_Handler();
	
	if(BSP_QSPI_Read(rData,0x00,0x100)== QSPI_OK)
		printf(" QSPI Read ok\r\n\r\n");
	else
		Error_Handler();
	
	printf("QSPI Read Data : \r\n");
	for(i =0;i<0x100;i++)
		printf("0x%02X ",rData[i]);
	printf("\r\n\r\n");
	
	for(i =0;i<0x100;i++)
		if(rData[i] != wData[i])printf("0x%02X 0x%02X ",wData[i],rData[i]);
	printf("\r\n\r\n");
	/* check date */
	if(memcmp(wData,rData,0x100) == 0 ) 
		printf(" W25Q128FV QuadSPI Test OK\r\n");
	else
		printf(" W25Q128FV QuadSPI Test False\r\n");

}

 

Associate III
March 19, 2025

I have tested it and made some changes but still not working. When I used the "void testQSPI()" function to check if QSPI is working, it is not working as "static uint8_t QSPI_AutoPollingMemReady(uint32_t Timeout)" this function give QSPI_ERROR on the line 

if (HAL_QSPI_AutoPolling(&QSPIHandle, &s_command, &s_config, Timeout) != HAL_OK)
{
 return QSPI_ERROR;
}

in QSPI_AutoPollingMemReady(uint32_t Timeout).

below is my attached updated files. and debug results.

Looking forward to your response...

Capture.PNG

mƎALLEm
Technical Moderator
March 19, 2025

Hello @NajeebUllahKhan,

Please use </> button to share your code. 

See this post.

Thank you for your understanding.

To give better visibility on the answered topics, please click "Best answer" on the reply which solved your issue or answered your question.
Associate III
March 19, 2025

@jumman_JHINGA 

OK, let me check and I'll update my code accordingly and then will let you know.

Is the code you provided has tested before?

jumman_JHINGA
Senior III
March 20, 2025

@NajeebUllahKhan wrote:

Is the code you provided has tested before?


Yes it is tested. make shure you have configured QUAD SPI properly in the cubeMX. it should be like this:

Quad SPI W25Qxx ConfigQuad SPI W25Qxx Config

 

Associate III
March 20, 2025

@jumman_JHINGA 

I am getting 0 0 0 on reading ID and when compare wdata, rdata, it goes into else and rdata shows 0 0 0 ..., below is all details. Now one thing is confirmed that the code runs fully without any error but now problem is, chip is not giving any information like ID etc which always is 0 0 0 etc. I am using W25Q256JV 32MB with Nucleo F767ZI with HSI CLK Source and with 216 MHz. Find all details below.

I am using following PINS:

CS -> PB10, CLK -> PB2, I0 -> PD11, I1 -> PD12, I2 (WP) -> PE2, I3 (HLD) -> PD13, rest I am using your files without any change and your test function.

Kindly assist me in this, it's very urgent for me.

Capture.PNG

Capture.PNG

Tesla DeLorean
Guru
March 21, 2025

QSPI_CommandTypeDef s_command = {0}; // clear the auto/local variable in all instances

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
Associate III
March 22, 2025

@jumman_JHINGA @Tesla DeLorean

I am following this link "https://os.mbed.com/platforms/ST-Nucleo-F767ZI/" to check out connection on my F767ZI board. As there is only QUADSPI in this board which can be mapped on the pins highlighted in the link. 

I am using following PINS:

CS -> PB10, CLK -> PB2, I0 -> PD11, I1 -> PD12, I2 (WP) -> PE2, I3 (HLD) -> PD13, comparing this with pins mentioned in the link, do you see any connection problem on F767ZI board side? If not, then maybe it's on the flash side. Highlighted pins in the picture are used.

Capture.PNG

QSPI configurations are below.

 

Capture.PNG

 

Sys Clk configurations are below.

Capture.PNG

These are all my configurations. Rest I am using all yours code without any change.

1) Does Sys Clk configuration have any error?

2) Does there is any connection issue on Board side like wrong pin for QSPI?

3) Does QSPI software configuration have any error?

4) If I use same code for 32MB flash, will it work (because commands are same throughout winbond W25Q series right?)? If yes, then upto 16MB region.

Looking forward to your assistance...

 

Tesla DeLorean
Guru
March 22, 2025

I tend to use PB6 rather than PB10 on NUCLEO-144 platforms, but should work, not sure if there are other nodes/stubs on that, or one vs the other.

>16MB would typically need to use the 4-byte (32-bit) addressing mode, rather than 3-byte (24-bit)

So W25Q256 code might not work on W25Q128 parts.

Could try this as an External Loader in STM32 Cube Programmer to test/check implementation

https://github.com/cturvey/stm32extldr/blob/main/h7_w25q256/CLIVEONE-W25Q256_STM32H7XX-PB2-PB10-PD11-PD12-PE2-PD13.stldr

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..