feat(board): 板载 USB HID 口接 PS/2 鼠标,与板载按键并存

Nexys4DDR 的 USB HOST 口由板上芯片把 USB 键鼠转成 PS/2 信号(官方母版
Nexys4DDR_Master.xdc 标明 ps2_clk = F4、ps2_data = B2),所以不用写 USB 协议栈。

- cpu21_board_top.v:
  * USE_PS2_MOUSE 默认打开;原来是 if/else 二选一(选鼠标就没按键),现在
    cpu21_pointer 与 cpu21_ps2_mouse 同时例化,两路事件在顶层合并成 ev_valid/
    ev_dx/ev_dy/ev_btn(同拍优先 PS/2),板载按键一个不删
  * 新增 DEBUG_PS2_RX_LED(默认 1):把 PS/2 收到的字节数显示到 LED[15:3]
    (LED[2:0] 仍是中断指示)。没有调试器时靠它判断 PS/2 到底有没有数据:
    鼠标一动数字就跳 = 通了;一直是 0 = 根本没收到
- cpu21_board_top.xdc:ps2_clk/ps2_data 从 Pmod JA(C17/D18)改到板载 USB HID
  (F4/B2),并在注释里留了改回 Pmod 的方法

软件侧不用改:鼠标事件与按键事件格式相同,且 PS/2 的 dx/dy 天然满足
"8 个计数 = 一格"(saolei.c 里就是 acc_x += dx; while (acc_x >= 8) 移动一格)。
额外好处:鼠标右键 = 插旗(handle_event 本来就支持)。
This commit is contained in:
2026-09-23 21:50:18 +08:00
parent 64428bdd85
commit 77dd102675
2 changed files with 55 additions and 23 deletions
+49 -20
View File
@@ -53,7 +53,10 @@ module cpu21_board_top #(
// 所以不会掩盖时序违例(约束偏严)。 // 所以不会掩盖时序违例(约束偏严)。
parameter integer CLK_DIV_INTRUPT = 32, parameter integer CLK_DIV_INTRUPT = 32,
// 鼠标事件源:0 = 板上 5 向按键(免外设,默认);1 = PS/2 鼠标(Pmod JA) // 鼠标事件源:0 = 板上 5 向按键(免外设,默认);1 = PS/2 鼠标(Pmod JA)
parameter integer USE_PS2_MOUSE = 0, parameter integer USE_PS2_MOUSE = 1, // 1 = 启用板载 USB HID 口(PS/2 键鼠),板载按键同时有效
// 调试:1 = 把 PS/2 收到的字节数显示在 LED[15:3](LED[2:0] 仍是中断指示)。
// 验证 PS/2 通不通时打开:鼠标一动数字就跳;一直是 0 说明根本没收到 PS/2 数据。
parameter integer DEBUG_PS2_RX_LED = 1,
// 5 向按键去抖的采样周期(CPU 周期数):上板默认 50000(1 ms @50 MHz)。 // 5 向按键去抖的采样周期(CPU 周期数):上板默认 50000(1 ms @50 MHz)。
// 仿真里可以传一个小值,让"按一下"在几十微秒内就被识别。 // 仿真里可以传一个小值,让"按一下"在几十微秒内就被识别。
parameter integer POINTER_SAMPLE_DIV = 50000 parameter integer POINTER_SAMPLE_DIV = 50000
@@ -160,6 +163,7 @@ module cpu21_board_top #(
wire [31:0] data_wdata; wire [31:0] data_wdata;
wire [ 3:0] data_wstrb; wire [ 3:0] data_wstrb;
wire data_we; wire data_we;
wire [15:0] ps2_rx_count; // PS/2 收到的字节数(用于 LED 调试显示)
wire [31:0] led_data; // LED_ADDR 写入值,同时作为七段数码管显示数据 wire [31:0] led_data; // LED_ADDR 写入值,同时作为七段数码管显示数据
wire led_valid; wire led_valid;
// 扫雷扩展外设的连线(先声明,例化在下面) // 扫雷扩展外设的连线(先声明,例化在下面)
@@ -337,7 +341,10 @@ module cpu21_board_top #(
end end
end end
assign led = {{13{1'b0}}, irq_led_q}; // LED[15:3] 平时熄灭;DEBUG_PS2_RX_LED=1 时用它显示 PS/2 收到的字节数,
// 便于在没有调试器的情况下判断"鼠标到底有没有数据进来"。
assign led = (DEBUG_PS2_RX_LED != 0) ? {ps2_rx_count[12:0], irq_led_q}
: {{13{1'b0}}, irq_led_q};
reg [23:0] led_we_cnt; reg [23:0] led_we_cnt;
@@ -417,12 +424,34 @@ module cpu21_board_top #(
.dp_o (dp) .dp_o (dp)
); );
// ---- 鼠标事件源 ---- // ---- 鼠标事件源:板载按键 与 PS/2 鼠标**同时**有效(两路事件在顶层合并) ----
wire ev_valid; wire ev_valid;
wire [7:0] ev_dx, ev_dy; wire [7:0] ev_dx, ev_dy;
wire [4:0] ev_btn; wire [4:0] ev_btn;
wire [4:0] btn_vec = {go, btnu, btnd, btnl, btnr}; // {BTNC, BTNU, BTND, BTNL, BTNR} wire [4:0] btn_vec = {go, btnu, btnd, btnl, btnr}; // {BTNC, BTNU, BTND, BTNL, BTNR}
// 1) 板载按键:永远保留(BTNC 当左键、上下左右当移动、SW3 当插旗开关)
wire ptr_valid;
wire [7:0] ptr_dx, ptr_dy;
wire [4:0] ptr_btn;
cpu21_pointer #(
.SAMPLE_DIV(POINTER_SAMPLE_DIV)
) u_ptr (
.clk (clk_cpu),
.reset (reset),
.btn_i (btn_vec),
.ev_valid_o(ptr_valid),
.ev_dx_o (ptr_dx),
.ev_dy_o (ptr_dy),
.ev_btn_o (ptr_btn)
);
// 2) PS/2 鼠标:板载 USB HID 口经板上芯片转成 PS/2 信号(ps2_clk/ps2_data)
wire ps2_valid;
wire [7:0] ps2_dx, ps2_dy;
wire [4:0] ps2_btn;
generate generate
if (USE_PS2_MOUSE != 0) begin : g_ps2 if (USE_PS2_MOUSE != 0) begin : g_ps2
cpu21_ps2_mouse u_ps2 ( cpu21_ps2_mouse u_ps2 (
@@ -432,27 +461,27 @@ module cpu21_board_top #(
.ps2_clk_oe_o (), .ps2_clk_oe_o (),
.ps2_data_i (ps2_data), .ps2_data_i (ps2_data),
.ps2_data_oe_o(), .ps2_data_oe_o(),
.ev_valid_o (ev_valid), .ev_valid_o (ps2_valid),
.ev_dx_o (ev_dx), .ev_dx_o (ps2_dx),
.ev_dy_o (ev_dy), .ev_dy_o (ps2_dy),
.ev_btn_o (ev_btn), .ev_btn_o (ps2_btn),
.rx_count_o () .rx_count_o (ps2_rx_count)
);
end else begin : g_btn
cpu21_pointer #(
.SAMPLE_DIV(POINTER_SAMPLE_DIV)
) u_ptr (
.clk (clk_cpu),
.reset (reset),
.btn_i (btn_vec),
.ev_valid_o(ev_valid),
.ev_dx_o (ev_dx),
.ev_dy_o (ev_dy),
.ev_btn_o (ev_btn)
); );
end else begin : g_no_ps2
assign ps2_valid = 1'b0;
assign ps2_dx = 8'd0;
assign ps2_dy = 8'd0;
assign ps2_btn = 5'd0;
assign ps2_rx_count = 16'd0;
end end
endgenerate endgenerate
// 3) 合并:同一拍只有一方产生事件(两个源相互独立),优先 PS/2
assign ev_valid = ps2_valid | ptr_valid;
assign ev_dx = ps2_valid ? ps2_dx : ptr_dx;
assign ev_dy = ps2_valid ? ps2_dy : ptr_dy;
assign ev_btn = ps2_valid ? ps2_btn : ptr_btn;
endmodule endmodule
`default_nettype wire `default_nettype wire
@@ -97,6 +97,9 @@ set_property -dict { PACKAGE_PIN D8 IOSTANDARD LVCMOS33 } [get_ports { vga_b[
set_property -dict { PACKAGE_PIN B11 IOSTANDARD LVCMOS33 } [get_ports { vga_hs }] set_property -dict { PACKAGE_PIN B11 IOSTANDARD LVCMOS33 } [get_ports { vga_hs }]
set_property -dict { PACKAGE_PIN B12 IOSTANDARD LVCMOS33 } [get_ports { vga_vs }] set_property -dict { PACKAGE_PIN B12 IOSTANDARD LVCMOS33 } [get_ports { vga_vs }]
## PS/2 鼠标(可选:Pmod JA 插 Pmod PS2,顶层 USE_PS2_MOUSE=1) ## PS/2 鼠标:接**板载 USB HID 口**(板上芯片把 USB 键鼠转成 PS/2)
set_property -dict { PACKAGE_PIN C17 IOSTANDARD LVCMOS33 PULLUP true } [get_ports { ps2_clk }] ## Nexys4DDR_Master.xdc 里写明:ps2_clk = F4、ps2_data = B2
set_property -dict { PACKAGE_PIN D18 IOSTANDARD LVCMOS33 PULLUP true } [get_ports { ps2_data }] ## 顶层参数 USE_PS2_MOUSE=1 启用;板载按键仍然可用(两路事件在顶层合并)
## 若改用 Pmod JA 上的 Pmod PS2,把下面两行的引脚换成 C17 / D18 即可
set_property -dict { PACKAGE_PIN F4 IOSTANDARD LVCMOS33 PULLUP true } [get_ports { ps2_clk }]
set_property -dict { PACKAGE_PIN B2 IOSTANDARD LVCMOS33 PULLUP true } [get_ports { ps2_data }]