Crystal语言访问串口

6
我想使用Crystal语言访问串口。
我有以下Python代码。我想为宠物项目编写相应的Crystal-lang代码。
import serial

def readSerData():

    s = ser.readline()
    if s:
        print(s)
        result = something(s) #do other stuff
        return result

if __name__ == '__main__':

    ser = serial.Serial("/dev/ttyUSB0", 9600)
    while True:
        data = readSerData()
        #do something with data

我找不到任何用于访问串口的库。

在crystal-lang中,访问串口的正确方法是什么?

提前感谢您的帮助。

1个回答

9

为了全面回答这个问题,需要分几个部分来讲解:

问:如何在linux/bsd上访问串口?

答:将其作为文件打开。 在linux/bsd上,一旦设备插入即建立串行连接,并在/dev/下的某处列出(通常是 /dev/ttyUSB0)。要访问此连接,只需像打开普通文件一样打开它。现代硬件通常适用于所有波特率和默认标志,因此有时这就足够开始与设备通信。

问:如何在linux/bsd上配置串行/tty设备?

答:对文件设置termios标志。 如果确实需要配置连接以设置诸如波特率、IXON/IXOFF等内容,则可以在运行程序之前使用stty进行设置,例如,要设置波特率,您可以运行:stty -F /dev/ttyUSB0 9600。设置完成后,您可以像打开文件一样打开它并开始使用。

您可以从Crystal中使用Process.run生成stty,如果您想轻松配置设备,则可能建议使用这种方法。相比下一个解决方案,我更倾向于推荐这种方法。

问:如何在Crystal中设置termios标志,而不使用stty?

A: 直接使用termios posix函数。 Crystal实际上提供了带有几个常见termios设置的FileDescriptor句柄,例如cooked,这意味着它已经具备最小的termios绑定。我们可以从现有代码开始获得灵感:

require "termios" # See above link for contents

#Open the file
serial_file = File.open("/dev/ttyACM0")
raise "Oh no, not a TTY" unless serial_file.tty?

# Fetch the unix FD. It's just a number.
fd = serial_file.fd

# Fetch the file's existing TTY flags
raise "Can't access TTY?" unless LibC.tcgetattr(fd, out mode) == 0

# `mode` now contains a termios struct. Let's enable, umm.. ISTRIP and IXON
mode.c_iflag |= (Termios::InputMode::ISTRIP | Termios::InputMode::IXON).value
# Let's turn off IXOFF too.
mode.c_iflag &= ~Termios::InputMode::IXOFF.value

# Unfun discovery: Termios doesn't have cfset[io]speed available
# Let's add them so changing baud isn't so difficult.
lib LibC
  fun cfsetispeed(termios_p : Termios*, speed : SpeedT) : Int
  fun cfsetospeed(termios_p : Termios*, speed : SpeedT) : Int
end

# Use the above funcs to set the ispeed and ospeed to your nominated baud rate.
LibC.cfsetispeed(pointerof(mode), Termios::BaudRate::B9600)
LibC.cfsetospeed(pointerof(mode), Termios::BaudRate::B9600)
# Write your changes to the FD.
LibC.tcsetattr(fd, Termios::LineControl::TCSANOW, pointerof(mode))

# Done! Your serial_file handle is ready to use.

要设置其他标志,可以参考termios手册或者我刚找到的这个不错的串行通信指南问:有没有现成的库可以帮我完成所有这些操作? 答:没有:( 我看不出来有现成的,但如果有人有兴趣制作一个,那会很棒。如果他们有相关利益,可能并不需要花费太多的时间 :)


通过 stty 设置速度后,将 com 端口读取为文件存在一个大问题。read_byteread_linegets 等不像预期的那样是阻塞调用。当从 unixsocket、tcp、udp 读取时,读取命令会像您预期的那样阻塞。 - isaacsloan
涉及*nix、IO和不同句柄上的非阻塞操作时,这是一个非常棘手的问题。但简而言之,Crystal使用非阻塞IO来使协作线程更加协作。因此,这可能会按预期工作。如果不行,请尝试调整VTIME+VMIN以及在FileDescriptor对象上设置Crystal的blocking=true标志。 - Jarrod Funnell
我发现可以设置一个超时时间,然后捕获抛出的异常并休眠几毫秒。虽然这似乎不是让下一个纤程执行的最优雅的方式。 - isaacsloan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接