SPI Flash驱动

Linux SPI W25Q128驱动

前一段时间在给一块板子移植驱动,在SPI驱动部分踩到好大一个坑,记录分享一下

板子的主控芯片用的是I.MX6ULL,板子上有一颗华邦W25Q128的FLASH芯片,通过SPI总线与主控连接,所以需要在设备树里添加SPI设备节点。起初我参照正点原子驱动开发指南的Linux SPI驱动部分给的例子写了一个设备节点,如下所示

&ecspi3 {
    fsl,spi-num-chipselects = <1>;
    cs-gpios = <&gpio4 12 GPIO_ACTIVE_LOW>;
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_ecspi3>;
    status = "okay";
  
  	flash: m25p80@0 {
        compatible = "winbond,w25q128";
        spi-max-frequency = <2000000>;
        reg = <0>;
    };
};

pinctrl_ecspi3: w25q128 {
    fsl,pins = <
        MX6UL_PAD_NAND_CE0_B__GPIO4_IO13   	0x10b1
        MX6UL_PAD_NAND_CE1_B__GPIO4_IO14    0x10b1
        MX6UL_PAD_NAND_CLE__GPIO4_IO15		0x10b1
        MX6UL_PAD_NAND_READY_B__GPIO4_IO12	0x10b0
    >;
};

编译设备树烧录之后,FLASH芯片并不能被注册,内核启动信息报错m25p80 spi32766.0: unrecognized JEDEC id bytes: ff, ff, ff,ID信息全为0xff,证明SPI读取过程出错了。用示波器看了一下波形发现一个很奇怪的现象,就是SPI每发送一个字节,片选信号就会拉高一次,如下图所示(图中蓝色信号为CS,黄色信号为MOSI)

在这里插入图片描述

但是之前在用STM32驱动华邦的W25Q128芯片时,片选信号只有在一帧的起始和结束的时候才会变化,我又查了查W25Q128的芯片资料,发现确实如此。下图为主机读取W25Q128芯片的流程图,可以看到片选信号只在起始时拉低,结束时拉高。在这里插入图片描述

经过苦苦的查找,终于让我发现了问题的原因。参照《IMX6ULL参考手册》如下图所示章节,原来I.MX6ULL芯片的ECSPI作为主机端发送数据时,是有单连发和多连发的区别的,寄存器ECSPIx_CONFIGREG[SS_CTL]就用来控制模式的选择。当SS_CTL[3:0] = 0时为单连发模式,片选信号在准备发送数据时拉低(选中),结束发送时拉高,在发送字节数据的间隔是不会变化的。当SS_CTL[3:0] = 1时为多连发模式,每发送完一个字节,片选信号就会拉高拉低一次。

单连发模式

多连发模式

虽然知道了问题出在SS_CTL标志位上,但是撸了两天SPI的驱动源码也没能将这个标志位置0,所以只能另寻出路,然后就想能不能用GPIO来模拟SPI接口。

答案是可以的,参照spi-gpio的绑定文档http://devicetree.org/schemas/spi/spi-gpio.yaml,写了新的设备树

spi4 {
    compatible = "spi-gpio";
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_spi4>;
    status = "okay";
    cs-gpios = <&gpio4 12 0>;
    gpio-sck = <&gpio4 13 0>;
    gpio-mosi = <&gpio4 14 0>;
    gpio-miso = <&gpio4 15 0>;
    num-chipselects = <1>;
    #address-cells = <1>;
    #size-cells = <0>;

    flash: m25p80@0 {
        compatible = "winbond,w25q128";
        #address-cells = <1>;
        #size-cells = <1>;
        spi-max-frequency = <2000000>;
        reg = <0>;
    };
};

pinctrl_spi4: spi4grp {
    fsl,pins = <
        MX6UL_PAD_NAND_CE0_B__GPIO4_IO13   	0x70a1
        MX6UL_PAD_NAND_CE1_B__GPIO4_IO14    0x70a1
        MX6UL_PAD_NAND_CLE__GPIO4_IO15		0x10b0
        MX6UL_PAD_NAND_READY_B__GPIO4_IO12	0x10b0
    >;
};

同时,也要在menuconfig中将GPIO-based bitbanging SPI Master这一项勾选上,编译出spi-gpio的驱动

location:
-> Device Drivers                                                   │  
   -> SPI support (SPI [=y])
   	  ->GPIO-based bitbanging SPI Master

再次查看开机启动信息,发现flash芯片的ID信息已经被成功识别

root@imx6ull-p557-01:/# dmesg | grep spi
[    1.295136] m25p80 spi32766.0: w25q128 (16384 Kbytes)

FLASH芯片已经被成功注册了,那怎么在应用层读写FLASH芯片呢?

这就涉及到MTD子系统,因为我们用的驱动是m25p80.c这个文件(设备节点中的"winbond,w25q128"即为m25p80.c中的属性),这个驱动会将FLASH芯片被注册成MTD设备,MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,可以说MTD是针对Flash设备设计的标准化硬件驱动框架。

在这里插入图片描述

FLASH芯片已经被注册成MTD设备,文件系统中 /dev 目录下mtd相关的3个文件即为flash注册的mtd设备,mtd0是对应的字符设备,mtdblock0是对应的块设备,mtd0ro是只读字符设备

root@localhost:/# ls /dev/mtd*
/dev/mtd0  /dev/mtd0ro	/dev/mtdblock0

因为我们把 SPI Flash 注册成 MTD 设备了,因此,我们可以通过 MTD 子系统和文件系统对其进行操作

首先对 Flash 进行格式化,然后挂载,接着就可以通过文件系统操作:

$ mkfs.vfat /dev/mtdblock0
$ mount -t vfat /dev/mtdblock0 /media/w25q128
$ cd /media/w25q128
$ echo “Hello W25Q128” > file.txt
$ syn

然后断电重启,用mount命令重新挂在后查看file.txt文件是否还在,并查看文件内容

参考博客:

Linux下SPI Flash-W25Q64驱动调试_heat.huang的博客-CSDN博客_zynq w25q 设备树

SPI 两帧数据之间的间隔-云社区-华为云 (huaweicloud.com)

【正点原子】I.MX6U嵌入式Linux驱动开发指南

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值