• RUST 环境 UDP UART CANFD


    测试环境

    WSL2
    Ubuntu 22.04
    Kernel 5.15.57.1
    cargo  1.63.0 (不定期更新)
    
    • 1
    • 2
    • 3
    • 4

    RUST 安装

    # 安装
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    
    # 版本检查
    # 第一句可以放到 ~/.bashrc里面
    source "$HOME/.cargo/env"
    cargo --version
    rustc --version
    
    # 更新
    rustup update
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    VSCode 配置

    插件:

    新工程

    cargo new playground
    cd playground
    cargo run
    
    • 1
    • 2
    • 3

    可执行文件的路径为

    playground/target/debug/playground
    
    # 查看文件大小发现有3.xM, 应该是打包进了rust的运行时之类的
    $ ls -lh target/debug/playground
    
    # 编译release版本, 会小一点
    $ cargo build --release
    $ ls -lh target/release/playground
    
    # 还有其它减少体积的方式, 可自行搜索
    
    # 清理
    $ cargo clean
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    args 命令行参数传入

    C语言编程时, 经常可以看到main函数

    int main(int argc, char *argv[])
    
    • 1

    这里argc是参数个数, argv[]是参数的字符串数组, argv[0], argv[1]… 这些就是通过命令行传入的参数.

    rust 中也有类似的, 如 std::env::args, 用法举例

    use std::env;
    fn main() {
        let args: Vec<String> = env::args().collect();
        println!("{:?}", args);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    测试

    $ cargo run 1 a 127.0.0.1
       Compiling playground v0.1.0 (/home/karoto/git/rust_note/playground)
        Finished dev [unoptimized + debuginfo] target(s) in 0.31s
         Running `target/debug/playground 1 a 127.0.0.1`
    ["target/debug/playground", "1", "a", "127.0.0.1"]
    
    • 1
    • 2
    • 3
    • 4
    • 5

    或者是for循环的方式, 一个一个打出来

        for i in env::args() {
            println!("{}", i);
        }
    
    • 1
    • 2
    • 3

    参考 args in std::env - Rust (rust-lang.org)

    多文件 mod 和 include

    现在有两个文件

    $ tree src
    src
    ├── main.rs
    └── test_args.rs
    
    # test_args 中要被调用的函数前面加上pub
    # 这里main改成其它名字比较好
    $ cat src/test_args.rs 
    use std::env;
    pub fn main() {
        let args: Vec<String> = env::args().collect();
        println!("{:?}", args);
    }
    
    # 用mod把文件引入, mod是modules缩写, 可以把一个文件看成一个module
    $ cat src/main.rs 
    mod test_args;
    fn main() {
        test_args::main();
    }
    
    # 运行
    $ cargo run 1 a 
       Compiling playground v0.1.0 (/home/karoto/git/rust_note/playground)
        Finished dev [unoptimized + debuginfo] target(s) in 0.30s
         Running `target/debug/playground 1 a`
    ["target/debug/playground", "1", "a"]
    
    • 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

    还有一种 include 的方式

    # test_args 中的main要改成其它名字
    $ cat src/test_args.rs 
    use std::env;
    pub fn print_args() {
        let args: Vec<String> = env::args().collect();
        println!("{:?}", args);
    }
    
    # include 引入文件/文件夹, 直接用里面pub过的fn
    $ cat src/main.rs
    include!("../src/test_args.rs");
    fn main() {
        print_args();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    参考 rust多文件/文件夹及模块管理 - 简书 (jianshu.com)

    多bin 与 workspace

    比如server/client, pub/sub… 参考 关于rust:如何使用Cargo构建多个二进制文件:

    • Cargo.toml文件中指定多个 [[bin]]
    • 或者 [workspace] 搭配多个工程, 每个工程都有一个 Cargo.toml

    个人开发怎么都行, 多人开发第二种应该会好一点, 或者两种组合使用

    第一种方式举例

    $ tree
    .
    ├── Cargo.toml
    └── src
        ├── play_args
        │   ├── main.rs
        │   └── test_args.rs
        └── play_hello
            └── main.rs
    
    # 在toml里面写两个[[bin]], 填入可执行文件名字和路径
    $ cat Cargo.toml 
    [package]
    name = "playground"
    version = "0.1.0"
    edition = "2021"
    
    [dependencies]
    
    [[bin]]
    name = "play_hello"
    path = "src/play_hello/main.rs"
    
    [[bin]]
    name = "play_args"
    path = "src/play_args/main.rs"
    
    $ cargo build
    $ ./target/debug/play_args  1 2 3
    ["./target/debug/play_args", "1", "2", "3"]
    $ ./target/debug/play_hello 
    Hello, world!
    
    • 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
    • 30
    • 31
    • 32

    第二种方式举例

    # play_args 和 play_hello 是直接 cargo new 出来的
    $ tree
    .
    ├── Cargo.toml
    ├── play_args
    │   ├── Cargo.toml
    │   └── src
    │       ├── main.rs
    │       └── test_args.rs
    └── play_hello
        ├── Cargo.toml
        └── src
            └── main.rs
            
    # 顶层手写一个Cargo.toml, 内容如下
    $ cat Cargo.toml 
    [workspace]
    members = ["play_args", "play_hello"]
    
    $ cargo build
       Compiling play_args v0.1.0 (/home/karoto/git/rust_note/playground/play_args)
       Compiling play_hello v0.1.0 (/home/karoto/git/rust_note/playground/play_hello)
        Finished dev [unoptimized + debuginfo] target(s) in 0.52s
    $ ./target/debug/play_args 1 a
    ["./target/debug/play_args", "1", "a"]
    $ ./target/debug/play_hello 
    Hello, world!
    
    • 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

    println

    println in std - Rust (rust-lang.org)

    fn add(x: i32, y: i32) -> i32 {
        x + y
    }
    
    fn main() {
        // 整数
        let a = 1;
        println!("{}", a);
        // 字符
        let b = 'a';
        println!("{}", b);
        // 字符串
        let s = "hello";
        println!("{}", s);
        // 布尔值
        let b = true;
        println!("{}", b);
        // 浮点数
        let f = 3.1415926;
        println!("{}", f);
        // 数组
        let arr = [10, 11, 13, 24, 50];
        println!("{:?}", arr);
        // 元组
        let tup = (1, "hello", true);
        println!("{:?}", tup);
        // 函数
        let f = add;
        println!("11 + 22 = {}", f(11, 22));
        // =================
        let c = {
            let a = 1;
            let b = 2;
            a + b
        };
        println!("{}", c);
        // lambda表达式
        let lambda = |x: i32, y: i32| x + y;
        println!("{}", lambda(3, 4));
        // 打印匿名函数
        let anon = |x: i32, y: i32| x + y;
        println!("{:?}", anon(5, 6));
        // 匿名函数作为参数传递
        let add_one = |x: i32| x + 1;
        println!("{}", add_one(9));
        // 匿名函数作为返回值
        let f = || {
            println!("hi");
        };
        f();
        // 打印十六进制, 不够2位补0
        let hex = |x: i32| -> String {
            format!("{:02x}", x)
        };
        println!("{}", hex(255));
        // 遍历数组, 打印十六进制
        for i in arr.iter() {
            println!("{}", hex(*i));
        }
        // 排序
        let mut v = vec![10, 30, 11, 20, 4, 330, 21, 110, 5, 10, 1];
        v.sort();
        println!("{:?}", v);
    }
    
    • 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
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    运行结果

    1
    a
    hello
    true
    3.1415926
    [10, 11, 13, 24, 50]
    (1, "hello", true)
    11 + 22 = 33
    3
    7
    11
    10
    hi
    ff
    0a
    0b
    0d
    18
    32
    [1, 4, 5, 10, 10, 11, 20, 21, 30, 110, 330]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    thread

    std::thread - Rust (rust-lang.org)

    use std::{thread, time};
    
    fn main() {
        // 线程1
        let handle = thread::spawn(|| {
            for i in 1..10 {
                println!("hi number {} from the spawned thread!", i);
                thread::sleep(time::Duration::from_millis(100));
            }
        });
        // 主线程
        for i in 1..5 {
            println!("hi number {} from the main thread!", i);
            thread::sleep(time::Duration::from_millis(100));
        }
        // 等待线程1结束
        handle.join().unwrap();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    运行结果

    $ ./target/debug/play_thread 
    hi number 1 from the main thread!
    hi number 1 from the spawned thread!
    hi number 2 from the main thread!
    hi number 2 from the spawned thread!
    hi number 3 from the main thread!
    hi number 3 from the spawned thread!
    hi number 4 from the spawned thread!
    hi number 4 from the main thread!
    hi number 5 from the spawned thread!
    hi number 6 from the spawned thread!
    hi number 7 from the spawned thread!
    hi number 8 from the spawned thread!
    hi number 9 from the spawned thread!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    多生产-单消费

    use std::{sync::mpsc, thread, time};
    
    fn main() {
        // 多生产者-单消费者
        let (tx, rx) = mpsc::channel();
        for i in 0..10 {
            let tx = tx.clone();
            thread::spawn(move|| {
                tx.send(i).unwrap();
                thread::sleep(time::Duration::from_millis(1000-i*100));
                tx.send(i+100).unwrap();
            });
        }
        for _ in 0..20 {
            let j = rx.recv().unwrap();
            println!("Got: {}", j);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    运行

    $ ./target/debug/play_mpsc 
    Got: 0
    Got: 2
    Got: 3
    Got: 1
    Got: 4
    Got: 5
    Got: 6
    Got: 7
    Got: 9
    Got: 8
    Got: 109
    Got: 108
    Got: 107
    Got: 106
    Got: 105
    Got: 104
    Got: 103
    Got: 102
    Got: 101
    Got: 100
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    关于 多生产者、单消费者FIFO队列通信, 参考:

    UDP

    UdpSocket in std::net - Rust (rust-lang.org)

    下面的例子就不区分server和client了

    $ cargo new play_udp
    
    # play_udp/Cargo.toml 最下面添加
    [[bin]]
    name = "play_udp_server"
    path = "src/play_udp_server/main.rs"
    
    [[bin]]
    name = "play_udp_client"
    path = "src/play_udp_client/main.rs"
    
    $ tree play_udp/
    play_udp/
    ├── Cargo.toml
    └── src
        ├── play_udp_client
        │   └── main.rs
        └── play_udp_server
            └── main.rs
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    7878端口

    // 导入UdpSocket
    use std::net::UdpSocket;
    fn main() {
        let addr = "127.0.0.1:7878";
        let socket = UdpSocket::bind(addr).unwrap();
        let mut buf = [0; 1024];
        let mut count = 0;
        loop {
            let (amt, src) = socket.recv_from(&mut buf).unwrap();
            println!(
                "Received {} bytes from {}: {:?}, {}",
                amt,
                src,
                &buf[..amt],
                String::from_utf8_lossy(&buf[..amt])
            );
            // buf扩展count
            buf[amt] = count + '0' as u8;
            socket.send_to(&buf[..amt + 1], src).unwrap();
            count += 1;
            if count == 10 {
                break;
            }
        }
    }
    
    • 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

    8888端口

    use std::net::UdpSocket;
    fn main() {
        let addr = "127.0.0.1:8888";
        let socket = UdpSocket::bind(addr).unwrap();
        let mut buf = [0; 1024];
        let mut count = 0;
        // 向7878端口发送数据, 引信
        socket.send_to(b"hello", "127.0.0.1:7878").unwrap();
        loop {
            let (amt, src) = socket.recv_from(&mut buf).unwrap();
            println!(
                "Received {} bytes from {}: {}",
                amt,
                src,
                String::from_utf8_lossy(&buf[..amt])
            );
            socket.send_to(&buf[..amt], src).unwrap();
            count += 1;
            if count == 10 {
                break;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    运行

    $ cargo build
    
    # 先运行server, 再运行client
    $ ./target/debug/play_udp_server 
    $ ./target/debug/play_udp_client
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (半)链式反应

    在这里插入图片描述

    库引入

    三种库:

    • crates.io: Rust Package Registry, RUST的官方仓库, 如 rand = "0.3"
    • Git仓库, 如 color = { git = "https://github.com/bjz/color-rs" }
    • 绝对/相对路径, 如 geometry = { path = "crates/geometry" }

    如果一直卡顿或报错

    # 引入库报
    # Blocking waiting for file lock on package cache
    rm -rf ~/.cargo/.package-cache 
    
    • 1
    • 2
    • 3

    UART serial

    serial - Rust (docs.rs)

    用python生成一对虚拟串口(尽量不要同时读写)

    #!/usr/bin/python3
    
    import pty
    import os
    import select
    
    def mkpty():
        #  Open the pseudo terminal
        master1, slave = pty.openpty()
        slaveName1 = os.ttyname(slave)
        master2, slave = pty.openpty()
        slaveName2 = os.ttyname(slave)
        print('slave device names: ', slaveName1, slaveName2)
        return master1, master2
    
    if __name__ == "__main__":
        master1, master2 = mkpty()
        while True:
            rl, wl, el = select.select([master1,master2], [], [], 1)
            for master in rl:
                data = os.read(master, 128)
                print("read %d data." % len(data) )
                if master==master1:
                    os.write(master2, data)
                else:
                    os.write(master1, data)
    
    • 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

    运行, 打印出的 /dev/pts/31, /dev/pts/32 就是一对虚拟串口

    $ python3 vcom.py 
    slave device names:  /dev/pts/6 /dev/pts/7
    
    • 1
    • 2

    rust工程引入serial, 在Cargo.toml

    [dependencies]
    serial = "0.4.0"
    
    • 1
    • 2

    类似上面的udp

    $ cargo new play_serial
    
    # play_serial/Cargo.toml 引入serial, 添加bin
    [dependencies]
    serial = "0.4.0"
    
    [[bin]]
    name = "play_serial_server"
    path = "src/play_serial_server/main.rs"
    
    [[bin]]
    name = "play_serial_client"
    path = "src/play_serial_client/main.rs"
    
    $ tree play_serial/
    play_serial/
    ├── Cargo.toml
    └── src
        ├── play_serial_client
        │   └── main.rs
        ├── play_serial_server
        │   └── main.rs
        └── vcom.py
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    server

    extern crate serial;
    
    use serial::prelude::*;
    use std::io::prelude::*;
    use std::time::Duration;
    
    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,
    };
    
    fn main() {
        let mut serial = serial::open("/dev/pts/6").unwrap();
        serial.configure(&SETTINGS).unwrap();
        serial
            .set_timeout(Duration::from_secs(1_000_000_000))
            .unwrap();
        let mut buf = [0; 1024];
        let mut count = 0;
        loop {
            let amt = serial.read(&mut buf[..]).unwrap();
            println!(
                "Received {} bytes: {:?}, {}",
                amt,
                &buf[..amt],
                String::from_utf8_lossy(&buf[..amt])
            );
            // buf扩展count
            buf[amt] = count + '0' as u8;
            serial.write(&buf[..amt + 1]).unwrap();
            count += 1;
            if count == 10 {
                break;
            }
        }
    }
    
    • 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
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    client

    extern crate serial;
    
    use serial::prelude::*;
    use std::io::prelude::*;
    use std::time::Duration;
    
    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,
    };
    
    fn main() {
        let mut serial = serial::open("/dev/pts/7").unwrap();
        serial.configure(&SETTINGS).unwrap();
        serial
            .set_timeout(Duration::from_secs(1_000_000_000))
            .unwrap();
        let mut buf = [0; 1024];
        let mut count = 0;
        // 发送数据, 引信
        serial.write(b"hello").unwrap();
        loop {
            let amt = serial.read(&mut buf[..]).unwrap();
            println!(
                "Received {} bytes: {:?}, {}",
                amt,
                &buf[..amt],
                String::from_utf8_lossy(&buf[..amt])
            );
            serial.write(&buf[..amt]).unwrap();
            count += 1;
            if count == 10 {
                break;
            }
        }
    }
    
    • 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
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    运行

    $ cargo build
    # Blocking waiting for file lock on package cache
    # 如果一直卡在上面这句, 就运行下面的命令
    # rm -rf ~/.cargo/.package-cache 
    
    # 串口操作可能需要加 sudo
    $ ./target/debug/play_serial_server
    $ ./target/debug/play_serial_client 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    UART serial2

    上面的 dcuddeback/serial-rs: Rust library for interacting with serial ports. (github.com) 可以看到已经停止维护5年了, 最近有个 de-vri-es/serial2-rs: Cross platform serial ports for Rust (github.com), 先不谈性能如何, 先用用看(当然, 如果对所有的都不满意, 可以自己写一个, 或者FFI套C)

    serial2 - Rust (docs.rs), examples中可以列出可用串口, 多线程, 多串口操作等

    $ cargo new play_serial2
    
    # play_serial/Cargo.toml 引入serial, 添加bin
    [dependencies]
    serial2 = "0.1.6"
    
    [[bin]]
    name = "play_serial2_server"
    path = "src/play_serial2_server/main.rs"
    
    [[bin]]
    name = "play_serial2_client"
    path = "src/play_serial2_client/main.rs"
    
    $ tree play_serial2/
    play_serial2
    ├── Cargo.toml
    └── src
        ├── play_serial2_client
        │   └── main.rs
        └── play_serial2_server
            └── main.rs
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    Server

    extern crate serial2;
    
    use serial2::SerialPort;
    use std::time::Duration;
    
    fn main() {
        let mut port = SerialPort::open("/dev/pts/6", 115200).unwrap();
        SerialPort::set_read_timeout(&mut port, Duration::from_millis(10000)).unwrap();
        // SerialPort::discard_buffers(&mut port).unwrap();
        SerialPort::discard_input_buffer(&mut port).unwrap();
        SerialPort::discard_output_buffer(&mut port).unwrap();
        let mut buf = [0; 1024];
        let mut count = 0;
        loop {
            let amt = port.read(&mut buf[..]).unwrap();
            println!(
                "Received {} bytes: {:?}, {}",
                amt,
                &buf[..amt],
                String::from_utf8_lossy(&buf[..amt])
            );
            // buf扩展count
            buf[amt] = count + '0' as u8;
            port.write(&buf[..amt + 1]).unwrap();
            count += 1;
            if count == 10 {
                break;
            }
        }
    }
    
    • 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
    • 30

    Client

    extern crate serial2;
    
    use serial2::SerialPort;
    use std::time::Duration;
    
    fn main() {
        let mut port = SerialPort::open("/dev/pts/7", 115200).unwrap();
        SerialPort::set_read_timeout(&mut port, Duration::from_millis(10000)).unwrap();
        // SerialPort::discard_buffers(&mut port).unwrap();
        SerialPort::discard_input_buffer(&mut port).unwrap();
        SerialPort::discard_output_buffer(&mut port).unwrap();
        let mut buf = [0; 1024];
        let mut count = 0;
        // 发送数据, 引信
        port.write(b"hello").unwrap();
        loop {
            let amt = port.read(&mut buf[..]).unwrap();
            println!(
                "Received {} bytes: {:?}, {}",
                amt,
                &buf[..amt],
                String::from_utf8_lossy(&buf[..amt])
            );
            port.write(&buf[..amt]).unwrap();
            count += 1;
            if count == 10 {
                break;
            }
        }
    }
    
    • 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
    • 30

    测试结果类似上小节, 过程略

    SocketCAN

    在Ubuntu虚拟出一对CAN, vxcan.sh

    #!/bin/sh
    sudo modprobe can_raw
    sudo modprobe vxcan
    
    # 如果 ip link 中存在 vcan0, 就删除 vcan0
    if ip link show can0 > /dev/null 2>&1; then
        sudo ip link set dev can0 down
        sudo ip link set dev vxcan0 down
        sudo ip link delete dev can0 type vxcan
    fi
    
    sudo ip link add dev can0 type vxcan
    sudo ip link set up can0
    sudo ip link set dev vxcan0  up
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    Search Results for ‘can’ - crates.io: Rust Package Registry 可以找找更多的封装can的, 还有很多新特性支持, 如异步, 多线程等, 但似乎没有找到太好用的

    Cargo.toml 中引入socketcan

    [dependencies]
    socketcan = "1.7.0"
    
    • 1
    • 2

    测试代码

    extern crate socketcan;
    
    fn main() {
        println!("Hello, world!");
        // 创建一个socketcan的客户端
        let socket = socketcan::CANSocket::open("can0").unwrap();
        // 创建一个帧, id, data, rtr, err
        let mut frame = socketcan::CANFrame::new(0x123, &[1, 3, 5, 7, 9, 11, 13, 15], false, false).unwrap();
        // 发送帧
        socket.write_frame(&frame).unwrap();
        // 修改id
        frame = socketcan::CANFrame::new(0x1FFFFFFF, &[1, 3, 5, 7, 9, 11, 13, 15], false, false).unwrap();
        // 发送帧
        socket.write_frame(&frame).unwrap();
        // 接收帧
        frame = socket.read_frame().unwrap();
        // 打印帧
        println!("{:?}", frame);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    测试结果

    在这里插入图片描述

    这个毕竟年代久远了, frame中的id, data之类的不是pub, 性能一般, 也不支持CANFD…

    其实不如直接完全照搬SocketCAN的C接口, 这样可以无缝理解, 下面用 libc 重写一下.

    CAN

    libc - Rust (docs.rs)

    $ cargo new play_can
    
    # play_can/Cargo.toml
    [dependencies]
    libc = "0.2.132"
    ifstructs = "0.1.1"
    iptool = "0.1.0"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    下面就把C代码逐行翻译成rust, 参考 main.rs

    extern crate ifstructs;
    extern crate iptool;
    extern crate libc;
    
    use std::ffi::CString;
    
    // can_id: 32 bit, CAN_ID + EFF/RTR/ERR flags
    struct CanFrame {
        can_id: u32,
        can_dlc: u8,
        __pad: u8,
        __res0: u8,
        __res1: u8,
        data: [u8; 8],
    }
    
    fn canopen(ifname: &str) -> i32 {
        let ifname = CString::new(ifname).unwrap();
        unsafe {
            let fd: libc::c_int = libc::socket(libc::AF_CAN, libc::SOCK_RAW, libc::CAN_RAW);
            if fd < 0 {
                println!("socket error");
                std::process::exit(1);
            }
            let mut ifr: ifstructs::ifreq = std::mem::zeroed();
            for i in 0..ifname.as_bytes().len() {
                ifr.ifr_name[i] = ifname.as_bytes()[i];
            }
            libc::ioctl(fd, iptool::SIOCGIFINDEX as libc::c_ulong, &mut ifr);
            let mut addr: libc::sockaddr_can = std::mem::zeroed();
            addr.can_family = libc::AF_CAN as libc::sa_family_t;
            addr.can_ifindex = ifr.ifr_ifru.ifr_ifindex;
            let ret = libc::bind(
                fd,
                &addr as *const libc::sockaddr_can as *const libc::sockaddr,
                std::mem::size_of::<libc::sockaddr_can>() as libc::socklen_t,
            );
            if ret < 0 {
                println!("bind error");
                std::process::exit(1);
            }
            return fd as i32;
        }
    }
    
    fn canclose(fd: i32) -> i32 {
        unsafe {
            return libc::close(fd as libc::c_int);
        }
    }
    
    fn canwrite(fd: i32, frame: &mut CanFrame) -> i32 {
        unsafe {
            let ret = libc::write(
                fd as libc::c_int,
                frame as *mut CanFrame as *const libc::c_void,
                std::mem::size_of::<CanFrame>() as libc::size_t,
            );
            return ret as i32;
        }
    }
    
    fn canread(fd: i32, frame: &mut CanFrame) -> i32 {
        unsafe {
            let ret = libc::read(
                fd as libc::c_int,
                frame as *mut CanFrame as *mut libc::c_void,
                std::mem::size_of::<CanFrame>() as libc::size_t,
            );
            return ret as i32;
        }
    }
    
    fn main() {
        let fd = canopen("can0");
        let mut frame = CanFrame {
            can_id: 0x123,
            can_dlc: 8,
            __pad: 0,
            __res0: 0,
            __res1: 0,
            data: [0; 8],
        };
        canwrite(fd, &mut frame);
        canread(fd, &mut frame);
        println!(
            "can_id: {:08x}, can_dlc: {}, __pad: {}, __res0: {}, __res1: {}, data: {:02x?}",
            frame.can_id, frame.can_dlc, frame.__pad, frame.__res0, frame.__res1, frame.data
        );
        canclose(fd);
    }
    
    // $ cansend vxcan0 12345678#11.22
    // can_id: 92345678, can_dlc: 2, __pad: 0, __res0: 0, __res1: 0, data: [11, 22, 00, 00, 00, 00, 00, 00]
    
    
    • 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
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95

    测试

    $ candump -td -x any
    
    $ cargo build
    $ ./target/debug/play_can
    
    $ cansend vxcan0 12345678#11.22
    # 因为是扩展帧, can_id 最高位置1, 所以can_id是92345678
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    如图

    在这里插入图片描述

    CANFD

    $ cargo new play_canfd
    
    # play_canfd/Cargo.toml
    [dependencies]
    libc = "0.2.132"
    ifstructs = "0.1.1"
    iptool = "0.1.0"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    把C代码逐行翻译成rust, 参考 canfd.rs

    extern crate ifstructs;
    extern crate iptool;
    extern crate libc;
    
    use std::ffi::CString;
    
    pub struct CanfdFrame {
        can_id: u32,
        len: u8,
        flags: u8,
        __res0: u8,
        __res1: u8,
        data: [u8; 64],
    }
    
    pub fn open(ifname: &str) -> i32 {
        let ifname = CString::new(ifname).unwrap();
        unsafe {
            let fd: libc::c_int = libc::socket(libc::AF_CAN, libc::SOCK_RAW, libc::CAN_RAW);
            if fd < 0 {
                println!("socket error");
                std::process::exit(1);
            }
            let mut ifr: ifstructs::ifreq = std::mem::zeroed();
            for i in 0..ifname.as_bytes().len() {
                ifr.ifr_name[i] = ifname.as_bytes()[i];
            }
            libc::ioctl(fd, iptool::SIOCGIFINDEX as libc::c_ulong, &mut ifr);
            let mut addr: libc::sockaddr_can = std::mem::zeroed();
            addr.can_family = libc::AF_CAN as libc::sa_family_t;
            addr.can_ifindex = ifr.ifr_ifru.ifr_ifindex;
            // canfd support
            let mut canfd_on: libc::c_int = 1;
            libc::setsockopt(
                fd,
                libc::SOL_CAN_RAW,
                libc::CAN_RAW_FD_FRAMES,
                &mut canfd_on as *mut libc::c_int as *mut libc::c_void,
                std::mem::size_of::<libc::c_int>() as libc::socklen_t,
            );
            let ret = libc::bind(
                fd,
                &addr as *const libc::sockaddr_can as *const libc::sockaddr,
                std::mem::size_of::<libc::sockaddr_can>() as libc::socklen_t,
            );
            if ret < 0 {
                println!("bind error");
                std::process::exit(1);
            }
            return fd as i32;
        }
    }
    
    pub fn close(fd: i32) -> i32 {
        unsafe {
            return libc::close(fd as libc::c_int);
        }
    }
    
    pub fn write(fd: i32, frame: &mut CanfdFrame) -> i32 {
        unsafe {
            let ret = libc::write(
                fd as libc::c_int,
                frame as *mut CanfdFrame as *const libc::c_void,
                std::mem::size_of::<CanfdFrame>() as libc::size_t,
            );
            return ret as i32;
        }
    }
    
    pub fn read(fd: i32, frame: &mut CanfdFrame) -> i32 {
        unsafe {
            let ret = libc::read(
                fd as libc::c_int,
                frame as *mut CanfdFrame as *mut libc::c_void,
                std::mem::size_of::<CanfdFrame>() as libc::size_t,
            );
            return ret as i32;
        }
    }
    
    pub fn test() {
        let fd = open("can0");
        let mut frame = CanfdFrame {
            can_id: 0x123,
            len: 64,
            flags: 0,
            __res0: 0,
            __res1: 0,
            data: [0; 64],
        };
        write(fd, &mut frame);
        read(fd, &mut frame);
        println!(
            "can_id: {:08x}, can_dlc: {}, __pad: {}, __res0: {}, __res1: {}, data: {:02x?}",
            frame.can_id, frame.len, frame.flags, frame.__res0, frame.__res1, frame.data
        );
        close(fd);
    }
    
    
    • 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
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100

    然后是 main.rs

    mod canfd;
    
    fn main() {
        canfd::test();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    测试

    $ candump -td -x any
    
    $ cargo build
    $ ./target/debug/play_canfd
    
    $ cansend vxcan0 12345678##3.11.22.33.44.55.66.77.88.99.AA.BB.CC.DD.EE.FF
    # 因为是扩展帧, can_id 最高位置1, 所以can_id是92345678
    # B, BRS
    # E, ESI
    # 15字节自动扩充到canfd的16字节
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    如图

    在这里插入图片描述

    除了直接翻译C以外, 另一种流行的思路是既然C底层受众广, 很多也比较稳, 那就直接FFI的方式, 让rust用C的头文件和编译好的静态库好了, 如用 bindgen, 完成了很多自动化的工作, 简单粗暴又高效.

    Github

    rust_note/playground at main · weifengdq/rust_note (github.com)

    欢迎扫描二维码关注微信公众号, 及时获取最新文章:
    在这里插入图片描述

  • 相关阅读:
    kafka ack确认机制
    Unity和UE4两大游戏引擎,你该如何选择?
    振弦采集模块UART 通讯协议
    HTB-Irked
    软件测试 接口测试 Jmeter 5.5 安装教程
    C++11 线程同步接口std::condition_variable和std::future的简单使用
    【Vue十日谈】浅谈vuex
    Field ‘id‘ doesn‘t have a default value错误解决办法
    Java EE——线程池
    单独编译某个驱动模块
  • 原文地址:https://blog.csdn.net/weifengdq/article/details/126393809