Linux GPIO模拟SPI接口介绍

一. 前言

        上次通过GPIO模拟IIC的驱动一块0.98英寸的OLED后,想着下次用下GPIO模拟SPI接口试下。本文会介绍怎么用GPIO模拟SPI接口的方式为设备添加一块spi nor flash,flash模块如下图所示:

        上图是一个spi nor模块,上面焊接了W25Q32FV(一块4M的spi nor flash),模块引出了flash的引脚,板子的GPIO接引脚来驱动flash。

        板子采用的是一块MT7620A的开发板,内核的版本是Linux 3.18.45。

二. 步骤

1. CPU与模块的连线
模块            MT7620A板子
VCC               3.3V
GND               GND
CS                MDIO(GPIO#22)
DO                SCL(GPIO#2)
CLK               SDA(GPIO#1)
DI                MDC(GPIO#23)
2. 内核修改
Device Drivers  --->
    [*] SPI support  --->
        <*>   GPIO-based bitbanging SPI Master

        内核SPI驱动选上"GPIO-based bitbanging SPI Master",表示内核支持GPIO模拟SPI接口。

3. 设备树修改

        由于SPI用到了MDIO和I2C接口的GPIO,所以需要在pinctrl中将他们复用为普通GPIO模式,如下所示:

pinctrl {
		state_default: pinctrl0 {
			default {
				ralink,group = "pa", "spi refclk", "wdt", "uartf", "nd_sd", "mdio", "i2c";
				ralink,function = "gpio";
			};
		};
	};

        接下来添加spi模块相关的设备树,如下所示:

palmbus@10000000 {
		gpio0: gpio@600 {
			status = "okay";
		};
	
		gpio2: gpio@660 {
			status = "okay";
		};

		gpio3: gpio@688 {
			status = "okay";
		};

		spi@b00 {
			status = "okay";

			m25p80@0 {
				#address-cells = <1>;
				#size-cells = <1>;
				compatible = "mx25l12805d";
				reg = <0 0>;
				linux,modalias = "m25p80", "w25q128";
				spi-max-frequency = <10000000>;

                partition@0 {
					label = "u-boot";
					reg = <0x0 0x30000>;
					read-only;
				};

				partition@30000 {
					label = "u-boot-env";
					reg = <0x30000 0x10000>;
					read-only;
				};

				factory: partition@40000 {
					label = "factory";
					reg = <0x40000 0x10000>;
					read-only;
				};

				partition@50000 {
					label = "firmware";
					reg = <0x50000 0xdb0000>;
				};
				
				partition@ff0000 {
					label = "config";
					reg = <0xe00000 0x100000>;
				};
		};
        spi {
            compatible = "spi-gpio";
            #address-cells = <0x1>;
            ranges;

            gpio-sck = <&gpio0 1 0>;
            gpio-miso = <&gpio0 2 0>;
            gpio-mosi = <&gpio0 23 0>;
            cs-gpios = <&gpio0 22 0>;
            num-chipselects = <1>;

            /* clients */
            w25q32@1 {
                #address-cells = <1>;
                #size-cells = <1>;
                compatible = "w25q32";
                reg = <0 0>;
                spi-max-frequency = <10000000>;
                
                partition@0 {
                    label = "other_flash";
                    reg = <0x0 0x0400000>;
                };
            };
        };
	};

        从以上设备树可以看出,我们的设备原来也是使用了一块flash来存放uboot和内核等其他内容的。      

        compatible = "spi-gpio"会与spi-gpio驱动match,导致spi-gpio驱动probe函数运行,gpio-sck,gpio-miso,gpio-mosi和cs-gpios表示分别用什么GPIO模块spi对应的信号线,num-chipselects=<1>表示片选的个数为1。

        w25q32@1表示spi的从设备了,这里就是我们的spi nor flash模块。"w25q32"表示我们使用的flash型号,这里指定的型号不对也没关系,内核会读flash的ID号,自动识别出flash的型号。

        以上就是全部的修改了。

三. 效果

1. 修改之前的内核打印
[    1.280000] m25p80 spi32766.0: found w25q256, expected mx25l12805d
[    1.290000] m25p80 spi32766.0: w25q256 (32768 Kbytes)
[    1.300000] 5 ofpart partitions found on MTD device spi32766.0
[    1.320000] Creating 5 MTD partitions on "spi32766.0":
[    1.330000] 0x000000000000-0x000000030000 : "u-boot"
[    1.340000] 0x000000030000-0x000000040000 : "u-boot-env"
[    1.350000] 0x000000040000-0x000000050000 : "factory"
[    1.360000] 0x000000050000-0x000000e00000 : "firmware"
[    1.370000] 0x000000e00000-0x000000f00000 : "config"
2. 修改之后的内核打印
[    1.290000] m25p80 spi32766.0: w25q32 (4096 Kbytes)
[    1.300000] 1 ofpart partitions found on MTD device spi32766.0
[    1.310000] Creating 1 MTD partitions on "spi32766.0":
[    1.320000] 0x000000000000-0x000000400000 : "other_flash"
[    1.350000] m25p80 spi32765.0: found w25q256, expected mx25l12805d
[    1.360000] m25p80 spi32765.0: w25q256 (32768 Kbytes)
[    1.370000] 5 ofpart partitions found on MTD device spi32765.0
[    1.380000] Creating 5 MTD partitions on "spi32765.0":
[    1.390000] 0x000000000000-0x000000030000 : "u-boot"
[    1.400000] 0x000000030000-0x000000040000 : "u-boot-env"
[    1.410000] 0x000000040000-0x000000050000 : "factory"
[    1.420000] 0x000000050000-0x000000e00000 : "firmware"
[    1.430000] 0x000000e00000-0x000000f00000 : "config"

        从以上的内核打印可以看出,内核识别到了一块w25q32 (4096 Kbytes)的flash,并且还创建了一块分区"other_flash",分区的大小是4M,这是我在设备上定义的。

3. 修改前的分区表
/ # cat /proc/mtd 
dev:    size   erasesize  name
mtd0: 00030000 00010000 "u-boot"
mtd1: 00010000 00010000 "u-boot-env"
mtd2: 00010000 00010000 "factory"
mtd3: 00db0000 00010000 "firmware"
mtd4: 00100000 00010000 "config"
4. 修改后的分区表
/ # cat /proc/mtd 
dev:    size   erasesize  name
mtd0: 00400000 00010000 "other_flash"
mtd1: 00030000 00010000 "u-boot"
mtd2: 00010000 00010000 "u-boot-env"
mtd3: 00010000 00010000 "factory"
mtd4: 00db0000 00010000 "firmware"
mtd5: 00100000 00010000 "config"
5. mount "other_flash"分区
/ # mount -t jffs2 /dev/mtdblock0 /media
[  560.280000] jffs2: notice: (2013) jffs2_build_xattr_subsystem: complete building xattr subsystem, 0 of xdatum (0 unchecked, 0 orphan) and 0 of xref (0 dead, 0 orphan) found.
/ # 
/ # df -h
Filesystem                Size      Used Available Use% Mounted on
tmpfs                    61.2M         0     61.2M   0% /tmp
/dev/mtdblock0            4.0M    260.0K      3.7M   6% /media

        从以上打印可以看出,新的4M的spi nor flash就成功mount到了/media目录。

四. 总结

        本文介绍了Linux通过GPIO模块SPI接口的方式,并且通过实例介绍了怎么驱动一块spi nor flash。这里有一处缺点:就是"other_flash"分区总是mtd0,这样会导致分区号整体向后偏移1,导致原来的代码需要修改这里还没有找到有效的办法解决。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GPIO 模拟 SPI 驱动通常用于在嵌入式系统中,通过软件模拟 SPI 总线来控制外设,以实现数据的传输和通信。下面是一个简单的 GPIO 模拟 SPI 驱动的代码示例: ```c #include <linux/module.h> #include <linux/kernel.h> #include <linux/gpio.h> #include <linux/spi/spi.h> static struct spi_board_info spi_board_info = { .modalias = "spi-gpio", .max_speed_hz = 1000000, .bus_num = 0, .chip_select = 0, .mode = SPI_MODE_0, }; static struct spi_device *spi_device; static int spi_gpio_probe(struct platform_device *pdev) { int ret; ret = gpio_request(SPI_GPIO_SCLK, "spi-gpio-sclk"); if (ret < 0) { printk(KERN_ERR "Failed to request SCLK GPIO\n"); goto err_sclk; } ret = gpio_request(SPI_GPIO_MOSI, "spi-gpio-mosi"); if (ret < 0) { printk(KERN_ERR "Failed to request MOSI GPIO\n"); goto err_mosi; } ret = gpio_request(SPI_GPIO_MISO, "spi-gpio-miso"); if (ret < 0) { printk(KERN_ERR "Failed to request MISO GPIO\n"); goto err_miso; } spi_device = spi_new_device(&pdev->dev, &spi_board_info); if (!spi_device) { printk(KERN_ERR "Failed to create SPI device\n"); goto err_spi; } ret = spi_setup(spi_device); if (ret < 0) { printk(KERN_ERR "Failed to setup SPI device\n"); goto err_setup; } return 0; err_setup: spi_unregister_device(spi_device); err_spi: gpio_free(SPI_GPIO_MISO); err_miso: gpio_free(SPI_GPIO_MOSI); err_mosi: gpio_free(SPI_GPIO_SCLK); err_sclk: return ret; } static int spi_gpio_remove(struct platform_device *pdev) { spi_unregister_device(spi_device); gpio_free(SPI_GPIO_MISO); gpio_free(SPI_GPIO_MOSI); gpio_free(SPI_GPIO_SCLK); return 0; } static struct platform_driver spi_gpio_driver = { .probe = spi_gpio_probe, .remove = spi_gpio_remove, .driver = { .name = "spi-gpio", }, }; static int __init spi_gpio_init(void) { return platform_driver_register(&spi_gpio_driver); } static void __exit spi_gpio_exit(void) { platform_driver_unregister(&spi_gpio_driver); } module_init(spi_gpio_init); module_exit(spi_gpio_exit); ``` 在这个例子中,我们通过 GPIO 口来模拟 SPI 总线的 SCLK、MOSI、MISO 三个信号线,然后创建一个 SPI 设备,并使用 spi_new_device() 函数来注册 SPI 设备。最后,我们在 spi_setup() 函数中设置 SPI 设备的一些参数,例如传输速率和传输模式等。需要注意的是,这只是一个简单的示例,具体实现可能会因为硬件接口和驱动程序的不同而有所不同。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值