ARM RL-USB协议栈采用典型的三层架构设计,这种分层结构在嵌入式USB开发中具有显著优势。最底层是设备控制器驱动层(Device Controller Driver),直接与硬件交互。这一层包含关键的中断服务例程(USB_ISR),负责处理USB控制器产生的硬件中断。当硬件接收到主机数据包或主机请求设备数据时,USB_ISR会分析中断类型并将相应事件传递到上层。
中间层是USB核心驱动层(USB Core Driver),这是协议栈的"大脑"。该层实现了USB协议的核心功能,包括:
最上层是功能驱动层(Function Driver),实现特定设备类的功能。RL-USB目前支持三大类设备:
典型的RL-USB初始化流程包含以下关键步骤:
c复制// 示例:USB初始化代码片段
void USB_Init (void) {
USB_ClkInit(); // 时钟初始化
USB_IOConfig(); // IO配置
USB_ResetCore(); // 核心复位
USB_EnableInt(); // 中断使能
os_tsk_create(USB_Task, USB_PRIORITY); // 创建USB主任务
}
RL-USB支持四种传输类型,每种类型对应不同的应用场景:
| 传输类型 | 最大包大小 | 典型应用 | 特点 |
|---|---|---|---|
| 控制传输 | 64字节 | 设备枚举、配置 | 双向传输,高优先级 |
| 中断传输 | 64字节 | HID设备 | 周期性,保证延迟 |
| 批量传输 | 512字节 | 大容量存储 | 大数据量,无带宽保证 |
| 等时传输 | 1024字节 | 音频设备 | 固定带宽,无错误重传 |
注意:实际包大小取决于具体USB控制器和速度模式(全速/高速)
RL-USB开发需要以下工具链:
工程配置关键点:
usbcfg.h是核心配置文件,主要参数包括:
c复制#define USB_VENDOR_ID 0x1234 // 厂商ID
#define USB_PRODUCT_ID 0x5678 // 产品ID
#define USB_MAX_PACKET0 64 // 端点0最大包大小
#define USB_EP_NUM 4 // 端点总数(含端点0)
// 端点事件配置
#define USB_EP_EVENT USB_EVENT_ENABLE(1) | \
USB_EVENT_ENABLE(2) | \
USB_EVENT_ENABLE(3)
常见问题排查:
不同ARM芯片的USB控制器差异需要特别关注:
LPC214x系列:
LPC23xx系列:
AT91SAM7S系列:
HID设备的描述符结构较为复杂,包含多个层级:
c复制const U8 USB_HID_ReportDescriptor[] = {
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined)
0x09, 0x01, // Usage (Vendor Defined)
0xA1, 0x01, // Collection (Application)
0x09, 0x02, // Usage (Vendor Defined)
0x15, 0x00, // Logical Minimum (0)
0x26, 0xFF, 0x00, // Logical Maximum (255)
0x75, 0x08, // Report Size (8)
0x95, 0x40, // Report Count (64)
0x81, 0x02, // Input (Data,Var,Abs)
0x09, 0x03, // Usage (Vendor Defined)
0x91, 0x02, // Output (Data,Var,Abs)
0xC0 // End Collection
};
关键参数说明:
HID设备通常使用中断传输,配置示例:
c复制const U8 USB_ConfigDescriptor[] = {
// ... 接口描述符
USB_ENDPOINT_DESC_SIZE,
USB_ENDPOINT_DESCRIPTOR_TYPE,
USB_ENDPOINT_IN(1), // 端点1 IN
USB_ENDPOINT_TYPE_INTERRUPT,
WBVAL(0x0040), // 包大小64字节
0x20, // 轮询间隔32ms
// ... 其他描述符
};
c复制void USB_EndPoint1 (void) __task {
U16 evt;
for (;;) {
os_evt_wait_or(USB_EVT_IN | USB_EVT_OUT, 0xFFFF);
evt = os_evt_get();
if (evt & USB_EVT_IN) {
// 准备发送数据
USB_WriteEP(0x81, &report_data, sizeof(report_data));
}
if (evt & USB_EVT_OUT) {
// 处理接收数据
USB_ReadEP(0x01, &received_data);
ProcessHIDData(received_data);
}
}
}
Windows系统自带HID类驱动,但开发者通常需要实现上位机应用。常用通信方式:
Windows API:
HIDAPI跨平台库:
c复制// 示例:使用HIDAPI读取数据
hid_device *handle = hid_open(0x1234, 0x5678, NULL);
if (handle) {
unsigned char buf[65]; // 第一个字节为报告ID
int res = hid_read(handle, buf, sizeof(buf));
// 处理数据...
hid_close(handle);
}
USB音频设备需要实现多个接口描述符:
音频控制接口:
音频流接口:
典型描述符结构:
c复制const U8 USB_ConfigDescriptor[] = {
// 标准配置描述符
// 音频控制接口
USB_INTERFACE_DESC_SIZE,
USB_INTERFACE_DESCRIPTOR_TYPE,
0x00, // 接口号
0x00, // 备用设置号
0x01, // 端点数量
USB_DEVICE_CLASS_AUDIO,
AUDIO_SUBCLASS_CONTROL,
AUDIO_PROTOCOL_UNDEFINED,
0x00, // 接口字符串索引
// 音频控制端点
// 音频流接口
// 音频流端点
};
音频设备使用等时传输保证实时性,关键配置:
c复制USB_ENDPOINT_DESC_SIZE,
USB_ENDPOINT_DESCRIPTOR_TYPE,
USB_ENDPOINT_IN(2), // 端点2 IN
USB_ENDPOINT_TYPE_ISOCHRONOUS,
WBVAL(0x0180), // 包大小384字节(48kHz 16bit立体声)
0x01, // 每ms一个微帧
c复制void Audio_SendData(const int16_t *pcm_data, uint32_t len) {
uint32_t packets = len / AUDIO_PACKET_SIZE;
for (uint32_t i = 0; i < packets; i++) {
while (!USB_EP_IsReady(2)); // 等待端点就绪
USB_WriteEP(0x82, &pcm_data[i*AUDIO_PACKET_SIZE],
AUDIO_PACKET_SIZE);
}
}
音频设备需要保持与主机的时钟同步,RL-USB提供两种方式:
自适应同步:
异步同步:
反馈端点配置示例:
c复制USB_ENDPOINT_DESC_SIZE,
USB_ENDPOINT_DESCRIPTOR_TYPE,
USB_ENDPOINT_IN(3), // 端点3 IN
USB_ENDPOINT_TYPE_ISOCHRONOUS,
WBVAL(0x0003), // 包大小3字节
0x04, // 每4ms报告一次
RL-USB实现的是Bulk-Only Transport协议,架构如下:
命令块包装器(CBW):
命令状态包装器(CSW):
数据传输阶段:
核心SCSI命令处理流程:
c复制void MSC_HandleCommand(void) {
CBW cbw;
CSW csw;
// 1. 接收CBW
USB_ReadEP(MSC_EP_OUT, &cbw, sizeof(CBW));
// 2. 解析命令
switch (cbw.CB[0]) {
case SCSI_READ_10:
HandleRead10(&cbw);
break;
case SCSI_WRITE_10:
HandleWrite10(&cbw);
break;
// ...其他命令处理
default:
csw.bStatus = CSW_FAILED;
}
// 3. 发送CSW
USB_WriteEP(MSC_EP_IN, &csw, sizeof(CSW));
}
RL-USB需要开发者实现底层存储接口:
c复制const MSC_MEDIA msc_media = {
.Init = FLASH_Init,
.GetInfo = FLASH_GetInfo,
.Read = FLASH_Read,
.Write = FLASH_Write,
.IsReady = FLASH_IsReady,
.IsWriteProtected = FLASH_IsWriteProtected
};
典型Flash实现要点:
启用DMA可以显著提升吞吐量,配置步骤:
c复制#define USB_DMA_ENABLE 1
#define USB_DMA_RX_BD_NUM 4 // 接收BD数量
#define USB_DMA_TX_BD_NUM 4 // 发送BD数量
c复制__align(32) uint8_t ep1_dma_tx_buf[2][1024]; // 双缓冲
c复制USB_DMA_ConfigEP(EP1_IN, ep1_dma_tx_buf[0], 1024, USB_DMA_DOUBLE_BUFFER);
设备无法枚举:
数据传输不稳定:
音频设备爆音:
带宽测试:
延迟测试:
稳定性测试:
实际项目中,我们发现在LPC2148上实现HID设备时,将端点1的轮询间隔从32ms降低到8ms可以将输入延迟从35ms减少到12ms,但会增加约15%的CPU负载。这种权衡需要根据具体应用场景决定。