STM32F103ZE单片机FSMC接口读取NAND Flash芯片K9F1G08U0E的数据时出现数据丢失的解决办法
【问题】
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 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);
}
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 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杜邦线短了近四分之一。
STM32F103ZE单片机FSMC接口读取NAND Flash芯片K9F1G08U0E的数据时出现数据丢失的解决办法相关推荐
- 镁光256Gb NAND Flash芯片介绍
总体概述 该芯片是一款典型的大容量NAND Flash存储颗粒,支持Open NAND Flash Interface (ONFI) 2.1的接口标准,采用ONFI NANDFlash的操作协议.该芯 ...
- STM32F407 FSMC驱动MT29F4G08A NAND FLASH源代码分享
一.MT29F4G08A概述 MT29F4G08是一颗 512MB 的 NAND FLASH 芯片相对于 SPI FLASH( W25Q256)和 SD 卡等存储设备,NAND FLASH 采用 8 ...
- 低功耗SD\SPI NAND Flash芯片
目前市面上很多SD/ SPI 的NAND FLASH芯片普遍面临功耗过高的情况,尤其是应用在穿戴类产品,包括手环,手表,耳机,心电贴等产品的时候,由于用户对于穿戴产品的待机时长要求很高,从而导致所有穿 ...
- 使用STM32F103ZET6驱动NAND FLASH(K9F1G08U0E)
经过2天的学习从不了解NAND FLASH,到驱动NAND FLASH完成一些简单的写,擦除,读取状态,复位等操作. 方法:使用正点原子的ST32F103ZET6和NAND FLASH(K9F1G08 ...
- 串口发数据到android数据错误,51单片机通过蓝牙串口模块发送JSON数据给安卓手机故障解决办法...
原标题:51单片机通过蓝牙串口模块发送JSON数据给安卓手机故障解决办法 JSON(Java Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式.它基于 ECMA (欧洲计 ...
- 对于stm32F103芯片读AT24C512得到的数据全是0xFF的解决办法
对于stm32F103芯片读AT24C512得到的数据全是0xFF的解决办法 由于项目需要,将正点原子开发板的at24c02换成了at24c512,结果读取数据全是0xFF,经过网上的资料发现是读的驱 ...
- Spark SQL读取Oracle的number类型的数据时精度丢失问题
Spark SQL读取Oracle的number类型的数据时精度丢失问题 在程序开发中,使用到了sparkSQL读取Oracle数据库,发现当sparkSQL读取Oracle的number类型字段时, ...
- C语言在读取txt类型文件中的汉字字符串出现乱码的解决办法
题目 C语言在读取txt类型文件中的汉字字符串出现乱码的解决办法 以下是本篇文章正文内容,欢迎朋友们进行指正,一起探讨,共同进步.--来自考研路上的lwj 一.前言 当我们在练习文件这一章节时,因为需 ...
- 100脚的STM32F103VE单片机通过FSMC接口读写DS12C887时钟芯片中的寄存器
STM32F1系列的单片机本身自带的RTC实时时钟外设只是一个单纯的32位计数器,没有分立为年月日.小时.分钟.秒等寄存器,使用起来不是很方便.这时可以考虑使用外部RTC芯片,比如使用SPI接口的双向 ...
最新文章
- 观点 | AI 的偏差,取决于人们如何使用机器学习系统
- python的快速入门-1.1、Python快速入门(0529)
- keras:Sequential API 和 Function API
- ea 如何画bce备选架构图_GitHub - wongdean/rime-settings at 941ea079832ff211723bce1ce8e47344e527575e...
- 计算机图形学实验报告百度云盘,计算机图形学实验报告(一).doc
- default在php定义什么,default(T)的含义
- Bias and Debias in Recommender System: A Survey and Future Directions学习笔记
- 黑马程序员14套经典IT教程+面试宝典
- java学习总结(16.06.07)类的静态成员和非静态成员
- Windows 11 22H2 中文版、英文版 (x64、ARM64) 下载 (updated Jan 2023)
- 知乎高赞的7个顶级资源网站,记住了帮你减少一半的搜索时间
- 微生物组学测序十大错误认知
- GitHub开源协议
- 寒冬降临程序猿们如何过冬
- STM32——不同的按键对应实现不同功能的灯闪烁
- 前端:水平垂直居中的10种方法
- 美团、锤子科技 众多品牌从这里走出
- ZYNQ学习之路17.自定义SDSoC硬件平台
- 运维发布系统原型设计
- [数据结构] 无头结点的头插法建立单链表(c语言)