赞
踩
1. 学习有限状态机的设计。 2. 学习信号边沿抖动的消除方法。
由于金属弹性形变的原因,按键/开关在状态切换 过程中总是会有或多或少的抖动情况,有时这种抖动 会导致电路误动作,甚至无法正常工作。比如在设置 参数时,按一下“加 1”可能会加 4~5 个数甚至更多; 生活中常见的情况是鼠标单击变成了双击等。因此, 在很多时候,输入电路的信号需要经过防抖动处理之 后才会送到后级电路。
按键抖动的电压波形如图 1 如示,tjitter是抖动时间,通常在 1ms~30ms 之间。
按键抖动消除有多种方法: 1. 积分法(模拟电路); 2. 施密特触发器(模拟电路); 3. 延迟再次判断法; 4. 持续稳定判断法。
实验要求:
用 Verilog HDL 设计一个按键防抖动电路,要求用有限状态机实现。防抖动电路的输入 接实验箱的按键/开关(SW0),输出接实验五 1 位计数译码显示电路的时钟输入,实现每按 一次按键(或拨一次开关)计数器加 1,多次测试不出现抖动乱加现象,电路对按键的响应 无明显滞后。
自拟实验方法,测试实验箱的按键抖动时间,精确到 0.1ms。
1. 简要写出电路设计思路,列出电路所需状态,画出状态转移图。 2. 分析、计算电路需要多高的时钟频率。 3. 自行查阅 Verilog HDL 编写有限状态机资料,完成实验程序代码编写。 4. 用 ModelSim 对实验电路进行功能仿真,并将仿真结果截图、打印。 5. 列出引脚锁定分配表(信号名->主板器件名->引脚号)。
1. 列出程序代码(有详细注释)。 2. 附 Quartus 生成的状态图 3. 附仿真测试波形。 4. 列出实验过程出现的问题及解决措施。
本次实验要求实现了一个用于防抖动的按键状态机,可以处理由于按键在状态切换时产生的抖动情况,并输出稳定的按键状态信号和状态机状态。以下是代码的主要设计思路和功能:
1.状态定义:定义了四个状态:`key0`、`key1`、`key2`、`key3`,分别代表按键的不同状态。这些状态用于表示按键的松开和按下过程中的不同状态情况。
2. 计数器和状态机更新:使用 `always` 块结合时钟信号和复位信号,在时钟的上升沿和复位信号变化时,更新状态机的状态和计数器的值。根据状态的变化和计数器的值,控制状态机状态的转换和计数器的计数过程。
3. 按键状态机逻辑: 在 `always` 块中通过 `case` 语句实现按键状态机的逻辑。根据当前状态和输入信号(按键是否按下),切换状态以及控制按键状态的输出。通过状态转换和计数器的变化,实现对按键状态的稳定检测和切换,避免了按键抖动带来的误触发问题。
4. 输出:输出了防抖后的按键状态 `key_state` 和进位信号 `CO`。`key_state` 表示稳定的按键状态,`CO` 则用于其他逻辑或模块的触发或控制。
状态机在按键输入的过程中会经历松开状态、松开毛刺状态、按下毛刺状态和按下状态,并根据输入信号和计数器的状态来稳定地输出按键状态,并在稳定状态下输出进位信号。整个设计的核心思想是通过状态机的状态切换和计数器的计数,实现对按键状态的稳定检测和输出。
- module top(clk,reset_n,key,key_state,Q,codeout,CO,state,cnt);
- input clk,reset_n,key;// 输入时钟信号、复位信号和按键信号
- output [3:0] Q ; // 计数器
- output [6:0] codeout; // 七段数码管
- output CO; // 进位信号
- output key_state; // 防抖状态
- output [3:0]state; // 临时状态输出
- output [20:0] cnt; // 计数器状态输出
-
- son1 fun1(clk,reset_n,key,key_state,state,cnt);
- son2 fun2(key_state,reset_n,Q,CO);
- son3 fun3(Q,codeout);
- endmodule
- module son1(clk,reset_n,key,key_state,state,cnt);
- input clk,reset_n,key;// 输入时钟信号、复位信号和按键信号
- output reg key_state; // 防抖状态
-
- reg cnt_en; // 计数使能信号
- output reg [20:0] cnt; // 计数器,用于计算时间
- output reg [3:0] state; // 状态寄存器,用于记录按键状态机的状态
-
- parameter key0 = 4'b0001; //松开状态 // 按键状态机的状态参数
- parameter key1 = 4'b0010; //松开毛刺状态
- parameter key2 = 4'b0100; //按下毛刺状态
- parameter key3 = 4'b1000; //按下状态
-
- // 计数器和状态机的更新
- always @ (posedge clk, negedge reset_n) begin
- if (!reset_n)
- cnt <= 0; // 复位时将计数器清零
- else if (cnt_en)
- cnt <= cnt + 1'b1; // 根据计数使能信号更新计数器值
- else
- cnt <= 0; // 当计数使能信号为0时,将计数器清零
- end
- // 按键状态机逻辑
- always @ (posedge clk, negedge reset_n) begin
- if (!reset_n) begin
- state <= key0; // 复位时,将状态设置为初始状态key0
- cnt_en <= 0; // 复位时,将计数使能信号清零
- key_state <= 0; // 复位时,将按键状态清零
- end
- else begin
- case(state)
- key0: begin
- key_state <= 1'b0; // 设置按键状态为低电平
- if (key == 1 && cnt_en == 0) begin
- state <= key1; // 如果按键被按下且计数使能信号为0,则切换到状态key1
- cnt_en <= 1'b1; // 启动计数使能信号
- end
- else state <= key0; // 否则保持当前状态
- end
- key1: begin
- key_state <= 1'b0; // 设置按键状态为低电平
- if (cnt == 21'd100000 && key == 1) begin
- state <= key2; // 如果计数器达到阈值且按键仍被按下,则切换到状态key2
- cnt_en <= 1'b0; // 关闭计数使能信号
- end
- else if (cnt == 21'd100000 && key == 0) begin
- state <= key0;
- cnt_en <= 1'b0;
- end
- else state <= key1; // 否则保持当前状态
- end
- key2: begin
- key_state <= 1'b1; // 设置按键状态为高电平
- if (key == 0 && cnt_en == 0) begin
- state <= key3; // 如果按键被释放且计数使能信号为0,则切换到状态key3
- cnt_en <= 1'b1; // 启动计数使能信号
- end
- else state <= key2; // 否则保持当前状态
- end
- key3: begin
- key_state <= 1'b1; // 按键状态返回初始状态key0
- if (cnt == 21'd100000 && key == 0) begin
- state <= key0; // 如果计数器达到阈值且按键已释放,则切换到空闲状态
- cnt_en <= 1'b0; // 关闭计数使能信号
- end
- else if (cnt == 21'd100000 && key == 1) begin
- state <= key2;
- cnt_en = 1'b0;
- end
- else state <= key3; // 否则保持当前状态
- end
- default: state <= key0; // 默认情况下将状态设置为初始状态key0
- endcase
- end
- end
- endmodule

- module son2(clk, clr, Q, CO);
- input clk, clr; // 输入时钟信号和清零信号
- output reg [3:0] Q; // 输出4位计数值
- output CO; // 输出进位信号
-
- always @ (posedge clk, negedge clr) begin
- if (!clr) // 当清零信号为低电平时
- Q <= 4'd0; // 将计数器值清零
- else begin
- if (Q == 4'd9) // 如果计数器值为9
- Q <= 4'd0; // 将计数器值重置为0
- else
- Q <= Q + 1'd1; // 否则计数器值加1
- end
- end
- assign CO = (clk & Q == 4'd9); // 判断是否达到9,输出进位信号
- endmodule

- module son3(indec,codeout);
- input[3: 0] indec; // 说明输入信号的数组宽度为4
- output[6: 0] codeout; // 说明输出信号的数组宽度为7
- reg[6: 0] codeout; // 定义为寄存器
- always@(indec)
- begin
- case(indec) // indec作为判别常量
- // 根据输入信号indec,输出相应的codeout
- 4'd0: codeout = 7'b1111110;
- 4'd1: codeout = 7'b0110000;
- 4'd2: codeout = 7'b1101101;
- 4'd3: codeout = 7'b1111001;
- 4'd4: codeout = 7'b0110011;
- 4'd5: codeout = 7'b1011011;
- 4'd6: codeout = 7'b1011111;
- 4'd7: codeout = 7'b1110000;
- 4'd8: codeout = 7'b1111111;
- 4'd9: codeout = 7'b1111011;
- default: codeout = 7'b0000001; // 将大于9的数显示为-负号
- endcase
- end
- endmodule

- `timescale 1ns / 1ns
- module test_7();
- reg clk;
- reg reset_n;
- reg key;
- wire key_state;
- wire [3:0] Q;
- wire [6:0] codeout;
- wire CO;
- wire [3:0] state;
- wire [20:0] cnt;
- top fun(
- .clk(clk),
- .reset_n(reset_n),
- .key(key),
- .key_state(key_state),
- .Q(Q),
- .codeout(codeout),
- .CO(CO),
- .state(state),
- .cnt(cnt)
- );
-
- initial begin
- clk = 1'b0;
- reset_n = 1'b0;
- key = 1'b1;
-
- #200 reset_n = 1'b1; // 在 200 时间单位后,将复位信号置为高电平
- end
-
- always begin // 无限循环块
- // 1ms变化一次 一共变化10ms
- key = 1'b1;
- #100000 key = 1'b0;
- #100000 key = 1'b1;
- #100000 key = 1'b0;
- #100000 key = 1'b1;
- #100000 key = 1'b0;
- #100000 key = 1'b1;
- #100000 key = 1'b0;
- #100000 key = 1'b1;
- #100000 key = 1'b0;
- // 保持按键信号为高电平,然后再置为低电平
- #(50*100000) key = 1'b1;
- // 继续10ms变化
- #100000 key = 1'b0;
- #100000 key = 1'b1;
- #100000 key = 1'b0;
- #100000 key = 1'b1;
- #100000 key = 1'b0;
- #100000 key = 1'b1;
- #100000 key = 1'b0;
- #100000 key = 1'b1;
- #100000 key = 1'b0;
- #100000 key = 1'b1;
- // 保持低电平50ms
- #(50*100000) key = 1'b0;
- end
-
- always #10 clk = ~clk; // 模拟50MHZ时钟
-
- endmodule

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。