STM32F4 NAND-Flash über FSMC, Unterschied zwischen geschriebenen und zurückgelesenen Bytes

Ich habe ein Waveshare Open407V-D- Entwicklungsboard, im Grunde ein "Motherboard", in das ein STM32F4DISCOVERY -Kit eingebaut ist (dieses Kit wird für diejenigen, die damit nicht vertraut sind, von ST selbst hergestellt und basiert auf der STM32F407VG Cortex-M4-MCU). Das Open407V-D-Board bietet sehr wenig Funktionalität und besteht hauptsächlich aus einer Reihe von Anschlüssen, in die Module (ebenfalls von Waveshare vertrieben) gesteckt werden. Eines dieser Module, „ NandFlash Board “ genannt, enthält einen Samsung K9F1G08U0D 1 Gbit NAND Flash IC. Der Anschluss auf dem Motherboard verbindet es mit den relevanten FSMC-Signalen:

  • FSMC_D0 bis FSMC_D7 auf der MCU sind mit I/O0 bis I/O7 auf dem Flash-IC verbunden
  • FSMC_A17 ist mit CLE verbunden
  • FSMC_A16 ist mit ALE verbunden
  • FSMC_NWAIT ist mit R/B verbunden
  • FSMC_NWE ist mit WE verbunden
  • FSMC_NOE ist mit RE verbunden
  • FSMC_A18/PD13 ist mit CE verbunden

Ich versuche, eine Schnittstelle zu diesem Modul herzustellen. Ich habe angefangen, meinen eigenen Init-Code zu schreiben, und habe dann Ideen aus diesem Forumsthread eingearbeitet. Für den Lese-/Schreib-/Löschcode habe ich einen Beispielcode für das STM32F10E-EVAL-Board, gebündelt mit der STM32F1xx-Standardperipheriebibliothek, wie sie von ST geliefert wird (insbesondere die Datei Utilities/STM32_EVAL/STM3210E_EVAL/stm3210e_eval_fsmc_nand.c), mit dem Beispielcode kombiniert für das WaveShare NandFlash Board, das hier heruntergeladen werden kann .

Ich habe Code geschrieben, der eine Seite (2048 Bytes) Speicher mit dem folgenden Array in Little-Endian-Form füllt (beachten Sie, dass es sich um ein Array von 32-Bit-Variablen handelt):

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

Daher würde man beim Zurücklesen der Seite Folgendes erwarten:

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

Was ich jedoch tatsächlich bekomme, ist normalerweise etwas in der Nähe davon (ein Auszug vom Anfang der Seite):

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

Wie zu sehen ist, wird manchmal ein Null-Byte entfernt und manchmal wird eins in den Strom eingefügt (obwohl der Fall des Einfügens in diesem speziellen Beispiel nicht vorkam). Sehen Sie sich zum Beispiel die 3C und 40 Bytes im Beispiel an. Ich habe auch Situationen gesehen, in denen eines der Bytes wiederholt wird, zum Beispiel statt 32 00 00 00 33 00 ...I get32 00 00 33 33 00 ...

Um die obigen Ergebnisse zu erhalten, kann ich die berechneten Setup/Hold/Wait/Hi-Z/tAR/tCLR-Timings nicht verwenden (siehe Code-Auszüge unten); Ich habe den genauen Schwellenwert nicht herausgefunden, aber zumindest die Verwendung eines Werts von 100 Taktzyklen für Setup / Hold / Wait / Hi-Z und 16 Zyklen für tAR / tCLR funktioniert. Die Ergebnisse scheinen bei jedem Durchlauf sehr ähnlich (und ziemlich oft genau gleich) zu sein. Ich habe versucht, die Adresse der Seite zu ändern, aber ich erhalte immer die gleichen Ergebnisse.

Eine Sache, die ich versucht habe, ist, das Muster nur einmal zu löschen und zu schreiben und dann die Daten in einer Schleife zurückzulesen und sie mit einem Debugger zu untersuchen. Meistens erhalte ich immer das gleiche Ergebnis, obwohl ich ein paar Mal etwas andere Ergebnisse erhalten habe (z. B. im Fall der 33oben beschriebenen Wiederholung). Es scheint also einen Fehler in der Lesefunktion zu geben, aber ich kann auch einen Fehler in der Schreibfunktion nicht ausschließen.

Als Referenz stelle ich den Großteil meines Codes zur Verfügung, falls es darauf ankommt. Zuerst eine Reihe von #defines, die möglicherweise als Referenz im restlichen Code benötigt werden:

#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 */

Dies ist der Init-Code:

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);
}

Dies ist der Code zum Löschen einer Seite:

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());
}

Der Code zum Schreiben einer oder mehrerer Seiten (im Moment schreibe ich nur eine Seite auf einmal):

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);
}

Der Code zum Lesen einer oder mehrerer Seiten (wieder lese ich vorerst nur eine Seite nach der anderen):

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);
}
Nun, wenn Sie die Daten mehrmals zurücklesen und dieselben Daten erhalten

Antworten (1)

Sehen Sie sich die Tabelle an, die Sie schreiben möchten. Sie haben als unsigned long definiert

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

Damit ist der Schreibvorgang in Ordnung. unsigned long Typ ist 4 Bytes lang.

Dieser Teil des Verhaltens wird erwartet, wie in der Frage erläutert.