Mini2440裸机开发之Nand Flash 编程

一、Nand Flash命令

1.1 命令表

对Nand Flash的操作需要发出命令,下面有个Nand Flash的命令表格,那么我们可以此表格上的命令来访问我们的Nand Flash。

 针对每一个命令的时序可以参考Nand Flash芯片使用手册。下面我们将会分析一些常用命令的时序。时序中部分信号的信息如下:

  • 当ALE为高电平时传输的是地址;
  • 当CLE为高电平时传输的是命令;
  • 当ALE,CLE都为低电平表示传输的是数据 ;
  • $\overline{CE}$片选信号,低电平有效;
  • $\overline{RE}$读使能,低电平有效;
  • $\overline{WE}$写使能,低电平有效;
1.2 Read ID时序分析

  • 第一条竖线位置,写命令,发送了$\overline{CE}$、$CLE$、$\overline{WE}$信号,90h命令被锁存;
  • 第二条竖线,写地址,发送了$\overline{WE}$、$ALE$、$\overline{CE}$信号,地址00被锁存;
  • 继续往后,命令、地址都发完了,要read数据了,所以释放$\overline{WE}$,$ALE$,这里$tAR$表示$ALE$释放多久后才可以发送$\overline{RE}$信号,$tREA$表示$\overline{RE}$信号的建立时间;
  • 第三条竖线位置,发送了$\overline{CE}$,$\overline{RE}$信号,所以数据被锁存,第一个访问周期锁存的数据为marker code,值为0xEC,第二个访问周期的数据为device code,值为0xDA。读id时读5个周期含义对应如下表:

该Nand Flash的5个周期读取出来的值对应如下:

第三个访问周期含义如下表:

 第四个访问周期含义如下表:

 第五个访问周期含义如下表:

根据第4、5个访问周期的结果0x15、0x44我们得知该Nand Flash的block_size=128K,page_size=2k, 有2个plane,plane_size=1Gb = 128M, 共256M。

1.3 页读取(page read)

  • 第一条竖线,写命令,发送了$\overline{CE}$、$CLE$、$\overline{WE}$信号,00h命令被锁存;
  • 然后,写地址,发送了$\overline{WE}$、$ALE$、$\overline{CE}$信号,NDFLASH的地址周期中可以看出来,先发出2个周期的col列地址,再发出3个周期的Row行地址,关于5个周期地址的计算可以参考上一篇博客;
  • 写命令,发送了$\overline{CE}$、$CLE$、$\overline{WE}$信号,30h命令被锁存;
  • 然后会有一个busy时间段,$R/\overline{B}$为低电平。$tRR$表示busy状态的持续时间(手册上最小为20ns);
  • .开始锁存数据,$\overline{RE}$使能,nand上的数据被同步到数据nand控制器上。我们的nand是8bit数据位宽,所以每隔一个read时钟周期($tRC$),传输1byte数据。每传输1byte数据,地址会自动往后偏移1byte,一般我们会连续读取1page数据;
1.4 块擦除(block erase)

  • 首先发送0x60命令;
  • 发送row地址(由于擦除是以block为单位的,所以无需知道页内地址,只需要知道要擦除哪个page、哪个block即可);
  • 发送0xDO命令,执行擦除动作
  • 然后会有一个busy时间段,$R/\overline{B}$为低电平;
  • 发送0x70命令,用来读取状态;
  • 判断NFDATA寄存器的第0位是否擦除成功;
1.5 页写入(page write)

  • 首先发送0x80命令;
  • 发送地址(5个周期);
  • 发送数据;
  •  发送0x10命令,执行烧写动作;
  • 然后会有一个busy时间段,$R/\overline{B}$为低电平;
  • 发送0x70命令,用来读取状态;
  • 判断NFDATA寄存器的第0位是否烧写成功

 二、寄存器介绍以及初始化

2.1 寄存器

Nand控制器要按照我们Nand Flash的实际型号和性能来设置初始值。

S3C2440 Nand  Flash相关寄存器如下:

2.2 配置寄存器(NFCONF)

寄存器信息:

寄存器地址R/W描述复位值
NFCONF0X4E000000R/WNand Flash配置寄存器0x0000100X

寄存器位信息:

NFCONF描述初始状态
保留[15:14]保留——
TACLS[13:12]

CLE 和ALE 持续值设置(0 至3)

Duration = HCLK × TACLS

01
保留[11]保留0
TWRPH0[10:8]

TWRPH0 持续值设置(0~7)

Duration = HCLK × ( TWRPH0 + 1 )

000
保留[7]保留0
TWRPH1[6:4]

TWRPH1 持续值设置(0~7)

Duration = HCLK × ( TWRPH1 + 1 )

000

AdvFlash(只读)

[3]

自动引导启动用的先进NAND Flash 存储器。0:支持256 字或512 字节/页的NAND Flash 存储器;1:支持1K 字或2K 字节/页的NAND Flash 存储器。此位由在复位和从睡眠模式中唤醒时的NCON0 引脚状态所决定。

硬件设置(NCON0)

PageSize(只读)

[2]

自动引导启动用的NAND Flash 存储器的页面大小。先进闪存页面大小当AdvFlash 为0 时0=256 字/页;1=512 字节/页当AdvFlash 为1 时0=1024 字/页;1=2048 字节/页此位由在复位和从睡眠模式中唤醒时的GPG13 引脚状态所决定。复位后,GPG13 可以用于通用I/O 口或外部中断。

硬件设置(GPG13)

AddrCycle(只读)

[1]

自动引导启动用的NAND Flash 存储器的地址周期。先进闪存地址周期当AdvFlash 为0 时0=3 个地址周期;1=4 个地址周期当AdvFlash 为1 时0=4 个地址周期;1=5 个地址周期此位由在复位和从睡眠模式中唤醒时的GPG14 引脚状态所决定。复位后,GPG14 可以用于通用I/O 口或外部中断。

硬件设置(GPG14)

BusWidth(R/W)

[0]

自动引导启动和普通访问用的NAND Flash 存储器的输入输出总线宽度。0=8 位总线;1=16 位总线此位由在复位和从睡眠模式中唤醒时的GPG15 引脚状态所决定。复位后,GPG15 可以用于通用I/O 口或外部中断。此位可以被软件改变。

硬件设置(GPG15)

假设$HCLK=100MHZ$,则$T=10ns$。前面一节分析了:

$$TACLS = max(tCLS,tALS) - tWP$$

我们从NAND手册得知$tCLS$、$tALS$、$tWP$最小都可以取到12ns, 所以我们可以取$TACLS=0$;

$$TWRPH0 = tWP$$

我们的NAND手册上要求$tWP$最少12ns, 那么取$TWRPH0 =1$, $Duration = HCLK*(TWRPH0+1)=20ns>12ns$,满足要求;

$$TWRPH1 = max(tCLH,tALH)$$

我们的NAND手册上要求$tCLH$、$tALH$最少5ns, 那么取$TWRPH1 =0$, $Duration = HCLK*(TWRPH1+1)=10ns>5ns$,满足要求。

再配置BusWidth总线位宽为8bit;

所以NFCONF寄存器设置如下:

#define  TACLS   0
#define  TWRPH0  1
#define  TWRPH1  0
/*设置Nand Flash的时序*/
NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
2.3 控制寄存器(NFCONT)

寄存器信息:

寄存器地址R/W描述复位值
NFCONF0X4E000004R/WNand Flash控制寄存器0x0384

寄存器位信息:

NFCONT描述初始状态
保留[15:14]保留0
Lock-tight[13]

紧锁配置(Lock-tight)0: 禁止紧锁 1:使能紧锁只要此位被设置为1 一次,你就不能清除了。只有复位或从睡眠模式中被唤醒才能使此位为禁止(即不能由软件清零)。当此位被设置为1 的情况下,在NFSBLK(0x4E000038)到NFEBLK(0x4E00003C)-1 的区域设置未被上锁,除了这些区域以外的区域,写入或擦除命令将会无效,只有只读命令有效。

0
SotLock[12]

软件上锁设置0: 禁止上锁 1:使能上锁软件锁定区域可以随时用软件修改。当此位被设置为1 的情况下, 在NFSBLK(0x4E000038)到NFEBLK(0x4E00003C)-1 的区域设置未被上锁,除了这些区域以外的区域,写入或擦除命令将会无效,只有只读命令有效。当你试图写入或擦除这些锁定区域时,将发生非法访问(NFSTAT[3]位将会置位)。如果NFSBLK 和NFEBLK相同时,整个区域都被锁定。

1
保留[11]保留0
EnbIllegalAccINT[10]

非法访问中断控制0: 禁止中断 1:使能中断当CPU 试图编程或擦除锁定区域( 由NFSBLK(0x4E000038) 到NFEBLK(0x4E00003C)-1 的区域设置)而产生非法访问中断。

0
EnbRnBINT[9]

RnB 状态输入信号传输中断控制0: 禁止RnB 中断 1:使能RnB 中断

0

RnB_TransMode

[8]

RnB 传输检测配置0: 检测上升沿 1:检测下降沿

0

保留

[7]

保留

0

SpareECCLock

[6]

锁定备份区域ECC 产生0: 开锁备份 ECC 1:锁定备份 ECC备份区域ECC 寄存器为NFSECC(0x4E000034)。

1

MainECCLock

[5]

锁定主数据区域ECC 生成0: 开锁主数据区域 ECC生成 1:锁定主数据区域 ECC生成主数据区域ECC 状态寄存器为NFMECC0/1(0x4E00002C/30)。

1

InitECC

[4] 

初始化ECC 编码器/译码器(只写)1:初始化ECC 编码器/译码器

 0
保留[3:2] 保留 00
Reg_nCE[1] 

NAND Flash 存储器nFCE 信号控制0:强制 nFCE 为低(使能片选) 1:强制 nFCE 为高(禁止片选)注意:在引导启动期间其自动被控制。只有MODE 位为1 该值才有效。

 1
MODE[0] 

NAND Flash 控制器运行模式0:NAND Flash 控制器禁止(不工作) 1:NAND Flash 控制器使能

 0

MODE [0]: 设置为1,使能NAND控制器。

Reg_nCE [1]: 设置为1,禁止片选(等要使用的时候再使能片选信号)。

所以NFCONF寄存器设置如下:

/*使能Nand Flash控制器,禁止片选*/
NFCONT = (1<<1) | (1<<0);
2.4 命令寄存器(NFCMMD)

寄存器信息:

寄存器地址R/W描述复位值
NFCMMD0X4E000008R/WNand Flash命令寄存器0x00

寄存器位信息:

NFCMMD描述初始状态
保留[15:8]保留0x00
NFCMMD[7:0]Nand Flash存储器命令值0x00

我们可以使用2440上的Nand Flash控制器简化操作,只需要往NFCMMD寄存器写入要传输的命令就可以了,Nand Flash控制器默认把上面复杂的时序发出来。

2.5 地址寄存器(NFADDR)

寄存器信息:

寄存器地址R/W描述复位值
NFADDR0X4E00000CR/WNand Flash地址寄存器0x00000XX00

寄存器位信息:

NFADDR描述初始状态
保留[15:8]保留0x00
NFADDR[7:0]Nand Flash存储器地址值0x00

发命令后,后面就需要发送地址了,当$nWE$和$ALE$有效的时候,表示锁存的是地址,往NFADDR寄存器中写值就可以了,比如:NFADDR=0x00。上一节我们得知地址需要用5个周期来发送,前2个周期为col地址,后三个周期为row(page)地址。

① column:列地址A0~A10,就是页内地址,地址范围是从0到2047。(A11用来确定oob的地址,即2048-2111这64个字节的范围)
② page:A12~A28,称作页号,page(row)编号。
2.6 数据寄存器(NFDATA) 

寄存器信息:

寄存器地址R/W描述复位值
NFDATA0X4E000010R/WNand Flash数据寄存器0xXXXX

寄存器位信息:

NFDATA描述初始状态
NFDATA[31:0]NAND Flash 读取/编程数据给I/O0xXXXX

当命令、地址都发送完后就可以从数据总线上DATA[7:0]获取数据或者写入数据。同样往NFDATA寄存器中写值或者读值就可以了,如unsigned char buf=NFDATA,由于是数据位宽是8位的,所以访问时数据组织形式如下:

从上图可以看出,当word access时,只需一个时钟周期;当byteaccess的时候,需要4个时钟周期,小端模式下第一个时钟周期对应低字节,第四个时钟周期对应高字节。nand_wait_rdle函数等待Nand Flash空闲,从上图可以看出当NFSTAT寄存器[0]的值为1时Nand Flash是空闲的,我们可以通过该位来判断Nand Flash是否繁忙。代码如下:

/* 等待NAND Flash就绪 */
void _nand_wait_idle(void)
{
    int i;
    while(!(NFSTAT & BUSY));
    for(i=0; i<10; i++);
}
2.7 状态寄存器(NFSTAT)

寄存器信息:

寄存器地址R/W描述复位值
NFSTAT0X4E000020R/WNand Flash运行状态寄存器0xXX00

寄存器位信息:

NFSTAT描述初始状态
保留[7]保留0xXXXX
保留[6:4]

保留

 X
illegalAccess[3]

软件锁定或紧锁一次使能。非法访问(编程,擦除)存储器屏蔽此位设置

0:不检测非法访问 1:检测非法访问

 0
$R/\overline{B}$_TransDetect[2]

当$R/\overline{B}$由低变高时发生传输,如果使能了此位则设置和发出中断。要清

除此位时对其写入‘1’

0:不检测$R/\overline{B}$ 传输 1:检测$R/\overline{B}$ 传输

传输配置设置在$R/\overline{B}$_TransMode(NFCONT[8])中

 0
$\overline{CE}$(只读)[1] $\overline{CE}$ 输出引脚的状态 1
$R/\overline{B}$(只读)[0]

 $R/\overline{B}$ 输入引脚的状态

0:NAND Flash 存储器忙 1:NAND Flash 存储器运行就绪

 1

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

三 代码

在前面我们已经介绍了NAND的读写等命令以及时序图,这里就不再单独介绍具体的步骤,下面直接上代码。

3.1 区分nand或者nor启动

我们知道nand启动0x00地址对应片内SRAM,可以像内存一样的写0x00地址;nor启动,0x00地址对应片内nor flash,nor falsh不能像内存一样的写地址, 所以往0x00地址写入数据成功表示nand启动,写不成功表示nor启动。

int is_boot_from_nor_flash(void)
{
    volatile u32 *p = (volatile u32 )0x00;

    u32 val = *p;                      /* get value from address 0x00 */
    *p = 0x12345678;                /* write value to address 0x00 */

    /* 写成功, 对应nand启动 */
    if(*p == 0x12345678){
        *p = val;
        return 0;
    }

    return 1;
}
3.2 nand.h
/*****************************************************************************************************************
 *
 *  FileName  : nand.h
 *  Function  : Nand Flash设置
 *  Author    : zy
 *              K9F2G08U0C   258M   28位地址
 *
 ****************************************************************************************************************/
#ifndef __NAND_H__
#define __NDND_H__

#include  "type.h"

#define GSTATUS1        (*(volatile u32 *)0x560000B0)
#define BUSY            1

/* page size 2048byte */
#define NAND_PAGE_SIZE           2048
#define NAND_PAGE_MASK_SIZE      (NAND_PAGE_SIZE - 1)
#define NAND_BLOCK_SIZE          (64*NAND_PAGE_SIZE)
#define NAND_BLOCK_MASK_SIZE     (NAND_BLOCK_SIZE-1)

/******************************************************************************************************************/
/* Nand Flash (see S3C2440 manual chapter 6, www.100ask.net) */
#define     NFCONF        (*(volatile u32 *)0x4e000000)        // 配置寄存器
#define     NFCONT       (*(volatile u32 *)0x4e000004)        // 控制寄存器
#define     NFCMMD       (*(volatile u8  *)0x4e000008)        // 命令寄存器
#define     NFADDR       (*(volatile u8  *)0x4e00000C)        // 地址寄存器
#define     NFDATA       (*(volatile u8  *)0x4e000010)        // 数据寄存器
#define     NFMECCD0     (*(volatile u32 *)0x4e000014)
#define     NFMECCD      (*(volatile u32 *)0x4e000018)
#define     NFSECCD      (*(volatile u32 *)0x4e00001C)
#define     NFSTAT       (*(volatile u32 *)0x4e000020)
#define     NFESTAT0     (*(volatile u32 *)0x4e000024)
#define     NFESTAT1     (*(volatile u32 *)0x4e000028)
#define     NFMECC0      (*(volatile u32 *)0x4e00002C)
#define     NFMECC1      (*(volatile u32 *)0x4e000030)
#define     NFSECC       (*(volatile u32 *)0x4e000034)
#define     NFSBLK       (*(volatile u32 *)0x4e000038)
#define     NFEBLK       (*(volatile u32 *)0x4e00003C)

/******************************************************************************************************************/
typedef struct {                                 /* 函数指针作为结构体成员 */
    /* 组成完整的命令操作的步骤 */
    void (*_nand_reset)(void);                         /* 初始化nand控制器 */
    void (*_nand_wait_idle)(void);                     /* 等待NAND Flash就绪 */
    void (*_nand_select_chip)(void);                   /* 使能片选 */
    void (*_nand_deselect_chip)(void);                 /* 禁止片选 */
    void (*_nand_write_cmd)(u32 cmd);                  /* 发命令 */
    void (*_nand_write_addr)(u32 addr);                /* 发地址 */
    u8 (*_nand_read_data)(void);                       /* 读数据 */

    /* 完整的命令操作 */
    int (*nand_read_data)(u8 *buf, u32 start_addr, u32 size);    /* 发送读取数据命令, start_addr参数必须按页对齐 success:0 fail:-1 */
    void (*nand_read_id)(u8 *buf);                               /* 读ID,buf length 5*/
    int  (*nand_block_erase)(u32 start_addr,u32 size);           /* 块擦除(block erase),参数必须按块对齐  success:0 fail:-1  */
    int (*nand_write_data)(u32 start_addr,u8 *buf, u32 size);    /* 发送写数据命令, start_addr参数必须按页对齐  success:0 fail:-1*/
}t_nand_chip;

/* 供外部调成员和方法 */
extern t_nand_chip nand_chip;                   /* nand_init执行后可以使用,extern全局变量避免重复定义 */
extern void nand_init(void);                    /* 初始化NAND Flash,给nand_chip函数指针成员赋值 */

#endif
3.3 nand.c
/****************************************************************************************************/
#include "nand.h"

/* 全局变量 */
t_nand_chip nand_chip;
/****************************************************************************************************/
/* 等待NAND Flash就绪 */
void _nand_wait_idle(void)
{
    int i;
    while(!(NFSTAT & BUSY));
    for(i=0; i<10; i++);
}

/* 发出片选信号 */
void _nand_select_chip(void)
{
    int i;
    NFCONT &= ~(1<<1);                   /* set CE=0 */
    for(i=0; i<10; i++);    
}

/* 取消片选信号 */
void _nand_deselect_chip(void)
{
    NFCONT |= (1<<1);                   /* set CE=1 */
}

/* 发出命令 */
void _nand_write_cmd(u8 cmd)
{
    int i;
    NFCMMD = cmd;
    for(i=0; i<10; i++);
}

/* 发出地址 */
void _nand_write_addr_byte(u8 addr)
{
    volatile int i;
    NFADDR = addr;
    for(i=0; i<10; i++);
}

/* 发出地址,5个周期来发送,前2个周期为col地址,后三个周期为row(page)地址 */
void _nand_write_addr(u32 addr)
{
    int col, page;

    col = addr & NAND_PAGE_MASK_SIZE;
    page = addr / NAND_PAGE_SIZE;     /* 块号  */

    _nand_write_addr_byte(col & 0xff);            /* Column Address A0~A7 */
    _nand_write_addr_byte((col >> 8) & 0x0f);     /* Column Address A8~A11 */
    _nand_write_addr_byte(page & 0xff);            /* Row Address A12~A19 */
    _nand_write_addr_byte((page >> 8) & 0xff);    /* Row Address A20~A27 */
    _nand_write_addr_byte((page >> 16) & 0x01);    /* Row Address A28 */
}

/* 复位 */
void _nand_reset(void)
{
    _nand_select_chip();
    _nand_write_cmd(0xff);          // 复位命令
    _nand_wait_idle();
    _nand_deselect_chip();
}

/* 读取数据 */
u8 _nand_read_data(void)
{
    return NFDATA;
}

/* 写入数据 */
void _nand_write_data(u8 data)
{
    int i;
    NFDATA = data;
    for(i=0; i<10; i++);
}

/* Read ID */
void nand_read_id(u8 *buf)
{
    /* 选中芯片 */
    _nand_select_chip();

    /* write cmd 0x90 */
    _nand_write_cmd(0x90);

    /* write addr 0x00 */
    _nand_write_addr(0x00);

    /* read */
    buf[0] = _nand_read_data();    /* 第一个访问周期锁存的数据为marker code,值为0xEC */
    buf[1] = _nand_read_data();    /* 第二个访问周期的数据为device code,值为0xDA */
    buf[2] = _nand_read_data();    /* 0X10 */
    buf[3] = _nand_read_data();    /* 0X95*/
    buf[4] = _nand_read_data();    /* 0X44 */

    /* 取消片选信号 */
    _nand_deselect_chip();
}

/* 发送读取数据命令,start_addr参数必须按页对齐   success:0 fail:-1  */
int nand_read_data(u8 *buf, u32 start_addr, u32 size)
{
    int i = 0;
    int j = 0;

    if (start_addr & NAND_PAGE_MASK_SIZE ) {
        return -1;    /* 地址或长度不对齐 */
    }


    /* 选中芯片 */
    _nand_select_chip();

    /* 按页读取 */
    while (i < size) {
      /* 发出READ命令 */
      _nand_write_cmd(0);

      /* write address */
      _nand_write_addr(start_addr+i);

      /* write cmd */
      _nand_write_cmd(0x30);

      /* wait */
      _nand_wait_idle();

      /* 读取一页数据 */
      for(; (j < NAND_PAGE_SIZE) &&  (i < size); j++)
      {
          buf[i++] = _nand_read_data();
      }
      j = 0;
    }

    /* 取消片选信号 */
    _nand_deselect_chip();
    
    return 0;
}

/* 块擦除(block erase),参数必须按块对齐  success:0 fail:-1  */
int nand_block_erase(u32 start_addr,u32 size)
{
    int i;
    int page = start_addr / NAND_PAGE_SIZE;     /* 块号  */

    if ((start_addr & NAND_BLOCK_MASK_SIZE) || (size & NAND_BLOCK_MASK_SIZE)) {
        return -1;    /* 地址或长度不对齐 */
    }

    /* 选中芯片 */
    _nand_select_chip();

    for(i=start_addr; i < (start_addr + size);)
    {
        page = i / NAND_PAGE_SIZE;

        /* write cmd 0x60 */
        _nand_write_cmd(0x60);

        /* write row addr  */
        _nand_write_addr_byte(page & 0xff);            /* Row Address A12~A19 */
        _nand_write_addr_byte((page >> 8) & 0xff);    /* Row Address A20~A27 */
        _nand_write_addr_byte((page >> 16) & 0x01);    /* Row Address A28 */

        /* write cmd 0xD0 */
        _nand_write_cmd(0xD0);

        /* wait */
        _nand_wait_idle();

        /* write cmd 0x70 */
        _nand_write_cmd(0x70);

        if (_nand_read_data()&0x1)
        {
            return -1;
        }
        i += NAND_BLOCK_SIZE;
    }

    /* 取消片选信号 */
    _nand_deselect_chip();
    return 0;
}


/* 发送写数据命令, start_addr参数必须按页对齐  success:0 fail:-1*/
int nand_write_data(u32 start_addr,u8 *buf, u32 size)
{
    int i = 0;
    int j = 0;

    if (start_addr & NAND_PAGE_MASK_SIZE )
    {
        return -1;    /* 地址或长度不对齐 */
    }

    /* 选中芯片 */
    _nand_select_chip();

    /* 按页写 */
    while (i < size)
    {
      /* 发出WRITE命令 */
      _nand_write_cmd(0x80);

      /* write address */
      _nand_write_addr(start_addr + i);

      /* WRITE数据 */
      for(; (j < NAND_PAGE_SIZE) && (i<size); j++)
      {
          _nand_write_data(buf[i++]);
      }

      /* write cmd */
      _nand_write_cmd(0x10);

      /* wait */
      _nand_wait_idle();

      /* write cmd 0x70 */
      _nand_write_cmd(0x70);

      if (_nand_read_data()&0x1)
      {
          return -1;
      }

      j = 0;
    }

    /* 取消片选信号 */
    _nand_deselect_chip();

    return 0;
}

/* 初始化NAND Flash, 给nand_chip函数指针成员赋值*/
void nand_init(void)
{
    #define TACLS   0
    #define TWRPH0  1
    #define TWRPH1  0

    nand_chip._nand_reset          = _nand_reset;
    nand_chip._nand_wait_idle      = _nand_wait_idle;
    nand_chip._nand_select_chip    = _nand_select_chip;
    nand_chip._nand_deselect_chip  = _nand_deselect_chip;
    nand_chip._nand_write_cmd      = _nand_write_cmd;
    nand_chip._nand_write_addr     = _nand_write_addr;
    nand_chip._nand_read_data      = _nand_read_data;
    nand_chip.nand_read_data       = nand_read_data;
    nand_chip.nand_read_id         = nand_read_id;
    nand_chip.nand_block_erase     = nand_block_erase;
    nand_chip.nand_write_data      = nand_write_data;

    /* 设置时序 */
    NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
    /* 使能NAND Flash控制器, 初始化ECC, 禁止片选, 使能NAND控制器 */
    NFCONT = (1<<4)|(1<<1)|(1<<0);

    /* 复位NAND Flash */
    _nand_reset();
}

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

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

nand_block_erase是以block为单位擦除的。

实际上,Nand Flash的型号K9F2G08U0C也支持随机读写,也就是支持单字节读写。这里就不介绍了,具体可以参考Nand Flash实验读ID和随机读写

需要注意的是:在这段代码中使用了全局变量t_nand_chip nand_chip,nand_chip未初始化,存放在未始化数据段,也就是bss段。

3.4 main.c
#include "led.h"
#include "uart.h"
#include "printf.h"
#include "nand.h"
#include "init.h"

u32 get_uint()
{
    return NAND_BLOCK_SIZE*10;
}

void do_erase_nand_flash(void)
{
    unsigned int addr;

    /* 获得地址 */
    addr = get_uint();

    printf("erasing ...\n\r");
    if(nand_chip.nand_block_erase(addr, 128*1024) == 0)
    {
        printf("erasing success\n\r");
    }
}

void do_read_nand_flash(void)
{
    unsigned int addr;
    volatile u8 *p;
    int i, j;
    unsigned char buf[64];

    /* 获得地址 */
    addr = get_uint();

    printf("reading ...\n\r");
    nand_chip.nand_read_data(buf, addr, 64);
    p = (volatile u8 *)buf;

    printf("Data : \n\r");
    /* 长度固定为64 */
    for (i = 0; i < 4; i++)
    {
        /* 每行打印16个数据 */
        for (j = 0; j < 16; j++)
        {
            /* 先打印数值 */
            printf("%c ", *p++);
        }
        printf("\n\r");
    }
}

void do_write_nand_flash(void)
{
    unsigned int addr;
    u8 str[64];
    int i;
    unsigned int val;

    /* 获得地址 */
    addr = get_uint();

    for(i=0;i<64;i++)
    {
        str[i] = 'A' + i%26;
    }

    printf("writing ...\n\r");
    nand_chip.nand_write_data(addr,str, NAND_PAGE_SIZE);
}

void do_read_nand_id()
{
    unsigned char buf[5]={0};
    nand_chip.nand_read_id(buf);
    printf("maker   id  = 0x%x\n\r",buf[0]);      // 0xEC
    printf("device  id  = 0x%x\n\r",buf[1]);      // 0xDA
    printf("3rd byte    = 0x%x\n\r",buf[2]);      // 0x10
    printf("4th byte    = 0x%x\n\r",buf[3]);      // 0x95
    printf("page  size  = %d kb\n\r",1  <<  (buf[3] & 0x03));        // 2kb
    printf("block size  = %d kb\n\r",64 << ((buf[3] >> 4) & 0x03));  // 128kb
    printf("5th byte    = 0x%x\n\r",buf[4]);                         // 0x44
}

int main()
{
    led_init();
    
    // 串口初始化以及接收中断
    uart_init();
    
    nand_init();

    int flag = is_boot_from_nor_flash();

    do_write_nand_flash();
    do_read_nand_flash();
    do_erase_nand_flash();
    do_read_nand_id();
    if(flag){
        led_on(LED2);
    }
    while(1)
    {
        led_turn(LED1);
        delay_ms(4000);
  }
   return 0;
}
3.5 编译下载

运行结果通过串口输出如下:

四、代码下载

Young / s3c2440_project【6.nand_flash】

这个代码已经远超过4kb,并且没有将代码从NAND拷贝到SDRAM运行,只可以下载到SDRAM 0x30000000处运行。

参考文章:

[1] s3c2440裸机-nandflash编程(三. 初始化及识别)

[2]s3c2440裸机-nandflash编程(四. nand读写擦实现)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
水资源是人类社会的宝贵财富,在生活、工农业生产中是不可缺少的。随着世界人口的增长及工农业生产的发展,需水量也在日益增长,水已经变得比以往任何时候都要珍贵。但是,由于人类的生产和生活,导致水体的污染,水质恶化,使有限的水资源更加紧张。长期以来,油类物质(石油类物质和动植物油)一直是水和土壤中的重要污染源。它不仅对人的身体健康带来极大危害,而且使水质恶化,严重破坏水体生态平衡。因此各国都加强了油类物质对水体和土壤的污染的治理。对于水中油含量的检测,我国处于落后阶段,与国际先进水平存在差距,所以难以满足当今技术水平的要求。为了取得具有代表性的正确数据,使分析数据具有与现代测试技术水平相应的准确性和先进性,不断提高分析成果的可比性和应用效果,检测的方法和仪器是非常重要的。只有保证了这两方面才能保证快速和准确地测量出水中油类污染物含量,以达到保护和治理水污染的目的。开展水中油污染检测方法、技术和检测设备的研究,是提高水污染检测的一条重要措施。通过本课题的研究,探索出一套适合我国国情的水质污染现场检测技术和检测设备,具有广泛的应用前景和科学研究价值。 本课题针对我国水体的油污染,探索一套检测油污染的可行方案和方法,利用非分散红外光度法技术,开发研制具有自主知识产权的适合国情的适于野外便携式的测油仪。利用此仪器,可以检测出被测水样中亚甲基、甲基物质和动植物油脂的污染物含量,为我国众多的环境检测站点监测水体的油污染状况提供依据。
### 内容概要 《计算机试卷1》是一份综合性的计算机基础和应用测试卷,涵盖了计算机硬件、软件、操作系统、网络、多媒体技术等多个领域的知识点。试卷包括单选题和操作应用两大类,单选题部分测试学生对计算机基础知识的掌握,操作应用部分则评估学生对计算机应用软件的实际操作能力。 ### 适用人群 本试卷适用于: - 计算机专业或信息技术相关专业的学生,用于课程学习或考试复习。 - 准备计算机等级考试或职业资格认证的人士,作为实战演练材料。 - 对计算机操作有兴趣的自学者,用于提升个人计算机应用技能。 - 计算机基础教育工作者,作为教学资源或出题参考。 ### 使用场景及目标 1. **学习评估**:作为学校或教育机构对学生计算机基础知识和应用技能的评估工具。 2. **自学测试**:供个人自学者检验自己对计算机知识的掌握程度和操作熟练度。 3. **职业发展**:帮助职场人士通过实际操作练习,提升计算机应用能力,增强工作竞争力。 4. **教学资源**:教师可以用于课堂教学,作为教学内容的补充或学生的课后练习。 5. **竞赛准备**:适合准备计算机相关竞赛的学生,作为强化训练和技能检测的材料。 试卷的目标是通过系统性的题目设计,帮助学生全面复习和巩固计算机基础知识,同时通过实际操作题目,提高学生解决实际问题的能力。通过本试卷的学习与练习,学生将能够更加深入地理解计算机的工作原理,掌握常用软件的使用方法,为未来的学术或职业生涯打下坚实的基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Graceful_scenery

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

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

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

打赏作者

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

抵扣说明:

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

余额充值