Флэш-память STM32F4 NAND через FSMC, разница между записанными и считанными байтами

У меня есть макетная плата Waveshare Open407V-D , по сути «материнская плата», в которой установлен комплект STM32F4DISCOVERY (этот комплект, для тех, кто с ним не знаком, сделан самой ST и основан на микроконтроллере STM32F407VG Cortex-M4) . Плата Open407V-D предоставляет очень мало функциональных возможностей, состоящая в основном из ряда разъемов, в которые подключаются модули (также продаваемые Waveshare). Один из этих модулей, называемый « NandFlash Board », содержит микросхему Samsung K9F1G08U0D 1 Gbit NAND Flash. Разъем на материнской плате подключает его к соответствующим сигналам FSMC:

  • FSMC_D0 через FSMC_D7 на MCU подключены к I/O0 через I/O7 на Flash IC
  • FSMC_A17 подключен к CLE
  • FSMC_A16 подключен к ALE
  • FSMC_NWAIT подключен к R/B
  • FSMC_NWE подключен к WE
  • FSMC_NOE подключен к RE
  • FSMC_A18/PD13 подключен к CE

Я пытаюсь подключиться к этому модулю. Я начал писать свой собственный код инициализации, а затем использовал идеи из этой ветки форума. Для кода чтения/записи/стирания я объединил некоторый пример кода для платы STM32F10E-EVAL, в комплекте со стандартной периферийной библиотекой STM32F1xx, поставляемой ST (особенно файл Utilities/STM32_EVAL/STM3210E_EVAL/stm3210e_eval_fsmc_nand.c), с примером кода для платы WaveShare NandFlash, которую можно скачать отсюда .

Я написал код, который заполняет страницу (2048 байт) памяти следующим массивом в форме с прямым порядком байтов (обратите внимание, что это массив 32-битных переменных):

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

Следовательно, при обратном чтении страницы можно было бы ожидать следующего:

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

Однако на самом деле я обычно получаю что-то близкое к этому (отрывок из начала страницы):

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

Как видно, иногда удаляется нулевой байт, а иногда в поток вставляется один (хотя случая вставки в данном конкретном примере не произошло). Посмотрите например на 3С и 40 байт в примере. Я также видел ситуации, в которых один из байтов повторяется, например, вместо того, чтобы 32 00 00 00 33 00 ...получить32 00 00 33 33 00 ...

На самом деле, чтобы получить приведенные выше результаты, я не могу использовать рассчитанные тайминги setup/hold/wait/Hi-Z/tAR/tCLR (см. фрагменты кода ниже); Я не вычислил точный порог, но, по крайней мере, использование значения 100 тактовых циклов для настройки/удержания/ожидания/Hi-Z и 16 циклов для tAR/tCLR действительно работает. Результаты кажутся очень похожими (и довольно часто абсолютно одинаковыми) при каждом прогоне. Я пытался изменить адрес страницы, но я продолжаю получать те же результаты.

Одна вещь, которую я пробовал, — стирать и записывать шаблон только один раз, а затем считывать данные в цикле и проверять их с помощью отладчика. В большинстве случаев я всегда получаю один и тот же результат, хотя пару раз я получил немного разные результаты (например, случай повторения, 33описанный выше). Таким образом, похоже, что есть ошибка в функции чтения, но я не могу исключить ошибку и в функции записи.

Для справки, я предоставляю большую часть своего кода, если это имеет значение. Во-первых, куча #defines, которые могут понадобиться для ссылки в остальной части кода:

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

Это код инициализации:

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

Это код для стирания страницы:

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

Код для написания одной или нескольких страниц (пока я пишу только одну страницу за раз):

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

Код для чтения одной или нескольких страниц (опять же, сейчас я просто читаю одну страницу за раз):

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);
}
Хорошо, если вы прочитаете данные несколько раз и получите те же данные

Ответы (1)

Посмотрите на таблицу, которую вы хотите написать. Вы определили как unsigned long

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

Так что - операция записи в порядке. тип unsigned long имеет длину 4 байта.

Эта часть поведения ожидается, как объясняется в вопросе.