Nand Flash编程实现读地址信息

  • 硬件平台:韦东山嵌入式Linxu开发板(S3C2440.v3)
  • 软件平台:运行于VMware Workstation 12 Player下UbuntuLTS16.04_x64 系统
  • 参考资料:开发版原理图,S3C2440A datasheet,K9F2G08U0C datasheet
  • 源码仓库:https://gitee.com/d_1254436976/Embedded-Linux-Phase-1

目录

  • Nand Flash编程实现读地址信息
    • 一、基础知识
      • 1、Nand Flash 存储结构介绍
      • 2、行列地址的提取
      • 3、Nand Flash OOB块介绍
      • 4、位反转
    • 二、实现功能
    • 三、编程原理
      • 1、编写读地址函数
      • 2、进一步封装读地址函数
      • 3、实现从Nand Flash启动
        • 3.1 修改sdram_init.c文件
        • 3.2 修改Makefile文件
    • 四、编程文件
      • 1、修改后的Makefile文件
      • 2、修改后的nand_init.c文件
      • 3、main.c文件
    • 五、运行结果

一、基础知识

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 + 64 Bytes。
  ④、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);

3、Nand Flash OOB块介绍

①、为什么会有OOB区?
  由于NAND Flash的工艺不能保证NAND的Memory Array在其生命周期中保持性能的可靠,因此,在NAND的生产中及使用过程中会产生坏块。当编程/擦除这个块时,会造成Page Program和Block Erase操作时的错误,相应地反映到OOB区的相应位
②、OOB区多大,在哪里?
  如上图所示,每个Page都会有OOB区,大小为64 Btyes,位置在每个Page的2048位
③、注意
   当读写擦除Nand Flash时,需要避免破坏OOB区。

4、位反转

   CPU在寻址的时候看不到OOB区,所以当CPU读到2048个数据时,此时访问的是下一个Page的第0个数据
  这里也可以看出,OOB的存在,是为了解决Nand Flash的缺陷。对于CPU而已,只关心数据,不需要看OOB

二、实现功能

  功能:通过串口输入指定的地址,并读取该地址的信息

三、编程原理

  1 Page = (2K(数据)+ 64 (OOB区))Bytes

  查K9F2G08U0C 芯片手册,读操作的时序图如下

1、编写读地址函数

/* Nand Flash 读地址 * addr:地址 buf:存储信息 len:读取的信息长度*/
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();
}

2、进一步封装读地址函数

/* 读某个地址 * 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(".");elseprintf("%c",str[j]);}printf("\n\r");}
}

3、实现从Nand Flash启动

  由于开发版上电时,Nand Flash中的4K会自动被复制到4K的RAM中,之前我们已经实现了SDRAM重定位,将这4K代码完全复制到SDRAM中。同时,为了确保4K代码包含nand_flash.c文件,需要修改Makefile文件与sdram.c文件。

3.1 修改sdram_init.c文件

/* 判断NOR启动还是NAND启动 * 先保存0地址中的数据,后往0地址里面写入0x123* 判断0地址中是否成功被修改* --修改成功为NOR启动,恢复0地址信息* --修改失败为NAND启动*/
int IsBootNorFlash(void)
{volatile unsigned int *p = (volatile unsigned int *)0;unsigned int val = *p;*p = 0x123;if(*p == 0x123){*p = val;return 0;}else{return 1;}
}/*功能:复制整个text,rodata,data段到SDRAM中*/
void copy_to_sdram(void)
{extern int start,__bss_start;    //建立外部变量,方便获取lds文件中的量int len;volatile unsigned int *text =(volatile unsigned int *) &start;     //text指向程序开头地址volatile unsigned int *end = (volatile unsigned int *)&__bss_start; //end指向bss段开头的地址volatile unsigned int *src = (volatile unsigned int *)0;            //src指向0地址.即FLASH的开头地址len = ((int)&__bss_start) - ((int)&start);if(IsBootNorFlash()){while(text < end){*text++ = *src++;        }}else{nand_init();nand_read(src,text,len);}
}

3.2 修改Makefile文件

确保复制的前4K代码中包含nand_flash.c文件,把start.o sdram_init.o nand_flash.o 放前面

all: start.o sdram_init.o nand_flash.o uart.o main.o uart.o led.o  exception.o eint.o timer.o my_printf.o string_utils.o lib1funcs.o

四、编程文件

1、修改后的Makefile文件

all: start.o sdram_init.o nand_flash.o uart.o main.o uart.o led.o  exception.o eint.o timer.o my_printf.o string_utils.o lib1funcs.oarm-linux-ld -T sdram.lds $^ -o sdram.elfarm-linux-objcopy -O binary -S sdram.elf sdram.binarm-linux-objdump -D sdram.elf > sdram.dis
clean:rm *.bin *.o *.elf *.dis%.o : %.carm-linux-gcc -march=armv4 -c -o $@ $<%.o : %.Sarm-linux-gcc -march=armv4 -c -o $@ $<

2、修改后的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();   //厂商IDbuf[1]  = nand_data();    //设备IDbuf[2]  = nand_data();    //3rdbuf[3]  = nand_data(); //4thbuf[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(".");elseprintf("%c",str[j]);}printf("\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':break;case 'w':case 'W':break;case 'r':case 'R':do_read_nand_flash();break;default:break;}}
}

3、main.c文件

#include "s3c2440_soc.h"
#include "uart.h"
#include "sdram_init.h"
#include "nand_flash.h"char g_Char = 'A';
char g_Char2 = 'a';
char i ='0';int main(void)
{unsigned char c;int flag;//interrupt_init();key_eint_int();
//  timer0_init();uart0_init();//sdram_init();nand_init();nand_flash_test();puts("uart0 init success!\n\r''");putchar(i);while(1){putchar(g_Char); g_Char++;delay(1000000);putchar(g_Char2); g_Char2++;delay(1000000);}return 0;
}

五、运行结果

通过两个图的对比可知,代码成功烧写到了Nand Flash,并进行了重定位,读地址功能实现成功。

韦东山嵌入式Linux学习——015 Nand Flash(2)-Nand Flash编程实现读地址信息相关推荐

  1. 使用u-boot的USB下载功能烧写程序到Nand Flash ——韦东山嵌入式Linux学习笔记06

    本文实验环境: 1. windows 7(64bit) 2. JZ2440(V2) 假设板子的Nor Flash上已经烧好了u-boot,如果我想利用u-boot的USB下载功能,把一个裸板程序烧写到 ...

  2. win7下不能使用dnw烧写的解决办法——韦东山嵌入式Linux学习笔记05

    本文实验环境: 1. windows 7(64bit) 2. JZ2440 一. 问题的提出--没有驱动 假设板子的 Nor Flash 上已经烧好了 u-boot,那么如何通过 u-boot 的US ...

  3. 烧写文件系统——韦东山嵌入式Linux学习笔记11

    本文实验环境: 1. windows 7(64bit) 2. JZ2440(V2) 使用 u-boot 烧写文件系统,一般有两种方法. 1. 通过USB下载功能 2. 通过TFTP功能 通过USB下载 ...

  4. 开发板、Windows、Ubuntu三者互联——韦东山嵌入式Linux学习笔记08

    实验环境: 1. Windows7 2. VMware12.0+Ubuntu 3. JZ2440 按理说,在物理机上安装Ubuntu操作系统是比较推荐的方法.不过,因为windows上有很多好用的工具 ...

  5. 使用u-boot的tftp下载功能烧写程序到Nand Flash ——韦东山嵌入式Linux学习笔记09

    本文实验环境: 1. windows 7(64bit) 2. JZ2440(V2) 操作步骤: (1)设置好开发板和PC的IP地址,使开发板可以 ping 通 PC 如果搞不定,可以参考我的博文 ht ...

  6. 使用 u-boot 烧写内核——韦东山嵌入式Linux学习笔记10

    本文实验环境: 1. windows 7(64bit) 2. JZ2440(V2) 使用 u-boot 烧写内核,一般有两种方法. 1. 通过USB下载功能 2. 通过TFTP功能 通过USB下载功能 ...

  7. 板子ping不通PC怎么办——韦东山嵌入式Linux学习笔记07

    实验环境: 1. JZ2440 2. Win7 如果想用 u-boot 的 tftp 下载功能,那么一定要保证板子可以 ping 通 PC. 一般来说,家用PC都是通过路由器上网的.针对这种情况,我想 ...

  8. 用OpenJTAG烧写程序到Flash—— 韦东山嵌入式Linux视频学习笔记03

    说明:本文仅在Windows环境下实验. 韦东山的JZ2440(v2),可以选择从Nor Flash启动,也可以选择从Nand Flash启动,不管从哪里启动,都需要Flash上有程序,没有程序的话, ...

  9. 韦东山 嵌入式Linux应用开发基础知识 上【gcc makefile 输入设备

    1 main的输入参数,并且在命令行运行文件的时候输入 我的练习 先写了个单纯输出的hello 按照教程里那样写hello 所以gcc编译过程应该是 先创建一个.c文件 gcc -c -o hello ...

  10. 如何告别半途而废——韦东山嵌入式Linux视频学习笔记00

    今天终于从柜子里翻出了韦东山的板子--JZ2440(V2),又翻出了当时买板子赠送的光盘,是的,我又要重新学习了.其实说"重新"是不对的,我当初就没有学完,学到一半放弃了(惭愧啊) ...

最新文章

  1. TensorFlow XLA优化与Memory
  2. 深度学习中的验证集和超参数简介
  3. 【 FPGA 】FIR 滤波器之多相抽取器(Polyphase Decimator)
  4. UNIX再学习 -- 标准I/O
  5. OCJP认证考试复习课-张晨光-专题视频课程
  6. hdu 2026 首字母变大写
  7. 地理文本处理技术在高德的演进(下)
  8. BugkuCTF-MISC题where is flag2
  9. matlab表示DFT和DTFT,【 MATLAB 】离散傅里叶级数(DFS)与DFT、DTFT及 z变换之间的关系...
  10. 怎样把电脑恢复出厂设置_数据蛙:苹果恢复出厂设置,彻底释放手机内存
  11. 【MATLAB】三维曲线(plot3)
  12. c语言最新标准 2017,2017最新C语言介绍
  13. 数据结构(主席树):HZOI 2016 采花
  14. 利用ArcMap渲染DEM的两种方式
  15. 自制H5页面工具盘点 宣传/相册/邀请函
  16. SQL server日志文件过大处理方式
  17. Ruff 将助力广东金融高新区“区块链+”金融科技创新与应用落地
  18. MySQL:指定索引+事务+存储引擎的配置 开发必备 天呐!为什么会有索引这种东西
  19. iReport —— A4打印,只占纸张的一半,如何解决
  20. Mybatis-01-配置详解

热门文章

  1. nginx做文件服务器
  2. 将文件转成base64 字符串
  3. 如何真机PC桥接小凡模拟器进行设备管理
  4. Node.js mm131图片批量下载爬虫1.00 iconv协助转码
  5. AltiumDesigner画图不求人12 AD库转换为PADS库
  6. css+js显示点阵字体/LED七段数码管字体(模拟)
  7. MacOS Big Sur 11.2.1 (20D75) 纯净恢复版黑苹果镜像下载
  8. CSDN页面完美格式打印
  9. linux怎么更改rpcbind端口号,rpcbind_rpcbind服务是什么_rpcbind 端口
  10. 机器人学基础(一):空间描述与坐标变换