按上述要求生成完整代码
时间: 2025-09-02 18:22:09 AIGC 浏览: 6
# 代码概述
根据题目要求,设计一个基于Verilog的按键计数器,使用单个八位数码管中的两位显示0~99之间的数值(或0~15循环),通过按键控制加1/减1,利用三个片选引脚(`sel[2:0]`)中的两个进行位选控制,实现动态扫描显示。
# 代码解析
```verilog
module key_counter(
input clk, // 系统时钟,例如50MHz
input rst_n, // 复位信号,低电平有效
input key_up, // 加1按键
input key_down, // 减1按键
output reg [2:0] sel, // 三位片选信号,使用sel[1:0]选择数码管位
output reg [7:0] seg // 段选信号,低电平有效(含小数点),假设共阴
);
// 参数定义
parameter CNT_WIDTH = 8; // 计数器位宽
localparam T10MS = 24'd500_000; // 10ms消抖计时(50MHz)
// 按键消抖相关信号
reg [CNT_WIDTH-1:0] cnt_reg;
reg key_up_sync, key_down_sync;
reg key_up_meta, key_down_meta;
reg key_up_pressed, key_down_pressed;
// 主计数器
reg [3:0] count; // 当前计数值,0~15循环
// 动态扫描相关
reg [1:0] digit_sel; // 当前扫描的数码管位(0:个位, 1:十位)
reg [3:0] digit1, digit0; // 十位和个位数字
// 段码表:0~9, A~F,共阴极数码管(a~g, dp),低电平点亮
// seg[7:0] = {dp,g,f,e,d,c,b,a}
always @(*) begin
case(digit_sel)
4'd0: digit0 <= count % 4'd10;
4'd1: digit1 <= count / 4'd10;
default: digit0 <= 4'd0;
endcase
end
// 7段译码
always @(*) begin
case({digit_sel, digit_sel ? digit1 : digit0})
{1'b0, 4'h0}: seg = 8'b00111111; // 0
{1'b0, 4'h1}: seg = 8'b00000110; // 1
{1'b0, 4'h2}: seg = 8'b01011011; // 2
{1'b0, 4'h3}: seg = 8'b01001111; // 3
{1'b0, 4'h4}: seg = 8'b01100110; // 4
{1'b0, 4'h5}: seg = 8'b01101101; // 5
{1'b0, 4'h6}: seg = 8'b01111101; // 6
{1'b0, 4'h7}: seg = 8'b00000111; // 7
{1'b0, 4'h8}: seg = 8'b01111111; // 8
{1'b0, 4'h9}: seg = 8'b01101111; // 9
{1'b1, 4'h0}: seg = 8'b00111111; // 十位0
{1'b1, 4'h1}: seg = 8'b00000110; // 十位1
default: seg = 8'b00000000;
endcase
end
// 位选控制:使用sel[1:0],sel[2]可悬空或置0
always @(*) begin
case(digit_sel)
2'd0: sel = 3'b001; // 个位
2'd1: sel = 3'b010; // 十位
default: sel = 3'b000;
endcase
end
// 按键消抖逻辑
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
key_up_meta <= 1'b0;
key_up_sync <= 1'b0;
key_down_meta <= 1'b0;
key_down_sync <= 1'b0;
end else begin
key_up_meta <= key_up;
key_up_sync <= key_up_meta;
key_down_meta <= key_down;
key_down_sync <= key_down_meta;
end
end
// 按键边沿检测(上升沿)
reg up_flag, down_flag;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
up_flag <= 0;
down_flag <= 0;
end else begin
up_flag <= key_up_sync & ~key_up_meta;
down_flag <= key_down_sync & ~key_down_meta;
end
end
// 主计数逻辑
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
count <= 4'd0;
end else if (up_flag) begin // 检测到加1按键按下
if (count == 4'hF)
count <= 4'd0;
else
count <= count + 1;
end else if (down_flag) begin // 检测到减1按键按下
if (count == 4'd0)
count <= 4'hF;
else
count <= count - 1;
end
end
// 动态扫描时钟分频(约1kHz)
reg [15:0] scan_cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
scan_cnt <= 0;
else
scan_cnt <= scan_cnt + 1;
end
// 数码管位切换(每1ms切换一次)
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
digit_sel <= 2'd0;
else if (scan_cnt == 16'd25000) begin // 50MHz / 50000 = 1kHz
scan_cnt <= 0;
digit_sel <= ~digit_sel; // 切换个位和十位
end
end
endmodule
```
# 知识点
- **按键消抖技术**:通过两级寄存同步并检测边沿,结合计数器延时消除机械按键抖动。
- **数码管动态扫描**:利用人眼视觉暂留,快速切换位选信号实现多位显示。
- **Verilog组合与时序逻辑设计**:段码译码用组合逻辑,计数与扫描用时序逻辑保证稳定性。
阅读全文
相关推荐




















