Proper way to read/write to SPI Flash

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Proper way to read/write to SPI Flash

1,205 Views
azone
Contributor I

Using RT1050 but this is generic to RT's. I'm new to embedded programming  and trying to understand how to read and write to SPI flash. I'm using a Winbond 128Mbit but also generic to any flash manufacturer. I have already written some working drivers for I2C and SPI making use of the RT1050 SDK so decent amount of experience there. With my other SPI implementation it's just a DAC with a fixed framesize of 32bits. Not too complicated. This time I have 

1) command functions with read-backs

2) various sized read/writes up to 256MB for flash.  

for 1) I'm not sure on how my transfer function should be done. For example I just want to read back the flash device ID to start. This requires a command byte followed by three dummy bytes and then two more dummy bytes for reading the device ID (so device ID is bytes 5 and 6).  So I'm sending 6 bytes. I've been unable to do this successfully, I think basically because of improper handling of the /CS line as in my current function the /CS is toggled after every byte. In this case do I simply set the framesize to 48bits so that the select /CS line is low for the whole transfer? Do I treat the command byte any differently than the data bytes? Obviously I know I have to have cpol and cpha set properly and enable SPI reads and writes with the appropriate flags, and well as have the CONT and CONTC bits set properly for what I'm trying to do. 


2) for sending larger chunks do I simply set the frame size accordingly? The rt1050 says it supports SPI frame sizes up to 256 bytes. Also, how does the TxFIFO come into play. It's apparently 16 32bit words in size. For sending say 256bytes do I simply check and make sure data has been transferred successfully and make sure the FIFO (effectively 64bytes) hasn't overflowed before sending next batch of data? 

0 Kudos
Reply
5 Replies

1,164 Views
azone
Contributor I

Thanks. Yes I used those examples to write my other SPI driver for a DAC. This application is different. My question is the term FRAMESIZE. In relation to my SPI drivers for flash I need to handle data in an appropriate framesize, but I am confused on the relation of framesize to the length of the command/data I am sending or receiving.

Example 1: reading flash Device ID requires 6 bytes

Example 2: reading a 4kb sector requires 4kb data + 4bytes command/address 

In the SPI drivers examples framesize is directly linked to how long the PCS is held low. However for communication with flash the PCS pin needs to be held low for the ENTIRE command (6bytes in example 1 and 4kb + 4bytes in example 2).  So if I use an SPI frame size of 8bits I cannot communicate with flash as PCS is pulled high prematurely. I have tried using the CONT bit to continue the transaction from the first bytes the final byte, while keeping framesize to 8bits, but it doesn't work. 

so in terms of the SPI drivers am I supposed to be concerned with FRAMESIZE as the the size of the entire transaction? Or am I supposed to be keeping framesize 8bits or 32bits etc... and somehow keeping the hardware PCS low for as long as needed using the CONT bit (even though no success  with this so far)?

0 Kudos
Reply

1,147 Views
azone
Contributor I

for more specifics here are two example functions of reading the Winbond W25Q128JV JEDEC ID.

1) Function ReadFlash. Framesize set to 32 bits. JEDEC ID command 0x9F and three dummy bytes all sent in one frame. Works fine, but can't increase the framesize indefinitely when comes time to read or write large blocks of data (but we confirmed the hardware is working).  Also pic from analyzer showing correct read.  

uint32_t JEDEC = 0x9F000000;
void FLASH::ReadJEDEC()
{
	lpspi3.ReadFlash(JEDEC, 32); //pass JEDEC command + 3 dummy bytes directly
}
void HardwareSPI::ReadFlash(const uint32_t data, uint32_t framesize)
{
	if (framesize != _framesize) // update TCR if new framesize 
	{
	    _framesize = framesize;
	    base->TCR = (base->TCR & ~LPSPI_TCR_FRAMESZ_MASK) | LPSPI_TCR_FRAMESZ(framesize - 1);
	    // wait for TCR update
	    while (LPSPI_GetTxFifoCount(base) != 0)
	    {
	    }
	}

    LPSPI_WriteData(base, data); // Write 32-bit data to the transmit data register (TDR) 

    while (LPSPI_GetTxFifoCount(base) > 0) {} // Wait for the transfer to complete 
    while (LPSPI_GetRxFifoCount(base) == 0) {} // Wait until there is data in the receive FIFO 
 
    uint32_t readData = LPSPI_ReadData(base); // Read the received data from the receive data register (RDR) 
}

SCR188.PNG

2) Function ReadFlashV2. Framesize set to 8 bits. JEDEC command sent as array of 4 bytes. The CONT bit (continuous transfer) not working, PCS never stays low, it always toggles high between bytes even when CONT used as described on page 2778 of the iMX RT1050 Reference Manual. How am I supposed to write or erase large blocks of data? Should I try disable hardware control of the PCS pin and set it up as GPIO? In this case I guess I would also have to handle PCS to CLK delays myself (using hardware PCS the API handles this already). 

uint8_t JEDECV2[4] = {0xF9, 0x00, 0x00, 0x00};
void FLASH::ReadJEDECV2()
{
	lpspi3.ReadFlashV2(JEDECV2, 4); //pass JEDEC command + 3 dummy bytes
}
void HardwareSPI::ReadFlashV2(const uint8_t* data, uint32_t len)
{
    for (uint8_t i = 0; i < len; ++i)
    {
        if (i == len - 1)
        {
            base->TCR &= ~LPSPI_TCR_CONT_MASK; // Clear CONT for the last byte
        }
        else
        {
            base->TCR |= LPSPI_TCR_CONT_MASK; // Set CONT for continuous transfer
        }

        LPSPI_WriteData(base, data[i]); // Write data to the transmit data register (TDR)

        while (LPSPI_GetTxFifoCount(base) > 0) {}
        while (LPSPI_GetRxFifoCount(base) == 0) {}

        LPSPI_ReadData(base); // Read the received data from the receive data register (RDR)
    }
}

 SCR189.PNG

0 Kudos
Reply

1,145 Views
azone
Contributor I

I have confirmed the second function 2) above works if I take over control to the PCS pin and set it up as a GPIO. In this case you would have to make sure you set appropriate delays between asserting and deasserting the PCS pin before and after clocks and between transfers etc.. the hardware LPSPI and API handles this if using the LPSPI modules kLPSPI_Pcs but in this case you are on your own.

I still want to know the correct way to use the CONT bit to allow continuous stream of frames while using the dedicated kLPSPI_Pcs?

SCR190.PNG 

0 Kudos
Reply

1,103 Views
Harry_Zhang
NXP Employee
NXP Employee

Hi @azone 

Abort the CONT bit, We can only refer to i.MX RT1050 Processor Reference
Manual 47.3.1.

Harry_Zhang_0-1735807279510.png

BR

Harry

0 Kudos
Reply

1,170 Views
Harry_Zhang
NXP Employee
NXP Employee

Hi @azone 

Based on your needs, I think you can refer to the lpspi examples in the SDK.

Harry_Zhang_0-1735610415719.png

 

BR

Harry

0 Kudos
Reply