spi 自带驱动spidev.c 的测试使用

本文介绍了如何在Linux中使用spidev用户接口控制SPI设备,重点讲解了probe函数的触发条件及在DTS文件中的配置示例。此外,还涵盖了如何通过设备文件节点进行应用层操作,包括创建文件节点和调用spidev的读写函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. Linux 提供了一个功能有限的 SPI 用户接口,如果不需要用到 IRQ 或者其他内核驱动接口,可以考虑使用接口 spidev 编写用户层程序控制 SPI 设备。代码在kernel\drivers\spi\spidev.c。

注册部分代码如下:


static const struct file_operations spidev_fops = {
	.owner =	THIS_MODULE,
	/* REVISIT switch to aio primitives, so that userspace
	 * gets more complete API coverage.  It'll simplify things
	 * too, except for the locking.
	 */
	.write =	spidev_write,
	.read =		spidev_read,
	.unlocked_ioctl = spidev_ioctl,
	.compat_ioctl = spidev_compat_ioctl,
	.open =		spidev_open,
	.release =	spidev_release,
	.llseek =	no_llseek,
};



/*-------------------------------------------------------------------------*/

/* 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 = "lineartechnology,ltc2488" },
	{ .compatible = "hoziron,j2" },
	{ .compatible = "barrot,i2224e" },
	{},
};
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
#endif

static int spidev_probe(struct spi_device *spi)
{
	struct spidev_data	*spidev;
	int			status;
	unsigned long		minor;

	/*
	 * 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.
	 */
	if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) {
		dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n");
		WARN_ON(spi->dev.of_node &&
			!of_match_device(spidev_dt_ids, &spi->dev));
	}

	dev_err(&spi->dev, "zsm into spidev_probe!\n");

	/* Allocate driver data */
	spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
	if (!spidev)
		return -ENOMEM;

	/* Initialize the driver data */
	spidev->spi = spi;
	spin_lock_init(&spidev->spi_lock);
	mutex_init(&spidev->buf_lock);

	INIT_LIST_HEAD(&spidev->device_entry);

	/* If we can allocate a minor number, hook up this device.
	 * Reusing minors is fine so long as udev or mdev is working.
	 */
	mutex_lock(&device_list_lock);
	minor = find_first_zero_bit(minors, N_SPI_MINORS);
	if (minor < N_SPI_MINORS) {
		struct device *dev;

		spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
		dev = device_create(spidev_class, &spi->dev, spidev->devt,
				    spidev, "spidev%d.%d",
				    spi->master->bus_num, spi->chip_select);

		dev_err(&spi->dev, "zsm: spidev%d.%d\n", spi->master->bus_num, spi->chip_select);

		status = PTR_ERR_OR_ZERO(dev);
	} else {
		dev_dbg(&spi->dev, "no minor number available!\n");
		status = -ENODEV;
	}
	if (status == 0) {
		set_bit(minor, minors);
		list_add(&spidev->device_entry, &device_list);
	}
	mutex_unlock(&device_list_lock);

	spidev->speed_hz = spi->max_speed_hz;

	if (status == 0)
		spi_set_drvdata(spi, spidev);
	else
		kfree(spidev);

	dev_err(&spi->dev, "zsm into spidev_probe over!\n");

	return status;
}

static int spidev_remove(struct spi_device *spi)
{
	struct spidev_data	*spidev = spi_get_drvdata(spi);

	/* make sure ops on existing fds can abort cleanly */
	spin_lock_irq(&spidev->spi_lock);
	spidev->spi = NULL;
	spin_unlock_irq(&spidev->spi_lock);

	/* prevent new opens */
	mutex_lock(&device_list_lock);
	list_del(&spidev->device_entry);
	device_destroy(spidev_class, spidev->devt);
	clear_bit(MINOR(spidev->devt), minors);
	if (spidev->users == 0)
		kfree(spidev);
	mutex_unlock(&device_list_lock);

	return 0;
}

static struct spi_driver spidev_spi_driver = {
	.driver = {
		.name =		"spidev",
		.of_match_table = of_match_ptr(spidev_dt_ids),
	},
	.probe =	spidev_probe,
	.remove =	spidev_remove,

	/* NOTE:  suspend/resume methods are not necessary here.
	 * We don't do anything except pass the requests to/from
	 * the underlying controller.  The refrigerator handles
	 * most issues; the controller driver handles the rest.
	 */
};

/*-------------------------------------------------------------------------*/

static int __init spidev_init(void)
{
	int status;

	/* Claim our 256 reserved device numbers.  Then register a class
	 * that will key udev/mdev to add/remove /dev nodes.  Last, register
	 * the driver which manages those device numbers.
	 */
	BUILD_BUG_ON(N_SPI_MINORS > 256);
	status = register_chrdev(SPIDEV_MAJOR, "spi_zsm", &spidev_fops);
	if (status < 0)
		return status;

	spidev_class = class_create(THIS_MODULE, "spidev_zsm");
	if (IS_ERR(spidev_class)) {
		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
		return PTR_ERR(spidev_class);
	}

	status = spi_register_driver(&spidev_spi_driver);
	if (status < 0) {
		class_destroy(spidev_class);
		unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
	}
	return status;
}
module_init(spidev_init);

static void __exit spidev_exit(void)
{
	spi_unregister_driver(&spidev_spi_driver);
	class_destroy(spidev_class);
	unregister_chrdev(SPIDEV_MAJOR, spidev_spi_driver.driver.name);
}
module_exit(spidev_exit);

MODULE_AUTHOR("Andrea Paterniani, <a.paterniani@swapp-eng.it>");
MODULE_DESCRIPTION("User mode SPI device interface");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:spidev");

2. 从以上代码看,如果要让 probe 能够触发,需要在 dts 文件里,设置spi的参数:

&spi2 {
	ble_i2224e:ble_i2224e {
		compatible = "barrot,i2224e";
		reg = <0>;
		spi-max-frequency = <20000000>;
		status = "okay";
	};
};

&spi2 代表这个设备挂载到了 spi2 总线上。

3. 在应用层读写设备文件节点的方法:

    给应用层调用的有一组文件节点,注册代码如下:

    #define SPIDEV_MAJOR            153    /* assigned */ 
    #define N_SPI_MINORS            32    /* ... up to 256 */


    BUILD_BUG_ON(N_SPI_MINORS > 256);
    status = register_chrdev(SPIDEV_MAJOR, "spi_zsm", &spidev_fops);
    if (status < 0)
        return status;

系统运行后,可以通过 cat /proc/devices 来查看当前注册情况。

    console:/ # cat /proc/devices                                                  
        Character devices:
         ... ...
        153 spi_zsm

如果打算在应用层读写这个节点,需要手动创建一下这个文件节点;供使用;

创建方法: # mknod /dev/spi_zsm0 c 153 0   // 这个名字 spi_zsm0 可以不和上面注册的“spi_zsm”一样;它是根据设备号来找设备的,不是根据名字。

则测试时,直接使用 echo 或者 cat 就可以调用到上面的 spidev_write()函数以及 spidev_read()函数。

<think>好的,我现在需要帮助用户了解如何在Linux使用spidev_test工具测试SPI设备spidev0.0的自收发。首先,我得确保自己对这个过程有清晰的理解,这样才能一步步引导用户。 首先,用户提到了spidev_test,这是一个测试SPI总线的工具。我需要确认这个工具通常的来源,比如是否属于linux内核源码的一部分。记得spidev_test可能在Linux源码的tools/spi目录下,用户可能需要编译它。所以第一步应该是指导用户如何获取和编译这个工具。 接下来,用户需要测试spidev0.0的自收发。这里的关键是SPI的自环模式,也就是将MISO和MOSI引脚短接,这样发送的数据会被接收回来。需要提醒用户进行硬件连接,但如果是某些开发板,可能已经支持软件回环,这样不需要物理连接。需要分情况说明,或者建议用户查阅硬件手册。 然后,执行测试命令。命令的格式应该是spidev_test加上参数,比如-D指定设备,-s设置速度等。需要给出具体的命令示例,比如使用-D /dev/spidev0.0,并说明各个参数的作用。比如-s设置SPI速度,-p指定发送的数据,-v显示详细信息等。 之后,解释输出结果。比如发送的数据和接收的数据是否一致,如果不一致可能存在什么问题,比如时钟极性或相位设置不正确,或者硬件连接问题。需要提醒用户检查SPI模式是否正确,速度是否合适,以及硬件连接是否可靠。 另外,可能需要提到SPI模式参数,比如使用--mode指定CPOL和CPHA,例如模式0、1、2、3。用户可能需要根据设备要求调整这些参数,否则通信可能失败。 还要考虑到用户可能遇到的常见问题,比如权限不足,无法访问/dev/spidev0.0。这时候需要提醒用户使用sudo或者修改udev规则。另外,如果spidev驱动没有加载,可能需要加载内核模块或者配置设备树。 在步骤中遗漏的可能性是,用户是否启用了SPI接口。比如在树莓派上可能需要通过raspi-config启用SPI,或者修改/boot/config.txt。这点可能需要提及,尤其是针对常见开发板。 总结整个流程:编译工具、连接硬件(如果需要)、运行命令并检查结果,最后排除常见问题。确保每个步骤清晰,并解释每个操作的目的,帮助用户理解原理而不仅仅是执行命令。 另外,需要确认所有的命令和参数是否正确,比如spidev_test的参数是否准确,是否有版本差异。比如有些版本的spidev_test可能参数不同,但通常-D、-s、-p、-v是比较常见的。如果有不确定的地方,可能需要建议用户查看工具的帮助文档(-h选项)。 最后,确保使用中文回答,结构清晰,分步骤说明,并提醒用户注意可能出现的问题和解决方法。避免使用过于专业的术语,但必要时解释关键概念,如SPI模式、自环测试等。</think>在Linux系统中,`spidev_test` 是一个常用的SPI设备测试工具,可用于验证SPI总线的自收发功能。以下是针对 `spidev0.0` 的测试步骤及原理说明: --- ### **1. 工具准备** - **获取 `spidev_test` 工具** 该工具通常位于Linux内核源码的 `tools/spi` 目录。若未安装,需手动编译: ```bash # 下载内核源码(以树莓派为例) git clone --depth=1 https://github.com/raspberrypi/linux cd linux/tools/spi make spidev_test sudo cp spidev_test /usr/local/bin/ ``` --- ### **2. 硬件连接** - **自环模式(Loopback)** 若需物理自收发测试,需将SPI设备的 **MOSI(主发从收)** 和 **MISO(主收从发)** 引脚短接。 *注:部分开发板支持软件回环模式(无需物理短接),需检查硬件手册。* --- ### **3. 执行自收发测试** 运行以下命令测试 `spidev0.0`: ```bash sudo spidev_test -D /dev/spidev0.0 -s 1000000 -p "\x01\x02\x03\x04" -v ``` #### **参数说明** - `-D /dev/spidev0.0`:指定SPI设备节点 - `-s 1000000`:设置SPI时钟频率为1MHz - `-p "\x01\x02\x03\x04"`:发送16进制数据 `01 02 03 04` - `-v`:显示详细输出 --- ### **4. 输出解析** 若测试成功,输出示例如下: ``` spi mode: 0x0 bits per word: 8 max speed: 1000000 Hz (1000 KHz) TX | 01 02 03 04 __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ ... RX | 01 02 03 04 __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ ... ``` - **关键验证点**:`TX`(发送数据)与 `RX`(接收数据)应完全一致。 --- ### **5. 常见问题排查** #### **问题1:数据不一致** - 检查硬件连接是否稳定(如短接线是否松动) - 调整SPI模式(`--mode` 参数),例如模式0、1、2、3: ```bash sudo spidev_test -D /dev/spidev0.0 --mode=0 -v ``` #### **问题2:无法打开设备** - 检查SPI驱动是否加载: ```bash lsmod | grep spi ``` - 确认用户权限: ```bash sudo chmod 666 /dev/spidev0.0 # 临时授权 ``` #### **问题3:SPI接口未启用** - 在树莓派等设备中,需通过 `raspi-config` 或编辑 `/boot/config.txt` 启用SPI: ```bash sudo raspi-config # 选择 Interface Options -> SPI -> Enable ``` --- ### **6. 原理解释** - **SPI自环测试**:通过短接MOSI和MISO,控制器发送的数据直接返回,验证SPI总线通信是否正常。 - **时钟与模式**:SPI的时钟极性(CPOL)和相位(CPHA)需与设备匹配,否则数据采样会错位。 --- 通过以上步骤,可系统化验证 `spidev0.0` 的功能性。若仍有异常,建议结合示波器检查SPI信号波形。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值