千家信息网

NAND FLASH控制器怎么实现

发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,本篇内容介绍了"NAND FLASH控制器怎么实现"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!存储
千家信息网最后更新 2026年11月01日NAND FLASH控制器怎么实现

本篇内容介绍了"NAND FLASH控制器怎么实现"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

  1. 存储组织形式


K9F2G08X0A共有2048个Block(块), 每个Block含有 64 Page(页), 每个Page含有2k byte的正常存储空间以及64 byte的校验空间 .

总空间 = 2048 * 64 * (2 * 1024 + 64) byte

实际存储空间 = 2048 * 64 * 2 * 1024 byte

2. 引脚定义及接法

3. 寻址方式

列地址: 进行 Block 和 Page 寻址

行地址: 进行 Page 内寻址

4. 命令

以下为几个命令操作示意, 更多命令请参靠芯片手册.

(1) Read ID

说明: 先发送 0x90 然后再发送 0x00, 然后 nand flash 会返回5个数据, 依据芯片型号的不同数据内容也不尽相同, 具体值见手册

(2) Page Read

说明: 先发送 0x00 然后发送要读取数据的地址, 之后发送 0x30, 根据 R/B 可以判断是否发送完成. 命令发送完成之后 从RE 的第一个下降沿开始, 芯片将从该地址开始到当页结束的所有数据依次输出

(3) Page Program

说明: 先发送 0x80 然后发送 地址 和数据, 之后发送 0x10, 读取 R/B , 命令写入完成之后 发送 0x70 再依据 I/O0来判断写入是否成功, 0 : 成功 1 : 失败

(4) Block Erase

说明: 先发送 0x90 然后再发送 行地址 1 2 3, 然后再发送 擦除命令 0xD0, 根据 R/B 引脚判断擦除操作是否完成, 完成之后发送 0x70 根据 I/O0的状态来判断擦除是否成功, 0 : 成功 1: 失败

注释1:

/* 初始化NAND Flash */void nand_init(void){#define TACLS   0#define TWRPH0  3#define TWRPH1  0    /* 判断是S3C2410还是S3C2440 */    if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))    //2410    {            nand_chip.nand_reset         = s3c2410_nand_reset;        nand_chip.wait_idle          = s3c2410_wait_idle;        nand_chip.nand_select_chip   = s3c2410_nand_select_chip;        nand_chip.nand_deselect_chip = s3c2410_nand_deselect_chip;        nand_chip.write_cmd          = s3c2410_write_cmd;        nand_chip.write_addr         = s3c2410_write_addr;        nand_chip.read_data          = s3c2410_read_data;        /* 使能NAND Flash控制器, 初始化ECC, 禁止片选, 设置时序 */        s3c2410nand->NFCONF = (1<<15)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);    }    else    {        nand_chip.nand_reset         = s3c2440_nand_reset;        nand_chip.wait_idle          = s3c2440_wait_idle;        nand_chip.nand_select_chip   = s3c2440_nand_select_chip;        nand_chip.nand_deselect_chip = s3c2440_nand_deselect_chip;        nand_chip.write_cmd          = s3c2440_write_cmd;#ifdef LARGER_NAND_PAGE        nand_chip.write_addr         = s3c2440_write_addr_lp;#else        nand_chip.write_addr         = s3c2440_write_addr;#endif        nand_chip.read_data          = s3c2440_read_data;        /* 设置时序 */        s3c2440nand->NFCONF = (TACLS << 12)|(TWRPH0 << 8)|(TWRPH1 << 4);                /* 使能NAND控制器, 初始化ECC, 禁止片选 */        s3c2440nand->NFCONT = (1 << 4)|(1 << 1)|(1 << 0);    }        /* 复位NAND Flash */    nand_reset();}

这里需要注意的是 大页 nand , 地址发送五次, 前两次是 colum addr 后三次是 row addr, colum addr负责页内寻址, 我们实际寻址的空间为2k = 2^11, 所以只需要11位即可, 所以程序中只取了低11位

A0~A10用来页内寻址. 另外64byte的OOB空间可以使用A11来寻址.

A12~A17用来在块内寻址页, 共64页

A18~A28用来寻址块, 共2048块

nand flash 底层操作函数:

/* 复位 */static void s3c2410_nand_reset(void){    s3c2410_nand_select_chip();   // 选中芯片     s3c2410_write_cmd(0xff);      // 复位命令    s3c2410_wait_idle();          // 等待nand就绪    s3c2410_nand_deselect_chip(); // 取消选中   }/* 等待NAND Flash就绪 */static void s3c2410_wait_idle(void){    int i;    volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFSTAT;    while(!(*p & BUSY))        for(i=0; i<10; i++);}/* 发出片选信号 */static void s3c2410_nand_select_chip(void){    int i;    s3c2410nand->NFCONF &= ~(1<<11);    for(i=0; i<10; i++);    }/* 取消片选信号 */static void s3c2410_nand_deselect_chip(void){    s3c2410nand->NFCONF |= (1<<11);}/* 发出命令 */static void s3c2410_write_cmd(int cmd){    volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFCMD;    *p = cmd;}/* 发出地址 */static void s3c2410_write_addr(unsigned int addr){    int i;    volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFADDR;        *p = addr & 0xff;    for(i=0; i<10; i++);    *p = (addr >> 9) & 0xff;    for(i=0; i<10; i++);    *p = (addr >> 17) & 0xff;    for(i=0; i<10; i++);    *p = (addr >> 25) & 0xff;    for(i=0; i<10; i++);}/* 读取数据 */static unsigned char s3c2410_read_data(void){    volatile unsigned char *p = (volatile unsigned char *)&s3c2410nand->NFDATA;    return *p;}/* S3C2440的NAND Flash操作函数 *//* 复位 */static void s3c2440_nand_reset(void){    s3c2440_nand_select_chip();    s3c2440_write_cmd(0xff);  // 复位命令    s3c2440_wait_idle();    s3c2440_nand_deselect_chip();}/* 等待NAND Flash就绪 */static void s3c2440_wait_idle(void){    int i;    volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT;//状态寄存器, 只用到位0. 0 : busy   1 : ready    while(!(*p & BUSY))        for(i=0; i<10; i++);}/* 发出片选信号 */static void s3c2440_nand_select_chip(void){    int i;    s3c2440nand->NFCONT &= ~(1<<1);    for(i=0; i<10; i++);    }/* 取消片选信号 */static void s3c2440_nand_deselect_chip(void){    s3c2440nand->NFCONT |= (1<<1);}/* 发出命令 */static void s3c2440_write_cmd(int cmd){    volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMD;//NFCMD 不同的flash命令不一样, 发送命令信号    *p = cmd;}/* 发出地址(小页 4周期) */static void s3c2440_write_addr(unsigned int addr){    int i;    volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;        *p = addr & 0xff;    for(i=0; i<10; i++);    *p = (addr >> 9) & 0xff;    for(i=0; i<10; i++);    *p = (addr >> 17) & 0xff;    for(i=0; i<10; i++);    *p = (addr >> 25) & 0xff;    for(i=0; i<10; i++);}/* 发出地址(大页 5周期) */static void s3c2440_write_addr_lp(unsigned int addr){        int i;        volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;//NFADDR当向该寄存器写入数据时, 芯片向nand发送地址信号        int col, page;             //#define NAND_SECTOR_SIZE_LP    2048             //#define NAND_BLOCK_MASK_LP     (NAND_SECTOR_SIZE_LP - 1)        col = addr & NAND_BLOCK_MASK_LP;        //2048 -1 = 11111111111b, 这里是屏蔽高位, 取低11位数据, 因为寻址空间只到 2k = 2^11 , 所以最多用11位, 这里直接不考虑第12位                                                //参考链接:http://bbs.csdn.net/topics/360034390        page = addr / NAND_SECTOR_SIZE_LP;      //2048 = 2^11, 这里将数据右移11位, 获取高位数据                *p = col & 0xff;                        /* Column Address A0~A7 */        for(i=0; i<10; i++);                       *p = (col >> 8) & 0x0f;    /* Column Address A8~A11 */        for(i=0; i<10; i++);        *p = page & 0xff;                       /* Row Address A12~A19 */        for(i=0; i<10; i++);        *p = (page >> 8) & 0xff;        /* Row Address A20~A27 */        for(i=0; i<10; i++);        *p = (page >> 16) & 0x03;       /* Row Address A28~A29 */        for(i=0; i<10; i++);}/* 读取数据 */static unsigned char s3c2440_read_data(void){    volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA;//数据寄存器, 读写都是这个寄存器. 只用到它的低 8 位    return *p;}/* 在第一次使用NAND Flash前,复位一下NAND Flash */static void nand_reset(void){    nand_chip.nand_reset();}

‍‍‍‍发送地址: NFADDR 发送命令: NFCMD 发送/读取数据: NFDATA 读取状态: NFSTAT 初始化控制器: NFCONT‍

由以上代码可以看出: 初始化完毕之后就不必关系总线上的时序只需要把 地址/命令/数据放到对应的寄存器, 芯片就能在总线上发出对应的时序

而初始化需要配置的是: 时序的参数/数据位宽/只读位/页的大小

注释2:

/* 读函数 */void nand_read(unsigned char *buf, unsigned long start_addr, int size)//在head.S中设置的r0 r1 r2 分别为该函数的三个参数{    int i, j;#ifdef LARGER_NAND_PAGE    if ((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP)) {        return ;    /* 地址或长度不对齐 */    }#else    if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {        return ;    /* 地址或长度不对齐 */    }#endif    /* 选中芯片 */    nand_select_chip();    for(i=start_addr; i < (start_addr + size);){      /* 发出READ0命令 */      write_cmd(0);      /* Write Address */      write_addr(i);#ifdef LARGER_NAND_PAGE      write_cmd(0x30);#endif      wait_idle();#ifdef LARGER_NAND_PAGE      for(j=0; j < NAND_SECTOR_SIZE_LP; j++, i++) {#else          for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {#endif          *buf = read_data();          buf++;      }    }    /* 取消片选信号 */    nand_deselect_chip();    return ;}

总结一下:

(1)选中芯片

(2)发送00h

(3)发出地址

(4)发30h

(5)等待就绪

(6)读一页数据

链接文件:

SECTIONS {   firtst    0x00000000 : { head.o init.o nand.o}  second         0x30000000 : AT(4096) { main.o }}

main.c存放到了nand的4096地址处.

入口文件:

@******************************************************************************@ File:head.s@ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行@******************************************************************************         .text.global _start_start:                                            @函数disable_watch_dog, memsetup, init_nand, nand_read_ll在init.c中定义            ldr     sp, =4096               @设置堆栈             bl      disable_watch_dog       @关WATCH DOG            bl      memsetup                @初始化SDRAM            bl      nand_init               @初始化NAND Flash                                                                 注释1                                            @将NAND Flash中地址4096开始的1024字节代码(main.c编译得到)复制到SDRAM中                                            @nand_read_ll函数需要3个参数:            ldr     r0,     =0x30000000     @1. 目标地址=0x30000000,这是SDRAM的起始地址            mov     r1,     #4096           @2.  源地址   = 4096,连接的时候,main.c中的代码都存在NAND Flash地址4096开始处            mov     r2,     #2048           @3.  复制长度= 2048(bytes),对于本实验的main.c,这是足够了            bl      nand_read               @调用C函数nand_read                                                               注释2            ldr     sp, =0x34000000         @设置栈            ldr     lr, =halt_loop          @设置返回地址            ldr     pc, =main               @b指令和bl指令只能前后跳转32M的范围,所以这里使用向pc赋值的方法进行跳转halt_loop:            b       halt_loop

这里将nand flash 从4096地址开始的2048字节复制到sdram的0x3000 0000地址处

main.c中是led闪烁程序.

"NAND FLASH控制器怎么实现"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

地址 命令 数据 芯片 函数 空间 寄存器 控制器 控制 成功 时序 注释 代码 信号 内容 参数 实际 更多 状态 程序 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 南京欣网互联网络科技 哈尔滨大商圈互联网科技有限公司 互联网电商科技 科技制造互联网公司 上海万想互联网科技 互联网科技公司名称 青海省互联网新科技 成都玖崟互联网科技有限公司乐商圈怎么样 温州喔噻互联网科技有限公司 互联网科技实习生岗位职责 以后互联网后是啥科技 链环科技传统企业怎么转型互联网 深圳艾果互联网通讯科技 工业互联网等科技实现智能化赋能 朱啸虎曾投资哪些互联网科技企业 用友工业互联网科技发展 特约风豹互联网科技为什么扣费 互联网数据科技专业 互联网方面的科技前沿 山东大美朱楼互联网科技有限公司 咖啡网 互联网和科技技术 互联网科技的未来发展 左右互联网络科技 互联网科技寓意 常德市电业局互联网科技部 互联网在线教育科技创业 爱医互联网络科技 烟台灵鸽互联网科技有限公司 科技互联网行业投资研究
0