韦东山嵌入式Linux学习----015 Nand Flash(4)

Nand Flash编程实现写、擦地址信息

/*
 *硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3)
 *软件平台:运行于VMware Workstation 12 Player下UbuntuLTS16.04_x64 系统,
 *参考资料:开发版原理图,S3C2440A datasheet,K9F2G08U0C datasheet
*/


一、基础知识

1、Nand Flash 存储结构介绍

具体分析点击这里
  在《韦东山嵌入式Linux学习----015 Nand Flash(1)》这一篇中简单的从电路图与芯片手册介绍了一下K9F2G08U0C这块芯片,下面讲具体分析下它的存储结构。

在这里插入图片描述

分析:
  ①、Nand Flash 的数据:以bit的方式保存在memory cell
  ②、Nand Device的位宽:一般来说,一个cell 中只能存储一个bit。这些cell 以8个或者16个为单位,连成bit line,形成所谓的byte(x8)/word(x16)图中可以看出芯片采用的时x8。
  ③、Nand Flash的Page:多条的Line会再组成Page图中可以看出1 Page = 2048 Bytes(data) + 64 Bytes(oob)。
  ④、Nand Flash的Block:多个的page形成一个Block。图中可以看出1 Block = 64 Page。
  ⑤、可以看到,每条公式以(xK + yK)的形式表示,yK代表的是OOB块的大小

2、行列地址的提取

  NAND flash以页为单位读写数据,而以块为单位擦除数据。按照这样的组织方式可以形成所谓的三类地址:列地址 :Column Address页地址 :Page Address块地址 :Block Address
  在具体操作中,我们发送地址时是一次性输入操作的内存的地址,如0x100000。但是由于我们使用的地址和命令只能在I/O[7:0]上传递,数据宽度是8位,所在传输写读地址时,程序设计需要单独提取行列地址
  结合上图,可以得出如下提取方式:
  col = (addr & 0xfff); //取列数
  page = (addr >> 12); //取页数

1st Cycle Column Address nand_addr(col & 0xff);
2st Cycle Column Address nand_addr((col >> 8) & 0x0f);
3st Cycle Row Address nand_addr((page & 0xff));
4st Cycle Row Address nand_addr((page >> 8) & 0xff);
5st Cycle Row Address nand_addr(page>> 16);

二、实现功能

功能:根据输入的地址,在对应的位置(按页)写入数据,(按快)擦除数据。(对于按页写数据、按块擦除数据,我会在后面写一篇博文进行具体介绍)

三、编程原理

1、写数据

查K9F2G08U0C芯片手册,可以知道写操作的时序图

在这里插入图片描述

1.1 编写写数据函数

void nand_write(unsigned int addr,unsigned char *buf,int len)
{
	int i = 0;
	int col = (addr & 0xfff);	//取列数
	int page = (addr >> 12);	//取页数

	//	1、选中芯片
	nand_select();
	
	while(1)
	{
		//	2、发出80命令
		nand_cmd(0x80);
		
		//	3、发出行列地址(坐标)

		/* 横坐标 */
		nand_addr(col & 0xff);
		nand_addr((col >> 8)	& 0x0f);
		
		/* 纵坐标 */
		nand_addr((page & 0xff));
		nand_addr((page >> 8) & 0xff);
		nand_addr(page>> 16);

		
		//	4、发送写入的数据
		for(;(col<2048) && (i<len);)
		{
			nand_write_data(buf[i++]);
		}	

		//	5、发出10命令
		nand_cmd(0x10);

		//	6、等待一段时间
		nand_wait_ready();
		
		if(i == len)
		{
			break;
		}
		/* 进行下一页的写 */
		col = 0;
		page++;		
	}

	//	6、发出70命令
	nand_cmd(0x70);

	//	7、查看是否写成功
	if((nand_data() & 0x01) == 0 )
	{
		printf("Nand Flash Write Successful!\n\r");
	}
	else
	{
		printf("Nand Flash Write Fail!\n\r");
	}
	//	8、取消片选
	nand_disselect();
	printf("\n\r");
}

1.2 进一步封装写函数

void do_write_nand_flash(void)
{
	/* 各变量对应含义
	 * 存储输入的地址
	 * 存储写入的信息
	 */
	unsigned int addr;
	unsigned char str[100];

	/* 获取地址 */
	printf("Enter the address of sector to write: ");
	addr = get_uint();

	/* 获取写入信息 */
	printf("Enter the address to write: ");
	gets(str);

	nand_write(addr,str,strlen(str)+1);
	printf("writing ...\n\r");
}

2、擦除

查K9F2G08U0C芯片手册,可以知道擦除操作的时序图

在这里插入图片描述

2.1编写擦除函数

void nand_erase(unsigned int addr)
{
	int page = (addr >> 12);		//取页数
	
	//	1、选中芯片
	nand_select();

	//	2、发60命令
	nand_cmd(0x60);

	//	3、发三个周期的行地址
	/* 纵坐标 */
	nand_addr((page & 0xff));
	nand_addr((page >> 8) & 0xff);
	nand_addr(page>> 16);

	//	4、发擦除DO命令
	nand_cmd(0xd0);

	//	5、等待周期
	nand_wait_ready();

	//	6、发出70命令
	nand_cmd(0x70);

	//	7、判断是否擦除成功
	if((nand_data() & 0x01) == 0 )
	{
		printf("Nand Flash Erase Successful!\n\r");
	}
	else
	{
		printf("Nand Flash Erase Fail!\n\r");
	}
	//	8 、取消选中
	nand_disselect();	
}

2.2 进一步封装擦除函数

void do_erase_nand_flash(void)
{
	unsigned int addr;	//存储输入的地址
	
	/* 获取地址 */
	printf("Enter the address of sector to erase: ");
	addr = get_uint();

	nand_erase(addr);
	printf("\n\r");
}

四、编程文件

1、完整的nand_init.c文件

#include "s3c2440_soc.h"
#include "my_printf.h"

void nand_init(void)
{
	#define  TACLS   0
	#define  TWRPH0  1
	#define  TWRPH1  0
	
	/* 初始化时序 TACLS = 1、TWRPH0 = 1、TWRPH1 = 0 */
	NFCONF = (0<<12)|(1<<8)|(0<<4);	
	
	/*使能NAND FLASH控制器,初始化ECC,禁止片选*/
	NFCONT = (1<<4) | (1<<1) | (1<<0);

}

void nand_select(void)
{
	/* 片选信号 */
	NFCONT &=~ (1<<1);
}

void nand_disselect(void)
{
	/* 禁止片选信号 */
	NFCONT |= (1<<1);
}

/* 写命令 */
void nand_cmd(unsigned char cmd)
{
	volatile int i;
	NFCCMD = cmd;
	for(i=0; i<10; i++);
}

/* 写地址 */
void nand_addr(unsigned char addr)
{
	volatile int i;
	NFADDR = addr;
	for(i=0; i<10; i++);
}

/* 读数据 */
unsigned char nand_data(void)
{
	return NFDATA;
}

void nand_wait_ready(void)
{
	while (!(NFSTAT & 1));
}

void nand_write_data(unsigned char val)
{
	NFDATA = val;
}

/* Nand Flash 读ID */
void do_scan_nand_flash(void)
{
	unsigned char buf[5];
	
	//	1、片选信号
	nand_select();

	//	2、写读ID命令
	nand_cmd(0x90);

	//	3、写地址
	nand_addr(0x00);

	//	4、读5次数据
	buf[0]  = nand_data();	//厂商ID
	buf[1]  = nand_data();	//设备ID
	buf[2]  = nand_data();	//3rd
	buf[3]  = nand_data();	//4th
	buf[4]  = nand_data();	//5th

	//	5、打印数据
	/* 打印5次读取的数据 */
	printf("Maker  Code :0x%x\n\r",buf[0]);
	printf("Device Code :0x%x\n\r",buf[1]);
	printf("3rd Cycle   :0x%x\n\r",buf[2]);
	printf("4rd Cycle   :0x%x\n\r",buf[3]);
	printf("5rd Cycle   :0x%x\n\r",buf[4]);

	//	6、禁止片选
	NFCONT |= (1<<1);
	
	/* 通过提取,打印页大小和块大小 */
	printf("Page Size   :%d KB\n\r",1 << (buf[3]&0x03));
	printf("Block Size  :%d KB\n\r",64 << ((buf[3]>>4)&0x03));
}

/* Nand Flash 读地址 */
void nand_read(unsigned int addr,unsigned char *buf ,int len)
{
	int i =0 ;
	int col = (addr & 0xfff);	//取列数
	int page = (addr >> 12);	//取页数
	
	//	1、片选
	nand_select();
	while(i<len)
	{
		//	2、发00命令
		nand_cmd(0x00);

		//	3、发五个周期的地址
		/* 横坐标 */
		nand_addr(col & 0xff);
		nand_addr((col >> 8)    & 0x0f);
		
		/* 纵坐标 */
		nand_addr((page & 0xff));
		nand_addr((page >> 8) & 0xff);
		nand_addr(page>> 16);

		//	4、发30命令
		nand_cmd(0x30);

		//	等待时间
		nand_wait_ready();
		
		//	5、读数据
		/* 保证列数不超过2047,i<len */
		for(; (col < 2048) && (i < len);col++)
		{
			buf[i++] = nand_data();
		}
		if (i == len)
			break;

		col = 0;
		page++;
		
	}
	
	//	6、取消片选
	nand_disselect();
}

/* 读某个地址 
 * 1、获取地址
 * 2、打印地址信息
 */
void do_read_nand_flash(void)
{
	int				i,j;	
	unsigned int	addr;	
	unsigned char	c;		
	unsigned char	buf[64];
	unsigned char	str[16];
	volatile unsigned char *tmpbuf;

	//	1、获取地址
	printf("Enter the address to read: ");
	addr = get_uint();

	//	2、执行读函数
	nand_read(addr,buf,64);
	tmpbuf = (volatile unsigned char *)buf;

	// 3、打印地址信息
	/* 固定长度64字节
	 * 按照市面上的标准以16个字节为一行,前为数值,后为字符
	 * 分辨字符是否可视
	 */
	 printf("Data: \n\r");
	 for(i=0;i<4;i++)
	 {
	 	for(j=0;j<16;j++)
	 	{
	 		c = *tmpbuf++;
			str[j] = c;
			printf("%02x ",c);	
		}
		printf("   ;");
		
		for(j=0;j<16;j++)
	 	{
	 		if((str[j] < 0x20) || (str[j] > 0x7E))
				printf(".");
			else
				printf("%c",str[j]);
		}
		printf("\n\r");
	 }
}

void nand_erase(unsigned int addr)
{
	int page = (addr >> 12);		//取页数
	
	//	1、选中芯片
	nand_select();

	//	2、发60命令
	nand_cmd(0x60);

	//	3、发三个周期的行地址
	/* 纵坐标 */
	nand_addr((page & 0xff));
	nand_addr((page >> 8) & 0xff);
	nand_addr(page>> 16);

	//	4、发擦除DO命令
	nand_cmd(0xd0);

	//	5、等待周期
	nand_wait_ready();

	//	6、发出70命令
	nand_cmd(0x70);

	//	7、判断是否擦除成功
	if((nand_data() & 0x01) == 0 )
	{
		printf("Nand Flash Erase Successful!\n\r");
	}
	else
	{
		printf("Nand Flash Erase Fail!\n\r");
	}
	//	8 、取消选中
	nand_disselect();
	
}

void do_erase_nand_flash(void)
{
	unsigned int addr;	//存储输入的地址
	
	/* 获取地址 */
	printf("Enter the address of sector to erase: ");
	addr = get_uint();

	nand_erase(addr);
	printf("\n\r");
}

void nand_write(unsigned int addr,unsigned char *buf,int len)
{
	int i = 0;
	int col = (addr & 0xfff);	//取列数
	int page = (addr >> 12);	//取页数

	//	1、选中芯片
	nand_select();
	
	while(1)
	{
		//	2、发出80命令
		nand_cmd(0x80);
		
		//	3、发出行列地址(坐标)

		/* 横坐标 */
		nand_addr(col & 0xff);
		nand_addr((col >> 8)	& 0x0f);
		
		/* 纵坐标 */
		nand_addr((page & 0xff));
		nand_addr((page >> 8) & 0xff);
		nand_addr(page>> 16);

		
		//	4、发送写入的数据
		for(;(col<2048) && (i<len);col++)
		{
			nand_write_data(buf[i++]);
		}	

		//	5、发出10命令
		nand_cmd(0x10);

		//	6、等待一段时间
		nand_wait_ready();
		
		if(i == len)
		{
			break;
		}
		/* 进行下一页的写 */
		col = 0;
		page++;		
	}

	//	6、发出70命令
	nand_cmd(0x70);

	//	7、查看时候写成功
	if((nand_data() & 0x01) == 0 )
	{
		printf("Nand Flash Write Successful!\n\r");
	}
	else
	{
		printf("Nand Flash Write Fail!\n\r");
	}
	//	8、取消片选
	nand_disselect();
	printf("\n\r");
}

void do_write_nand_flash(void)
{
	/* 各变量对应含义
	 * 存储输入的地址
	 * 存储写入的信息
	 */
	unsigned int addr;
	unsigned char str[100];

	/* 获取地址 */
	printf("Enter the address of sector to write: ");
	addr = get_uint();

	/* 获取写入信息 */
	printf("Enter the address to write: ");
	gets(str);

	nand_write(addr,str,strlen(str)+1);
	printf("writing ...\n\r");
}

void nand_flash_test(void)
{
	char c;

	while (1)
	{
		/* 打印菜单, 供我们选择测试内容 */
		printf("[s] Scan  nand flash\n\r");
		printf("[e] Erase nand flash\n\r");
		printf("[w] Write nand flash\n\r");
		printf("[r] Read  nand flash\n\r");
		printf("[q] Quit\n\r");
		printf("Enter selection: ");

		c = getchar();
		printf("%c\n\r", c);

		/* 测试内容:
		 * 1. 识别nand flash
		 * 2. 擦除nand flash某个扇区
		 * 3. 编写某个地址
		 * 4. 读某个地址
		 */
		switch (c)		 
		{
			case 'q':
			case 'Q':
				return;
				break;
				
			case 's':
			case 'S':
				do_scan_nand_flash();
				break;

			case 'e':
			case 'E':
				do_erase_nand_flash();
				break;

			case 'w':
			case 'W':
				do_write_nand_flash();
				break;

			case 'r':
			case 'R':
				do_read_nand_flash();
				break;
			default:
				break;
		}
	}
}

五、运行结果

擦除操作运行截图:

在这里插入图片描述

可以看到,执行擦除操作后,0x100000对应的block全部数据为x0ff,擦除操作执行成功。
写操作运行截图:

在这里插入图片描述

可以看到,执行写操作后,0x100000对应的Page全部数据为01234,写操作执行成功。

发布了40 篇原创文章 · 获赞 29 · 访问量 3613

猜你喜欢

转载自blog.csdn.net/weixin_42813232/article/details/105165536
今日推荐