Tengo un Waveshare Open407V-D placa de desarrollo, básicamente una" placa base "en la que una STM32F4DISCOVERY está instalado (este kit, para aquellos que no están familiarizados con él, está hecho por el propio ST y se basa en el STM32F407VG Cortex-M4 MCU). La placa Open407V-D proporciona muy poca funcionalidad, ya que consiste principalmente en una serie de conectores en los que los módulos (también vendidos por Waveshare) están conectados. 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 través de FSMC_D7 en la MCU están conectados a I / O0 a través de I / O7 en el IC de Flash
- FSMC_A17 está conectado a CLE
- FSMC_A16 está conectado a ALE
- FSMC_NWAIT está conectado a R / B
- FSMC_NWE está conectado a WE
- FSMC_NOE está conectado a RE
- FSMC_A18 / PD13 está conectado a CE
Estoy intentando conectar con este módulo. Comencé a escribir mi propio código de inicio y luego incorporé ideas de esta hilo del foro. Para el código de lectura / escritura / borrado combiné un código de ejemplo para la placa STM32F10E-EVAL, incluido con la biblioteca de periféricos estándar STM32F1xx tal como fue enviado por ST (especialmente el archivo Utilities / STM32_EVAL / STM3210E_EVAL / stm3210e_eval_fsmc_nand.c), con el código de ejemplo para la tarjeta WaveShare NandFlash Board, que se puede descargar desde aquí .
Escribí un código que llena una página (2048 bytes) de memoria con la siguiente matriz en forma little-endian (note 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 realmente obtengo es 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 se puede ver, a veces se elimina un byte cero y, en otras, se inserta uno en el flujo (aunque el caso de inserción no ocurrió en este ejemplo en particular). Busque 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 ...
obtengo 32 00 00 33 33 00 ...
De hecho, para obtener los resultados anteriores, no puedo usar los tiempos calculados de configuración / espera / espera / Hi-Z / tAR / tCLR (que se muestran 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 / mantener / esperar / Hi-Z y 16 ciclos para tAR / tCLR funciona. Los resultados parecen ser muy similares (ya 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 del 33
repetido descrito anteriormente). Así que parece que hay un error en la función de lectura, pero tampoco puedo descartar un error en la función de escritura.
Para referencia, proporciono la mayor parte de mi código en caso de que importe. Primero, un montón de #define
s que puede ser necesario para 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 escribo 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);
}