Spidev 不使用 ioctl 同时写入/读取
Posted
技术标签:
【中文标题】Spidev 不使用 ioctl 同时写入/读取【英文标题】:Spidev do not write/read simultaneously using ioctl 【发布时间】:2013-03-31 11:50:32 【问题描述】:我希望能找到一些帮助,即使这个问题可能与硬件相关而不是软件相关(我们将拭目以待)。我正在开发基于飞思卡尔 P1021 处理器(ppc,e500v2 内核)的定制板。外部 PCB 将被连接并可通过 SPI 进行配置。该外部 PCB 的规格在全双工模式下读取 2 字节命令,并且只有最后一个字节用于将数据传回 MISO。
知道了这一点,我目前正在准备一些软件来测试这个设备。所以我从众所周知的spi_test 程序开始。
root@p1021rdb:~# ./spi_test -D /dev/spidev32766.3
spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)
00 00 00 00 00 00
00 00 00 00 00 00
00 00 00 00 00 00
00 00 00 00 00 00
00 00 00 00 00 00
00 00 00 00 00 00
00 00
root@p1021rdb:~#
信号显示 608 个时钟,似乎只有前半部分的数据。我决定使用环回来调查和测试它 - 缩短 MOSI-MISO 将数据循环回 rx 缓冲区。结果:
root@p1021rdb:~# ./spi_test -D /dev/spidev32766.3
spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)
FF FF FF FF FF FF
40 00 00 00 00 95
FF FF FF FF FF FF
FF FF FF FF FF FF
FF FF FF FF FF FF
DE AD BE EF BA AD
F0 0D
root@p1021rdb:~#
这个信号表明,整个电报因任何原因被重复(我不知道为什么)。但是程序在控制台中正确显示了接收到的数据,所以可能和spi_test预期的一样。
我进一步操作将在这个程序中发送的模式降低到 2 个字节(以模拟我的目标请求的命令格式),如下所示:
#ifdef ORIG
uint8_t tx[] =
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
0xF0, 0x0D,
;
#else
uint8_t tx[] =
0xAA, 0x81,
;
#endif
但我没想到 32 位被移出到 SPI 总线 - 而不是 16。在前两个字节期间,MOSI 提供来自 tx[] 的两个字节,而对于其他 2 个字节,它是低/0。以下是控制台输出和信号的结果:
root@p1021rdb:~# ./spi_test_2bytes -D /dev/spidev32766.3
spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)
00 00
root@p1021rdb:~#
即使我将 MOSI 环回到 MISO 也没有收到数据(控制台输出仍然是相同的接收“00 00”):
我对所有参数进行了一些调整,并决定将测试程序更改为使用半双工(仅传输)模式:
#ifdef ORIG
uint8_t tx[] =
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
0xF0, 0x0D,
;
#else
uint8_t tx[] =
0xAA, 0x81,
;
#endif
uint8_t rx[ARRAY_SIZE(tx)] = 0, ;
struct spi_ioc_transfer tr =
.tx_buf = (unsigned long)tx,
#ifdef ORIG
.rx_buf = (unsigned long)rx,
#else
.rx_buf = 0,
#endif
因为这是编译和执行的事情就像预期的那样。 16 位的 SPI_CLK 循环 16 次,MOSI 按预期提供数据。 Cosole 输出显示没有接收到数据并且信号与预期一致:
root@p1021rdb:~# ./spi_test_2bytes -D /dev/spidev32766.3
spi mode: 0
bits per word: 8
max speed: 500000 Hz (500 KHz)
00 00
root@p1021rdb:~#
实际上,在我看来,我不是进行 2 字节全双工传输,而是先进行 N 字节传输,然后进行 N 字节接收。
其实有两个问题:
-
为什么要传输 0xAA、0x81 和 0x00、0x00?
为什么(使用环回)原始代码能够在 rx 缓冲区中取回数据,但如果减少到 2 个字节,则没有接收到数据?
【问题讨论】:
将图像发布到其他地方并在答案中添加指向它们的链接 - 然后更高代表的用户可以编辑您的答案以包含它们 今天我将检查 spidev 是否在编译时启用了 SPI_MASTER_HALF_DUPLEX 标志,这会强制 spi 设备为半双工。 SPI_MASTER_HALF_DUPLEX 未设置。 【参考方案1】:嗯,这个帖子很安静。我刚刚阅读了一些部分,最近接触了 Linux 上的 SPI。然而,正如在 https://www.kernel.org/doc/Documentation/spi/spidev 异步读/写在用户空间中不可用。 AFAIK 读/写只是 fcntl 的包装。因此,您需要编写自己的内核模块来实现异步 I/O。
【讨论】:
这个答案似乎是错误的。 kernel.org/doc/Documentation/spi/spidev 文章专门说“使用 ioctl() 请求,全双工传输……可用。”。并且 spi-test(例如github.com/KnCMiner/spi-test/blob/master/spi-test.c#L97)使用那个 ioctl()。【参考方案2】:我知道这是一个非常古老的线程,但我一直在运行 OpenWRT 的 RT5350 上使用 spidev。我得到的结果和你完全一样;我只是无法进行全双工传输。阅读 RT5350 数据表,硬件似乎只能进行半双工 SPI 传输。每次传输要么是写入(在 MOSI 上输出字节,不读取任何内容),要么是读取(在 MOSI 上输出零,读取 MISO)。
我无法获得您的 P1021 芯片的数据表,但考虑到我们结果的相似性,我想说它的硬件 SPI 以类似的方式实现。
这意味着内核模块不是答案(ioctl SPI_IOC_MESSAGE 最终还是调用了 spi_async())。实现全双工 SPI 的唯一方法是在软件中使用 GPIO。
RT5350 参考: http://forum.vocore.io/viewtopic.php?f=3&t=72#p233
【讨论】:
以上是关于Spidev 不使用 ioctl 同时写入/读取的主要内容,如果未能解决你的问题,请参考以下文章