FPGA 实现 USB 1.1 控制器:HID 键盘集成实战指南
在 FPGA 上自制 USB 控制器,需要深入理解协议细节和硬件特性。本指南详细讲解如何实现 USB 1.1 控制器并连接键盘,涵盖核心模块、设备初始化以及 HID 描述符处理。
USB 1.1 硬件特性
与 USB 2.0 不同,USB 1.1 只用两条数字线(D+ 和 D-),采用差分模式,可直接连接到 FPGA 引脚。它支持两种速率:低速(1.5 Mbps)和全速(12 Mbps)。设备在连接时通过拉高 D-(低速)或 D+(全速)来指示速率。
关键硬件注意事项:
- 主机侧数据线需接地拉低电阻。
- 用 3.3V 稳压二极管保护 FPGA 输入免受 5V 尖峰影响。
- 测试时,可用 STM32 BluePill 等开发板模拟 HID 设备。
USB 控制器模块架构
控制器拆分成互连模块,每个模块负责 USB 协议栈的具体任务。
- M_DATA — 数据收发核心模块。
- M_CRC16_USB — 计算数据包 CRC16 校验和。
- M_GET_PACKET — 处理数据接收:发送 IN 令牌、接收数据包、校验 CRC 并发送 ACK。
- M_RECEIVE_MODULE — 数据接收模块,包括:
* M_GET_DATA — 从总线读取原始数据。
* M_MEMORY_BUF_CRC16 — 实时计算 CRC16 的缓冲区。
* M_BUF_RETRANSLATOR — CRC 校验成功后转发缓冲区数据。
- M_SEND_PACKET — 管理数据发送:OUT 令牌、数据发送并等待 ACK。
- M_TRANSMIT_MODULE — 发送数据、令牌、帧起始(SOF)包和握手包。
- M_CRC_5 — 计算令牌、SOF 和握手的 CRC5。
- M_SEND_DATA — 发送带 CRC16 的数据包。
- M_SEND_TOKEN — 发送令牌、SOF 和握手(均带 CRC5)。
- M_SEND_END_OF_PACKET — 生成数据包结束(EOP)信号。
- M_DATA_TRANSFER — 总线上的比特传输物理层。
- M_SOF_SENDER — 定期发送 SOF 包以同步时钟。
- M_MAIN_AUTOMAT — 控制整个控制器的主状态机。
- M_USB_INIT — 处理 USB 设备初始化。
- M_TRANSACTION — 处理控制传输。
- M_HID_ANALYZER — 解析 HID 描述符以理解数据格式。
- MEMORY_BUF (4 个实例) — 用于 HID 分析的数据缓冲区。
设备初始化流程
初始化从检测 D+ 和 D- 状态确定速率开始。然后主状态机(M_MAIN_AUTOMAT)执行精确序列。
always @(posedge clk)
begin
if (rst)
begin
State_main <= S_IDLE;
SOF_En <= 1'b0;
FullSpeedConnect <= 1'b0;
// ... 重置其他信号
end
else
begin
case (State_main)
S_IDLE:
// 等待连接和速率检测
if (Dm & !Dp) // 低速
begin
FullSpeedConnect <= 1'b0;
ResetTime <= 15_000; // 1.5 MHz 下 10 ms
// ... 设置低速定时器
State_main <= S_POWER_RISE;
end
else if(Dp & !Dm) // 全速
begin
FullSpeedConnect <= 1'b1;
ResetTime <= 120_000; // 12 MHz 下 10 ms
// ... 设置全速定时器
State_main <= S_POWER_RISE;
end
S_POWER_RISE:
// 等待 100 ms 电源稳定
if (WaiteCount == PowerRiseTime || SKIP_POWER_RISE)
begin
WaiteCount <= 0;
State_main <= S_USB_RESET;
end
S_USB_RESET:
// 设备复位(D+ 和 D- 拉低 10 ms)
if (WaiteCount == ResetTime || SKIP_POWER_RISE)
begin
WaiteCount <= 0;
State_main <= S_INIT_SOF_SENDER;
end
S_INIT_SOF_SENDER:
// 复位后立即启动 SOF 包
if (Eof1) State_main <= S_WAITE_SOF;
else SOF_En <= 1'b1;
S_WAITE_SOF:
if (!Eof1) State_main <= S_USB_RESET_RECOWERY;
S_USB_RESET_RECOWERY:
// 复位后恢复(10 ms)
if (WaiteCount == ResetTime || SKIP_POWER_RISE)
begin
WaiteCount <= 0;
State_main <= S_USB_INIT;
end
S_USB_INIT:
// 执行控制传输序列
if (InitComplite)
begin
State_main <= S_REQUEST;
end
else if (InitFail)
State_main <= S_FAIL;
// ... 轮询状态(S_REQUEST、S_WAIT 等)
endcase
end
end
关键提示: 复位阶段(S_USB_RESET)结束后立即发送 SOF(帧起始)包,而不是在恢复期发送。SOF 保持设备与主机同步。全速使用带 CRC5 的帧号;低速仅发送 EOP(两条线各两个零周期)。延迟 SOF 可能导致某些设备兼容问题。
HID 描述符解析与设备轮询
初始化完成后(包括分配非零地址),控制器进入轮询状态(S_REQUEST)。M_HID_ANALYZER 模块通过控制传输获取描述符,分析数据包格式和大小。对于键盘,它提取报告计数、大小和 ID。
典型的 HID 轮询循环:
- 向设备地址和端点 1 发送 IN 令牌。
- 接收数据包。
- 验证 CRC16。
- 校验通过则发送 ACK。
- 提取有效载荷(如键扫描码),跳过前导头字节。
- 等待轮询间隔(例如 24 ms)后下次轮询。
如果设备发送 NAK(未确认),稍后重试。无响应或错误?回退到等待或重新初始化。
核心要点
- 及时 SOF 包: 复位后立即发送帧起始包——HID 稳定运行的关键。
- FPGA 输入保护: 用合适电路屏蔽数据线免受 5V 影响,因为 USB 混用 3.3V/5V 电平。
- 描述符解析: 必须拆解描述符映射数据结构,否则 HID 无法工作。
- 错误处理: 优雅管理 NAK、STALL 和超时,确保链路稳健。
- 模块化设计: 拆分成模块,便于调试和跨项目复用。
— Editorial Team
暂无评论。