【问题】

STM32单片机使用FSMC读取K9F1G08U0E NAND Flash时,出现部分字节丢失的情况。例如:Flash存储器中存储有连续的0xff字节,则在进行连续读(Page Read)操作时可能会丢失部分0xff。

例如,写入以下数据到某一页的开头(如地址0x00800000):

{0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x21, 0xff, 0xff, 0xff, 0xff, 0x74, 0x68, 0x65, 0x73, 0x65, 0xff, 0xff, 0xff, 0xff, 0x77, 0x68, 0x61, 0x74, 0x3f, 0xff, 0xff, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x21, 0xff}
#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);
}
用上述函数从该页首地址连续读取50字节内容:
uint8_t data[50];
K9F1G08U0E_ReadPage(0x800000, data, sizeof(data));
则读出来的内容是:54686973206973206120737472696E6721FFFF7468657365FFFFFFFF776861743FFFFF48656C6C6F20576F726C6421FFFFFF
4个0xff变成了两个0xff。
如果打开串口接收中断,在中断处理函数中每接收到一个字符a,就从0x800000地址开始读取一个字节并打印。收到b接着读取一个字节,收到c读取两个字节,收到d读取四个字节:
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);
}
那么,程序运行时先按下a,然后按住b不放(对于STM32F103,打印每一行都需要花13*8/0.115200≈903μs的时间,相当于903*72=65016个HCLK时钟周期),则只能读到三个0xff:
[Byte] 0x21
[Byte] 0xff
[Byte] 0xff
[Byte] 0xff
[Byte] 0x74
若按下a后,每隔一段时间按b,则可以完整地读到4个0xff:
[Byte] 0x21
[Byte] 0xff
[Byte] 0xff
[Byte] 0xff
[Byte] 0xff
[Byte] 0x74
如果这4个字节的内容不是0xff,则不会出现任何问题。

【解决方法】

读取第一字节后,每读取一个字节,都发送一次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 0x80uint8_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);
}
如果最后实在没有办法解决问题再使用备用函数。
【示例程序(推荐):主读取函数执行后若出现ECC校验错误,则调用备用读取函数】
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;elsereturn 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开发板上的测试】
使用40cm长的杜邦线时,K9F1G08U0E_ReadPage和K9F1G08U0E_ReadPage2函数均不能正常工作,函数返回0,甚至K9F1G08U0E_ReadID都不能正确读取。
不修改程序,改接20cm的杜邦线,只有两页(4KB)能够用K9F1G08U0E_ReadPage函数读取成功,其余页用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开发板上的测试】
为了确定这个问题是不是因为连线太长了导致的,笔者又把该模块插在了STM32F207VE板子上,线的长度大概为5cm左右,比之前的20cm杜邦线短了近四分之一。
工程下载地址:https://pan.baidu.com/s/1nvaHTSt
这一次距离已经近的不能再近了,连在MCU上侧的连线有6条,右侧(最近侧)有4条,下侧有4条,再加两根电源线。最长的线也就5cm左右。
先运行一下程序(OLED_ENABLE),把结果显示在OLED屏上,全是Y,好像都可以成功读取。。
(Y表示K9F1G08U0E_ReadPage函数直接就能读取成功,感叹号表示ECC校验错误但K9F1G08U0E_ReadPage2能读取成功,X表示两个函数都不能读取成功,N表示K9F1G08U0E_ReadPage函数读取后出现ECC校验错误且程序没有尝试用Read2函数再次读取)
可是,一旦在fputc函数中加入了串口输出,或者把操作OLED屏的代码注释掉,就只有部分页能成功读取。问题仍没有解决。
串口输出内容:启用了OLED屏(定义了OLED_ENABLE,fputc函数中不含串口操作代码,只含写OLED屏的代码)
页面全部读取成功,ECC校验通过,但有时有一个页面读取不成功。
串口输出内容:禁用了OLED屏,fputc把字符先放入缓冲区,最后统一输出到串口
OLED屏一关,就有一大片的页面不能正常读取,必须用Read2函数重读才能通过ECC校验。
【结论】
(Read函数为连续读取,Read2函数为随机读取)
当连线的长度为40cm时,该NAND Flash芯片完全无法正常工作(全是X),无论是连续读取还是随机读取都不能成功,就连器件ID也无法读取。
当连线的长度为20cm时,几乎所有的页面都不能连续读取(全是感叹号),最多只有两个页面能连续读取,但所有的页面均能随机读取并通过ECC校验。
当连线的长度为5cm时,有一部分页面能够连续读取并通过ECC校验,其他页面仍需要靠随机读取才能成功(感叹号和Y各占一半)。

STM32F103ZE单片机FSMC接口读取NAND Flash芯片K9F1G08U0E的数据时出现数据丢失的解决办法相关推荐

  1. 镁光256Gb NAND Flash芯片介绍

    总体概述 该芯片是一款典型的大容量NAND Flash存储颗粒,支持Open NAND Flash Interface (ONFI) 2.1的接口标准,采用ONFI NANDFlash的操作协议.该芯 ...

  2. STM32F407 FSMC驱动MT29F4G08A NAND FLASH源代码分享

    一.MT29F4G08A概述 MT29F4G08是一颗 512MB 的 NAND FLASH 芯片相对于 SPI FLASH( W25Q256)和 SD 卡等存储设备,NAND FLASH 采用 8 ...

  3. 低功耗SD\SPI NAND Flash芯片

    目前市面上很多SD/ SPI 的NAND FLASH芯片普遍面临功耗过高的情况,尤其是应用在穿戴类产品,包括手环,手表,耳机,心电贴等产品的时候,由于用户对于穿戴产品的待机时长要求很高,从而导致所有穿 ...

  4. 使用STM32F103ZET6驱动NAND FLASH(K9F1G08U0E)

    经过2天的学习从不了解NAND FLASH,到驱动NAND FLASH完成一些简单的写,擦除,读取状态,复位等操作. 方法:使用正点原子的ST32F103ZET6和NAND FLASH(K9F1G08 ...

  5. 串口发数据到android数据错误,51单片机通过蓝牙串口模块发送JSON数据给安卓手机故障解决办法...

    原标题:51单片机通过蓝牙串口模块发送JSON数据给安卓手机故障解决办法 JSON(Java Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式.它基于 ECMA (欧洲计 ...

  6. 对于stm32F103芯片读AT24C512得到的数据全是0xFF的解决办法

    对于stm32F103芯片读AT24C512得到的数据全是0xFF的解决办法 由于项目需要,将正点原子开发板的at24c02换成了at24c512,结果读取数据全是0xFF,经过网上的资料发现是读的驱 ...

  7. Spark SQL读取Oracle的number类型的数据时精度丢失问题

    Spark SQL读取Oracle的number类型的数据时精度丢失问题 在程序开发中,使用到了sparkSQL读取Oracle数据库,发现当sparkSQL读取Oracle的number类型字段时, ...

  8. C语言在读取txt类型文件中的汉字字符串出现乱码的解决办法

    题目 C语言在读取txt类型文件中的汉字字符串出现乱码的解决办法 以下是本篇文章正文内容,欢迎朋友们进行指正,一起探讨,共同进步.--来自考研路上的lwj 一.前言 当我们在练习文件这一章节时,因为需 ...

  9. 100脚的STM32F103VE单片机通过FSMC接口读写DS12C887时钟芯片中的寄存器

    STM32F1系列的单片机本身自带的RTC实时时钟外设只是一个单纯的32位计数器,没有分立为年月日.小时.分钟.秒等寄存器,使用起来不是很方便.这时可以考虑使用外部RTC芯片,比如使用SPI接口的双向 ...

最新文章

  1. 观点 | AI 的偏差,取决于人们如何使用机器学习系统
  2. python的快速入门-1.1、Python快速入门(0529)
  3. keras:Sequential API 和 Function API
  4. ea 如何画bce备选架构图_GitHub - wongdean/rime-settings at 941ea079832ff211723bce1ce8e47344e527575e...
  5. 计算机图形学实验报告百度云盘,计算机图形学实验报告(一).doc
  6. default在php定义什么,default(T)的含义
  7. Bias and Debias in Recommender System: A Survey and Future Directions学习笔记
  8. 黑马程序员14套经典IT教程+面试宝典
  9. java学习总结(16.06.07)类的静态成员和非静态成员
  10. Windows 11 22H2 中文版、英文版 (x64、ARM64) 下载 (updated Jan 2023)
  11. 知乎高赞的7个顶级资源网站,记住了帮你减少一半的搜索时间
  12. 微生物组学测序十大错误认知
  13. GitHub开源协议
  14. 寒冬降临程序猿们如何过冬
  15. STM32——不同的按键对应实现不同功能的灯闪烁
  16. 前端:水平垂直居中的10种方法
  17. 美团、锤子科技 众多品牌从这里走出
  18. ZYNQ学习之路17.自定义SDSoC硬件平台
  19. 运维发布系统原型设计
  20. [数据结构] 无头结点的头插法建立单链表(c语言)

热门文章

  1. CSDN客服联系方式(有QQ联系方式)
  2. 【MATLAB】遗传算法(GA)求解TSP问题
  3. 物联网外设学习笔记-语音识别模块
  4. xp系统扫描仪服务器,WinXP如何安装扫描仪?WinXP扫描仪安装步骤
  5. ARM与NEON加速指令
  6. 任务11 雷达与超声波感应灯(arduino程序)
  7. 拒绝跟风勇于创新的诚意之作:vivo NEX评测体验
  8. 学习Python数据分析,需要几个阶段?
  9. 支持电竞比分实时查询的软件~和比分网之间的对比
  10. 欧海:海梦易商道APP是教育与商业的双创新