新闻  |   论坛  |   博客  |   在线研讨会
嵌入式linux之iMX6ULL驱动开发 | 通用spi驱动之spidev使用总结
电子禅石 | 2024-02-03 19:17:19    阅读:81759   发布文章

因为兴趣,业余时间把玩下手边的imax6ul开发板。Linux内核集成了spidev驱动,

提供了SPI设备的用户空间API。支持用于半双工通信的read和write访问接口以及用于

全双工通信和I/O配置的ioctl接口。使用时,只需将SPI从设备的compatible属性值添加

到spidev区动的spidev dt ids[]数组中,即可将该SPI从设备创建为spidev设备。

如果不想编写单独的spi设备驱动,那么使用linux内核提供的通用spidev设备驱动就够了,

它提供统一的字符设备操作,那么只需要在应用层读写和控制即可。

spidev驱动

spidev是一个Linux内核驱动,用于与SPI(串行外设接口)设备进行通信。SPI是一种全双工、

同步的串行通信协议,常用于连接微控制器和外部设备。spidev驱动允许用户空间程序通过

Linux的设备文件接口与SPI设备进行通信。用户可以通过打开和读写设备文件来发送和

接收SPI数据。spidev驱动提供了一组控制IO口和SPI参数的ioctl命令。


同时Linux内核也集成了SPI测试工具spidev test,用于在用户态对spidev动功能进行测试

和验证。


spidev设备驱动源码位置在:linux-imx-4.1.15driversspispidev.c

驱动框架框图:

1706957541784792.png

   

除了使用spidev驱动外,当然也可以自己编写SPI驱动。


使用现有的spidev驱动可以简化开发过程,因为它提供了一组用户空间接口,

可以直接在应用程序中使用标准的文件操作函数(如open、read、write和ioctl)

来操作SPI设备。这种方式适用于大多数应用场景,特别是对于简单的SPI设备操作,

可以快速实现功能。


如果使用自己编写的SPI驱动也可以,也不算麻烦,需要在内核中实现SPI子系统,

包括SPI控制器驱动和SPI设备驱动,最后根据需要实现个如字符型设备驱动操作接口,

供上层应用使用即可。

应用层使用步骤

用户应用层使用spidev驱动的步骤如下:


1. 打开SPI设备文件:用户可以通过打开/dev/spidevX.Y文件来访问SPI设备,

其中X是SPI控制器的编号,Y是SPI设备的编号。


2. 配置SPI参数:用户可以使用ioctl命令SPI_IOC_WR_MODE、

SPI_IOC_WR_BITS_PER_WORD和SPI_IOC_WR_MAX_SPEED_HZ来设置SPI模式

、数据位数和时钟速度等参数。


3. 发送和接收数据:用户可以使用read和write系统调用来发送和接收SPI数据。

写入的数据将被传输到SPI设备,而从设备读取的数据将被存储在用户提供的缓冲区中。


4. 关闭SPI设备文件:当不再需要与SPI设备通信时,用户应该关闭SPI设备文件。


总结起来,spidev驱动提供了一种简单而灵活的方式来与SPI设备进行通信,

使得用户可以轻松地在Linux系统上开发和控制SPI设备。

内核配置

使能spidev用户态驱动

1706957938689736.png在生成的config文件中可以看到以下配置生效了。我的是在imx6ul开发板的imx_v7_defconfig的linux内核配置文件中。

CONFIG_SPI=y
CONFIG_SPI_GPIO=y
CONFIG_SPI_IMX=y
CONFIG_SPI_SPIDEV=y
编写设备树

&ecspi3 {
        fsl,spi-num-chipselects = <2>;/*cs管脚数配置*/
        cs-gpios = <0>,<&gpio1 20 GPIO_ACTIVE_LOW>;/*cs管脚配置*/
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_ecspi3>;
        status = "okay";/* status属性值为"okay" 表示该节点使能*/
 
	spidev: icm20608@0 {
	compatible = "alientek,icm20608";
        spi-max-frequency = <8000000>;
        reg = <0>;/*spi设备是没有设备地址的, 这里是指使用spi控制器的cs-gpios里的第几个片选io */
    };
 
	oled: oledsh1106@1 {
	compatible = "yang,oledsh1106";/*重要,会匹配spidev.c中指定的compatible*/
	spi-cpol;/*配置spi信号模式*/
	spi-cpha;
	spi-max-frequency = < 8000000 >;/* 指定spi设备的最大工作时钟 */
    reg = <1>;
    };
};

以上需要注意的是:如果该spi接口下挂载有多个从设备,

需要设置fsl,spi-num-chipselects = <2>;默认该值为1。还有需要注意的地方是,

cs-gpios 片选信号需要配置对应的个数。以上的为配置了两路片选GPIO管脚,

第一个默认的,第二个是指定的。如果仅有一个从设备,可以配置cs-gpio就行了。

注意cs-gpio和cs-gpios的区别,带s的标识可以有多个。

如果忽略cs管脚数配置,则会出现以下错误:

需要设置fsl,spi-num-chipselects = <2>; 


注意上面的compatible 属性,在新版linux内核,可以写任意的字符串,

最好不再写”spidev”,老版的是要写成”spidev”。

给出的理由是: spidev should never be referenced in DT without a specific

 compatible string, it is a Linux implementation thing rather than a description 

of the hardware。


此外还有一些额外配置,以下为自定义属性,用于指定工作时序方式及其它功能设置等。

如CPOL需要设1, 则只需在spi设备节点里加上"spi-cpol"属性即可; CPOL设0,

则不写"spi-cpol"属性即可 。

  • buswidth = <8>; /* 传输以8位为单位 */
    mode = <0>; /* 使用第几种工作时序(CPOL, CPHA) */
    /*但在现用的内核源码里发现, spi设备的工作时序并不是用mode属性值来指定的*/
    /* 如CPOL需要设1, 则只需在spi设备节点里加上"spi-cpol"属性即可;
  • CPOL设0,则不写"spi-cpol"属性即可 */
    /* CPHA设1时,则在设备节点里加上"spi-cpha"属性即可 */

pinctrl的配置

pinctrl_ecspi3: ecspi3grp {
                fsl,pins = <
                        MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO        0x100b1  /* MISO*/
                        MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI        0x100b1  /* MOSI*/
                        MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK      0x100b1  /* CLK*/
                        MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20       0x100b0  /* CS*/
                    >;
            };
编译内核和设备树
#加载环境
source /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi
#编译内核
make zImage -j16
#编译指定的设备树
make imx6ull-14x14-nand-4.3-480x272-c.dtb
修改spidev驱动

默认的spidev.c中,是没有匹配你添加的设备的,因此需要修改spidev.c代码,增加compatible匹配。

/* The main reason to have this class is to make mdev/udev create the
 * /dev/spidevB.C character device nodes exposing our userspace API.
 * It also simplifies memory management.
 */
 
static struct class *spidev_class;
 
//#ifdef CONFIG_OF
static const struct of_device_id spidev_dt_ids[] = {
	{ .compatible = "rohm,dh2228fv" },
  { .compatible = "yang,oledsh1106" },
	{},
};
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
//#endif

内核编译成功后,更新内核和设备树文件。启动设备后,在/sys/class/spidev下可以确认spidev枚举出了多少个spi设备。


设备树查看

查看设备树是否有新添加的节点:


更新设备树到板子上后查看下是否有生成spi设备节点:

                        


开源测试工具

spidev驱动有现成的测试工具。其中一个常用的测试工具是spi_test,

它是spidev驱动自带的测试工具,可以用于测试和调试SPI设备。

spi_test可以通过命令行参数设置SPI设备的各种参数,如设备文件、

传输速率、字节顺序等。使用spi_test可以发送和接收SPI数据,

以验证spidev驱动的功能和性能。


在源码linux-imx-4.1.15-2.1.0-v2.7\Documentation\spi路径下,

有两个测试工具的源码文件,spidev_fdx.c和spidev_test.c文件。

可以直接交叉编译为可执行文件使用。

这些工具都基于spidev通用设备驱动以及对应的ioctl命令实现,

可以方便的用来对spi的通用型驱动来进行测试。


编译方法

#加载环境
source /opt/fsl-imx-x11/4.1.15-2.1.0/
environment-setup-cortexa7hf-neon-poky-linux-gnueabi
#编译
$(CC) spidev_fdx.c -o spidev_fdx
$(CC) spidev_test.c -o spidev_test
回环测试

首先,将spi接口的MISO和MOSI接口短接。

其次,编译测试代码:

Documentation/spi/spidev_test.c


如,在/dev/spidev2.0上发送"string_to_send"字符串,显示发送和接收的数据:

 ./spidev_test -D /dev/spidev2.0 -v -p string_to_send

如果要发送32位/16位的数据,则需要先生成二进制文件,如生成32字节的随机数据:

dd if=/dev/urandom of=test_data bs=16 count=2

用hexdump来查看这个二进制文件:

hexdump -v test_data -C


00000000 74 6a 59 3e 1e 81 73 fb 5a 3f 94 c7 d8 20 ca e9 |tjY>..s.Z?... ..|


00000010 24 2e a5 68 75 ab f7 12 af e6 c1 3d e2 d8 9a ba |$..hu......=....|


00000020


发送:

./spidev_test -D /dev/spidev2.0 -b 32 -v -i test_data————————————————

最后,输出结果与输入相同即为正确。

 

结语

在这样一个速食的时代,坚持做自己,慢下来,潜心琢磨,心怀敬畏,领悟知识,

才能向下扎到根,向上捅破天,背着世界往前行!

原文链接:https://blog.csdn.net/yyz_1987/article/details/131918983

                        



                        



         

                    

                        


*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
属于自己的技术积累分享,成为嵌入式系统研发高手。
推荐文章
最近访客