您的位置:首页 > 其它

微雪K9F1G08U0E NAND Flash模块读数据的时候出现部分字节漏读的问题及其解决方案

2018-01-04 21:46 471 查看
【现象】

数据能正常写入到存储器中,但读的时候会发现总是有一些字节出现丢失,如下图所示。





【程序】

以下程序直接用GPIO模拟了FSMC的时序来读写NAND Flash,方便从时序上诊断问题。

main.c:

#include <stdio.h>
#include <stm32f10x.h>
#include "NAND.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(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, '\r');
}
while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
USART_SendData(USART2, ch);
}
return ch;
}

void test(void)
{
NAND_ReadID(buffer);
printf("Device ID: ");
dump_data(buffer, 5);

NAND_ReadPage(64, buffer);
dump_data(buffer, 2048);
}

int main(void)
{
GPIO_InitTypeDef gpio;
USART_InitTypeDef usart;

RCC_SYSCLKConfig(RCC_SYSCLKSource_HSE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE, ENABLE);

gpio.GPIO_Mode = GPIO_Mode_AF_PP;
gpio.GPIO_Pin = GPIO_Pin_2;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpio);

GPIO_SetBits(GPIOD, GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7); // NOE=NWE=NE1=1
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
gpio.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_11 | GPIO_Pin_12; // NOE, NWE, CLE, ALE
GPIO_Init(GPIOD, &gpio);

// NWAIT接了外部上拉电阻, 只需保持默认的浮空输入

gpio.GPIO_Mode = GPIO_Mode_Out_OD; // NE1外部接了上拉电阻
gpio.GPIO_Pin = GPIO_Pin_7;
GPIO_Init(GPIOD, &gpio);

USART_StructInit(&usart);
usart.USART_BaudRate = 115200;
USART_Init(USART2, &usart);
USART_Cmd(USART2, ENABLE);
printf("STM32F103ZE NAND Flash by GPIO\n");

test();

while (1);
}NAND.h:
#define NAND_SetALE(v) GPIO_WriteBit(GPIOD, GPIO_Pin_12, v)
#define NAND_SetCLE(v) GPIO_WriteBit(GPIOD, GPIO_Pin_11, v)
#define NAND_SetNE1(v) GPIO_WriteBit(GPIOD, GPIO_Pin_7, v)
#define NAND_SetNOE(v) GPIO_WriteBit(GPIOD, GPIO_Pin_4, v)
#define NAND_SetNWE(v) GPIO_WriteBit(GPIOD, GPIO_Pin_5, v)

uint8_t NAND_Read(void);
uint8_t NAND_ReadData(void);
void NAND_ReadID(uint8_t data[5]);
void NAND_ReadPage(uint16_t page, uint8_t data[2048]);
void NAND_SetMode(GPIOMode_TypeDef mode);
void NAND_Wait(void);
void NAND_Write(uint8_t data);
void NAND_WriteAddress(uint8_t addr);
void NAND_WriteCommand(uint8_t cmd);
NAND.c:
#include <stm32f10x.h>
#include "NAND.h"

//#define NAND_RANDOMREAD

uint8_t NAND_Read(void)
{
uint16_t data = GPIO_ReadInputData(GPIOD);
data = ((data & 0x03) << 2) | (data >> 14);
data |= (GPIO_ReadInputData(GPIOE) >> 3) & 0xf0;
return (uint8_t)data;
}

uint8_t NAND_ReadData(void)
{
uint8_t data;
NAND_SetNE1(Bit_RESET);
NAND_SetNOE(Bit_RESET);
data = NAND_Read();
NAND_SetNOE(Bit_SET);
NAND_SetNE1(Bit_SET);
return data;
}

void NAND_ReadID(uint8_t data[5])
{
uint8_t i;
NAND_WriteCommand(0x90);
NAND_WriteAddress(0x00);
NAND_Wait();
for (i = 0; i < 5; i++)
data[i] = NAND_ReadData();
}

void NAND_ReadPage(uint16_t page, uint8_t data[2048])
{
uint16_t i;
NAND_WriteCommand(0x00);
NAND_WriteAddress(0x00);
NAND_WriteAddress(0x00);
NAND_WriteAddress(page & 0xff);
NAND_WriteAddress(page >> 8);
NAND_WriteCommand(0x30);
for (i = 0; i < 2048; i++)
{
data[i] = NAND_ReadData();
#ifdef NAND_RANDOMREAD
// 每读完一个字节, 都指明下一个字节的地址 (随机读)
NAND_WriteCommand(0x05);
NAND_WriteAddress((i + 1) & 0xff);
NAND_WriteAddress((i + 1) >> 8);
NAND_WriteCommand(0xe0);
#endif
}
}

void NAND_SetMode(GPIOMode_TypeDef mode)
{
GPIO_InitTypeDef gpio;
gpio.GPIO_Mode = mode;
gpio.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_14 | GPIO_Pin_15;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &gpio);
gpio.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10;
GPIO_Init(GPIOE, &gpio);
}

void NAND_Wait(void)
{
while (GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_6) == Bit_RESET);
}

void NAND_Write(uint8_t data)
{
uint16_t port[2];
port[0] = GPIO_ReadOutputData(GPIOD);
port[1] = GPIO_ReadOutputData(GPIOE);

port[0] = (port[0] & 0x3ffc) | ((data & 0x03) << 14) | ((data >> 2) & 0x03);
port[1] = (port[1] & 0xf87f) | ((data & 0xf0) << 3);
GPIO_Write(GPIOD, port[0]);
GPIO_Write(GPIOE, port[1]);
}

void NAND_WriteAddress(uint8_t addr)
{
NAND_SetALE(Bit_SET);
NAND_SetNE1(Bit_RESET);

NAND_SetMode(GPIO_Mode_Out_PP);
NAND_SetNWE(Bit_RESET);
NAND_Write(addr);
NAND_SetNWE(Bit_SET);
NAND_SetMode(GPIO_Mode_IN_FLOATING);

NAND_SetNE1(Bit_SET);
NAND_SetALE(Bit_RESET);
}

void NAND_WriteCommand(uint8_t cmd)
{
NAND_SetCLE(Bit_SET);
NAND_SetNE1(Bit_RESET);

NAND_SetMode(GPIO_Mode_Out_PP);
NAND_SetNWE(Bit_RESET);
NAND_Write(cmd);
NAND_SetNWE(Bit_SET);
NAND_SetMode(GPIO_Mode_IN_FLOATING);

NAND_SetNE1(Bit_SET);
NAND_SetCLE(Bit_RESET);
}


【解决方案】
不使用默认的顺序读方式,改用随机读。即每读完一个字节,都指明下一个字节的地址(在上面的程序中定义NAND_RANDOMREAD)。这是唯一、最可靠的解决办法,基本上能够100%保证读到的数据是正确的。

笔者已经在很多开发板上测试过这个程序了。

当连接NAND模块和单片机开发板的杜邦线为40cm的时候,无论是顺序读还是随机读都不能正确的读出数据,就连5字节的器件ID都不能正确读取!

当杜邦线的长度为20cm的时候,器件ID两种方式都可以正确读出来,顺序读数据基本上是100%出错,但随机读数据能够100%读正确。

当线的长度改为5cm的时候(这大概是在洞洞板上焊接飞线的极限了),单片机不打开串口,只开FSMC,可以做到用顺序读方式读完125页只有一两页是错误的,一旦打开了串口,就会有50%以上的页出错。

在上面的用GPIO方式模拟时序的程序中,在NAND_ReadPage函数和NAND_Read函数里面加delay()函数延时,均不能解决问题,花了几十秒时间读出来的一页数据,仍然会提前结束。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  stm32 单片机 NAND Flash
相关文章推荐