zoukankan      html  css  js  c++  java
  • s3c2440裸机-nandflash编程(四. nand读写擦实现)

    1.顺寻访问(Page Read)

    下图的表格,来说明NAND FLASH内部结构,前面2K(02047)表示页数据,后边64字节(20482111)表示oob。

    CPU想读取,第2048个数据,它是哪以一个?

    是Page1的第0个字节。CPU使用某个地址访问数据的时候,是在页数据空间来寻址的。
    

    下图为读NAND FLASH的read时序操作:

    1.首先需要锁存00命令,nCE、CLE、nWE有效,0x00命令被锁存;

    2.此时CLE无效,ALE开始有效,地址被锁存(从NAND FLASH的地址周期中可以看出来,先发出2个周期的col列地址,再发出3个周期的Row行地址);

    3.锁存0x30命令;

    4.然后会有一个busy时间段,R/nB为低电平。tRR表示busy状态的持续时间(手册上最小为20ns)。

    5.开始锁存数据,nRE使能,nand上的数据被同步到数据nand控制器上。我们的nand是8bit数据位宽,所以每隔一个read时钟周期(tRC),传输1byte数据。每传输1byte数据,地址会自动往后偏移1byte,一般我们会连续读取1page数据。

    下面开始写代码:

    当发完命令、地址后再进行读数据前我们知道有一段时间tRR处于busy状态,我们可以通过查询NFSTAT寄存器来确定busy状态有没有结束,是不是已经ready了。

    wait_ready函数等待NAND FLASH空闲,从上图可以看出当NFSTAT寄存器[0]的值为1时NAND FLASH是空闲的,我们可以通过该位来判断NAND FLASH是否繁忙。代码如下:

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

    nand_read函数为NAND FLASH的读函数,代码如下:

      void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
      {
          int i = 0;
          int page = addr / 2048;
          int col  = addr & (2048 - 1);
    
          nand_select(); 
    
          while (i < len)
          {
    	      /* 发出00h命令 */
    	      nand_cmd(00);
    
    	      /* 发出地址 */
    	      /* col addr */
    	      nand_addr_byte(col & 0xff);
    	      nand_addr_byte((col>>8) & 0xff);
    
    	      /* row/page addr */
    	      nand_addr_byte(page & 0xff);
    	      nand_addr_byte((page>>8) & 0xff);
    	      nand_addr_byte((page>>16) & 0xff);
    
    	      /* 发出30h命令 */
    	      nand_cmd(0x30);
    
    	      /* 等待就绪 */
    	      wait_ready();
    
    	      /* 读数据 */
    	      for (; (col < 2048) && (i < len); col++)
    	      {
    	            buf[i++] = nand_data();			
    	      }
    
    	      col = 0;
    	      page++;
          }
    
          nand_deselect(); 	
      }
    

    我们看到每read一个page,都要重新发送命令地址,因为这里是顺序访问,flash的读写都是以page为单位的。

    软件上如何自动区分是nand启动还是nor启动?

    在init.c文件中,加上如下代码,用来判断所使用的FLASH是NOR FLASH还是NAND FLASH。代码如下:

    /*我们知道nand启动0地址对应片内SRAM,可以像内存一样的写0地址;nor启动,0地址对应nor,nor不能像内存一样的写地址,
    **所以往0地址写入数据成功表示nand启动,写不成功表示nor启动
    */
    int isBootFromNorFlash(void)
    {
    	volatile unsigned int *p = (volatile unsigned int *)0;
    	unsigned int val = *p;
    
    	*p = 0x12345678;
    	if (*p == 0x12345678)
    	{
    		/* 写成功, 对应nand启动 */
    		*p = val;
    		return 0;
    	}
    	else
    	{
    		return 1;
    	}
    }
    

    下面是代码重定位时可以自动区分nand和nor启动,无论是nand启动还是nor启动,都能将程序重定位到sdram中去。

      void copy2sdram(void)
      {
            /* 要从lds文件中获得 __code_start, __bss_start
            * 然后从0地址把数据复制到__code_start
        */
    
        extern int __code_start, __bss_start;
    
        volatile unsigned int *dest = (volatile unsigned int *)&__code_start;
        volatile unsigned int *end = (volatile unsigned int *)&__bss_start;
        volatile unsigned int *src = (volatile unsigned int *)0;
        unsigned int len = (unsigned int)(&__bss_start) - (unsigned int)(&__code_start);
    
        if (isBootFromNorFlash())
        {
              while (dest < end)
              {
                    *dest++ = *src++;
    	  }
        }
        else
        {
    	    nand_init();
    	    nand_read((unsigned int)src, dest, len);
        }
      }
    

    2.擦除(block erase)

    block erase时序图的过程大致如下:

    1.首先发送0x60命令
    2.发送row地址(由于擦除是以block为单位的,所以无需知道页内地址,只需要知道要擦除哪个page、哪个block即可)
    3.发送0xd0,执行擦除动作
    4.然后会有一个busy时间段,R/nB为低电平
    5.发送0x70命令,用来读取状态
    6.判断NFDATA寄存器的第0位是否擦除成功
    

    代码实现如下:

    int nand_erase(unsigned int addr, unsigned int len)
    {
    	int page = addr / 2048;
    
    	if (addr & (0x1FFFF))
    	{
    		printf("nand_erase err, addr is not block align
    
    ");
    		return -1;
    	}
    	
    	if (len & (0x1FFFF))
    	{
    		printf("nand_erase err, len is not block align
    
    ");
    		return -1;
    	}
    	
    	nand_select(); 
    
    	while (1)
    	{
    		page = addr / 2048;
    		
    		nand_cmd(0x60);
    		
    		/* page addr */
    		nand_addr_byte(page & 0xff);
    		nand_addr_byte((page>>8) & 0xff);
    		nand_addr_byte((page>>16) & 0xff);
    
    		nand_cmd(0xD0);
    
    		wait_ready();
    
    		nand_cmd(0x70);
    		if (nand_data()&0x1)
    		{
    			printf("nand_erase err, at addr:0x%x
    
    ", addr);
    			return -1;
    		}
    
    		len -= (128*1024);
    		if (len == 0)
    			break;
    		addr += (128*1024);
    	}
    	
    	nand_deselect(); 	
    	return 0;
    }
    

    3.顺序写(page write)

    往NAND FLASH写数据时,只需要把要写的数据复制给NFDATA寄存器即可。代码如下:

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

    page write的写时序图如下:

    1.首先发送0x80命令
    2.发送地址(5个周期)
    3.发送数据
    4.发送0x10命令,执行烧写动作
    4.然后会有一个busy时间段,R/nB为低电平
    5.发送0x70命令,用来读取状态
    6.判断NFDATA寄存器的第0位是否烧写成功
    
    void nand_write(unsigned int addr, unsigned char *buf, unsigned int len)
    {
    	int page = addr / 2048;
    	int col  = addr & (2048 - 1);
    	int i = 0;
    
    	nand_select(); 
    
    	while (1)
    	{
    		nand_cmd(0x80);
    
    		/* 发出地址 */
    		/* col addr */
    		nand_addr_byte(col & 0xff);
    		nand_addr_byte((col>>8) & 0xff);
    		
    		/* row/page addr */
    		nand_addr_byte(page & 0xff);
    		nand_addr_byte((page>>8) & 0xff);
    		nand_addr_byte((page>>16) & 0xff);
    
    		/* 发出数据 */
    		for (; (col < 2048) && (i < len); col++)  //???还需确认
    		{
    			nand_w_data(buf[i++]);
    		}
    		
    		nand_cmd(0x10);
    		wait_ready();
            
    		nand_cmd(0x70);
    		if (nand_data()&0x1)
    		{
    			printf("nand_write err, at page:0x%x, addr:0x%x
    
    ", page, page<<11);
    			return -1;
    		}
    
    		if (i == len)
    			break;
    
    		/* 开始下一个循环page */
    		col = 0;
    		page++;	
    	}
    	
    	nand_deselect(); 	
    }
    

    我们看到每写一个page,都要重新发送命令地址,因为这里是顺序访问,flash的读写都是以page为单位的。

    4.测试

    void do_erase_nand_flash(void)
    {
    	unsigned int addr;
    	
    	/* 获得地址 */
    	printf("Enter the address of sector to erase: ");
    	addr = get_uint();
    
    	printf("erasing ...
    
    ");
    	nand_erase(addr, 128*1024);
    }
    
    void do_read_nand_flash(void)
    {
    	unsigned int addr;
    	volatile unsigned char *p;
    	int i, j;
    	unsigned char c;
    	unsigned char str[16];
    	unsigned char buf[64];
    	
    	/* 获得地址 */
    	printf("Enter the address to read: ");
    	addr = get_uint();
    
    	nand_read(addr, buf, 64);
    	p = (volatile unsigned char *)buf;
    
    	printf("Data : 
    
    ");
    	/* 长度固定为64 */
    	for (i = 0; i < 4; i++)
    	{
    		/* 每行打印16个数据 */
    		for (j = 0; j < 16; j++)
    		{
    			/* 先打印数值 */
    			c = *p++;
    			str[j] = c;
    			printf("%02x ", c);
    		}
    
    		printf("   ; ");
    
    		for (j = 0; j < 16; j++)
    		{
    			/* 后打印字符 */
    			if (str[j] < 0x20 || str[j] > 0x7e)  /* 不可视字符 */
    				putchar('.');
    			else
    				putchar(str[j]);
    		}
    		printf("
    
    ");
    	}
    }
    
    void do_write_nand_flash(void)
    {
    	unsigned int addr;
    	unsigned char str[100];
    	int i, j;
    	unsigned int val;
    	
    	/* 获得地址 */
    	printf("Enter the address of sector to write: ");
    	addr = get_uint();
    
    	printf("Enter the string to write: ");
    	gets(str);
    
    	printf("writing ...
    
    ");
    	nand_write(addr, str, strlen(str)+1);
    }
    

    测试结果如下:

    说明:本节的读、写、擦都只涉及到页数据区,不涉及到oob区的操作。

    坏快的标记和解除

    Nand Flash怎么标记某一个BLOCK是坏的?如何识别一个flash中的坏快?

      它使用该BLOCK中第1个扇区的OOB数据中某一个字节来标记: 其值为0xff表示该BLOCK是好的, 其值为非0xff表示该BLOCK是坏的。
      在uboot中直接输入“nand bad ”命令即可识别某一个块是否为坏快,在linux用户态的情况下,需要用ioctl(MEMGETBADBLOCK)来获取该block是否为坏快。
      有时候我们会误写这个OOB区的值导致有些BLOCK被误认为是"坏块",可以在u-boot中执行"nand scrub"后, 根据提示信息输入小写字母'y'并回车, 它会强制擦除整个Nand Flash(包括把OOB擦除为0xff), 这样就可以恢复被误标为坏块的区域了。
  • 相关阅读:
    css grid 随笔
    网页“console”输出图文信息
    2017
    自适应css 框架 PURE
    获取去除参数url地址
    微信分享
    video 播放
    手机端 默认字体
    video 手机全屏自动播放
    jquery 获取元素背景图片backgroungImage的url
  • 原文地址:https://www.cnblogs.com/fuzidage/p/13099385.html
Copyright © 2011-2022 走看看