Working with STM32 and External Flash W25QXX Part2: Reading data

In the previous guide (here), we took a look at the W25QXX chip and it’s feature and how to reset the chip and get the full ID.

In this second part, we shall see how the read process is handled and how to read multiple data from the sectors.

In this guide, we shall cover the following:

  • The process of reading.
  • Getting the correct position.
  • Developing the driver.
  • Main code.
  • Results.

1. The process of reading:

The process of reading as following:

  • Set the CS pin low.
  • Send the instruction 0x03h.
  • Then send the 24-bit or 32-bit address (depending on the size).
  • After the address has been written, the data shall be shifted with MSB first and stream of the data will be available as long as the CLK is available.

2. Getting the correct position:

The memory layout of W25QXX is as following:

W25Q32 is consist of 64 blocks of memory (block 0 to block 63). Each block consists of 256 Byte of data as shown below:

Hence, to get the address of the data to be read, the following formula is utilised:

uint32_t memAddr = (sector*256) + offset; /*Calculate the address */

the sector is the block number, offset the is data position to be read.

3. Developing the driver:

We start off by the header file.

Open the W25QXX.h header file and include the following new functions:

/*
 * @brief This function will set the number of sectors for W25QXX.
 * @param numOfSector is the number of sectors available in W25QXX.
 * @return nothing
 * @see W25QXX datasheet for the number of sectors.
 *
 * */
void W25QXX_NumberOfSector(uint16_t numOfSector);

This function will set the number of the sector and takes number of sector as argument and returns nothing.

The second functions:

/*
 * @brief This function will read from W25QXX.
 * @param sector is the sector number of the flash
 * @param offset is the offset of the memory location to be read.
 * @param *rData is a pointer to buffer that will hold the data to be read.
 * @param size if the data to be read.
 * @return nothing
 *
 * */
void W25QXX_Read(uint32_t sector, uint8_t offset, uint8_t *rData, uint32_t size);

This function will read from the desired address and it takes the following:

  • Sector number which is the sector number.
  • Offset, offset from the sector to be read.
  • Pointer to buffer to hold the data to be read.
  • Size of the data to be read.

Thats all for the header file.

Now, open W25QXX.c source file.

Declare new global variable as following:

uint16_t NumberOfSector;

This will hold the number of sectors within the W25QXX.

Now, for the set number of sector function which is as simple as setting the global variable to same value passed as argument as following:

void W25QXX_NumberOfSector(uint16_t numOfSector)
{
	NumberOfSector=numOfSector;
}

For the read function:

void W25QXX_Read(uint32_t sector, uint8_t offset, uint8_t *rData, uint32_t size)
{

We start with declare an array to hold the command and the address to be send as following:

uint8_t txData[6]; /*This will hold the command and address to be send*/

variable to hold the address:

uint32_t memAddr = (sector*256) + offset; /*Calculate the address */

Set the CS pin low:

W25QXX_CS_LOW();

Now, if the sector size is less than 512 which is for flash size less than 256Mb (32MB):

	if (NumberOfSector<512) /*less than 256Mb*/
	{
		txData[0] = 0x03;  // enable Read
		txData[1] = (memAddr>>16)&0xFF;  // MSB of the memory Address
		txData[2] = (memAddr>>8)&0xFF;
		txData[3] = (memAddr)&0xFF; // LSB of the memory Address

		SPI1_Transmit(txData,4);
	}

In case bigger than 512 (32MB):

	else /*bigger than 256Mb*/
	{
		txData[0] = 0x13;  // Read Data with 4-Byte Address
		txData[1] = (memAddr>>24)&0xFF;  // MSB of the memory Address
		txData[2] = (memAddr>>16)&0xFF;
		txData[3] = (memAddr>>8)&0xFF;
		txData[4] = (memAddr)&0xFF; // LSB of the memory Address

		SPI1_Transmit(txData,5);
	}

After sending the command and address, start receiving the data following:

SPI1_Receive(rData, size);

Set CS pin to high:

W25QXX_CS_HIGH();

Hence, the function as following:

void W25QXX_Read(uint32_t sector, uint8_t offset, uint8_t *rData, uint32_t size)
{
	uint8_t txData[6]; /*This will hold the command and address to be send*/
	uint32_t memAddr = (sector*256) + offset; /*Calculate the address */

	W25QXX_CS_LOW();

	if (NumberOfSector<512) /*less than 256Mb*/
	{
		txData[0] = 0x03;  // enable Read
		txData[1] = (memAddr>>16)&0xFF;  // MSB of the memory Address
		txData[2] = (memAddr>>8)&0xFF;
		txData[3] = (memAddr)&0xFF; // LSB of the memory Address

		SPI1_Transmit(txData,4);
	}

	else /*bigger than 256Mb*/
	{
		txData[0] = 0x13;  // Read Data with 4-Byte Address
		txData[1] = (memAddr>>24)&0xFF;  // MSB of the memory Address
		txData[2] = (memAddr>>16)&0xFF;
		txData[3] = (memAddr>>8)&0xFF;
		txData[4] = (memAddr)&0xFF; // LSB of the memory Address

		SPI1_Transmit(txData,5);
	}

	SPI1_Receive(rData, size);
	W25QXX_CS_HIGH();
}

4. Main code:

In main.c code:

Define the ID of W25Q32 as following:

#define W25Q32_ID	(uint32_t)0xEF4016

Declare an array to hold the data to be read:

uint8_t rxData[10];

In main function, after the initialization of SPI, reset the chip and getting the ID, we shall check if the ID is matching or not:

	/*If the ID is matched read the data*/
	if(W25QXX_ID==W25Q32_ID) 

Read the flash as following:

		/*Do something here*/
		W25QXX_Read(0,0,rxData,10);

		W25QXX_Read(10,10,rxData,10);

		W25QXX_Read(20,20,rxData,10);

Hence, the main code as following:

#include "delay.h"
#include "SPI1.h"
#include "W25QXX.h"

uint32_t W25QXX_ID;
#define W25Q32_ID	(uint32_t)0xEF4016

uint8_t rxData[10];


int main(void)
{	delay_init(16000000);

	SPI1_Pins_Init();
	W25QXX_CS_Pins_Init();
	SPI1_Config();

	W25QXX_Reset();
	delay(10);

	W25QXX_ID=W25QXX_ReadID();
	
	/*If the ID is matched read the data*/
	if(W25QXX_ID==W25Q32_ID) 
	{
		/*Do something here*/
		W25QXX_Read(0,0,rxData,10);

		W25QXX_Read(10,10,rxData,10);

		W25QXX_Read(20,20,rxData,10);

	}


	while(1)
	{


	}

}

5. Results:

Start a debugging session and add rxData to the live expression and run the code and you should get the following:

We have successfully read multiple data in different sectors of the W25QXX. Since the flash is empty, it shows 0xFF.

Next, we shall start writing data to the flash.

Happy coding.

Add Comment

Your email address will not be published. Required fields are marked *