1. 背景
如果使用 C 来编写 Linux 上的串口通信程序,需要使用 termios,tldp 有详细的示例:Serial-Programming-HOWTO。
使用 Rust 编写串口通信程序,需要借助三个库:serial、ioctl-rs 以及 termios。serial 既支持 Linux,也支持 Windows,ioctl-rs 是对 Unix 上系统调用的部分 C 库的封装,termios 是对 Unix 上终端 I/O 的 C 库的封装,serial 依赖后面的两个库。
2. 编码
(1) 配置工程
1
cargo new serial_port
在配置文件中添加依赖:
1
2
[dependencies]
serial = "0.4.0"
(2) 示例程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
extern crate serial;
use std::time::Duration;
use std::thread;
use serial::prelude::*;
use std::io::prelude::*;
fn main() {
const SETTINGS: serial::PortSettings = serial::PortSettings {
baud_rate: serial::Baud115200,
char_size: serial::Bits8,
parity: serial::ParityNone,
stop_bits: serial::Stop1,
flow_control: serial::FlowNone,
};
let mut port = serial::open("/dev/ttyS9").unwrap();
port.configure(&SETTINGS).unwrap();
port.set_timeout(Duration::from_millis(1000)).unwrap();
let mut buf: Vec<u8> = (0..255).collect();
loop {
if let Ok(n) = port.read(&mut buf[..]) {
for i in 0..n {
print!("{:02x }", buf[i]);
}
};
thread::sleep(Duration::from_millis(10));
}
}
在串口上接上诸如 Arduino 之类的设备,让其按上面的串口配置向上位机发送数据,然后运行上述示例程序,不出意外的话就可以看到输出。
(3) 为本地机器生成绑定
实际使用过程中,可能会遇到一些问题,例如:
thread ‘main’ panicked at ‘called
Result::unwrap()
on anErr
value: Error { kind: Io(Other), description: “Inappropriate ioctl for device” }’, src/main.rs:17:47
错误信息显示下发给该设备的系统调用不正确。这个其实是之前提到的 ioctl-rs 库的问题。不同的 Linux 版本,ioctl 的命令很有可能是不同的,即 C 库头文件中各命令对应的宏的数值可能不一样。ioctl-rs 是系统调用相关的 C 库的封装,serial 会直接使用 crates.io 上的 ioctl-rs 库,本地的机器上和生成 ioctl-rs 库的机器,ioctl 命令值有差异时,就会出现上面的问题。
需要为本地机器重新生成相应的绑定。
生成绑定之前,将需要库下载到工程目录,并修改相应库的路径:
1
2
3
git clone https://github.com/dcuddeback/serial-rs.git
git clone https://github.com/dcuddeback/ioctl-rs.git
git clone https://github.com/dcuddeback/termios-rs.git
修改 serial-rs/serial-unix/Cargo.toml 文件内的依赖项:
1
2
termios = { version = "0.3", path = "../../termios-rs" }
ioctl-rs = { path = "../../ioctl-rs" }
使用 ioctl_list 工具生成绑定:
1
2
3
cd ioctl_list/
./autogen.sh && ./configure && make
./ioctl_list
使用屏幕上输出的内容替换 ioctl-rs/src/os/linux.rs 文件内相应的内容即可。
ioctl-rs 作者提到,该工具打印的常量,其类型可能会不准确[2],编译过程中可能需要把部分常量的类型由 c_int 改为 c_uint。
3. 故障排查
使用最新的库可能会有问题,例如,在 wsl2 上可能还会报系统调用错误的问题,大部分还是命令值的问题,可能还得手工矫正,必要的话,可以试一试这几个矫正过的:
1
2
3
https://github.com/qianchenzhumeng/serial-rs.git
https://github.com/qianchenzhumeng/ioctl-rs.git
https://github.com/qianchenzhumeng/termios-rs.git
切换到分支 pi。
参考
[1] https://tldp.org/HOWTO/Serial-Programming-HOWTO/x56.html#AEN88
[2] https://github.com/dcuddeback/ioctl-rs/tree/master/ioctl_list