【问题】
STM32单片机使用FSMC读取K9F1G08U0E NAND Flash时,出现部分字节丢失的情况。例如:Flash存储器中存储有连续的0xff字节,则在进行连续读(Page Read)操作时可能会丢失部分0xff。
例如,写入以下数据到某一页的开头(如地址0x00800000):
#define NANDFLASH2 ((volatile uint8_t *)0x70000000)
#define NANDFLASH2C ((volatile uint8_t *)0x70010000)
#define NANDFLASH2A ((volatile uint8_t *)0x70020000)
#define NANDFLASH2A16 ((volatile uint16_t *)0x70020000)
#define NANDFLASH2A32 ((volatile uint32_t *)0x70020000)
void K9F1G08U0E_ReadPage(uint32_t addr, void *buffer, uint16_t len)
{
*NANDFLASH2C = 0x00;
*NANDFLASH2A32 = addr;
*NANDFLASH2C = 0x30;
memcpy(buffer, (void *)NANDFLASH2, len);
}
uint8_t data[50];
K9F1G08U0E_ReadPage(0x800000, data, sizeof(data));
4个0xff变成了两个0xff。
void USART2_IRQHandler(void)
{
uint8_t data = USART2->DR;
if (data == 'a')
{
K9F1G08U0E_ReadPage(0x800000, &data, 1);
printf("[First] 0x%02x\n", data);
}
else if (data == 'b')
printf("[Byte] 0x%02x\n", *NANDFLASH2);
else if (data == 'c')
printf("[Half Word] 0x%04x\n", *(volatile uint16_t *)NANDFLASH2);
else if (data == 'd')
printf("[Word] 0x%08x\n", *(volatile uint32_t *)NANDFLASH2);
}
[Byte] 0x21
[Byte] 0xff
[Byte] 0xff
[Byte] 0xff
[Byte] 0x74
[Byte] 0x21
[Byte] 0xff
[Byte] 0xff
[Byte] 0xff
[Byte] 0xff
[Byte] 0x74
【解决方法】
读取第一字节后,每读取一个字节,都发送一次Random Data Output命令,指明下一个字节的地址。这种方式不影响ECC的计算。
void K9F1G08U0E_Read(uint32_t addr, void *buffer, uint16_t len)
{
uint8_t *p = buffer;
uint16_t i;
*NANDFLASH2C = 0x00;
*NANDFLASH2A32 = addr;
*NANDFLASH2C = 0x30;
p[0] = *NANDFLASH2;
for (i = 1; i < len; i++)
{
*NANDFLASH2C = 0x05;
*NANDFLASH2A16 = (addr + i) & 0xffff;
*NANDFLASH2C = 0xe0;
p[i] = *NANDFLASH2;
}
}
笔者发现,之前的程序如果读写大块的数据,几乎每一页都有丢失数据的可能,就连4字节的ECC码也不例外!
ECC存储在每一页的第2048~2052字节处,必须一个字节一个字节读取,不可连读!
笔者把NAND Flash模块接到微雪STM32F103VE核心板上发现,当模块上的两个VCC端口和两个GND端口都接到3.3V电源上时,原来的函数能正常工作。
另外,STM32单片机的每个电源引脚上接上电容对于保证单片机本身和外围器件工作的稳定性也非常重要。
如图,接口上两组VCC和GND都要接到电源上,不能只接一组。另外,ALE接D17(PD12),CLE接D16(PD11)。
尽管在VE核心板上测试成功了,但是笔者在自己焊的ZE板上即使插上5V的外置电源,用AMS1117转成3.3V后再并联上100μF的电容,把插在FSMC上的NOR Flash和SRAM都取下来,也未能解决问题。
【示例程序:带ECC检测的读写】
main.c:
#include <stdio.h>
#include <stm32f10x.h>
#include "K9F1G08U0E.h"
uint8_t buffer[2048];
void dump_data(const void *data, uint16_t len)
{
const uint8_t *p = data;
while (len--)
printf("%02X", *p++);
printf("\n");
}
int fputc(int ch, FILE *fp)
{
if (fp == stdout)
{
if (ch == '\n')
{
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, '\r');
}
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, ch);
}
return ch;
}
void read_check(void)
{
uint16_t page;
for (page = 64; page <= 188; page++)
{
if (!K9F1G08U0E_ReadPage(page, buffer))
printf("ECC failed at page %d\n", page);
}
printf("Checked!\n");
}
int main(void)
{
uint8_t data[5];
FSMC_NANDInitTypeDef fsmc;
FSMC_NAND_PCCARDTimingInitTypeDef fsmc_timing;
GPIO_InitTypeDef gpio;
USART_InitTypeDef usart;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE | RCC_APB2Periph_USART1, ENABLE);
gpio.GPIO_Mode = GPIO_Mode_AF_PP;
gpio.GPIO_Pin = GPIO_Pin_9;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio);
gpio.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_Init(GPIOD, &gpio);
gpio.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10;
GPIO_Init(GPIOE, &gpio);
USART_StructInit(&usart);
usart.USART_BaudRate = 115200;
usart.USART_Mode = USART_Mode_Tx;
USART_Init(USART1, &usart);
USART_Cmd(USART1, ENABLE);
printf("STM32F103ZE FSMC NAND Flash\n");
fsmc.FSMC_AttributeSpaceTimingStruct = &fsmc_timing;
fsmc.FSMC_CommonSpaceTimingStruct = &fsmc_timing;
FSMC_NANDStructInit(&fsmc);
fsmc.FSMC_ECCPageSize = FSMC_ECCPageSize_2048Bytes;
fsmc.FSMC_Waitfeature = FSMC_Waitfeature_Enable;
FSMC_NANDInit(&fsmc);
FSMC_NANDCmd(FSMC_Bank2_NAND, ENABLE);
K9F1G08U0E_ReadID(data);
printf("ID: ");
dump_data(data, sizeof(data));
read_check();
while (1)
__WFI();
}
K9F1G08U0E.h:
#define NANDFLASH2 (*(volatile uint8_t *)0x70000000)
#define NANDFLASH2_32 (*(volatile uint32_t *)0x70000000)
#define NANDFLASH2C (*(volatile uint8_t *)0x70010000)
#define NANDFLASH2A (*(volatile uint8_t *)0x70020000)
#define NANDFLASH2A16 (*(volatile uint16_t *)0x70020000)
#define NANDFLASH2A32 (*(volatile uint32_t *)0x70020000)
#define NANDFLASH2S_FAIL 0x01
#define NANDFLASH2S_READY 0x40
#define NANDFLASH2S_NOPROTECTION 0x80
uint8_t K9F1G08U0E_EraseBlock(uint16_t block);
uint8_t K9F1G08U0E_ProgramPage(uint16_t page, const void *data);
void K9F1G08U0E_ReadID(uint8_t data[5]);
uint8_t K9F1G08U0E_ReadPage(uint16_t page, void *data);
void K9F1G08U0E_Reset(void);
K9F1G08U0E.c:
#include <stdio.h>
#include <stm32f10x.h>
#include "K9F1G08U0E.h"
/* See [Figure 2] K9F1G08U0E Array Organization */
/* Column (byte) address byte 1 and 2: A7~0, A11~8; Range: 0x0000~0x083f (or 0x07ff) */
/* Row (page) address byte 1 and 2: A19~12, A27~20 Range: 0x00000000~0xffff0000 */
static void K9F1G08U0E_Wait(void);
/* Block count: 1024, each block has 64 pages and each page is 2KB */
uint8_t K9F1G08U0E_EraseBlock(uint16_t block)
{
NANDFLASH2C = 0x60;
NANDFLASH2A16 = block << 6;
NANDFLASH2C = 0xd0;
K9F1G08U0E_Wait();
return (NANDFLASH2 & NANDFLASH2S_FAIL) == 0;
}
/* Page count: 65536 */
uint8_t K9F1G08U0E_ProgramPage(uint16_t page, const void *data)
{
uint16_t i;
NANDFLASH2C = 0x80;
NANDFLASH2A32 = page << 16;
FSMC_NANDECCCmd(FSMC_Bank2_NAND, ENABLE);
for (i = 0; i < 2048; i++)
NANDFLASH2 = *((const uint8_t *)data + i);
while (FSMC_GetFlagStatus(FSMC_Bank2_NAND, FSMC_FLAG_FEMPT) == RESET); // 获取ECC码前必须等待FIFO变空
NANDFLASH2_32 = FSMC_GetECC(FSMC_Bank2_NAND);
FSMC_NANDECCCmd(FSMC_Bank2_NAND, DISABLE);
NANDFLASH2C = 0x10;
K9F1G08U0E_Wait();
return (NANDFLASH2 & NANDFLASH2S_FAIL) == 0;
}
void K9F1G08U0E_ReadID(uint8_t data[5])
{
uint8_t i;
NANDFLASH2C = 0x90;
NANDFLASH2A = 0x00;
for (i = 0; i < 5; i++)
data[i] = NANDFLASH2;
}
// 板上两个VCC和GND都必须接到电源上才可以使用此函数!
uint8_t K9F1G08U0E_ReadPage(uint16_t page, void *data)
{
uint16_t i;
uint32_t ecc[2];
FSMC_NANDECCCmd(FSMC_Bank2_NAND, ENABLE);
NANDFLASH2C = 0x00;
NANDFLASH2A32 = page << 16;
NANDFLASH2C = 0x30;
for (i = 0; i < 2048; i++)
*((uint8_t *)data + i) = NANDFLASH2;
ecc[0] = NANDFLASH2_32;
while (FSMC_GetFlagStatus(FSMC_Bank2_NAND, FSMC_FLAG_FEMPT) == RESET);
ecc[1] = FSMC_GetECC(FSMC_Bank2_NAND);
FSMC_NANDECCCmd(FSMC_Bank2_NAND, DISABLE);
return ecc[0] == ecc[1];
}
/*
// 备用函数, 速度大约比上面的慢4倍
uint8_t K9F1G08U0E_ReadPage(uint16_t page, void *data)
{
uint16_t i;
uint8_t ecc1[4];
uint32_t ecc2;
FSMC_NANDECCCmd(FSMC_Bank2_NAND, ENABLE);
NANDFLASH2C = 0x00;
NANDFLASH2A32 = page << 16;
NANDFLASH2C = 0x30;
for (i = 0; i < 2048; i++)
{
*((uint8_t *)data + i) = NANDFLASH2;
NANDFLASH2C = 0x05;
NANDFLASH2A16 = i + 1;
NANDFLASH2C = 0xe0;
}
for (i = 0; i < 4; i++)
{
ecc1[i] = NANDFLASH2;
if (i != 3)
{
NANDFLASH2C = 0x05;
NANDFLASH2A16 = i + 2049;
NANDFLASH2C = 0xe0;
}
}
while (FSMC_GetFlagStatus(FSMC_Bank2_NAND, FSMC_FLAG_FEMPT) == RESET);
ecc2 = FSMC_GetECC(FSMC_Bank2_NAND);
FSMC_NANDECCCmd(FSMC_Bank2_NAND, DISABLE);
return *(uint32_t *)ecc1 == ecc2;
}
*/
void K9F1G08U0E_Reset(void)
{
NANDFLASH2C = 0xff;
}
static void K9F1G08U0E_Wait(void)
{
NANDFLASH2C = 0x70;
while ((NANDFLASH2 & NANDFLASH2S_READY) == 0);
}
static uint8_t K9F1G08U0E_ReadPage2(uint16_t page, void *data);
uint8_t K9F1G08U0E_ReadPage(uint16_t page, void *data)
{
uint16_t i;
uint32_t ecc[2];
FSMC_NANDECCCmd(FSMC_Bank2_NAND, ENABLE);
NANDFLASH2C = 0x00;
NANDFLASH2A32 = page << 16;
NANDFLASH2C = 0x30;
for (i = 0; i < 2048; i++)
*((uint8_t *)data + i) = NANDFLASH2;
ecc[0] = NANDFLASH2_32;
while (FSMC_GetFlagStatus(FSMC_Bank2_NAND, FSMC_FLAG_FEMPT) == RESET);
ecc[1] = FSMC_GetECC(FSMC_Bank2_NAND);
FSMC_NANDECCCmd(FSMC_Bank2_NAND, DISABLE);
if (ecc[0] == ecc[1])
return 1;
else
return K9F1G08U0E_ReadPage2(page, data);
}
static uint8_t K9F1G08U0E_ReadPage2(uint16_t page, void *data)
{
uint16_t i;
uint8_t ecc1[4];
uint32_t ecc2;
FSMC_NANDECCCmd(FSMC_Bank2_NAND, ENABLE);
NANDFLASH2C = 0x00;
NANDFLASH2A32 = page << 16;
NANDFLASH2C = 0x30;
for (i = 0; i < 2048; i++)
{
*((uint8_t *)data + i) = NANDFLASH2;
NANDFLASH2C = 0x05;
NANDFLASH2A16 = i + 1;
NANDFLASH2C = 0xe0;
}
for (i = 0; i < 4; i++)
{
ecc1[i] = NANDFLASH2;
if (i != 3)
{
NANDFLASH2C = 0x05;
NANDFLASH2A16 = i + 2049;
NANDFLASH2C = 0xe0;
}
}
while (FSMC_GetFlagStatus(FSMC_Bank2_NAND, FSMC_FLAG_FEMPT) == RESET);
ecc2 = FSMC_GetECC(FSMC_Bank2_NAND);
FSMC_NANDECCCmd(FSMC_Bank2_NAND, DISABLE);
return *(uint32_t *)ecc1 == ecc2;
}
通过调整FSMC时序的延时时间可以大幅度提高读取速度,添加如下代码:
fsmc_timing.FSMC_HiZSetupTime = 0;
fsmc_timing.FSMC_HoldSetupTime = 1;
fsmc_timing.FSMC_SetupTime = 0;
fsmc_timing.FSMC_WaitSetupTime = 2;
可以发现,即便是使用K9F1G08U0E_ReadPage2函数来读取,也能在瞬间完成。
//STM32F407VE FSMC NAND Flash
//SYSCLK=168.00MHz HCLK=168.00MHz PCLK1=42.00MHz PCLK2=84.00MHz
//HSIRDY=1, HSERDY=1, LSIRDY=0, LSERDY=1, SYSCLK=8
fsmc_timing.FSMC_HiZSetupTime = 0;
fsmc_timing.FSMC_HoldSetupTime = 8;
fsmc_timing.FSMC_SetupTime = 0;
fsmc_timing.FSMC_WaitSetupTime = 3;
为了确定这个问题是不是因为连线太长了导致的,笔者又把该模块插在了STM32F207VE板子上,线的长度大概为5cm左右,比之前的20cm杜邦线短了近四分之一。