GD32450Z NAND FLASH介绍及初始化设置——超详细教程教会调试NAND FLASH

1.  基本知识介绍

本文介绍了GD32F4开发板NAND FLASH的初始化配置过程,使用GD32450Z芯片对NAND FLASH(HY27US08281A)进行配置,实现读写操作。

1.1  NAND FLASH介绍

NAND FLASH 存储器具有容量较大,改写速度快等优点,适用于大量数据的存储,在业界得到了广泛应用,如: SD 卡、TF 卡、U盘等,一般都是采用 NAND FLASH 作为存储的。

1.1.1  NAND FLASH信号线

信号线说明
CLE

命令锁存使能,高电平有效,表示写入的是命令

ALE

地址锁存使能,高电平有效,表示写入的是地址

CE#

芯片使能,低电平有效,用于选中 NAND 芯片

RE#

读使能,低电平有效,用于读取数据

WE#写使能,低电平有效,用于写入数据
WP#

写保护,低电平有效

R/P

就绪/忙,注意用于判断编程/擦除操作是否完成

I/00-7

地址/数据 输入/输出口

NAND FLASH 信号线如上,因为地址/数据是共用数据线的,所以必须有 CLE/ALE 信号,告诉 NAND FLASH,发送的数据是命令还是地址。

1.1.2  NAND FLASH储存结构

通过查阅HY27US08281A的数据数据手册,可知一块该芯片由1024个Blocks构成,每个Blocks由32个Page构成,每个Page有512+16字节(528字节)的存储容量。所以,HY27US08281A的总容量为:1024*32*528=17301504字节( 16MB)。具体 block、 page 等的个数根据 NAND FLASH 型号的不同,会有所区别,应查看对应 NAND FLASH 芯片的数据手册。

NAND FLASH 的最小擦除单位是 block,对应HY27US08281A来说是(16+0.5)K字节。NAND FLASH 的写操作具有只可以写 0,不能写 1 的特性,所以,在写数据的时候,必须先擦除 block(擦除后, block 数据全部为 1),才可以写入。

NandFlash是以页(Page)为最小单位进行读写的,以块(Block)为最小单位进行擦除的,也就是说当我们给定了读取的起始位置后,读操作将从该位置开始,连续读取到本Page的最后一个 Byte为止。

NAND FLASH 的地址分为三类:块地址( Block Address)、页地址( Page Address)和列地址( Column Address)。以HY27US08281A为例,通过查阅数据手册,这三个地址,通过3个周期发送,通过查看时序图第一个循环发送列地址( Column Address),第二三个循环发送页地址( Page Address)。(不同的芯片地址发送的方式不一样,具体查看数据手册)

1.1.3  HY27US08281A地址计算方式

NAND FLASH读写操作时需要我们传送行地址和列地址,以HY27US08281A为例,一共有1024个块,每个块32页,每个页是512+16 Byte。假设访问第 500个块中的第 16 页中的 400字节处的信息,具体的物理地址为:

物理地址 = 块大小×块号+页大小×页号+页内地址 = 16K × 500 + 512B × 16 + 400B = 0X007D2190。

由图所示,HY27US08281A寻址分为三个循环发送,分为1个列(Column),2个行(Row)周期。由此其发送的过程为:

0X007D2190 = 0111 1101 0010 0001 1001 0000

1st周期,A7~A0:     1001 0000 = 0x90

2nd周期,A9~A16:    0010 0001 = 0x21

3rd周期,A17~A23:   0111 1101 = 0x7D

第一个周期发送的为页内偏移地址,512B理论上需要9位数据来表示,HY27US08281A将地址信号A8的高低电平通过发送PAGE PROGRAM控制命令(介绍见1.1.4小节)0X00 和 0X01 来代表。第二第三个循环发送页地址,其中第三个循环的最高位必须置0。

1.1.4  控制命令

NAND FLASH 的驱动需要用到一系列命令,下表我们列出常用的一些命令,方便大家了解 NAND FLASH 的操作,具体的操作下文会说明。(这些指令对于大部分NAND是通用的,具体可以查看数据手册)

命令 (HEX)名称说明
1#2#
0X90READID

读取NAND 的ID和相关特性,可以此判断NAND的容量等信息

0XEFSET FEATURE

设置 NAND 的相关参数,比如时序模式

0XFFRESET复位NAND
0X70READ STATUS读取 NAND 的状态,比如可以判断编程/擦除操作是否完成
0X00/010X30READ PAGE该指令由 2 部分组成(分 2 次发),用于读取一个Page 里面的数据(不能跨页读)
0X800X10WRITE PAGE该指令由 2 部分组成(分 2 次发),用于写入一个Page 的数据(不能跨页写)
0X600XD0ERASE BLOCK

该指令由 2 部分组成(分 2 次发),用于擦除一个Block

0X000X35

READ FOR INTERNAL DATA MOVE

这两个指令(分四次发),组成 NAND 的内部数据移动操作,该操作可以实现拷贝一个 Page 到另外一个 Page(仅限同一 plane 内),且支持拷贝时写入数据,该操作可以极大的方便数据写入

0X800X10

PROGRAM FOR INTERNAL DATA MOVE

1.2  外部存储器控制器(EXMC)

GD EXMC可以把AMBA协议转换为专用的片外存储器通信协议, 包括SRAM, ROM, NOR Flash, NAND Flash, PC Card和SDRAM。EXMC模块划分为许多个子Bank(如下图所示),每个Bank支持特定的存储器类型,用户可以通过对Bank的寄存器配置来控制外部存储器。

 EXMC将外部存储器分成多个Bank,每个Bank占256M字节,其中Bank1和Bank2用于连接NAND Flash,且每个Bank连接一个NAND。同时,每个bank可以分为通用存储空间和属性存储空间,通用存储空间又可以划分为数据区域,指令区域和地址区域。
接下来讲解一下NAND FLASH的初始化配置。

2.  NAND FLASH初始化配置

(1)首先定义两个NAND初始化结构体:

exmc_nand_parameter_struct nand_init_struct;
exmc_nand_pccard_timing_parameter_struct nand_timing_init_struct;

(2)引脚初始化设置

具体的引脚配置需要查看原理图,相对应的进行修改。注意其中的 NWAIT 为输入引脚,其余的信号线、控制线设置为复用输出,AF_12。

  /* -----------引脚初始化--------------*/
    rcu_periph_clock_enable(RCU_EXMC);
    rcu_periph_clock_enable(RCU_GPIOD);
    rcu_periph_clock_enable(RCU_GPIOE);
	
    /* common GPIO configuration */
		/* D2(PD0),D3(PD1),D0(PD14),D1(PD15) pin configuration */
    gpio_af_set(GPIOD, GPIO_AF_12, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_14 | GPIO_PIN_15);
    gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_14 | GPIO_PIN_15);
    gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_14 | GPIO_PIN_15);

    /* D4(PE7),D5(PE8),D6(PE9),D7(PE10) pin configuration */
    gpio_af_set(GPIOE, GPIO_AF_12, GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10);
    gpio_mode_set(GPIOE, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10);
    gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10);

		/* CLE(PD11),ALE(PD12) pin configuration */
    gpio_af_set(GPIOD, GPIO_AF_12, GPIO_PIN_11 | GPIO_PIN_12);
    gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_11 | GPIO_PIN_12);
    gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_11 | GPIO_PIN_12);

    /* NOE(PD4),NWE(PD5) pin configuration */
    gpio_af_set(GPIOD, GPIO_AF_12, GPIO_PIN_4 | GPIO_PIN_5 );
    gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_4 | GPIO_PIN_5);
    gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4 | GPIO_PIN_5);
	
		/* NWAIT(PD6) pin configuration */
    gpio_mode_set(GPIOD, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN_6);

    /* NCE1(PD7) pin configuration */
    gpio_af_set(GPIOD, GPIO_AF_12, GPIO_PIN_7);
    gpio_mode_set(GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_7);
    gpio_output_options_set(GPIOD, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7);

(3)NAND 控制时序设置

EXMC可配置的时序参数如下表所示,用户可以根据需求和外部存储器的特性来进行相应的配置。

 

nand_timing_init_struct.setuptime = 10;					//存储器建立时间
nand_timing_init_struct.waittime = 10;					//存储器等待时间
nand_timing_init_struct.holdtime = 10;					//存储器保持时间
nand_timing_init_struct.databus_hiztime = 10;		//存储器数据总线高阻时间

(4)NAND 初始化参数设置

    nand_init_struct.nand_bank = EXMC_BANK1_NAND;							// 选择NAND bank1
    nand_init_struct.ecc_size = EXMC_ECC_SIZE_512BYTES;						// ECC页大小为512字节
    nand_init_struct.atr_latency = EXMC_ALE_RE_DELAY_2_HCLK;
    nand_init_struct.ctr_latency = EXMC_CLE_RE_DELAY_2_HCLK;
    nand_init_struct.ecc_logic = DISABLE;									//禁止ECC
    nand_init_struct.databus_width = EXMC_NAND_DATABUS_WIDTH_8B;	        //8位数据宽度
    nand_init_struct.wait_feature = DISABLE;								//关闭等待特性
    nand_init_struct.common_space_timing = &nand_timing_init_struct;        //时序设置
    nand_init_struct.attribute_space_timing = &nand_timing_init_struct;     //时序设置
    exmc_nand_init(&nand_init_struct);

(5)使能 NAND

    /* enable EXMC NAND bank1 */
    exmc_nand_enable(EXMC_BANK1_NAND);

以上便完成了NAND FLASH的初始化配置,接下来介绍NAND FLASH读写操作。

3.  NAND FLASH读写操作

不同NAND FLASH寻址方式不一样,移植时要对照数据手册对寻址相关的代码进行修改。

3.1  读取芯片ID

这里先从读取芯片ID简单操作说明起,以便理解NAND的工作过程。下面定义了NAND FLASH的控制、地址、数据存储的地址。我们可以通过 NAND_CMD_AREA 发送操作指令、NAND_ADDR_AREA 发送地址数据、NAND_DATA_AREA 写入或读取数据。

#define BANK_NAND_ADDR     ((uint32_t)0x70000000)

/* NAND area definition */
/* A16 = CLE high command area */
#define EXMC_CMD_AREA              (uint32_t)(1<<16)
/* A17 = ALE high address area */
#define EXMC_ADDR_AREA             (uint32_t)(1<<17)
/* data area */
#define EXMC_DATA_AREA             ((uint32_t)0x0000000)

/* define operating nand flash macro */
#define NAND_CMD_AREA       *(__IO uint8_t *)(BANK_NAND_ADDR | EXMC_CMD_AREA)
#define NAND_ADDR_AREA      *(__IO uint8_t *)(BANK_NAND_ADDR | EXMC_ADDR_AREA)
#define NAND_DATA_AREA      *(__IO uint8_t *)(BANK_NAND_ADDR | EXMC_DATA_AREA)

通过查询HY27US08281A的数据手册,向指令操作区写入读取芯片ID的控制命令为 0X90,在地址写入0X00,之后在两个循环中,将会输出芯片的ID。

 读取芯片ID代码如下:

void nand_read_id(nand_id_struct *nand_id)
{
    uint32_t data = 0;
	
    /* send command to the command area */
    NAND_CMD_AREA = NAND_CMD_READID;

    /* send address to the address area */
    NAND_ADDR_AREA = 0x00;

    /* read id from NAND flash */
    data = *(__IO uint32_t *)(BANK_NAND_ADDR | EXMC_DATA_AREA);

    nand_id->maker_id = ADDR_1ST_CYCLE(data);
    nand_id->device_id = ADDR_2ND_CYCLE(data);
}

3.2  写操作

NAND FLASH 的写操作时序图如下所示:首先向指令操作区写入控制命令0X80,之后的三个循环中发送写入地址,之后传输需要写入的数据,最后入控制命令0X10结束写操作。

 NAND FLASH(HY27US08281A)写入数据代码如下,其中PageNum为页地址; ColNum为列地址,数值为0~511(地址的计算方式详见1.1.3小节);*pBuffer 为要写入的数据;NumByteToWrite 为要写入数据的个数。

unsigned char Nand_WritePage(unsigned long PageNum,unsigned short ColNum,unsigned char *pBuffer,unsigned short NumByteToWrite)
{
	volatile unsigned short i=0;
	
	if(ColNum<256)
		NAND_CMD_AREA = 0x00;
	else 
		NAND_CMD_AREA = 0x01;

    /* send 1st cycle page programming command to the command area */
    NAND_CMD_AREA = 0x80;

    /* send address to the address area
                    bit7	bit6	bit5	bit4	bit3	bit2	bit1	bit0
       first byte:  A7		A6		A5		A4		A3		A2		A1		A0    			(column address)
       second byte: A16		A15		A14		A13		A12		A11		A10		A9    			(row address)
       third byte: 	0		A23		A22		A21		A20		A19		A18		A17   			(row address)
    */
	
	if(ColNum<256)
		NAND_ADDR_AREA = ColNum;	
	else 
		NAND_ADDR_AREA = ColNum-255;	
	
    NAND_ADDR_AREA = (uint8_t)((PageNum)& 0xFF);		//页地址
    NAND_ADDR_AREA = (uint8_t)(((PageNum)& 0xFF00) >> 8);
		delay_ms(30);
	
    /* write data to data area */
		for(i=0;i<NumByteToWrite;i++)		//写入数据
		{
			*(volatile unsigned char*)BANK_NAND_ADDR = *(volatile unsigned char*)pBuffer++;
		}

    /* send 2nd cycle page programming command to the command area */
    NAND_CMD_AREA = 0x10;

    /* check operation status */
    if(NAND_READY == exmc_nand_getstatus()) {
        return NAND_OK;
    }
    return NAND_FAIL;
}

3.3  读操作

NAND FLASH 的写操作时序图如下所示:首先向指令操作区写入控制命令 0X00 或是 0X01(由列地址大小判断),之后的三个循环中发送写入地址,之后输入控制命令0X30,之后就可以依次读取出数据。

 NAND FLASH(HY27US08281A)读取数据代码如下,其中PageNum为页地址; ColNum为列地址,数值为0~511(地址的计算方式详见1.1.3小节);*pBuffer 为存放读取数据的数组;NumByteToWrite 为要读出数据的个数。

unsigned char PhyNand_ReadPage(unsigned long PageNum,unsigned short ColNum,unsigned char *pBuffer,unsigned short NumByteToRead)
{
	volatile unsigned short i=0;

	/* send 1st cycle read command to the command area */
	if(ColNum>255){
		NAND_CMD_AREA = 0x01;
		NAND_ADDR_AREA = ColNum-255;
	}
	else {
		NAND_CMD_AREA = 0x00;
		NAND_ADDR_AREA = ColNum;
	}
    /* send address to the address area
                    bit7	bit6	bit5	bit4	bit3	bit2	bit1	bit0
       first byte:  A7		A6		A5		A4		A3		A2		A1		A0    			(column address)
       second byte: A16		A15		A14		A13		A12		A11		A10		A9    			(row address)
       third byte: 	0			A23		A22		A21		A20		A19		A18		A17   			(row address)
    */  
    NAND_ADDR_AREA = (uint8_t)((PageNum)& 0xFF);
    NAND_ADDR_AREA = (uint8_t)(((PageNum)& 0xFF00) >> 8);

    /* send 2nd cycle read command to the command area */
    NAND_CMD_AREA = NAND_CMD_READ1_2ND;

		delay_ms(30);
    /* read data to pbuffer */
		for(i=0;i<NumByteToRead;i++)
		{
			*(volatile unsigned char*)pBuffer++ = *(volatile unsigned char*)BANK_NAND_ADDR;
		}

    /* check operation status */
    if(NAND_READY == exmc_nand_getstatus()) {
        return NAND_OK;
    }

    return NAND_FAIL;
}

参考资料:

《STM32H7开发指南-HAL库版本_V1.0.pdf》、《GD32F450xx_Datasheet_Rev2.2.pdf》、《HY27US08281A_Datasheet.pdf》、《GD32F4xx_用户手册_Rev2.5.pdf》

  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

河狸打捞员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值