spi nor flash驱动

2019-04-14 21:13发布

嵌入式系统中flash的作用相当于PC上的硬盘来存放永久的数据,不像内存那样掉电数据就会消失。而我们使用的是nor flash,操作也比较简单,直接读取flash的地址就可以将数据通过memcpy到内存(当然是需要给相关寄存器配一些值),然后传给上层使用。 专用SPI控制器主要有CTRL,CMD,DATA,ADDR,STATUS寄存器。 相关命令 /* Command Value N Description Addr Dummy Data */ #define MX25L_WREN 0x06 /* 1 Write Enable 0 0 0 */ #define MX25L_WRDI 0x04 /* 1 Write Disable 0 0 0 */ #define MX25L_RDID 0x9f /* 1 Read Identification 0 0 1-3 */ #define MX25L_RDSR 0x05 /* 1 Read Status Register 0 0 >=1 */ #define MX25L_WRSR 0x01 /* 1 Write Status Register 0 0 1 */ #define MX25L_READ 0x03 /* 1 Read Data Bytes 3 0 >=1 */ #define MX25L_FAST_READ 0x0b /* 1 Higher speed read 3 1 >=1 */ #define MX25L_PP 0x02 /* 1 Page Program 3 0 1-256 */ #define MX25L_SE 0x20 /* 1 Sector Erase 3 0 0 */ #define MX25L_BE 0xd8 /* 1 Block Erase 3 0 0 */ #define MX25L_CE 0xc7 /* 1 chip Erase 0 0 0 */ #define MX25L_DP 0xb9 /* 2 Deep power down 0 0 0 */ #define MX25L_RES 0xab /* 2 Read Electronic Signature 0 3 >=1 */ #define MX25L_NOP 0x00 /* 2 No operation */

首先是对专门的SPI控制器进行初始化,也就是控制寄存器中设置分频系数和片选端,地址和命令寄存器中全部写0.并且提前写好扇区大小,页数等相关值,这些值放在一个全局结构体变量中,会被后面所用到。 #define MX25L_MX25L6433f_SECTOR_SHIFT 12 /* block size 1 << 12 = 4k */ #define MX25L_MX25L6433f_NSECTORS 2048 // 8M #define MX25L_MX25L6433f_PAGE_SHIFT 9 /* Page size 1 << 9 = */ #define MX25L_MX25L6433f_NPAGES 8192*2
然后注册一些回调函数,主要是读写相关,上层最终调用的还是这些函数。 priv->mtd.erase = drv_porting_flash_erase; priv->mtd.bread = drv_porting_flash_bread; priv->mtd.bwrite = drv_porting_flash_bwrite; priv->mtd.read = drv_porting_flash_read; priv->mtd.ioctl = drv_porting_flash_ioctl;
flash属于一个块设备,所以也就会有分区的概念,这些分区信息(分几个区,每个区的起始地址和大小),是在config文件里面就事先写好的。 创建分区的函数 FAR struct mtd_dev_s *mtd_partition(FAR struct mtd_dev_s *mtd, off_t firstblock,off_t nblocks) 该函数,主要是创建相关结构体, 并且把相关回调函数注册好 part->child.erase = part_erase; part->child.bread = part_bread; part->child.bwrite = part_bwrite; part->child.read = mtd->read ? part_read : NULL; part->child.ioctl = part_ioctl; #ifdef CONFIG_MTD_BYTE_WRITE part->child.write = mtd->write ? part_write : NULL; #endif part->parent = mtd; part->firstblock = erasestart * blkpererase; part->neraseblocks = eraseend - erasestart; part->blocksize = geo.blocksize; part->blkpererase = blkpererase; 这些函数最终还是会调用到刚刚之前注册号的最底层的SPI控制器相关的回调函数中去。 Note:Nor flash介质和内核之间还有一个FTL(flash translation layer)中间层,这么做的主要原因还是因为SPI每次写之前是需要擦除的,所以也就是flash的读写次数是有寿命的,中间的ftl转换层,作为一个缓冲可以增加flash的使用寿命。 所以需要调用ftl相关函数 int ftl_initialize(int minor, FAR struct mtd_dev_s *mtd) 该函数最主要的还是创建ftl相关结构体,并且注册块设备函数。 register_blockdriver(devname, &g_bops, 0, dev);相关注册函数 static const struct block_operations g_bops = { ftl_open, /* open */ ftl_close, /* close */ ftl_read, /* read */ #ifdef CONFIG_FS_WRITABLE ftl_write, /* write */ #else NULL, /* write */ #endif ftl_geometry, /* geometry */ ftl_ioctl /* ioctl */ };
当然,flash也可以做一个字符设备,也就可以调用字符设备注册的相关函数。 int bchdev_register(FAR const char *blkdev, FAR const char *chardev,bool readonly)
该函数主要完成两步: /* Setup the BCH lib functions */ ret = bchlib_setup(blkdev, readonly, &handle); /* Then setup the character device */ ret = register_driver(chardev, &bch_fops, 0666, handle);
相关回调函数和注册块设备的是相同的。 总的来说,也就是相当于注册了三次回调函数,然后最上层开始一层一层调用,一般都是类似****_i_node->read()的方式来调用的。