关闭

spi nor flash驱动

970人阅读 评论(0) 收藏 举报
分类:

嵌入式系统中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

然后注册一些回调函数,主要是读写相关,上层最终调用的还是这些函数。

 <span style="white-space:pre">	</span> 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()的方式来调用的。



0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:21316次
    • 积分:605
    • 等级:
    • 排名:千里之外
    • 原创:41篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条