在应用程序中实现对NandFlash的操作
来源:互联网 发布:mysql 数据库模式 编辑:程序博客网 时间:2024/04/29 13:43
在应用程序中实现对NandFlash的操作
以TC58NVG2S3ETA00
下面是它的一些物理参数:
图一
图二
图三
图四
图五
图6-0
图6-1
说明一下,在图6-1中中间的那个布局表可以看做是实际的NandFlash一页数据的布局,其中Data区域用于存放有效的数据,也就是我们可以通过类似read、write、pread、pwrite可以访问的区域,那每页中的64字节的OOB区域是无法通过前面的几个函数访问的,他们会自动跳过OOB区域,访问OOB区域需要借助特殊的命令。
简单说明一下:Data A(512B)对应的ECC校验码存放在ECC for Data A(4 byte)中,OOB A (8byte) 对应的ECC校验码存放在紧接着的下一个ECC for Data A(4 byte)中,虽然用4字节存放ECC,但是对于本例,ECC只占3个字节。在实际使用中如果解决方案中用不到OOB A/B/C/D,可以不用管他们对应的ECC,只需要关心Data区域对应的ECC。如果使能了硬件ECC,硬件会自动把计算生成的ECC写到OOB中。可以参考http://www.cnblogs.com/pengdonglin137/p/3467960.html
读NandFlash需要按页读,即一次读一页;写NandFlash需要按页写,即每次写一页;擦除NandFlash需要按块擦,即每次要擦除一块。
对与NandFlash等块设备的访问操作,mtd-utils工具集中提供了非常好的支持(可以到http://www.linux-mtd.infradead.org/进行了解),要使用mtd-utils工具集首先需要搞到mtd-utils的源码,并且使用目标设备上的交叉工具编译链进行编译,具体方法可以参考:http://www.cnblogs.com/pengdonglin137/p/3415550.html,其中介绍了如何生成可以再目标板上运行的mtd-utils工具。关于mtd-utils工具的使用可以参考:http://www.cnblogs.com/pengdonglin137/p/3415663.html
我们可以参考mtd-utils中工具的实现,从而完成在自己的应用程序中实现对NandFlash的操作。常用的命令如下:
#define MEMGETINFO
#define MEMERASE
#define MEMWRITEOOB
#define MEMREADOOB
#define MEMLOCK
#define MEMUNLOCK
#define MEMGETREGIONCOUNT
#define MEMGETREGIONINFO
#define MEMSETOOBSEL
#define MEMGETOOBSEL
#define MEMGETBADBLOCK
#define MEMSETBADBLOCK
#define OTPSELECT
#define OTPGETREGIONCOUNT
#define OTPGETREGIONINFO
#define OTPLOCK
#define ECCGETLAYOUT
#define ECCGETSTATS
#define MTDFILEMODE
#define MEMERASE64
#define MEMWRITEOOB64
#define MEMREADOOB64
#define MEMISLOCKED
打开设备
这里需要注意的是,打开的设备结点是/dev/mtd?,而不是/dec/mtdblock?,原因可以参考:
http://www.cnblogs.com/pengdonglin137/p/3316523.html,其中介绍了mtd与mtdblock的区别。
获取设备信息
__u32 erasesize; __u32 writesize; __u32 oobsize;// Amount of OOB data per block (e.g. 16)
__u32 ecctype; __u32 eccsize;};struct mtd_info_user mtd;
擦除NandFlash
写NandFlash
这里分为写数据区和写OOB区
写数据区,对于本例一次要写一页,也就是2KB,写OOB区,对于本例可以操作的只有32字节,剩下的32字节用于存放ECC。
struct mtd_oob_buf { __u32 start; __u32 length; unsigned char *ptr;};int nandwrite(DeviceInfo* meminfo){ int imglen = 0, pagelen; bool baderaseblock = false; int blockstart = -1; loff_t offs; int ret, readlen; unsigned char tmp_oob[32];//OOB A/B/C/D,一共32字节 struct mtd_oob_buf OOB_INFO ; sourceaddr = meminfo->head->file_offset; //要读的部分在镜像文件中的偏移量 sourcelen = meminfo->head->size; //要读的部分的大小 int num_to_read = 0; OOB_INFO.start = 0; OOB_INFO.length = meminfo->head->oob_usr_length; //32字节,用户可以访问的OOB的大小,也就是OOB A/B/C/D OOB_INFO.ptr = tmp_oob; pagelen = meminfo->writesize; // 2KB imglen = sourcelen; // 镜像文件的长度 mtdoffset = meminfo->head->flash_offset; //要写的部分在/dev/mtdx中的偏移量,以字节为单位 if (0 == sourceaddr) { DEBUG("Have no sourceaddr return ****************************\n"); return 1; } // Check, if length fits into device if ( ((imglen / pagelen) * meminfo->writesize) > (meminfo->size - mtdoffset)) { fprintf (stderr, "Image %d bytes, NAND page %d bytes, OOB area %u bytes, device size %u bytes\n", imglen, pagelen, meminfo->writesize, meminfo->size); perror ("Input file does not fit into device"); goto closeall; } while ((imglen>0) && (mtdoffset < meminfo->size)) { //blockstart 将来存放的是正在写的那块的起始地址,并且是块对齐的 //mtdoffset 表示的是在某个mtd设备中的整体偏移量,可以按块递增,也可以按页递增 //设置blockstart的目的是: // 假如检测到一个好的块,开始进行写操作,但是在写的过程中发生了写错误,可以认为这块已经 //是坏块了,需要重新再找一个好的块,然后重新写入之前的数据,因此需要知道刚才那个坏块的起始地址 // mtdoffset & (~meminfo->erasesize + 1) 这种获取块起始地址的算法值得借鉴 while (blockstart != (mtdoffset & (~meminfo->erasesize + 1))) { blockstart = mtdoffset & (~meminfo->erasesize + 1); offs = blockstart; baderaseblock = false; if (!quiet) { fprintf (stderr, "\rWriting data to block %d at offset 0x%x", \ blockstart / meminfo->erasesize, blockstart); } // meminfo->fd 是某个/dev/mtdx的文件描述符 do { if ((ret = ioctl(meminfo->fd, MEMGETBADBLOCK, &offs)) < 0) { perror("ioctl(MEMGETBADBLOCK)"); goto closeall; } if (ret == 1) { baderaseblock = true; if (!quiet) { fprintf (stderr, "Bad block at %x block(s) " "from %x will be skipped\n", (int) offs, blockstart); } } if (baderaseblock) { mtdoffset = blockstart + meminfo->erasesize; } offs += meminfo->erasesize; } while ( offs < blockstart + meminfo->erasesize ); } readlen = meminfo->writesize; // 2KB if (0 != sourceaddr) { if((meminfo->head->imageType == YAFFS) || (meminfo->head->imageType == OOB_RAW)) { writeoob = true; } else { writeoob = false; } memset(writebuf, 0xff, sizeof(writebuf)); if(imglen <<span style="font-family: 'Courier New' !important; font-size: 12px !important;"> readlen) { num_to_read = imglen; } else { num_to_read = readlen; } // 从镜像文件中偏移量为sourceaddr处读取num_to_read个字节到writebuf中 // ALLIMAGEFD 为镜像文件的文件描述符 if(pread(ALLIMAGEFD, writebuf, num_to_read, sourceaddr) < 0) { perror("fail to pread\n"); return -1; } sourceaddr += num_to_read; if(writeoob) { memset(tmp_oob, 0xff , OOB_FREE_MAX); // 从镜像文件中偏移量为sourceaddr+meminfo->head->oob_usr_offset处读取meminfo->head->oob_usr_length个字节到tmp_oob中,其中meminfo->head->oob_usr_offset是OOB A相对与OOB区域的偏移量,meminfo->head->oob_usr_length 在本例中为32字节 if(pread(ALLIMAGEFD, tmp_oob, meminfo->head->oob_usr_length, sourceaddr+meminfo->head->oob_usr_offset) < 0) { perror("fail to pread\n"); return -1; } sourceaddr += meminfo->oobsize; } } if(-1 == pwrite(meminfo->fd, writebuf, meminfo->writesize, mtdoffset)) //写NandFlash { int rewind_blocks; off_t rewind_bytes; erase_info_t erase; perror("ioctl(MEMEWRITEPAGE)"); rewind_blocks = (mtdoffset - blockstart) / meminfo->writesize; rewind_bytes = (rewind_blocks * meminfo->writesize) + readlen; if (writeoob) { rewind_bytes += (rewind_blocks + 1) * meminfo->oobsize; } sourceaddr -= rewind_bytes; erase.start = blockstart; erase.length = meminfo->erasesize; fprintf(stderr, "Erasing failed write from lx-lx\n", (long)erase.start, (long)erase.start+erase.length-1); if (ioctl(meminfo->fd, MEMERASE, &erase) != 0) { perror("MEMERASE"); goto closeall; } if (markbad) { loff_t bad_addr = mtdoffset & (~meminfo->erasesize + 1); fprintf(stderr, "Marking block at lx bad\n", (long)bad_addr); if (ioctl(meminfo->fd, MEMSETBADBLOCK, &bad_addr)) { perror("MEMSETBADBLOCK"); } } mtdoffset = blockstart + meminfo->erasesize; imglen += rewind_blocks * meminfo->writesize; if(writeoob) { imglen += rewind_blocks * meminfo->oobsize; } continue; } imglen -= readlen; if(writeoob) { imglen -= meminfo->oobsize; OOB_INFO.start = mtdoffset; if (ioctl(meminfo->fd, MEMWRITEOOB, &OOB_INFO)) { perror("fail to ioctl"); } } mtdoffset += meminfo->writesize; }closeall: if ((imglen > 0)) { perror ("Data was only partially written due to error\n"); exit (EXIT_FAILURE); } return EXIT_SUCCESS;}
对于写NandFlash,有的设备支持一次性把data和oob一块写进去。代码如下:
读OOB
读OOB跟写OOB类似,只不过使用的命令是MEMREADOOB。
#include #include #include #include #include #include #include #include <<span style="color: rgb(0, 0, 255); font-family: 'Courier New' !important; font-size: 12px !important;">string.h>#include #define N 32#define OFS (0)#define block_size (128*1024)#define page_size (2*1024)int main(int argc, const char *argv[]){ int fd; int i, j; unsigned char oob_data[32] = { 0x53, 0x50, 0x4c, 0x20, 0, 0xff, 0, 0xff, 0x53, 0x50, 0x4c, 0x20, 0, 0xff, 0, 0xff, 0x53, 0x50, 0x4c, 0x20, 0, 0xff, 0, 0xff, 0x53, 0x50, 0x4c, 0x20, 0, 0xff, 0, 0xff }; unsigned char oobbuf[N]; struct mtd_oob_buf oob = {0, N, oobbuf}; struct mtd_oob_buf my_oob = {0, N, oob_data}; fd = open("/dev/mtd0", O_RDWR); if(fd < 0) { perror("fail to open\n"); exit(-1); } if(ioctl(fd, MEMWRITEOOB, &my_oob)) { perror("fail to ioctl"); exit(-1); } memset(oobbuf, 0, sizeof(oobbuf)); oob.start = OFS; if (ioctl(fd, MEMREADOOB, &oob)) { perror("fail to ioctl"); exit(-1); } for(i=0; i) { if(i%8 == 0) { printf("\n"); } printf("%#x ", oobbuf[i]); } printf("\n\n"); close (fd); return 0;}
include sys/ioctl.hinclude stdio.hinclude mtd/mtd-user.hinclude sys/types.hinclude sys/stat.hinclude fcntl.hinclude unistd.hinclude string.hinclude stdlib.h#define N 32#define OFS(0) #define block_size (128*1024)#define page_size(2*1024) int main(int argc, const char *argv[]){int fd; int i, j; unsigned char oob_data[1024*2] = { 0x53, 0x50, 0x4c, 0x20, 0, 0xff, 0, 0xff, 0x53, 0x50, 0x4c, 0x20, 0, 0xff, 0, 0xff, 0x53, 0x50, 0x4c, 0x20, 0, 0xff, 0, 0xff, 0x53, 0x50, 0x4c, 0x20, 0, 0xff, 0, 0xff }; unsigned char oobbuf[N]; struct mtd_oob_buf oob = {0, N, oobbuf}; struct mtd_oob_buf my_oob = {0, N, oob_data}; fd = open("/dev/mtd3", O_RDWR); if(fd < 0) { perror("fail to open\n"); exit(-1); } pwrite(fd, oob_data, 1024*2, 1024*4);//写时必须页对齐,一次写一页(2k)的整数位 memset(oob_data,0,32); pread(fd, oob_data, 32, 1024*4); for(i=0;i<32;i++) { if(i%8==0) printf("\n"); printf("%2x ",oob_data[i]); } printf("\n"); return 0; }
- 在应用程序中实现对NandFlash的操作
- 在应用程序中实现对NandFlash的操作
- 在应用程序中实现对NandFlash的操作
- s3c2440对nandflash的操作
- s3c2440对nandflash的操作
- s3c2440对nandflash的操作
- s3c2440对nandflash的操作
- s3c2440对nandflash的操作
- s3c2440对nandflash的操作
- S3C2440对nandflash的操作
- s3c2440对nandflash的操作
- s3c2440对nandflash的操作
- s3c2440对nandflash的操作
- S3C2416 对nandflash的操作
- 在应用程序中对xml文件的各种操作
- 在 Silverlight 应用程序中实现对 FLV 视频格式的支持
- s3c2410/s3c2440对nandflash的读写操作
- s3c2410/s3c2440对nandflash的读写操作
- bind方法解决this作用域问题
- 递归实现 全排列
- malloc函数解析
- C++11笔记(3)——引用&指针辨析
- 6种有效的iOS团队开发技巧
- 在应用程序中实现对NandFlash的操作
- 说话人识别中的VAD
- git异常:\bin\sh.exe:*** Couldn't reserve space for cygwin's heap,Win32 error 0
- opencv之多边形抠图
- R语言简单(一元)线性回归分析
- 推理集 —— 举一反三
- Mac 下修改mysql密码
- [模板]求解欧拉路径,欧拉回路
- 自定义控件三部曲之动画篇(四)——ValueAnimator基本使用