RUST 环境 UDP UART CANFD

测试环境

WSL2
Ubuntu 22.04
Kernel 5.15.57.1
cargo  1.63.0 (不定期更新)

RUST 安装

# 安装
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 版本检查
# 第一句可以放到 ~/.bashrc里面
source "$HOME/.cargo/env"
cargo --version
rustc --version

# 更新
rustup update

VSCode 配置

插件:

新工程

cargo new playground
cd playground
cargo run

可执行文件的路径为

playground/target/debug/playground

# 查看文件大小发现有3.xM, 应该是打包进了rust的运行时之类的
$ ls -lh target/debug/playground

# 编译release版本, 会小一点
$ cargo build --release
$ ls -lh target/release/playground

# 还有其它减少体积的方式, 可自行搜索

# 清理
$ cargo clean

args 命令行参数传入

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

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

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

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

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

测试

$ 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"]

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

    for i in env::args() {
        println!("{}", i);
    }

参考 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"]

还有一种 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();
}

参考 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!

第二种方式举例

# 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!

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
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]

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();
}

运行结果

$ ./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!

多生产-单消费

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);
    }
}

运行

$ ./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

关于 多生产者、单消费者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

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;
        }
    }
}

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;
        }
    }
}

运行

$ cargo build

# 先运行server, 再运行client
$ ./target/debug/play_udp_server 
$ ./target/debug/play_udp_client

(半)链式反应

在这里插入图片描述

库引入

三种库:

  • 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 

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)

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

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

rust工程引入serial, 在Cargo.toml

[dependencies]
serial = "0.4.0"

类似上面的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

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;
        }
    }
}

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;
        }
    }
}

运行

$ 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 

在这里插入图片描述

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

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;
        }
    }
}

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;
        }
    }
}

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

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

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

Cargo.toml 中引入socketcan

[dependencies]
socketcan = "1.7.0"

测试代码

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);
}

测试结果

在这里插入图片描述

这个毕竟年代久远了, 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"

下面就把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]

测试

$ candump -td -x any

$ cargo build
$ ./target/debug/play_can

$ cansend vxcan0 12345678#11.22
# 因为是扩展帧, can_id 最高位置1, 所以can_id是92345678

如图

在这里插入图片描述

CANFD

$ cargo new play_canfd

# play_canfd/Cargo.toml
[dependencies]
libc = "0.2.132"
ifstructs = "0.1.1"
iptool = "0.1.0"

把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);
}

然后是 main.rs

mod canfd;

fn main() {
    canfd::test();
}

测试

$ 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字节

如图

在这里插入图片描述

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

Github

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

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

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值