对Nand Flash的操作(S3C2440)

发布者:山宝宝最新更新时间:2019-05-10 来源: eefocus关键字:Nand  Flash  操作  S3C2440 手机看文章 扫描二维码
随时随地手机看文章

Nand Flash的概述


nandflash主要以page(页)为单位进行读写,以block(块)为单位进行擦除。每一页中又分为main区和spare区,main区用于正常数据的存储,spare区用于存储一些附加信息,如块好坏的标记、块的逻辑地址、页内数据的ECC校验和等。

先看下电路原理图

在这里插入图片描述

从原理图可以看出:

在DATA0~DATA7上既传输数据,又传输地址,也传输命令:

当ALE为高电平时传输的是地址。

当CLE为高电平时传输的是命令。

当ALE和CLE都为低电平时传输的是数据。


假设烧写NAND FLASH,把命令、地址、数据发给它之后,NAND FLASH肯定不可能瞬间完成烧写的,怎么判断烧写完成?

通过状态引脚RnB来判断:它为高电平表示就绪,它为低电平表示正忙。


看一下NAND的引脚:


在这里插入图片描述

根据NAND FLASH的芯片手册,一般的过程是:


发出命令

发出地址

发出数据/读数据

相应的命令字如图:

在这里插入图片描述

s3c2440来说,内部集成了一个NAND FLASH控制器,CPU只需操作NAND FLASH控制器,就实现对NAND FLASH的操作。

NAND Flash控制器提供了NFCONF、NFCONT、NFCMMD、NFADDR、NFDATA、NFSTAT和其他一些寄存器

在这里插入图片描述

NAND FLASH控制器的时序,是为了让NAND FLASH外设工作起来,假如外接不同的 NAND FLASH外设,那么它的操作时序可能就会不同,所以对Nand Flash控制器的设置也不同。

需要根据不同的nand flash,设置nand flash控制器。


看一下nand flash控制器时序:

在这里插入图片描述

由图可知,需要设置nand flash控制器的TACLS、TWRPH0、TWRPH1。

TACLS、TWRPH0、TWRPH1分别是NFCONF寄存器的某几位。


看一下nand flash芯片的时序:

图2
在这里插入图片描述

分析:

依次看这3张图

图1 ,TACLS是CLE / ALE使能之后,过多久nWE使能。就对应图2中nand flash芯片的时序中的tCLS和tALS减去tWP。再看图3,tCLS、tALS、tWP最小都为12ns;也就是TACLS可以为12-12=0。


图1,TWRPH0是nWE的使能时间。对应图2中的tWP。再看图3,为12ns。


图1,TWRPH1是nWE失能之后,过多久CLE / ALE失能。就对应图2中nand flash芯片的时序中的tCLH、tALH。再看图3,都为5ns。


然后根据时序,设置寄存器。

在这里插入图片描述

HCLK为100MHz,也就是10ns。

所以,可设置TACLS即[13:12] = 0;TWRPH0即[10:8] = 1;TWRPH1即[6:4] = 0;

再根据NFCONT寄存器,初始化NAND Flash控制器。

void nand_init(void)

{

#define  TACLS   0

#define  TWRPH0  1

#define  TWRPH1  0

/*设置NAND FLASH的时序*/

NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);

/*使能NAND FLASH控制器,初始化ECC,禁止片选*/

NFCONT = (1<<4) | (1<<1) | (1<<0);

}


再实现一个函数,读取nand的ID


void nand_select(void)

{

/*使能片选*/

NFCONT &=~(1<<1);

}


void nand_deselect(void)

{

/*禁止片选*/

NFCONT |= (1<<1);

}


void nand_chip_id(void)

unsigned char buf[5]={0};

nand_select(); 

nand_cmd(0x90);

nand_addr_byte(0x00);


buf[0] = nand_data();

buf[1] = nand_data();

buf[2] = nand_data();

buf[3] = nand_data();

buf[4] = nand_data();

nand_deselect();


printf("maker   id  = 0x%xnr",buf[0]);

printf("device  id  = 0x%xnr",buf[1]);

printf("3rd byte    = 0x%xnr",buf[2]);

printf("4th byte    = 0x%xnr",buf[3]);

printf("page  size  = %d kbnr",1  <<  (buf[3] & 0x03));

printf("block size  = %d kbnr",64 << ((buf[3] >> 4) & 0x03));

printf("5th byte    = 0x%xnr",buf[4]);


}



在之前的博文中,重定位代码到SDRAM,只能实现从NOR Flash复制代码到SDRAM。现在实现 读NAND 函数,然后实现从nand到SDRA的重定位。


具体代码如下:


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();

}

if (i == len)

break;


col = 0;

page++;

}

nand_deselect();

}



//然后在重定位函数中,添加代码

void nand_init(void);

void nand_read_relocate(unsigned int addr, unsigned char *buf, unsigned int len);


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;

int len;


len = ((int)&__bss_start) - ((int)&__code_start);


if (isBootFromNorFlash()) //判断是nor还是nand启动

{//nor启动

while (dest < end)

{

*dest++ = *src++;

}

}

else

{//nand启动

nand_init();

nand_read(src, dest, len);//从nand复制代码到SDRAM

}

}



最后实现,读、写、擦除函数。


#include "s3c2440_soc.h"

#include "printf.h"


void nand_init(void)

{

#define  TACLS   0

#define  TWRPH0  1

#define  TWRPH1  0

/*设置NAND FLASH的时序*/

NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);

/*使能NAND FLASH控制器,初始化ECC,禁止片选*/

NFCONT = (1<<4) | (1<<1) | (1<<0);

}


void nand_select(void)

{

/*使能片选*/

NFCONT &=~(1<<1);

}


void nand_deselect(void)

{

/*禁止片选*/

NFCONT |= (1<<1);

}


void nand_cmd(unsigned char cmd)

{

volatile int i;

NFCCMD = cmd;

for(i=0; i<10; i++);

}


void nand_addr_byte(unsigned char addr)

{

volatile int i;

NFADDR = addr;

for(i=0; i<10; i++);

}


unsigned char nand_data(void)

{

return NFDATA;

}


void nand_w_data(unsigned char val)

{

NFDATA = val;

}



void wait_ready(void)

{

while (!(NFSTAT & 1));

}


void nand_chip_id(void)

unsigned char buf[5]={0};

nand_select(); 

nand_cmd(0x90);

nand_addr_byte(0x00);


buf[0] = nand_data();

buf[1] = nand_data();

buf[2] = nand_data();

buf[3] = nand_data();

buf[4] = nand_data();

nand_deselect();


printf("maker   id  = 0x%xnr",buf[0]);

printf("device  id  = 0x%xnr",buf[1]);

printf("3rd byte    = 0x%xnr",buf[2]);

printf("4th byte    = 0x%xnr",buf[3]);

printf("page  size  = %d kbnr",1  <<  (buf[3] & 0x03));

printf("block size  = %d kbnr",64 << ((buf[3] >> 4) & 0x03));

printf("5th byte    = 0x%xnr",buf[4]);


}



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();

}

if (i == len)

break;


col = 0;

page++;

}

nand_deselect();

}



int nand_erase(unsigned int addr, unsigned int len)

{

int page = addr / 2048;


if (addr & (0x1FFFF))

{

printf("nand_erase err, addr is not block alignnr");

return -1;

}

if (len & (0x1FFFF))

{

printf("nand_erase err, len is not block alignnr");

return -1;

}

nand_select(); 


while (1)

{

page = addr / 2048;

nand_cmd(0x60);

/* row/page addr */

nand_addr_byte(page & 0xff);

nand_addr_byte((page>>8) & 0xff);

nand_addr_byte((page>>16) & 0xff);


nand_cmd(0xD0);


wait_ready();


len -= (128*1024);

if (len == 0)

break;

addr += (128*1024);

}

nand_deselect();

return 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); )

{

nand_w_data(buf[i++]);

}

nand_cmd(0x10);

wait_ready();


if (i == len)

break;

else

{

/* 开始下一个循环page */

col = 0;

page++;

}

}

nand_deselect();

}


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 : nr");

/* 长度固定为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("nr");

}

}


void do_erase_nand_flash(void)

{

unsigned int addr;

/* 获得地址 */

printf("Enter the address of sector to erase: ");

addr = get_uint();


printf("erasing ...nr");

nand_erase(addr, 128*1024);

}



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 ...nr");

nand_write(addr, str, strlen(str)+1);


}



void nand_flash_test(void)

{

char c;


while (1)

{

/* 打印菜单, 供我们选择测试内容 */

printf("[s] Scan nand flashnr");

printf("[e] Erase nand flashnr");

printf("[w] Write nand flashnr");

printf("[r] Read nand flashnr");

printf("[q] quitnr");

printf("Enter selection: ");


c = getchar();

printf("%cnr", c);


/* 测试内容:

* 1. 识别nand flash

* 2. 擦除nand flash某个扇区

* 3. 编写某个地址

* 4. 读某个地址

*/

switch (c)  

{

case 'q':

case 'Q':

return;

break;

case 's':

case 'S':

nand_chip_id();

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;

}

}

}


关键字:Nand  Flash  操作  S3C2440 引用地址:对Nand Flash的操作(S3C2440)

上一篇:对LCD的操作(S3C2440)
下一篇:对Nor Flash的操作(S3C2440)

推荐阅读最新更新时间:2024-11-17 16:24

【补充】s3c2440启动过程详细分析
2440启动过程算是一个难点,不太容易理解,而对于2440启动过程的理解,影响了后面裸机代码执行流程的分析,从而看出2440启动过程的重要性。 2440启动方式和启动方式选择 在S3C2440的datasheet《S3C2440A_UserManual_Rev13.pdf》中搜索map,可以在第5章(P195)中搜索到下图。 从此图中,可以得知 OM = 01,10,Not using NAND flash for boot ROM OM = 00, Using NAND flash for boot ROM 而OM 又是什么呢? 从S3C2440的datasheet《S3C2440A_UserMan
[单片机]
【补充】<font color='red'>s3c2440</font>启动过程详细分析
基于FA526处理器SoC平台的Linux操作系统实现
引言 智原科技的FIE8100 SoC平台是一种低功耗、便携式视频相关应用开发SoC平台,也可用于基于FA526 CPU的SoC设计验证。 基于FA526的Linux软件开发套件,开发人员可将Linux一2.4.19软件环境在FIE8100平台上安装实现,并完成对平台上所有IP的驱动程序安装和对FA526的内部调试。 FA526介绍 FA526是一颗有着广泛用途的32位RISC处理器。它包括一个同步CPU内核(core)、独立的指令/数据缓存(cache)、独立的指令/数据暂存器(scratchpads)、一个写缓存(write buffer)、一个内存管理单元(memory management uni
[嵌入式]
144层QLC(四级单元)NAND预计2020年面世
英特尔透露,2019年第四季度将会推出96层的3D NAND闪存产品,并且还率先在业内展示了用于数据中心级固态盘的144层QLC(四级单元)NAND,预计将于2020年推出。 事实上,近年来闪存的快速发展,的确在数据中心很多领域取代了传统机械硬盘的位置,高性能机械硬盘已经开始退出历史舞台,以采用高性能硬盘的高端存储阵列为例,DELL EMC、华为等多个存储厂商的最新高端存储产品全部采用了闪存,彻底告别了机械硬盘,闪存容量技术的不断创新,一方面进一步提升了闪存容量和性价比,另一方面也加快了对传统机械硬盘市场的蚕食。 在本次大会上,英特尔还介绍了5级单元闪存(5位/秒单元)技术,相比于QLC,该技术可以让闪存容量进一步提升,并
[嵌入式]
144层QLC(四级单元)<font color='red'>NAND</font>预计2020年面世
【ARM裸板】S3C2440 时钟设置与分析
由时钟树分析,时钟源通过选择器接入给MPLL(Main PLL)与UPLL(USB PLL) 经过MPLL得到FCLK提供给CPU FCLK分别通过HDIVN与PDIVN分频得到HCLK与PCLK HCLK接入给AHB总线,再给各种高速设备(Nand Flash、内存控制器、中断控制器…) PCLK接入给APB总线,再给各种低速设别(I2C、PWM、GPIO、UART…) 1.时钟源最大值 2.时钟源 2.1 两种时钟源 1.晶振 2.外部引脚时钟输入 2.2 选择时钟源 通过改变OM 引脚来选择时钟源 JZ2440 OM 接入GND,则主时钟源与USB时钟源都选择外部晶振 3.上电时钟分析 复位等待,
[单片机]
【ARM裸板】<font color='red'>S3C2440</font> 时钟设置与分析
S3C2440 之SPI
概述: S3C2440有两个串行外设SPI接口,SPI具有全双工通信 SPI方框图 S3C2440 之SPI SPI操作: 通过使用SPI接口,S3C2440可以与外部器件同时发送、接收8位数据。当SPI接口为主机时,可以通过设置SPPREn寄存器来设置发送频率,当SPI为从机时,由其它主机提供时钟频率。当程序员写字节数据到SPTDATn寄存器,将同时开始发送和接受,在一些情况下,应该在写字节数据到SPTDATn之前激活nSS。 S3C2440 之SPI SPI传输模式: S3C2440 之SPI S3C2440 之SPI SPI特殊寄存器 S3C2440 之SPI S3C2440 之SPI S3C24
[单片机]
<font color='red'>S3C2440</font> 之SPI
IAR for AVR 学习笔记(5)--SRAM操作
SARM数据类型的具体操作方法 SARM空间是AVR单片机最重要的部分,所有的操作必须依赖该部分来完成。变量在SARM空间的存储模式有tiny ,small large 三种,也就是对应于__tiny, __near __far三中存储属性。一旦选择为哪种存储模式,对应的数据默认属性也就确定了,但可以采用__tiny, __near __far关键字来更改。 对于程序中的局部变量,编译器会自动处理的,我们也不可能加什么储存属性,但IAR提供了强大的外部变量定义。 5.1.定义变量在工作寄存器 IAR编译器内部使用了部分工作寄存器,留给用户的只有R4-R15供12个寄存器供用户使用,要使用工作寄存器必须在工程选项里打开锁定选项。
[单片机]
S3c2440裸机程序【1】跑马灯
由JZ2440v2原理图可知: nLED1--- GPF4,nLED2--- GPF5,nLED3--- GPF6 S3C2440芯片手册关于IO口配置: 程序代码: #define GPFCON (*(volatile unsigned long *)0x56000050) #define GPFDAT (*(volatile unsigned long *)0x56000054) #define Led1_On ~(1 4) #define Led1_Off (1 4) #define Led2_On ~(1 5) #define Led2_Off (1 5) #define Led3_On
[单片机]
<font color='red'>S3c2440</font>裸机程序【1】跑马灯
新型机器人有望在毛细血管中运行
  据BBC报道,著名的美国科学家雷·库兹威尔大胆预测人工智能机器人将会首先出现在医学领域,而传统的人工智能的观念将会被彻底颠覆。雷·库兹威尔认为,目前的技术水平已经达到了生产微型机器人的阶段,美国科学家和欧洲科学家已经成功的研制出用于人类血管治疗的微型机器人,在不久的将来就会制造出可以在毛细血管里运的机器人。   而这种可以在毛细血管中运动的微型机器人的出现将彻底改变传统观念对人工智能的理解。因为这种通过毛细血管运动的机器人,可以通过毛细血管,进入人类大脑,机器人可以通过控制人类脑细胞这样更高级的操作,达到一种全新的“人工智能”概念。   俄罗斯科学家曾经认为人工智能机器人的瓶颈在于微型电脑芯片的运算速度无法适应机器人的体积
[焦点新闻]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved