cancel
Showing results for 
Search instead for 
Did you mean: 

QSPI nor flash write data with 2-bytes address offset!

MiladChalipa
Senior

I successfully initialize W25Q256 nor flash with STM32F746 MCU through Quad SPI protocol. Every command works fine (write memory, write Enable, erase sector, read and write status registers)

1-But when I debug the code and inspect the memory address 0x90000000, the data is written 2-bytes earlier (from 0x8FFFFFE memory address)

2-And chip-erase command (C7h) is not working. this is weird because the sector-erase command just works fine! I checked the manufacturer datasheet several times but no luck with the erase-command to get to work.

I uploaded the codes and memory address views to maybe experts in the community to help me out. note that the raw code is copied from ST public storage on GitHub with slight changes according to the Winbond flash datasheet.

uint8_t
CSP_QSPI_WriteMemory(uint8_t* buffer, uint32_t address, uint32_t buffer_size) {
 
    QSPI_CommandTypeDef sCommand;
    uint32_t end_addr, current_size, current_addr;
 
    /* Calculation of the size between the write address and the end of the page */
    current_addr = 0;
 
 
    //
    while (current_addr <= address) {
        current_addr += MEMORY_PAGE_SIZE;
    }
    current_size = current_addr - address;
 
    /* Check if the size of the data is less than the remaining place in the page */
    if (current_size > buffer_size) {
        current_size = buffer_size;
    }
 
    /* Initialize the adress variables */
    current_addr = address;
    end_addr = address + buffer_size;
 
    sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    sCommand.AddressSize = QSPI_ADDRESS_32_BITS;
    sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
    sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
    sCommand.Instruction = QUAD_IN_FAST_PROG_CMD;
    sCommand.AddressMode = QSPI_ADDRESS_1_LINE;
    sCommand.DataMode = QSPI_DATA_4_LINES;
    sCommand.NbData = buffer_size;
    sCommand.Address = address;
    sCommand.DummyCycles = 0;
 
    /* Perform the write page by page */
    do {
        sCommand.Address = current_addr;
        sCommand.NbData = current_size;
 
        if (current_size == 0) {
            return HAL_OK;
        }
 
        /* Enable write operations */
        if (QSPI_WriteEnable() != HAL_OK) {
            return HAL_ERROR;
        }
 
        /* Configure the command */
        if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
            != HAL_OK) {
 
            return HAL_ERROR;
        }
 
        /* Transmission of the data */
        if (HAL_QSPI_Transmit(&hqspi, buffer, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
 
            return HAL_ERROR;
        }
 
        /* Configure automatic polling mode to wait for end of program */
        if (QSPI_AutoPollingMemReady() != HAL_OK) {
            return HAL_ERROR;
        }
 
        /* Update the address and size variables for next page programming */
        current_addr += current_size;
        buffer += current_size;
        current_size =
            ((current_addr + MEMORY_PAGE_SIZE) > end_addr) ?
            (end_addr - current_addr) : MEMORY_PAGE_SIZE;
    } while (current_addr <= end_addr);
 
    return HAL_OK;
 
}
 
uint8_t
CSP_QSPI_Erase_Chip(void) {
    QSPI_CommandTypeDef sCommand;
 
 
    /* Erasing Sequence --------------------------------- */
    sCommand.Instruction = CHIP_ERASE_CMD;
    sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    sCommand.AddressSize = QSPI_ADDRESS_32_BITS;
    sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
    sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
    sCommand.AddressMode = QSPI_ADDRESS_NONE;
    sCommand.Address = 0;
    sCommand.DataMode = QSPI_DATA_NONE;
    sCommand.DummyCycles = 0;
 
 
    if (QSPI_WriteEnable() != HAL_OK) {
        return HAL_ERROR;
    }
 
 
    if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
        != HAL_OK) {
        return HAL_ERROR;
    }
 
    if (QSPI_AutoPollingMemReady() != HAL_OK) {
        return HAL_ERROR;
    }
 
    return HAL_OK;
}

0693W000006GuREQA0.png

1 ACCEPTED SOLUTION

Accepted Solutions
MiladChalipa
Senior

@Community member​ @Andreas Bolsch​ Finally worked! thanks for your kindness and information. Here is how to W25Q256FV nor flash problem solved:

according to Mr. Tesla's info, there has to be a "data and address synchronization" between STM32 QSPI peripheral and nor flash chip itself, So in the Write-memory function, I had to set alternateByteMode for data compensation and add 2 lines of code to QSPI write structure routine which I attached below:

It solved all the shifted data problem and everything works like a charm now 😋 (Commented lines is added)

uint8_t
CSP_QSPI_WriteMemory(uint8_t* buffer, uint32_t address, uint32_t buffer_size) {
 
    QSPI_CommandTypeDef sCommand;
    uint32_t end_addr, current_size, current_addr;
 
    /* Calculation of the size between the write address and the end of the page */
    current_addr = 0;
 
 
    //
    while (current_addr <= address) {
        current_addr += MEMORY_PAGE_SIZE;
    }
    current_size = current_addr - address;
 
    /* Check if the size of the data is less than the remaining place in the page */
    if (current_size > buffer_size) {
        current_size = buffer_size;
    }
 
    /* Initialize the adress variables */
    current_addr = address;
    end_addr = address + buffer_size;
 
    sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    sCommand.AddressSize = QSPI_ADDRESS_32_BITS;
    sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_4_LINES;  //Add alternate bytes to 4 lines 
    sCommand.AlternateBytesSize = QSPI_ALTERNATE_BYTES_16_BITS; //2 bytes of alternateByte is solved the problem
    sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
    sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
    sCommand.Instruction = QUAD_IN_FAST_PROG_CMD;
    sCommand.AddressMode = QSPI_ADDRESS_1_LINE;
    sCommand.DataMode = QSPI_DATA_4_LINES;
    sCommand.NbData = buffer_size;
    sCommand.Address = address & 0x0FFFFFFF;
    sCommand.DummyCycles = 0;
 
    /* Perform the write page by page */
    do {
        sCommand.Address = current_addr & 0x0FFFFFFF;  //add mask 0x0FFFFFFF
        sCommand.NbData = current_size;
 
        if (current_size == 0) {
            return HAL_OK;
        }
 
        /* Enable write operations */
        if (QSPI_WriteEnable() != HAL_OK) {
            return HAL_ERROR;
        }
 
        /* Configure the command */
        if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
            != HAL_OK) {
 
            return HAL_ERROR;
        }
 
        /* Transmission of the data */
        if (HAL_QSPI_Transmit(&hqspi, buffer, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
 
            return HAL_ERROR;
        }
 
        /* Configure automatic polling mode to wait for end of program */
        if (QSPI_AutoPollingMemReady() != HAL_OK) {
            return HAL_ERROR;
        }
 
        /* Update the address and size variables for next page programming */
        current_addr += current_size;
        buffer += current_size;
        current_size =
            ((current_addr + MEMORY_PAGE_SIZE) > end_addr) ?
            (end_addr - current_addr) : MEMORY_PAGE_SIZE;
    } while (current_addr <= end_addr);
 
    return HAL_OK;

View solution in original post

25 REPLIES 25
Andreas Bolsch
Lead II

1) Address size must be consistent: The flash chip usually powers up in 3-byte address mode (unless "Power-Up Address Mode (ADP)" bit in Status Register-3 had been programmed), hence to use "sCommand.AddressSize = QSPI_ADDRESS_32_BITS;" for read or write, you must switch the *FLASH* to 4-byte address mode, e.g. by issuing "Enter 4-Byte Address Mode" (0xB7) command. This command itself doens't take any address, see 2 below. It's a good idea to check "Current Address Mode (ADS)" bit in Status Register-3 afterwards to verify that this command was successful.

2) "Chip Erase" doesn't take any address, the command consists of the single command byte only. Hence "sCommand.AddressSize = QSPI_ADDRESS_32_BITS;" is wrong here.

@Andreas Bolsch​ I comment out the AddressSize and address (line number 9 and 15) and debug but no lock!. the memory address should be all 0xFF due to erasing command but all are question marks (??), I attach the configuration (initialization function) to inspect together.

MiladChalipa
Senior

This is the configuration function for W25Q256: all the individual sections are commented for ease of reading.

uint8_t
QSPI_Configuration(void) {
 
    QSPI_CommandTypeDef sCommand;
    QSPI_AutoPollingTypeDef sConfig;
    uint8_t reg = 0x02;
 
    HAL_Delay(10);
 
    /*enter 4 byte address*/
    sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    sCommand.Instruction = ENTER_4_BYTE_ADD_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;
    sCommand.NbData = 0;
 
    QSPI_WriteEnable();
 
    if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
        != HAL_OK) {
        return HAL_ERROR;
    }
 
 
    /*set QE bit to 1*/
    sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
    sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
    sCommand.Instruction = 0x31;
    sCommand.AddressMode = QSPI_ADDRESS_NONE;
    sCommand.DataMode = QSPI_DATA_1_LINE;
    sCommand.DummyCycles = 0;
    sCommand.NbData = 1;
 
    QSPI_WriteEnable();
 
    if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
        != HAL_OK) {
        return HAL_ERROR;
    }
 
    if (HAL_QSPI_Transmit(&hqspi, &reg,
                          HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
        Error_Handler();
        return HAL_ERROR;
    }
 
 
    /*enter QPI mode*/
    sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    sCommand.Instruction = ENTER_QPI_MODE;
    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;
    sCommand.NbData = 0;
 
    if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE)
        != HAL_OK) {
        return HAL_ERROR;
    }
 
 
    /* check if QE bit is 1 or not ------ */
    sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    sCommand.Instruction = 0x35;
    sCommand.AddressMode = QSPI_ADDRESS_NONE;
    sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    sCommand.DataMode = QSPI_DATA_1_LINE;
    sCommand.DummyCycles = 0;
    sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
    sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
 
    sConfig.Match = 0x02;
    sConfig.Mask = 0x02;
    sConfig.MatchMode = QSPI_MATCH_MODE_AND;
    sConfig.StatusBytesSize = 1;
    sConfig.Interval = 0x10;
    sConfig.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE;
 
    if (HAL_QSPI_AutoPolling(&hqspi, &sCommand, &sConfig,
                             HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
        return HAL_ERROR;
    }
 
 
    /* check if ADP bit is 1 or not ------ */
    sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
    sCommand.Instruction = 0x15;
    sCommand.AddressMode = QSPI_ADDRESS_NONE;
    sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
    sCommand.DataMode = QSPI_DATA_1_LINE;
    sCommand.DummyCycles = 0;
    sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
    sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
    sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
 
    sConfig.Match = 0x01;
    sConfig.Mask = 0x01;
    sConfig.MatchMode = QSPI_MATCH_MODE_AND;
    sConfig.StatusBytesSize = 1;
    sConfig.Interval = 0x10;
    sConfig.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE;
 
    if (HAL_QSPI_AutoPolling(&hqspi, &sCommand, &sConfig,
                             HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
        return HAL_ERROR;
    }
 
    return HAL_OK;
}

MiladChalipa
Senior

I was looking at the QSPI code written by Mr lixpaulian on GitHub (https://github.com/lixpaulian/stm32f7-qspi), He sets the dummy cycle numbers in his code which I am not! the datasheet (W25Q256) is not clear about the number of dummy cycles in QPI mode, would someone help me out @Andreas Bolsch​ . I think I have to set the number of dummy cycles and burst Wrap too, cause its default value is set to 2, and wrap length is 8-bytes! I am completely unfamiliar with these settings.

Andreas Bolsch
Lead II

Ah, so you did switch to 4-byte addresses ... But your init sequence still won't work: As soon as the flash receives "Enter QPI Mode", *ALL* further commands must use 4-line mode, *INCLUDING* command and address bytes.

Don't mix up "Fast Read Quad Output", "Quad Page Program" vs. QPI mode, these are two completely different things. The "quad" commands expect command and address to be transmitted on *single* line only, but data on all four lines, whereas in QPI mode everthing is transmitted on four lines. The datasheet doesn't make this distiction very clear ... The command sets for Standard/Dual/Quad SPI and QPI are quite different as well.

Regarding erase: What do you mean by "but all are question marks (??)"? This means probably "not readable by debugger", i.e. memory mapped mode hasn't been set up properly. It doesn't mean anything about the flash's contents.

Oh, you mean that maybe the problem is memory-mapped-mode code, I wasn't thinking about it before, let me double check the memory-mapped-mode code.

@Andreas Bolsch​ You were right I'd forgotten to call memory-mapped-mode right after the chip-erase command so now I am able to inspect the whole memory written 0xFF, The second problem solved. but I still have 2-byte earlier data written problem, So please help me get through it 😁

In the attached photo you can see that in the first byte of address 0x90000000, data should be 0x00 then 0x01, and so on, but actually whole data shifted 2-byte back! maybe because of bad code in the WriteMemory command:confounded_face:

Andreas Bolsch
Lead II

Dummy cycles are relevant only for fast read. The number of dummy cycle in the flash chip and the QSPI interface have to coincide, too, right. Be aware of number of dummy *clocks* vs. dummy *bytes* when switching from normal SPI to QPI mode or vice versa.

Copying code for the QSPI interface is quite trouble-prone, as the various flash chips vary significantly. Everything beyond the basic one line SPI commands is highly vendor and device specific.

Needs to be a consistency between cycles expected on the memory, and those on the host MCU interface.

Generally easy enough to write a pattern that can be tested across modes, ie 1-bit, 2-bit, 4-bit etc

Full erase (0xC7) takes upward of 118 seconds on a fresh device, have a timeout here of 200s waiting for the device to complete/come-ready

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