V-USB 数据收发实现分析
通过深入分析 V-USB 的源代码,详细解释数据收发部分的实现机制。V-USB 的数据传输实现分为硬件层面的信号处理和软件层面的协议处理两个部分。
1. 数据结构和全局变量
核心数据缓冲区
// 接收缓冲区 - 存储原始USB数据包
uchar usbRxBuf[2*USB_BUFSIZE]; /* PID + 8字节数据 + 2字节CRC */
uchar usbInputBufOffset; /* 接收缓冲区偏移量 */
volatile schar usbRxLen; /* 接收数据长度,0表示空闲,-1表示流控 */
// 发送缓冲区
volatile uchar usbTxLen = USBPID_NAK; /* 发送数据长度或握手令牌 */
uchar usbTxBuf[USB_BUFSIZE]; /* 发送数据缓冲区 */
// 中断端点状态
usbTxStatus_t usbTxStatus1; /* 中断端点1状态 */
usbTxStatus_t usbTxStatus3; /* 中断端点3状态 */
消息传输状态
usbMsgPtr_t usbMsgPtr; /* 待传输数据指针(ROM或RAM) */
static usbMsgLen_t usbMsgLen; /* 剩余传输字节数 */
static uchar usbMsgFlags; /* 标志位 */
#define USB_FLG_MSGPTR_IS_ROM (1<<6) /* 数据在ROM中 */
#define USB_FLG_USE_USER_RW (1<<7) /* 使用用户读写函数 */
2. 数据接收实现
2.1 硬件层接收 (汇编实现)
在 中实现了精确的时序控制:
; USB中断向量入口
USB_INTR_VECTOR:
push YL ; 快速保存寄存器
in YL, SREG
push YL
push YH
; 同步检测
waitForJ:
inc YL
sbis USBIN, USBMINUS ; 等待J状态
brne waitForJ
waitForK:
sbis USBIN, USBMINUS ; 检测K状态边沿
rjmp foundK
; 数据接收循环
rxByteLoop:
in x1, USBIN ; 采样USB信号
andi x1, USBMASK ; 屏蔽无关位
eor x2, x1 ; 差分解码
ror shift ; 移位重构数据
st y+, x3 ; 存储到缓冲区
关键特性:
- 精确时序:每个指令周期都经过精确计算,确保在正确时间采样
- 位填充处理:自动检测和移除USB位填充
- 差分解码:将USB差分信号转换为数字数据
- CRC校验:硬件级CRC计算和验证
2.2 软件层接收处理
函数处理接收到的数据:
static inline void usbProcessRx(uchar *data, uchar len)
{
usbRequest_t *rq = (void *)data;
// 根据令牌类型处理
if(usbRxToken == (uchar)USBPID_SETUP){
// SETUP包处理
usbTxBuf[0] = USBPID_DATA0; // 初始化数据切换
usbTxLen = USBPID_NAK; // 中止待发送传输
// 区分标准请求和类/厂商请求
uchar type = rq->bmRequestType & USBRQ_TYPE_MASK;
if(type != USBRQ_TYPE_STANDARD){
replyLen = usbFunctionSetup(data); // 用户处理
}else{
replyLen = usbDriverSetup(rq); // 驱动处理
}
}else{
// OUT数据包处理
if(usbMsgFlags & USB_FLG_USE_USER_RW){
uchar rval = usbFunctionWrite(data, len);
if(rval == 0xff) usbTxLen = USBPID_STALL;
}
}
}
3. 数据发送实现
3.1 发送数据准备
函数构建发送数据包:
static inline void usbBuildTxBlock(void)
{
usbMsgLen_t wantLen = usbMsgLen;
if(wantLen > 8) wantLen = 8; // USB低速最大8字节
usbMsgLen -= wantLen;
usbTxBuf[0] ^= USBPID_DATA0 ^ USBPID_DATA1; // DATA0/DATA1切换
// 读取数据
len = usbDeviceRead(usbTxBuf + 1, wantLen);
if(len <= 8){
usbCrc16Append(&usbTxBuf[1], len); // 添加CRC
len += 4; // 包含同步字节长度
if(len < 12) usbMsgLen = USB_NO_MSG; // 短包表示传输结束
}else{
len = USBPID_STALL; // 错误时发送STALL
}
usbTxLen = len;
}
3.2 数据读取机制
函数支持多种数据源:
static uchar usbDeviceRead(uchar *data, uchar len)
{
if(usbMsgFlags & USB_FLG_USE_USER_RW){
// 用户自定义读取函数
len = usbFunctionRead(data, len);
}else{
// 从ROM或RAM读取
usbMsgPtr_t r = usbMsgPtr;
if(usbMsgFlags & USB_FLG_MSGPTR_IS_ROM){
// ROM数据读取
do{
uchar c = USB_READ_FLASH(r);
*data++ = c;
r++;
}while(--i);
}else{
// RAM数据读取
do{
*data++ = *((uchar *)r);
r++;
}while(--i);
}
usbMsgPtr = r;
}
return len;
}
3.3 硬件层发送 (汇编实现)
发送过程同样在汇编中实现精确时序:
usbSendAndReti:
in x2, USBDDR ; 获取总线控制
ori x2, USBMASK
out USBDDR, x2 ; 占用总线
txByteLoop:
ldi bitcnt, 0x35 ; 位计数器
txBitLoop:
sbrs shift, 0 ; 检查要发送的位
eor x1, x4 ; 切换输出状态
out USBOUT, x1 ; 输出到USB引脚
ror shift ; 移位到下一位
; 位填充检测
cpi x2, 0xfc
brcc bitstuffN ; 需要位填充
; SE0结束包
cbr x1, USBMASK ; 准备SE0状态
out USBOUT, x1 ; 输出SE0
4. 主循环协调
函数协调整个收发过程:
USB_PUBLIC void usbPoll(void)
{
schar len = usbRxLen - 3;
// 处理接收数据
if(len >= 0){
usbProcessRx(usbRxBuf + USB_BUFSIZE + 1 - usbInputBufOffset, len);
usbRxLen = 0; // 标记缓冲区可用
}
// 处理发送数据
if(usbTxLen & 0x10){ // 发送系统空闲
if(usbMsgLen != USB_NO_MSG){ // 有待发送数据
usbBuildTxBlock();
}
}
// USB复位检测
for(i = 20; i > 0; i--){
uchar usbLineStatus = USBIN & USBMASK;
if(usbLineStatus != 0) goto isNotReset;
}
// 处理复位状态
usbNewDeviceAddr = 0;
usbDeviceAddr = 0;
usbResetStall();
}
5. 关键技术特点
5.1 时序精确控制
- 汇编优化:关键路径用汇编实现,确保周期级精度
- 中断驱动:D+连接到INT0,实现快速响应
- 时钟同步:通过USB同步模式实现时钟恢复
5.2 数据完整性
- CRC校验:硬件级CRC16计算和验证
- 位填充:自动处理USB位填充机制
- 数据切换:DATA0/DATA1包序列号管理
5.3 内存管理
- 双缓冲:接收使用双缓冲区避免数据丢失
- 零拷贝:直接从ROM/RAM发送,减少内存拷贝
- 流控制:通过NAK/STALL实现流量控制
5.4 协议栈分层
- 物理层:汇编实现的信号处理
- 数据链路层:包格式化和CRC处理
- 传输层:端点管理和数据切换
- 应用层:用户回调函数接口
V-USB 通过这种精心设计的分层架构,成功地用软件模拟了USB设备的硬件功能,是嵌入式系统中软件模拟硬件的经典范例。