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();
}
}
使用说明
- 首先需要在Cargo.toml中添加依赖:
usbd-serial = "0.2.2"
-
实现需要根据具体的硬件平台初始化USB总线
-
该库支持两种工作模式:
- SerialPort: 高级缓冲串口模式,使用更简单
- CdcAcmClass: 低级模式,提供更多控制但需要更谨慎使用
-
典型应用场景包括:
- 嵌入式设备与PC通信
- 调试输出
- 设备固件升级(DFU)
注意事项
- 需要配合具体的硬件平台USB实现使用
- 需要正确设置VID/PID(示例中使用了测试用ID)
- 在嵌入式环境中使用时需要考虑内存限制
- 错误处理应完善,特别是对于嵌入式应用
该库是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(_) => {}
}
}
}
注意事项
- 需要正确配置USB时钟(通常是48MHz)
- 主机端需要安装适当的CDC-ACM驱动程序
- 在Linux上,设备通常会显示为
/dev/ttyACMx
- 在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类,设备可以像标准串口一样被大多数操作系统识别和使用,而无需特殊驱动程序。