SPI用户空间API

SPI设备有一个有限的用户空间API,支持对SPI从设备的基本半双工 read() 和 write() 访问。使用 ioctl() 请求,全双工传输和设备I/O配置也可用。

#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>

你可能想使用这个编程接口的一些原因包括:

  • 在不容易崩溃的环境中进行原型设计;用户空间中的游离指针通常不会导致任何Linux系统宕机。
  • 开发简单的协议,用于与充当SPI slave的微控制器进行对话,这可能需要经常更改。

当然,有些驱动程序永远不能在用户空间中编写,因为它们需要访问用户空间无法访问的内核接口(如IRQ处理程序或驱动程序堆栈的其他层)。

设备创建,驱动绑定

安排使用这个驱动程序的最简单的方法是在设备的spi_board_info中列出它作为它应该使用的驱动程序:“modalias”条目是“spidev”,与暴露这个API的驱动程序的名称相匹配。像往常一样设置其他设备特征(bits per word、SPI时钟、chipselect极性等),这样以后就不需要重写它们了。

(Sysfs还支持用户空间驱动的驱动程序到设备的绑定/解绑定。这个机制将来可能会在这里得到支持。)

当您这样做时,SPI设备的sysfs节点将包含一个带有“dev”属性的子设备节点,udev或mdev可以理解该属性。(更大的系统将有“udev”。较小的可以配置“mdev”到busybox;它的功能不那么强大,但已经足够了。)对于在总线B上使用chipselect C的SPI设备,您应该看到:

/dev/spidevB.C …

  字符特殊设备,主要号码153与一个动态选择的次要设备号码。这是用户空间程序将要打开的节点,由“udev”或“mdev”创建。

/sys/devices/…/spiB.C …

  和往常一样,SPI设备节点将是它的SPI主控制器的子节点。

/sys/class/spidev/spidevB.C …

  当“spidev”驱动绑定到该设备时创建。(目录或符号链接,取决于您是否启用了“deprecated sysfs files”Kconfig选项。)

不要尝试手动管理 /dev 字符设备特殊文件节点。这很容易出错,您需要仔细注意系统安全问题;应该已经安全地配置了udev /mdev。

如果您从该设备中解绑定“spidev”驱动程序,那么这两个“spidev”节点(在sysfs和/dev中)应该被自动删除(分别由内核和udev/mdev)。您可以通过移除“spidev”驱动模块来解除绑定,这将影响所有使用该驱动的设备。您还可以通过让内核代码删除SPI设备来解除绑定,可能是通过删除SPI控制器的驱动程序(这样它的spi_master就消失了)。

由于这是一个标准的Linux设备驱动程序——尽管它只是向用户空间公开了一个低级的API——它可以一次与任意数量的设备相关联。只需为每个这样的SPI设备提供一个spi_board_info记录,就可以为每个设备获得一个/dev设备节点。

基本字符设备API

正常的open() 和 close()操作/dev/ spidevB.D文件的工作方式与您期望的一样。

标准的read()和write()操作显然只是半双工的,而chipselect在这些操作之间是不激活的。使用 SPI_IOC_MESSAGE(N) 请求可以获得全双工访问和不带chipselect去激活的复合操作。

几个ioctl()请求让你的驱动程序读取或覆盖设备当前的数据传输参数设置:

SPI_IOC_RD_MODE, SPI_IOC_WR_MODE …

  传递一个指向字节的指针,该字节将返回(RD)或分配(WR) SPI传输模式。使用常量SPI_MODE_0..SPI_MODE_3;或者,如果你喜欢,你可以结合SPI_CPOL(时钟极性,idle high iff this is set)或SPI_CPHA(时钟相位,sample on trailing edge iff this is set)标志。请注意,此请求仅限于适合单个字节的SPI模式标志。

SPI_IOC_RD_MODE32, SPI_IOC_WR_MODE32 …

  传递一个指向uin32_t的指针,它将返回(RD)或分配(WR)完整的SPI传输模式,而不限于适合一个字节的位。

SPI_IOC_RD_LSB_FIRST, SPI_IOC_WR_LSB_FIRST …

  传递一个指向字节的指针,该字节将返回(RD)或分配(WR)用于传输SPI字的位对齐。零表示MSB-first;其他值表示较不常见的LSB-first编码。在这两种情况下,指定的值在每个单词中右对齐,因此未使用的(TX)或未定义的(RX)位在MSB中。

SPI_IOC_RD_BITS_PER_WORD, SPI_IOC_WR_BITS_PER_WORD …

  传递一个指向字节的指针,该字节将返回(RD)或分配(WR)每个SPI传输字的比特数。值0表示8位。

SPI_IOC_RD_MAX_SPEED_HZ, SPI_IOC_WR_MAX_SPEED_HZ …

  将一个指针传递给u32,它将返回(RD)或分配(WR)最大SPI传输速度,以Hz为单位。控制器不能指定特定的时钟速度。

注意:

  • 此时没有异步I/O支持;一切都是纯粹同步的。
  • 目前还没有办法报告实际的比特率用于转移数据到/从一个给定的设备。
  • 从用户空间,你目前不能改变芯片选择极性;可能破坏对共享SPI总线的其他设备的传输。每个SPI设备在不活跃使用时被取消选择,允许其他驱动程序与其他设备通信。
  • 对于每个I/O请求可以传输到SPI设备的字节数是有限制的。它默认为一个页面,但可以使用模块参数更改。
  • 因为SPI没有 low-level 的传输确认,所以在与不存在的设备对话时,通常不会看到任何I/O错误。

全双工字符设备API

请参阅spidev_fdx.c示例程序,以获得显示全双工编程接口使用的示例。(尽管它不执行全双工传输。)该模型与内核spi_sync()请求中使用的模型相同;单个传输提供了与内核驱动程序相同的功能(除了它不是异步的)。

该示例显示了一个半双工RPC-style的请求和响应消息。这些请求通常要求在请求和响应之间不deselect芯片。多个这样的请求可以链接到一个内核请求中,甚至允许在每个响应之后deselect该芯片。(其他协议选项包括更改每个传输段的字大小和比特率。)

要发出全双工请求,请同时为相同的传输提供rx_buf和tx_buf。即使它们是相同的缓冲区也是可以的。

posted @ 2021-08-23 16:14  闹闹爸爸  阅读(1240)  评论(0编辑  收藏  举报