Winbond W25Q01 (1G-BIT) 闪存擦除 256 字节而非 4K,并且在地址 0x07000000 之后写入-读取-验证失败

问题描述 投票:0回答:1

我正在编写 STM32 固件以与 Winbond W25Q01JV 闪存芯片连接。该芯片在两个 512M 位芯片中提供 1GBit 内存,每个芯片都有自己的状态寄存器。闪存芯片用作两个非易失性 FIFO 缓冲区(每个 512M 位芯片上一个)写入和验证 256 字节页面。

虽然大多数命令使用 3 字节寻址,但芯片可以置于 4 字节地址模式,并且存在用于擦除、写入和读取的 4 字节地址命令(无论芯片是 3 字节还是 4 字节)地址模式)。

命令 0x21 应该擦除从给定 4 字节地址开始的 4K 扇区。它前面必须有写使能命令 0x06。相反,我发现命令 0x21 只会擦除 256 字节的页面,无论我如何尝试使用它。

我还发现,当使用擦除、写入、读取和验证循环时,从 0x00000000 到 0x06FFFFFF 的所有 256 字节页面验证都会失败,但 0x07000000 页面会失败。我已经在两个相同的开发板上尝试过这一点。两者都从相同的地址 0x07000000 失败,其中一个失败到地址范围的末尾,另一个失败从 0x07000000 到 0x7000D00。即使在全芯片擦除后尝试写入、读取验证后,这些位置也会失败。

我以为芯片可能被设置了写保护。两个芯片都清除了三个寄存器,除了设置了寄存器 #3 SRV1(输出驱动强度 = 01,默认为 75%)。

我已经进行了广泛的测试、代码修订和数据表的重新阅读。结果保持不变。为了简洁起见,代码示例是作为整体函数给出的最新迭代。

任何人都可以告诉我我做错了什么吗?

/********************************************************************
 * NVR - DATA WRITE
 ********************************************************************
 * Write array of up to 256 bytes to memory page specified by Addr.
 * Assume that the memory page has already been erased.
 * Addr should be the start of a 256-byte memory page.
 * Return HAL_OK if success.
 *
 * Declared above
 * const uint8_t NVR_TIMEOUT = 100;
 * uint8_t NvrHead[5];
 * uint8_t NvrData[252];
 */
HAL_StatusTypeDef nvrDataWrite(uint32_t Addr, const uint8_t * Data, uint16_t DataLen) {
    HAL_StatusTypeDef LocalResult = HAL_OK;
    uint8_t LocalEraseFlag = 0;



    /* Are we starting 4K sector */
    if((Addr & 0x00000FFF) == 0) {
        LocalEraseFlag = 1;
    }


    /* Prepare SPI header with reverse address */
    NvrBuff[4] = ((Addr >> 24) & 0xFF);
    NvrBuff[3] = ((Addr >> 16) & 0xFF);
    NvrBuff[2] = ((Addr >> 8) & 0xFF);
    NvrBuff[1] = ((Addr >> 0) & 0xFF);


    /* Set pin to mark transaction for Logic Analyser */
    HAL_GPIO_WritePin(GPIOF, GPIO_PIN_2, GPIO_PIN_SET);


    /**
     * Software Die Select
     * Explicity select the Die to ensure that the correct status
     * register is polled for busy.
     */
    if(Addr < 0x04000000) {
        NvrBuff[1] = 0;
    } else {
        NvrBuff[1] = 1;
    }
    NvrBuff[0] = 0xC2;
    CS_LO
    HAL_SPI_Transmit(&hspi1, NvrBuff, 2, NVR_TIMEOUT);
    CS_HI


    /**
     * Erase
     * This is supposed to erase a 4K sector starting at the given
     * address. Test show that only 256-bytes is being erased.
     */
    if(LocalEraseFlag > 0) {

        /* Wait until memory chip is not busy */
        do {
            /* Read Status Register-1 (05h) */
            NvrBuff[0] = 0x05;
            CS_LO;
            HAL_SPI_Transmit(&hspi1, NvrBuff, 1, NVR_TIMEOUT);
            HAL_SPI_Receive(&hspi1, NvrData, 1, NVR_TIMEOUT);
            CS_HI;
        } while((NvrData[0] & 0b00000001) > 0);

        /* 8.5.1 Write Enable (06h) */
        CS_LO;
        NvrBuff[0] = CMD_BWRITE_ENABLE;
        HAL_SPI_Transmit(&hspi1, NvrBuff, 1, NVR_TIMEOUT);
        CS_HI;

        /* Erase 4K sector (21h) */
        NvrBuff[0] = 0x21;
        CS_LO;
        LocalResult = HAL_SPI_Transmit(&hspi1, NvrBuff, 5, NVR_TIMEOUT);
        CS_HI;
        HAL_Delay(63); /* tSE = 50 to 400 ms. Measured at 62ms by logic analyser */

    }


    /* Wait until memory chip is not busy */
    do {
        /* 8.5.4 Read Status Register-1 (05h) */
        NvrBuff[0] = 0x05;
        CS_LO;
        HAL_SPI_Transmit(&hspi1, NvrBuff, 1, NVR_TIMEOUT);
        HAL_SPI_Receive(&hspi1, NvrData, 1, NVR_TIMEOUT);
        CS_HI;
    } while((NvrData[0] & 0b00000001) > 0);


    /* Write Enable 8.5.1 */
    NvrBuff[0] = 0x06;
    CS_LO
    HAL_SPI_Transmit(&hspi1, NvrBuff, 1, NVR_TIMEOUT);
    CS_HI


    /* 8.6.1 WRITE or Page Program 1 to 256-bytes with 4-byte address (12h) */
    CS_LO;
    NvrBuff[0] = 0x12;
    HAL_SPI_Transmit(&hspi1, NvrBuff, 5, NVR_TIMEOUT);
    HAL_SPI_Transmit(&hspi1, Data, DataLen, NVR_TIMEOUT);
    CS_HI;
    HAL_Delay(3); /* tPP = 0.7 to 3.5 ms from Datasheet */


    /* Wait until memory chip is not busy */
    do {
        /* 8.5.4 Read Status Register-1 (05h) */
        NvrBuff[0] = 0x05;
        CS_LO;
        HAL_SPI_Transmit(&hspi1, NvrBuff, 1, NVR_TIMEOUT);
        HAL_SPI_Receive(&hspi1, NvrData, 1, NVR_TIMEOUT);
        CS_HI;
    } while((NvrData[0] & 0b00000001) > 0);


    /* READ 1 to 256-bytes with 4-byte address (13h) */
    NvrBuff[0] = 0x13;
    CS_LO;
    HAL_SPI_Transmit(&hspi1, NvrBuff, 5, NVR_TIMEOUT);
    HAL_SPI_Receive(&hspi1, NvrData, DataLen, NVR_TIMEOUT);
    CS_HI;


    /* Set pin to mark transaction for Logic Analyser */
    HAL_GPIO_WritePin(GPIOF, GPIO_PIN_2, GPIO_PIN_SET);


    /* Verify payload and report result */
    if(memcmp(Data, NvrData, DataLen) != 0) {
        LocalResult = HAL_ERROR;
    }

    return LocalResult;

}

郑重声明,我使用的是 STM32H733ZGT6 MCU,具有 12.5MBit/S 的常规 SPI,没有增强功能,如 CRC、自动 CS 等。

memory flash spi
1个回答
0
投票

您在擦除交易中错误地格式化了地址:

/* Prepare SPI header with reverse address */
NvrBuff[4] = ((Addr >> 24) & 0xFF);
NvrBuff[3] = ((Addr >> 16) & 0xFF);
NvrBuff[2] = ((Addr >> 8) & 0xFF);
NvrBuff[1] = ((Addr >> 0) & 0xFF);
        ↑ 
        │
        └──── Errors Here

地址应该以大端格式编码,但您以小端格式编码。

汇编地址时应反转NvrBuff中的索引。地址的 LSB 应位于缓冲区的第 4 个字节,MSB 应位于缓冲区的第 1 个字节。

/* Prepare SPI header */
NvrBuff[1] = ((Addr >> 24) & 0xFF);
NvrBuff[2] = ((Addr >> 16) & 0xFF);
NvrBuff[3] = ((Addr >> 8) & 0xFF);
NvrBuff[4] = ((Addr >> 0) & 0xFF);
© www.soinside.com 2019 - 2024. All rights reserved.