gpt4 book ai didi

c - Ruby、ioctl 和复杂结构

转载 作者:数据小太阳 更新时间:2023-10-29 07:37:24 26 4
gpt4 key购买 nike

我有一个硬件,我正试图通过我计算机的内置 SPI 驱动程序来控制它。 SPI 驱动程序通过 ioctl 控制。

我可以通过一个小的 C 程序成功地驱动硬件;但是当我尝试在 Ruby 中复制 C 程序时,我遇到了问题。

使用 IO#ioctl设置基本寄存器(使用 u32 和 u8 整数)工作正常(我知道因为我也可以使用 ioctl 来读回我设置的值);但是一旦我尝试设置一个复杂的结构,程序就会失败

small.rb:51:in 'ioctl': Connection timed out @ rb_ioctl - /dev/spidev32766.0 (Errno::ETIMEDOUT)

我可能会遇到麻烦,因为 spi_ioc_transfer struct有两个指向字节缓冲区的指针,但即使在 32 位平台上,指针也被键入为无符号 64 位整数——需要在 C 中转换为 (unsigned long)。我正在尝试复制它在 Ruby 中,但我对自己很不确定。

下面是工作的 C 程序和不工作的 Ruby 端口。 do_latch 函数是必需的,这样我就可以在我的硬件中看到结果;但可能与这个问题无关。

C(有效):

#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>

int do_latch() {
int fd = open("/sys/class/gpio/gpio1014/value", O_RDWR);
write(fd, "1", 1);
write(fd, "0", 1);

close(fd);
}

int do_transfer(int fd, uint8_t *bytes, size_t len) {
uint8_t *rx_bytes = malloc(sizeof(uint8_t) * len);

struct spi_ioc_transfer transfer = {
.tx_buf = (unsigned long)bytes,
.rx_buf = (unsigned long)rx_bytes,

.len = len,
.speed_hz = 100000,

.delay_usecs = 0,
.bits_per_word = 8,
.cs_change = 0,
.tx_nbits = 0,
.rx_nbits = 0,
.pad = 0
};

if(ioctl(fd, SPI_IOC_MESSAGE(1), &transfer) < 1) {
perror("Could not send SPI message");
exit(1);
}

free(rx_bytes);
}

int main() {
int fd = open("/dev/spidev32766.0", O_RDWR);

uint8_t mode = 0;
ioctl(fd, SPI_IOC_WR_MODE, &mode);

uint8_t lsb_first = 0;
ioctl(fd, SPI_IOC_WR_LSB_FIRST, lsb_first);

uint32_t speed_hz = 100000;
ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, speed_hz);

size_t data_len = 36;
uint8_t *tx_data = malloc(sizeof(uint8_t) * data_len);

memset(tx_data, 0xFF, data_len);

do_transfer(fd, tx_data, data_len);
do_latch();

sleep(2);

memset(tx_data, 0x00, data_len);

do_transfer(fd, tx_data, data_len);
do_latch();

free(tx_data);
close(fd);
return 0;
}

Ruby(在 do_transfer 的 ioctl 行失败):

SPI_IOC_WR_MODE = 0x40016b01
SPI_IOC_WR_LSB_FIRST = 0x40016b02
SPI_IOC_WR_BITS_PER_WORD = 0x40016b03
SPI_IOC_WR_MAX_SPEED_HZ = 0x40046b04
SPI_IOC_WR_MODE32 = 0x40046b05
SPI_IOC_MESSAGE_1 = 0x40206b00

def do_latch()
File.open("/sys/class/gpio/gpio1014/value", File::RDWR) do |file|
file.write("1")
file.write("0")
end
end

def do_transfer(file, bytes)
##########################################################################################
#begin spi_ioc_transfer struct (cat /usr/include/linux/spi/spidev.h)

#pack bytes into a buffer; create a new buffer (filled with zeroes) for the rx
tx_buff = bytes.pack("C*")
rx_buff = (Array.new(bytes.size) { 0 }).pack("C*")

#on 32-bit, the struct uses a zero-extended pointer for the buffers (so it's the same
#byte layout on 64-bit as well) -- so do some trickery to get the buffer addresses
#as 64-bit strings even though this is running on a 32-bit computer
tx_buff_pointer = [tx_buff].pack("P").unpack("L!")[0] #u64 (zero-extended pointer)
rx_buff_pointer = [rx_buff].pack("P").unpack("L!")[0] #u64 (zero-extended pointer)

buff_len = bytes.size #u32
speed_hz = 100000 #u32

delay_usecs = 0 #u16
bits_per_word = 8 #u8
cs_change = 0 #u8
tx_nbits = 0 #u8
rx_nbits = 0 #u8
pad = 0 #u16

struct_array = [tx_buff_pointer, rx_buff_pointer, buff_len, speed_hz, delay_usecs, bits_per_word, cs_change, tx_nbits, rx_nbits, pad]
struct_packed = struct_array.pack("QQLLSCCCCS")

#in C, I pass a pointer to the the structure; so mimic that here
struct_pointer_packed = [struct_packed].pack("P")
#end spi_ioc_transfer struct
##########################################################################################

file.ioctl(SPI_IOC_MESSAGE_1, struct_pointer_packed)
end

File.open("/dev/spidev32766.0", File::RDWR) do |file|

file.ioctl(SPI_IOC_WR_MODE, [0].pack("C"));
file.ioctl(SPI_IOC_WR_LSB_FIRST, [0].pack("C"));
file.ioctl(SPI_IOC_WR_MAX_SPEED_HZ, [0].pack("L"));

data_bytes = Array.new(36) { 0x00 }

do_transfer(file, data_bytes)
do_latch()

sleep(2)

data_bytes = []
data_bytes = Array.new(36) { 0xFF }

do_transfer(file, data_bytes)
do_latch()

end

我通过让 C 打印它们来提取魔数(Magic Number)常量(它们是 C 中的宏)。我可以验证它们中的大多数都有效;我对失败的 ioctl 消息 (SPI_IOC_MESSAGE_1) 有点不确定,因为它不起作用,而且它是一个复杂的宏。不过,我没有理由认为它不正确,而且当我从 C 看它时,它总是一样的。

当我在 C 中打印出结构然后在 Ruby 中打印出来时,唯一的区别在于缓冲区地址,所以如果出现问题,那感觉就是正确的地方。但是我已经没有什么可以尝试的了。

我还可以打印出两个版本的地址,它们看起来像我期望的那样,32 位扩展到 64 位,并匹配结构中的值(尽管结构是小端的——这是一个 ARM ).

C 中的结构(有效):

60200200 00000000 a8200200 00000000 24000000 40420f00 00000800 00000000

Ruby 中的结构(失败):

a85da27f 00000000 08399b7f 00000000 24000000 40420f00 00000800 00000000

我在 Ruby 中布置结构时是否犯了一个明显的错误?还有什么我想念的吗?

我的下一步是用 C 编写一个库并使用 FFI 从 Ruby 访问它。但这似乎是放弃;如果我能让它工作的话,使用原生的 ioctl 函数感觉是更好的方法。

更新

以上,我在做

    struct_array = [tx_buff_pointer, rx_buff_pointer, buff_len, speed_hz, delay_usecs, bits_per_word, cs_change, tx_nbits, rx_nbits, pad]
struct_packed = struct_array.pack("QQLLSCCCCS")

#in C, I pass a pointer to the the structure; so mimic that here
struct_pointer_packed = [struct_packed].pack("P")
file.ioctl(SPI_IOC_MESSAGE_1, struct_pointer_packed)

因为我必须在 C 中传递一个指向结构的指针。但这就是导致错误的原因!

相反,它需要是

    struct_array = [tx_buff_pointer, rx_buff_pointer, buff_len, speed_hz, delay_usecs, bits_per_word, cs_change, tx_nbits, rx_nbits, pad]
struct_packed = struct_array.pack("QQLLSCCCCS")

file.ioctl(SPI_IOC_MESSAGE_1, struct_packed)

我猜 Ruby 在整理它时会自动将它变成一个数组?

不幸的是,现在它只能间歇性地工作。第二个调用从不有效,如果我传入全零,第一个调用无效。这很神秘。

最佳答案

不刷新缓冲区是一个常见问题,您可以检查一下并尝试。

Flush :将 ios 中的任何缓冲数据刷新到底层操作系统(请注意,这只是 Ruby 内部缓冲;操作系统也可以缓冲数据)。

rb_io_flush(VALUE io)
{
return rb_io_flush_raw(io, 1);
}

关于c - Ruby、ioctl 和复杂结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41726292/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com