微雪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:
【解决方案】
不使用默认的顺序读方式,改用随机读。即每读完一个字节,都指明下一个字节的地址(在上面的程序中定义NAND_RANDOMREAD)。这是唯一、最可靠的解决办法,基本上能够100%保证读到的数据是正确的。
笔者已经在很多开发板上测试过这个程序了。
当连接NAND模块和单片机开发板的杜邦线为40cm的时候,无论是顺序读还是随机读都不能正确的读出数据,就连5字节的器件ID都不能正确读取!
当杜邦线的长度为20cm的时候,器件ID两种方式都可以正确读出来,顺序读数据基本上是100%出错,但随机读数据能够100%读正确。
当线的长度改为5cm的时候(这大概是在洞洞板上焊接飞线的极限了),单片机不打开串口,只开FSMC,可以做到用顺序读方式读完125页只有一两页是错误的,一旦打开了串口,就会有50%以上的页出错。
在上面的用GPIO方式模拟时序的程序中,在NAND_ReadPage函数和NAND_Read函数里面加delay()函数延时,均不能解决问题,花了几十秒时间读出来的一页数据,仍然会提前结束。
数据能正常写入到存储器中,但读的时候会发现总是有一些字节出现丢失,如下图所示。
【程序】
以下程序直接用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()函数延时,均不能解决问题,花了几十秒时间读出来的一页数据,仍然会提前结束。
相关文章推荐
- 新导入的工程出现乱码问题 及其 R文件没有生成 解决方案.
- java学习日记_57:Scanner获取数据出现的小问题及解决方案
- 应聘Java笔试时可能出现问题及其答案(第二版 第三部分)
- Ubuntu不能利用ssh进行远程连接的解决方案及其解决过程中出现的问题
- sqoop 导入数据的时候出现Unsupported major.minor version 52.0的问题描述
- 关于数据表映射在更新时候出现的一些问题
- GROUP BY 与count(*) 连用的时候出现问题,读不到数据
- 使用Kettle抽取数据时,出现中文乱码问题解决方案
- [转] 应聘Java笔试时可能出现问题及其答案(第十部分)
- 常见对象_Scanner获取数据出现的小问题及解决方案
- 从linux下的mongo恢复数据到windows下的mongo库时出现的问题及解决方案
- 应聘Java笔试时可能出现问题及其答案(第二版 第四部分)
- 网页客户端使用php访问mysql数据时出现NetworkError: 500 Internal Server Error问题的解决方案
- cmd路径包含空格时易出现的问题及其解决方案
- IIS7中 Get方式提交数据时超过2K时出现问题的解决方案
- 使用ObjectOutputStream进行socket通信的时候出现固定读到四个字节乱码的问题
- [转] 应聘Java笔试时可能出现问题及其答案(第十一部分)
- 使用Kettle抽取数据时,出现中文乱码问题解决方案
- 在论坛中出现各种疑难问题: MSSQL 导入导出的时候提示 无法检索源数据和目标数据的列信息
- mysql写入数据库后,数据出现乱码问题的解决方案