65.9K
CodeProject 正在变化。 阅读更多。
Home

Ruby 的 Win32 串口

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2009 年 6 月 10 日

CPOL

6分钟阅读

viewsIcon

51273

downloadIcon

902

适用于 Ruby 的 Win32 串行端口类。

引言

Win32SerialPort::SerialPort 是一个简单的 Ruby 类,用于访问 Windows 上的串行端口。此类使用标准的 Win32 API,不需要任何外部 C/C++ 库。

使用代码

此代码已在 Windows XP、Ruby 1.8.6 和 Win32API gem 1.4.8 上进行了测试。

1. 创建并打开串行端口

串行端口类存储在 win32serial 模块中,并封装在 Win32SerialPort 命名空间中。使用 require 将模块附加到脚本。

require "win32serial"

使用 new 函数创建串行端口对象实例,如下所示:

serial = Win32SerialPort::SerialPort.new

下一步是打开串行端口并使其准备好使用。例如,您可能希望以 115200,8,n,1 模式打开 COM1,而不使用流控制模式(波特率:115200,8 数据位,无校验,1 停止位)。以下示例可以完成此操作:

# Open COM1 serial port in 115200,8,n,1 mode without flow control
return 0 if false == serial.open(
    "COM1",                        # port name
    115200,                        # baudrate
    Win32SerialPort::FLOW_NONE,    # no flow control
    8,                             # 8 data bits
    Win32SerialPort::NOPARITY,     # no parity bits
    Win32SerialPort::ONESTOPBIT)   # one stop bit

传递给函数的参数将按原样传递给 Windows 库,如果打开失败,则归咎于 Windows 的限制。如果无法打开串行端口,open 函数将返回 false,当串行端口准备好使用时,返回 true

要启用硬件流控制,请使用 FLOW_HARDWARE 开关而不是 FLOW_NONE

要切换校验,请使用标志 NOPARITYODDPARITYEVENPARITYMARKPARITYSPACEPARITY

要更改停止位数,可以选择:

  • ONESTOPBIT – 一个停止位
  • ONE5STOPBITS  - 1.5 个停止位
  • TWOSTOPBITS – 两个停止位

数据位数必须是 5 到 8 位。

使用 5 个数据位和 2 个停止位是一种无效组合,同样 6、7 或 8 个数据位和 1.5 个停止位也是无效组合。

使用 close 函数关闭串行端口。

有一个选项可以配置串行端口超时。Windows API 中的 COMMTIMEOUTS 结构定义了五个超时:ReadIntervalTimeoutReadTotalTimeoutMultiplierReadTotalTimeoutConstantWriteTotalTimeoutMultiplierWriteTotalTimeoutConstant

以下示例将前三个读取超时设置为 0,将最后两个写入超时设置为 100ms 和 1000ms。这些超时作为包含 5 个数字的表传递给 setCommTimeouts 函数。

# Configure serial port read/write timeouts
timeouts = [0,0,0,100,1000]
result = serial.setCommTimeouts(timeouts)
print "\nSetCommTimeouts result: " + result.to_s + "\n\n"

2. 准备要发送的数据

Ruby 语言的特殊性导致要发送的数据需要特别准备。这一段更多的是关于如何准备数据,而不是关于使用串行端口类本身。如果您熟悉 Array::packString::unpack 方法,可以跳过此段。

Ruby 中的 Array 是一个对象,不能被解释为字节流,而是在将参数传递给 Windows 内核时需要这样。这意味着要发送到串行端口的数据必须提前准备好。

Ruby 库中有两个函数有助于将数据从数组转换为字节流,以及将字节流转换回数组格式。第一个在传输时有用,第二个在接收字节时有用。

Array 类实例的每个项可能有不同的类型,也意味着不同的大小。Array 类有一个 pack 方法,该方法将数组的项作为字节字符串返回。数组的每一项在字符串中占用所需的字节数,并且每一项一个接一个地放置。pack 方法不知道如何解释其项。因此,该方法接受一个称为 formatter 的参数,该参数描述如何格式化项。例如,‘i’ 表示有符号整数,‘f’ 表示浮点数,‘a’ 表示字符串。所有格式化程序都在 pack 方法文档中有描述。

当从串行端口接收到数据时,它表示为字节字符串。每个期望从串行端口接收数据的应用程序也知道如何解析接收到的字节,并知道如何从该字符串中提取信息。String 类有一个 unpack 方法,该方法接受格式化程序参数(与前面提到的 pack 方法具有相同的开关),描述如何解释接收到的字节。该方法返回一个 Array 类实例,其中包含根据格式化程序参数从二进制字符串中提取的项。

例如,准备一个包含两个整数和一个字符字符串的二进制字符串。首先,创建一个数组:

toSend = [4,7,"Hello World!"]

数组 toSend 有三个项。两个整数(4、7)和一个字符串(Hello World!)。

必须按如下方式将数组转换为二进制字符串:

binaryString = toSend.pack("iia12")

“iia12” 是格式化程序参数,它告诉 pack 方法如何解释存储在 toSend 数组中的项。‘i’ 是有符号整数,‘a12’ 是 12 个字节的字符串。结果,binaryString 包含 20 个字节:每个整数 4 个字节(32 位 Windows)和 12 个字符字节。

binaryString 包含已准备好发送格式的数据。

3. 发送数据

write 方法只接受一个参数。该参数是一个字符串。这里有一些示例:

  • 发送一个简单的字符串
  • # send simple string
    written = serial.write(“Hello World!”)
  • 将数字作为字符串发送
  • # one character “7”
    i = 7
    written = serial.write(i.to_s)
     
    # two characters: 7 and 6
    i = 76
    Written = serial.write(i.to_s)
  • 发送字节数组(请参阅“准备要发送的数据”段落以了解解释)
  • # an array of items
    toSend = [4,7,"Hello World!"]
     
    # the array of items converted to a binary string
    binaryString = toSend.pack("iia12")
     
    # send the binary string
    written = serial.write(binaryString)
     
    #
    # Test if data has been sent
    if 0 < written  
      print "Data has been successfully sent\n"
    else
      print "Could not send data\n"
    end

write 方法返回已发送的字节数。

4. 接收数据

该类中有两个方法允许读取接收到的字节。read 方法尝试读取输入参数中指定的字节数。它会立即返回输入缓冲区中可用的字节,但不会超过指定数量。

第二个方法 readUntil 会阻塞程序的执行,直到读取指定的字节数。如果串行端口已关闭,它将返回比指定字节数少的字节。如果串行端口超时设置为不等待数据,readUntil 将立即返回可读取的字节。

这两个函数都返回一个包含接收到的字节的二进制字符串。请参阅“准备要发送的数据”段落,了解如何解析/解释接收到的数据。示例:

  • 读取所有可用(已接收)字节
  • # Reads as many bytes as it is stored in
    # the input buffer of the serial port.
    binString = serial.read
    if binString.length > 0 
      print "Received data: " + binString + "\n"
    else
      print "Nothing received\n"
    end
  • 读取不超过 10 个字节
  • # Returns 10 or less bytes of data
    binString = serial.read(10)
    if binString.length > 0 
      print "Received " + binString.length.to_s + " bytes\n"
    else 
      print "Nothing received\n"
    end
  • 读取不少于 10 个字节
  • # blocks until 10 bytes is available
    binString = serial.readUntil(10)
     
    # test the length in case if the serial port has been closed
    if binString.length > 0 
      print "Received " + binString.length.to_s + " bytes\n"
    else 
      print "Nothing received\n"
    end

    该类中有一个 bytesToRead 属性,它返回可从串行端口接收缓冲区读取的字节数。

    bytesAvailable = serial.bytesToRead
    print "\nBytes available to read: " + bytesAvailable.to_s + "\n"
     
    binString = serial.readUntil(bytesAvailable)

5. 缺失的功能

例如,简单查看 Microsoft .NET 库中的 System.IO.Ports.SerialPort 类,就可以发现此处描述的类缺少一些有趣的功能。最重要的可能是:

  • 实现 IO 接口,
  • 以及访问调制解调器引脚(DCD、DTR 等)
© . All rights reserved.