STM32F4 NAND flash a través de FSMC, diferencia entre bytes escritos y leídos

Tengo una placa de desarrollo Waveshare Open407V-D , básicamente una "placa base" en la que se instala un kit STM32F4DISCOVERY (este kit, para aquellos que no están familiarizados con él, está hecho por ST y se basa en el MCU STM32F407VG Cortex-M4). La placa Open407V-D proporciona muy poca funcionalidad, y consiste principalmente en una serie de conectores en los que se conectan módulos (también vendidos por Waveshare). Uno de estos módulos, llamado " NandFlash Board ", contiene un Samsung K9F1G08U0D 1 Gbit NAND Flash IC. El conector de la placa base lo conecta a las señales FSMC relevantes:

  • FSMC_D0 a FSMC_D7 en la MCU están conectados a I/O0 a I/O7 en Flash IC
  • FSMC_A17 está conectado a CLE
  • FSMC_A16 está conectado a ALE
  • FSMC_NWAIT está conectado a R/B
  • FSMC_NWE está conectado a NOSOTROS
  • FSMC_NOE está conectado a RE
  • FSMC_A18/PD13 está conectado a CE

Estoy tratando de conectarme a este módulo. Empecé a escribir mi propio código de inicio y luego incorporé ideas de este hilo del foro. Para el código de lectura/escritura/borrado, combiné un código de ejemplo para la placa STM32F10E-EVAL, incluida con la biblioteca de periféricos estándar STM32F1xx enviada por ST (especialmente el archivo Utilities/STM32_EVAL/STM3210E_EVAL/stm3210e_eval_fsmc_nand.c), con el código de ejemplo para la placa WaveShare NandFlash que se puede descargar desde aquí .

Escribí un código que llena una página (2048 bytes) de memoria con la siguiente matriz en formato little-endian (observe que es una matriz de variables de 32 bits):

uint32_t array[] = { 0, 1, 2, 3, 4, 5, ..., 509, 510, 511 };

Por lo tanto, al volver a leer la página, uno esperaría lo siguiente:

00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 ...

Sin embargo, lo que en realidad obtengo suele ser algo parecido a esto (un extracto del principio de la página):

00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00
04 00 00 00 05 00 00 00 06 00 00 00 07 00 00 00
08 00 00 00 09 00 00 00 0A 00 00 00 0B 00 00 00
0C 00 00 00 0D 00 00 00 0E 00 00 00 0F 00 00 00
10 00 00 00 11 00 00 00 12 00 00 00 13 00 00 00
14 00 00 00 15 00 00 00 16 00 00 00 17 00 00 00
18 00 00 00 19 00 00 00 1A 00 00 00 1B 00 00 00
1C 00 00 00 1D 00 00 00 1E 00 00 00 1F 00 00 00
20 00 00 00 21 00 00 00 22 00 00 00 23 00 00 00
24 00 00 00 25 00 00 00 26 00 00 00 27 00 00 00
28 00 00 00 29 00 00 00 2A 00 00 00 2B 00 00 00
2C 00 00 00 2D 00 00 00 2E 00 00 00 2F 00 00 00
30 00 00 00 31 00 00 00 32 00 00 00 33 00 00 00
34 00 00 00 35 00 00 00 36 00 00 00 37 00 00 00
38 00 00 00 39 00 00 00 3A 00 00 00 3B 00 00 3C
00 00 00 3D 00 00 00 3E 00 00 00 3F 00 00 40 00
00 00 41 00 00 00 42 00 00 00 43 00 00 00 44 00
00 00 45 00 00 00 46 00 00 00 47 00 00 00 48 00
00 00 49 00 00 00 4A 00 00 00 4B 00 00 00 4C 00
00 00 4D 00 00 00 4E 00 00 00 4F 00 00 00 50 00
00 00 51 00 00 00 52 00 00 00 53 00 00 00 54 00
00 00 55 00 00 00 56 00 00 00 57 00 00 00 58 00

Como puede verse, a veces se elimina un byte cero y, en otras ocasiones, se inserta uno en el flujo (aunque el caso de la inserción no ocurrió en este ejemplo en particular). Mire por ejemplo a los 3C y 40 bytes en el ejemplo. También he visto situaciones en las que se repite uno de los bytes, por ejemplo, en lugar de 32 00 00 00 33 00 ...obtener32 00 00 33 33 00 ...

De hecho, para obtener los resultados anteriores, no puedo usar los tiempos calculados de configuración/retención/espera/Hi-Z/tAR/tCLR (visto en los extractos de código a continuación); No calculé el umbral exacto, pero al menos usar un valor de 100 ciclos de reloj para configurar/esperar/Hi-Z y 16 ciclos para tAR/tCLR funciona. Los resultados parecen ser muy similares (y muy a menudo exactamente iguales) en cada ejecución. He intentado cambiar la dirección de la página pero sigo obteniendo los mismos resultados.

Una cosa que intenté es borrar y escribir el patrón solo una vez, y luego leer los datos en un bucle e inspeccionarlos con un depurador. La mayoría de las veces siempre obtengo el mismo resultado, aunque un par de veces obtuve resultados ligeramente diferentes (por ejemplo, el caso de la repetición 33descrita anteriormente). Entonces parece que hay un error en la función de lectura, pero no puedo descartar un error en la función de escritura también.

Como referencia, proporciono la mayor parte de mi código en caso de que sea importante. Primero, un montón de #defines que pueden ser necesarios como referencia en el resto del código:

#define FSMC_Bank_NAND     FSMC_Bank2_NAND
#define Bank_NAND_ADDR     Bank2_NAND_ADDR
#define Bank2_NAND_ADDR    ((uint32_t)0x70000000)

#define ROW_ADDRESS (Address.Page + (Address.Block + (Address.Zone * NAND_ZONE_SIZE)) * NAND_BLOCK_SIZE)

#define CMD_AREA                   (uint32_t)(1<<17)  /* A17 = CLE  high */
#define ADDR_AREA                  (uint32_t)(1<<16)  /* A16 = ALE high */

#define DATA_AREA                  ((uint32_t)0x00000000)

#define NAND_CMD_AREA_A            ((uint8_t)0x00)
#define NAND_CMD_AREA_B            ((uint8_t)0x01)
#define NAND_CMD_AREA_C            ((uint8_t)0x50)

#define NAND_CMD_READ_1             ((uint8_t)0x00)
#define NAND_CMD_READ_TRUE          ((uint8_t)0x30)

#define NAND_CMD_WRITE0            ((uint8_t)0x80)
#define NAND_CMD_WRITE_TRUE1       ((uint8_t)0x10)

#define NAND_CMD_ERASE0            ((uint8_t)0x60)
#define NAND_CMD_ERASE1            ((uint8_t)0xD0)

#define NAND_CMD_READID            ((uint8_t)0x90)
#define NAND_CMD_STATUS            ((uint8_t)0x70)
#define NAND_CMD_LOCK_STATUS       ((uint8_t)0x7A)
#define NAND_CMD_RESET             ((uint8_t)0xFF)

#define NAND_VALID_ADDRESS         ((uint32_t)0x00000100)
#define NAND_INVALID_ADDRESS       ((uint32_t)0x00000200)
#define NAND_TIMEOUT_ERROR         ((uint32_t)0x00000400)
#define NAND_BUSY                  ((uint32_t)0x00000000)
#define NAND_ERROR                 ((uint32_t)0x00000001)
#define NAND_READY                 ((uint32_t)0x00000040)

#define NAND_PAGE_SIZE             ((uint16_t)0x0800) /* 2 * 1024 bytes per page w/o Spare Area */
#define NAND_BLOCK_SIZE            ((uint16_t)0x0040) /* 64 pages per block */
#define NAND_ZONE_SIZE             ((uint16_t)0x0400) /* 1024 Block per zone */
#define NAND_SPARE_AREA_SIZE       ((uint16_t)0x0040) /* last 64 bytes as spare area */
#define NAND_MAX_ZONE              ((uint16_t)0x0001) /* 1 zones of 1024 block */

#define ADDR_1st_CYCLE(ADDR)       (uint8_t)((ADDR)& 0xFF)               /* 1st addressing cycle */
#define ADDR_2nd_CYCLE(ADDR)       (uint8_t)(((ADDR)& 0xFF00) >> 8)      /* 2nd addressing cycle */
#define ADDR_3rd_CYCLE(ADDR)       (uint8_t)(((ADDR)& 0xFF0000) >> 16)   /* 3rd addressing cycle */
#define ADDR_4th_CYCLE(ADDR)       (uint8_t)(((ADDR)& 0xFF000000) >> 24) /* 4th addressing cycle */

Este es el código de inicio:

void NAND_Init()
{
    GPIO_InitTypeDef GPIO_InitStructure;
    FSMC_NANDInitTypeDef FSMC_NANDInitStructure;
    FSMC_NAND_PCCARDTimingInitTypeDef p;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource4, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource5, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource6, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource11, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_FSMC);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOE, &GPIO_InitStructure);

    GPIO_PinAFConfig(GPIOE, GPIO_PinSource7, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource8, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource9, GPIO_AF_FSMC);
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource10, GPIO_AF_FSMC);

    RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC, ENABLE);

    FSMC_NANDInitStructure.FSMC_AttributeSpaceTimingStruct = &p;
    FSMC_NANDInitStructure.FSMC_CommonSpaceTimingStruct = &p;

    FSMC_NANDStructInit(&FSMC_NANDInitStructure);

    p.FSMC_SetupTime = 0;
    p.FSMC_WaitSetupTime = 2;
    p.FSMC_HoldSetupTime = 1;
    p.FSMC_HiZSetupTime = 4;

    FSMC_NANDInitStructure.FSMC_Bank = FSMC_Bank2_NAND;
    FSMC_NANDInitStructure.FSMC_Waitfeature = FSMC_Waitfeature_Enable;
    FSMC_NANDInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_8b;
    FSMC_NANDInitStructure.FSMC_ECC = FSMC_ECC_Disable;
    FSMC_NANDInitStructure.FSMC_TCLRSetupTime = 0;
    FSMC_NANDInitStructure.FSMC_TARSetupTime = 0;
    FSMC_NANDInit(&FSMC_NANDInitStructure);

    FSMC_NANDCmd(FSMC_Bank2_NAND, ENABLE);
}

Este es el código para borrar una página:

uint32_t NAND_EraseBlock(NAND_ADDRESS Address)
{
    *(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_ERASE0;

    *(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = ADDR_1st_CYCLE(ROW_ADDRESS);
    *(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = ADDR_2nd_CYCLE(ROW_ADDRESS);

    *(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_ERASE1;

    return (NAND_GetStatus());
}

El código para escribir una o más páginas (por ahora solo estoy escribiendo una página a la vez):

uint32_t NAND_WriteSmallPage(uint8_t *pBuffer, NAND_ADDRESS Address, uint32_t NumPageToWrite)
{
    uint32_t index = 0x00, numpagewritten = 0x00, addressstatus = NAND_VALID_ADDRESS;
    uint32_t status = NAND_READY, size = 0x00;

    while((NumPageToWrite != 0x00) && (addressstatus == NAND_VALID_ADDRESS) && (status == NAND_READY))
    {
        /*!< Page write command and address */
        *(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_WRITE0;

        *(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = 0x00;
        *(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = 0x00;
        *(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = ADDR_1st_CYCLE(ROW_ADDRESS);
        *(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = ADDR_2nd_CYCLE(ROW_ADDRESS);

        /*!< Calculate the size */
        size = NAND_PAGE_SIZE + (NAND_PAGE_SIZE * numpagewritten);

        /*!< Write data */
        for(; index < size; index++)
        {
            *(__IO uint8_t *)(Bank_NAND_ADDR | DATA_AREA) = pBuffer[index];
        }

        *(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_WRITE_TRUE1;

        /*!< Check status for successful operation */
        status = NAND_GetStatus();

        if(status == NAND_READY)
        {
            numpagewritten++;

            NumPageToWrite--;

            /*!< Calculate Next small page Address */
            addressstatus = NAND_AddressIncrement(&Address);
        }
    }

    return (status | addressstatus);
}

El código para leer una o más páginas (nuevamente, solo estoy leyendo una página a la vez por ahora):

uint32_t NAND_ReadSmallPage(uint8_t *pBuffer, NAND_ADDRESS Address, uint32_t NumPageToRead)
{
    uint32_t index = 0x00, numpageread = 0x00, addressstatus = NAND_VALID_ADDRESS;
    uint32_t status = NAND_READY, size = 0x00;

    while((NumPageToRead != 0x0) && (addressstatus == NAND_VALID_ADDRESS))
    {
        /*!< Page Read command and page address */
        *(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_READ_1;

        *(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = 0x00;
        *(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = 0x00;
        *(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = ADDR_1st_CYCLE(ROW_ADDRESS);
        *(__IO uint8_t *)(Bank_NAND_ADDR | ADDR_AREA) = ADDR_2nd_CYCLE(ROW_ADDRESS);

        *(__IO uint8_t *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_READ_TRUE;

        /*!< Calculate the size */
        size = NAND_PAGE_SIZE + (NAND_PAGE_SIZE * numpageread);

        /*!< Get Data into Buffer */
        for(; index < size; index++)
        {
            pBuffer[index]= *(__IO uint8_t *)(Bank_NAND_ADDR | DATA_AREA);
        }

        numpageread++;

        NumPageToRead--;

        /*!< Calculate page address */
        addressstatus = NAND_AddressIncrement(&Address);
    }

    status = NAND_GetStatus();

    return (status | addressstatus);
}
Bueno, si lees los datos varias veces y obtienes los mismos datos

Respuestas (1)

Eche un vistazo a la tabla que desea escribir. Ha definido como largo sin firmar

values uint32_t array[] = { 0, 1, 2, 3, 4, 5, ..., 509, 510, 511 };

Entonces, la operación de escritura está bien. el tipo largo sin firmar tiene una longitud de 4 bytes.

Se espera esa parte del comportamiento, como se explica en la pregunta.