返回首页

FPGA 上的 USB 控制器:以键盘为例的 HID 设备开发

本文致力于基于 FPGA 的 USB 1.1 控制器开发,用于连接如键盘等 HID 设备。它涵盖 Verilog 中的模块化架构、设备初始化过程、及时发送 SOF 数据包的重要性以及 HID 描述符分析。

FPGA 上创建 USB 控制器:从理论到工作键盘
Advertisement 728x90

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+(全速)来指示速率。

关键硬件注意事项:

Google AdInline article slot
  • 主机侧数据线需接地拉低电阻。
  • 用 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 的缓冲区。

Google AdInline article slot

* 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 可能导致某些设备兼容问题。

Google AdInline article slot

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

Advertisement 728x90

继续阅读