实验目标
-
七段数码管低两位显示当前按键的键码,中间两位显示对应的ASCII码(转换可以考虑自行设计一个ROM并初始化)。只需完成字符和数字键的输入,不需要实现组合键和小键盘。
-
当按键松开时,七段数码管的低四位全灭。
-
七段数码管的高两位显示按键的总次数。按住不放只算一次按键。只考虑顺序按下和放开的情况,不考虑同时按多个键的情况。
实验收获
一开始对实验目标没啥思路,第七个实验难度突然陡增。一开始没能把整体都思考清楚。就想着用分步走的思路,先实现一个功能框架,再实现它的逻辑部分。
第一版,模块设计和功能实现:
-
捕获键值模块,输出键值
-
统计按键次数模块,基于F0键值进行判断次数
-
SEG显示模块,
第二版,添加状态机
-
捕获键值模块,输出键值和状态
-
统计按键次数模块,根据状态信息进行计数
-
SEG显示模块,根据状态信息进行显示
实验过程
-
参考nvboard的demo ,实现键值捕获模块
module ps2_keyboard_ctl(clk,resetn,ps2_clk,ps2_data,ps2_code,ps2_count);
input clk,resetn,ps2_clk,ps2_data;
output [7:0] ps2_code;
reg [9:0] buffer; // ps2_data bits
reg [3:0] count; // count ps2_data bits
reg [2:0] ps2_clk_sync;
output reg [7:0] ps2_count;always @(posedge clk) begin ps2_clk_sync <= {ps2_clk_sync[1:0],ps2_clk}; end wire sampling = ps2_clk_sync[2] & ~ps2_clk_sync[1]; always @(posedge clk) begin if (resetn == 0) begin // reset count <= 0; ps2_count <=0; end else begin if (sampling) begin if (count == 4'd10) begin if ((buffer[0] == 0) && // start bit (ps2_data) && // stop bit (^buffer[9:1])) begin // odd parity $display("receive %x ", buffer[8:1]); if (buffer[8:1] == 8'hF0) begin ps2_count <= ps2_count + 8'b1;
// $display("ps2_count %x, ", ps2_count);
end
end
count <= 0; // for next
end else begin
buffer[count] <= ps2_data; // store ps2_data
count <= count + 3'b1;
end
end
end
end
assign ps2_code = buffer[8:1]; //always set output dataendmodule
-
实现键值统计模块
module ps2_keyboard_to_count (
input wire clk, // 系统时钟
input wire rst_n, // 复位信号(低有效)
input wire [7:0] ps2_code, // 8位输入数据
input wire sampling,
output wire [7:0] key_count // F0出现的累计次数
);reg [7:0] count ; // F0出现的累计次数 always @(posedge clk) begin //always @(posedge clk or negedge rst_n) begin if (!rst_n) begin count <= 8'd0; // 复位时计数器清零 end else if ( (ps2_code == 8'hF0 ) && sampling ) begin count <= count + 1; // 检测到F0且数据有效时计数加1 $display("ps2code %x,count: %x", ps2_code,count); end end assign key_count = count;
endmodule
-
实现SEG显示模块
module ps2_keyboard_ctl_seg(
input wire clk, // 时钟输入
input wire rst, // 复位信号(低电平有效)
input wire [7:0] data, // 8位数据输入
input wire [7:0] ascii_code, // 8位数据输入
input wire [7:0] data_count, // 8位数据输入
output [7:0] o_seg0,
output [7:0] o_seg1,
output [7:0] o_seg2,
output [7:0] o_seg3,
output [7:0] o_seg4,
output [7:0] o_seg5,
output [7:0] o_seg6,
output [7:0] o_seg7
);// 七段数码管编码表(共阳极,段顺序:dp,g,f,e,d,c,b,a) reg [7:0] seg_table [0:15]; reg [7:0] seg0, seg1,seg2,seg3,seg4,seg5,seg6,seg7; // 初始化七段编码表 initial begin seg_table[0] = 8'b11111100; // 0 → abcdef亮 [1](@ref) seg_table[1] = 8'b01100000; // 1 → bc亮 seg_table[2] = 8'b11011010; // 2 → abdeg亮 seg_table[3] = 8'b11110010; // 3 → abcdg亮 seg_table[4] = 8'b01100110; // 4 → bcfg亮 seg_table[5] = 8'b10110110; // 5 → acdfg亮 seg_table[6] = 8'b10111110; // 6 → acdefg亮 seg_table[7] = 8'b11100000; // 7 → abc亮 seg_table[8] = 8'b11111110; // 8 → abcdefg全亮(缺dp)[1, seg_table[9] = 8'b11110110; // 9 → abcdfg亮(缺e段) seg_table[10] = 8'b11101110; // A(10) → abcefg亮(缺d段) seg_table[11] = 8'b00111110; // B(11) → cdefg亮(缺ab段) seg_table[12] = 8'b10011100; // C(12) → adef亮(缺bcg段) seg_table[13] = 8'b01111010; // D(13) → bcdeg亮(缺af段) seg_table[14] = 8'b11011110; // E(14) → adefg亮(缺bc段) seg_table[15] = 8'b11101110; // F(15) → aefg亮(缺bcd段) end // 仅使用前两位数码管显示8位数据(高4位和低4位) always @(posedge clk ) begin if (rst) begin seg0 <= 8'b11111111; // 复位时关闭所有段 seg1 <= 8'b11111111; // 复位时关闭所有段 end else begin if (data == 8'b11110000 ) begin seg0 <= 8'b11111111; seg1 <= 8'b11111111; seg2 <= 8'b11111111; seg3 <= 8'b11111111; seg6 <= ~ seg_table[data_count[3:0]]; seg7 <= ~ seg_table[data_count[7:4]]; end else begin // 使用低4位和高4位数据来显示 // 注意:这里假设data是8位二进制数,seg_table的索引范围是0-15 // 因此data[3:0]和data[7:4]分别对应低4位和高4位 seg0 <= ~ seg_table[data[3:0]]; seg1 <= ~ seg_table[data[7:4]]; seg3 <= ~ seg_table[ascii_code[3:0]]; seg4 <= ~ seg_table[ascii_code[7:4]]; seg6 <= ~ seg_table[data_count[3:0]]; seg7 <= ~ seg_table[data_count[7:4]]; end end end assign o_seg0 = seg0 ; assign o_seg1 = seg1 ; assign o_seg2 = 8'b11111111; // dp段关闭 assign o_seg3 = seg3; assign o_seg4 = seg4; assign o_seg5 = 8'b11111111; // dp段关闭 assign o_seg6 = seg6; assign o_seg7 = seg7;
endmodule
-
第一版运行结果遗留多个问题,有的需要引入状态机机制去解决
-
统计按键次数统计就不准。由于只简单的采用了F0作为统计机制,导致了有了多次的冗余统计。

- 改进了判断逻辑
但也遗留两个问题,状态机没能实现,键盘收起后要求能灭灯,也还没实现。目前没想出好的办法。刚觉是要同时判断结束码(F0)和键码才行,这样的话就要加入 fifo机制,保存两个状态。

