Ruby,ioctl和复杂的结构
我有一个硬件,我试图通过我的计算机的内置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中转换为(无符号长整数).我正在尝试在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_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打印它们来消除幻数常量(它们是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,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,pad] struct_packed = struct_array.pack("QQLLSCCCCS") file.ioctl(SPI_IOC_MESSAGE_1,struct_packed) 我猜Ruby会在它编组时自动将它变成一个数组吗? 不幸的是,现在它只能间歇性地工作.第二个调用永远不会起作用,如果我传入全零,第一个调用就不起作用.这很神秘. 解决方法
不刷新缓冲区是一个常见的问题,你可以检查一下并试一试.
Flush: rb_io_flush(VALUE io) { return rb_io_flush_raw(io,1); } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |