Rust USB设备串行通信库usbd-serial的使用,实现嵌入式USB CDC-ACM虚拟串口功能

Rust USB设备串行通信库usbd-serial的使用,实现嵌入式USB CDC-ACM虚拟串口功能

usbd-serial简介

CDC-ACM USB串口实现库,用于usb-device。CDC-ACM是一种USB类,大多数操作系统都内置支持,用于实现调制解调器和通用串行端口。SerialPort类实现了类似流的缓冲串行端口,可以像普通UART一样使用。

该库还包含CdcAcmClass,这是一个更低级别的实现,开销更小,但需要更谨慎地使用。

示例代码

完整示例需要使用硬件驱动,但硬件独立部分如下:

let mut serial = SerialPort::new(&usb_bus);

let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
    .product("Serial port")
    .device_class(USB_CLASS_CDC)
    .build();

loop {
    if !usb_dev.poll(&mut [&mut serial]) {
        continue;
    }

    let mut buf = [0u8; 64];

    match serial.read(&mut buf[..]) {
        Ok(count) => {
            // count字节被读取到&buf[..count]
        },
        Err(UsbError::WouldBlock) => // 没有接收到数据
        Err(err) => // 发生错误
    };

    match serial.write(&[0x3a, 0x29]) {
        Ok(count) => {
            // count字节被写入
        },
        Err(UsbError::WouldBlock) => // 无法写入数据(缓冲区已满)
        Err(err) => // 发生错误
    };
}

完整示例代码

以下是一个更完整的嵌入式USB CDC-ACM虚拟串口实现示例:

use usb_device::prelude::*;
use usbd_serial::{SerialPort, USB_CLASS_CDC};

fn main() {
    // 假设usb_bus已经初始化(具体实现取决于硬件平台)
    let usb_bus = /* 初始化USB总线 */;
    
    // 创建串口实例
    let mut serial = SerialPort::new(&usb_bus);

    // 构建USB设备
    let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
        .manufacturer("Rust Embedded")
        .product("USB Serial Port")
        .serial_number("12345678")
        .device_class(USB_CLASS_CDC)
        .build();

    // 主循环
    loop {
        // 轮询USB设备
        if !usb_dev.poll(&mut [&mut serial]) {
            continue;
        }

        // 读取缓冲区
        let mut buf = [0u8; 64];
        
        // 尝试读取数据
        match serial.read(&mut buf) {
            Ok(count) => {
                if count > 0 {
                    // 处理接收到的数据
                    // 这里可以添加数据处理逻辑,例如回显
                    serial.write(&buf[..count]).ok();
                }
            },
            Err(UsbError::WouldBlock) => {
                // 没有数据可读,继续等待
            },
            Err(e) => {
                // 处理其他错误
            }
        };

        // 这里可以添加发送数据的逻辑
        // 例如:serial.write(b"Hello World").ok();
    }
}

使用说明

  1. 首先需要在Cargo.toml中添加依赖:
usbd-serial = "0.2.2"
  1. 实现需要根据具体的硬件平台初始化USB总线

  2. 该库支持两种工作模式:

    • SerialPort: 高级缓冲串口模式,使用更简单
    • CdcAcmClass: 低级模式,提供更多控制但需要更谨慎使用
  3. 典型应用场景包括:

    • 嵌入式设备与PC通信
    • 调试输出
    • 设备固件升级(DFU)

注意事项

  1. 需要配合具体的硬件平台USB实现使用
  2. 需要正确设置VID/PID(示例中使用了测试用ID)
  3. 在嵌入式环境中使用时需要考虑内存限制
  4. 错误处理应完善,特别是对于嵌入式应用

该库是Rust嵌入式生态系统中实现USB虚拟串口的常用选择,特别适合需要与现有串口工具兼容的项目。


1 回复

Rust USB设备串行通信库usbd-serial的使用指南

简介

usbd-serial 是一个用于Rust的USB设备串行通信库,它实现了USB CDC-ACM类(通信设备类抽象控制模型),允许嵌入式设备通过USB模拟串行端口(虚拟COM端口)。

主要特性

  • 实现USB CDC-ACM类规范
  • 支持全速(Full Speed)和高速(High Speed)USB
  • 提供简单的串行接口抽象
  • usb-device框架兼容

使用方法

1. 添加依赖

首先在Cargo.toml中添加依赖:

[dependencies]
usb-device = "0.2"
usbd-serial = "0.1"

2. 基本设置

use usb_device::prelude::*;
use usbd_serial::{SerialPort, USB_CLASS_CDC};

// 假设你已经有了USB设备外设
let usb_bus = UsbBus::new(peripheral);

// 创建串行端口设备
let mut serial = SerialPort::new(&usb_bus);

// 创建USB设备
let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
    .manufacturer("My Company")
    .product("Serial port")
    .serial_number("123456")
    .device_class(USB_CLASS_CDC)
    .build();

3. 轮询USB设备

在应用程序的主循环中,需要定期轮询USB设备:

loop {
    // 必须定期轮询USB堆栈
    usb_dev.poll(&mut [&mut serial]);
    
    // 处理接收数据
    let mut buf = [0u8; 64];
    match serial.read(&mut buf) {
        Ok(count) => {
            if count > 0 {
                // 处理接收到的数据
                // 例如回显数据
                serial.write(&buf[..count]).ok();
            }
        },
        Err(_) => {}
    }
    
    // 其他应用逻辑...
}

4. 发送数据

let data = b"Hello, world!\r\n";
serial write(data).expect("Failed to send data");

5. 接收数据

let mut buf = [0u8; 64];
match serial.read(&mut buf) {
    Ok(count) => {
        if count > 0 {
            println!("Received {} bytes: {:?}", count, &buf[..count]);
        }
    },
    Err(e) => eprintln!("Read error: {:?}", e),
}

完整示例

下面是一个完整的嵌入式USB CDC-ACM示例:

#![no_std]
#![no_main]

use panic_halt as _;
use cortex_m_rt::entry;
use stm32f1xx_hal::{prelude::*, stm32};

use usb_device::prelude::*;
use stm32f1xx_hal::usb::{Peripheral, UsbBus};
use usbd_serial::{SerialPort, USB_CLASS_CDC};

#[entry]
fn main() -> ! {
    let dp = stm32::Peripherals::take().unwrap();
    
    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();
    
    let clocks = rcc.cfgr
        .sysclk(48.mhz())
        .pclk1(24.mhz())
        .freeze(&mut flash.acr);
    
    let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
    
    let usb_bus = UsbBus::new(
        Peripheral {
            usb: dp.USB,
            pin_dm: gpioa.pa11,
            pin_dp: gpioa.pa12,
        },
        &clocks,
    );
    
    let mut serial = SerialPort::new(&usb_bus);
    
    let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
        .manufacturer("Fake company")
        .product("Serial port")
        .serial_number("TEST")
        .device_class(USB_CLASS_CDC)
        .build();
    
    loop {
        usb_dev.poll(&mut [&mut serial]);
        
        let mut buf = [0u8; 64];
        match serial.read(&mut buf) {
            Ok(count) => {
                if count > 0 {
                    // 回显接收到的数据
                    serial.write(&buf[..count]).ok();
                }
            },
            Err(_) => {}
        }
    }
}

注意事项

  1. 需要正确配置USB时钟(通常是48MHz)
  2. 主机端需要安装适当的CDC-ACM驱动程序
  3. 在Linux上,设备通常会显示为/dev/ttyACMx
  4. 在Windows上,可能需要安装.inf文件或自动识别为COM端口

高级功能

设置波特率

虽然USB CDC-ACM是虚拟串口,不依赖实际波特率,但可以通知主机"建议"的波特率:

serial.set_baudrate(115200).unwrap();

控制线路状态

可以获取和设置DTR/RTS等控制线路状态:

if serial.dtr() {
    // 数据终端就绪
}

serial.set_rts(true).unwrap();  // 设置RTS

中断处理

对于更高效的处理,可以在USB中断中处理数据:

#[interrupt]
fn USB_HP_CAN_TX() {
    usb_dev.poll(&mut [&mut serial]);
}

总结

usbd-serial为Rust嵌入式开发提供了简单的USB虚拟串口功能,使得与主机的通信变得简单。通过CDC-ACM类,设备可以像标准串口一样被大多数操作系统识别和使用,而无需特殊驱动程序。

回到顶部