当前位置: 首页 > news >正文

【FPGA零基础学习之旅#10】按键消抖模块设计与验证(一段式状态机实现)

🎉欢迎来到FPGA专栏~按键消抖模块设计与验证


  • ☆* o(≧▽≦)o *☆~我是小夏与酒🍹
  • 博客主页:小夏与酒的博客
  • 🎈该系列文章专栏:FPGA学习之旅
  • 文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
  • 📜 欢迎大家关注! ❤️
    FPGQ2

CSDN

🎉 目录-按键消抖模块设计与验证

  • 一、效果演示
  • 二、模块设计
  • 三、仿真测试
    • 3.1 常规编写
    • 3.2 task编写
  • 四、仿真模型

遇见未来

一、效果演示

🥝模块设计:
模块设计


🥝按键消抖模块的完整代码,可直接使用:

//
//模块:按键消抖模块
//key_state:输出消抖之后按键的状态
//key_flag:按键消抖结束时产生一个时钟周期的高电平脉冲
/
module KeyFilter(input Clk,input Rst_n,input key_in,output reg key_flag,output reg key_state
);//按键的四个状态localparamIDLE 		= 4'b0001,FILTER1 	= 4'b0010,DOWN 		= 4'b0100,FILTER2 	= 4'b1000;//状态寄存器reg [3:0] curr_st;//边沿检测输出上升沿或下降沿wire pedge;wire nedge;//计数寄存器reg [19:0]cnt;//使能计数寄存器reg en_cnt;//计数满标志信号reg cnt_full;//计数满寄存器//------<边沿检测电路的实现>------//边沿检测电路寄存器reg key_tmp0;reg key_tmp1;//边沿检测always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)beginkey_tmp0 <= 1'b0;key_tmp1 <= 1'b0;endelse beginkey_tmp0 <= key_in;key_tmp1 <= key_tmp0;end	endassign nedge = (!key_tmp0) & (key_tmp1);assign pedge = (key_tmp0)  & (!key_tmp1);//------<状态机主程序>------	//状态机主程序always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)begincurr_st <= IDLE;en_cnt <= 1'b0;key_flag <= 1'b0;key_state <= 1'b1;endelse begincase(curr_st)IDLE:beginkey_flag <= 1'b0;if(nedge)begincurr_st <= FILTER1;en_cnt <= 1'b1;endelsecurr_st <= IDLE;endFILTER1:beginif(cnt_full)beginkey_flag <= 1'b1;key_state <= 1'b0;curr_st <= DOWN;en_cnt <= 1'b0;end	else if(pedge)begincurr_st <= IDLE;en_cnt <= 1'b0;endelsecurr_st <= FILTER1;endDOWN:beginkey_flag <= 1'b0;if(pedge)begincurr_st <= FILTER2;en_cnt <= 1'b1;endelsecurr_st <= DOWN;endFILTER2:beginif(cnt_full)beginkey_flag <= 1'b1;key_state <= 1'b1;curr_st <= IDLE;en_cnt <= 1'b0;end	else if(nedge)begincurr_st <= DOWN;en_cnt <= 1'b0;endelsecurr_st <= FILTER2;enddefault:begincurr_st <= IDLE;en_cnt <= 1'b0;key_flag <= 1'b0;key_state <= 1'b1;endendcaseendend//------<20ms计数器>------		//20ms计数器//Clk 50_000_000Hz//一个时钟周期为20ns//需要计数20_000_000 / 20 = 1_000_000次always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)cnt <= 20'd0;else if(en_cnt)cnt <= cnt + 1'b1;elsecnt <= 20'd0;endalways@(posedge Clk or negedge Rst_n)beginif(!Rst_n)cnt_full <= 1'b0;else if(cnt == 999_999)cnt_full <= 1'b1;elsecnt_full <= 1'b0;endendmodule

🥝RTL视图:
RTL


🥝状态转移:
状态转移


🥝仿真结果:
仿真结果


二、模块设计

🥝模块设计:

信号作用
clk时钟信号输入
rst_n复位信号输入
key_in按键信号输入
key_flag消抖结束之后的标志位
key_state消抖结束之后按键的状态

模块设计


🥝上升沿检测电路:
上升


🥝下降沿检测电路:

下降


🥝边沿检测电路的实现:
检测到下降沿,nedge输出高电平;检测到上升沿,pedge输出高电平。

//------<边沿检测电路的实现>------
//边沿检测电路寄存器
reg key_tmp0;
reg key_tmp1;//边沿检测
always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)beginkey_tmp0 <= 1'b0;key_tmp1 <= 1'b0;endelse beginkey_tmp0 <= key_in;key_tmp1 <= key_tmp0;end	
endassign nedge = (!key_tmp0) & (key_tmp1);//检测到下降沿,nedge输出高电平
assign pedge = (key_tmp0)  & (!key_tmp1);//检测到上升沿,pedge输出高电平

🥝一段式状态机设计:
按键的四种状态:

//按键的四个状态
localparamIDLE 		= 4'b0001,FILTER1 	= 4'b0010,DOWN 		= 4'b0100,FILTER2 	= 4'b1000;

计数器:

//------<20ms计数器>------		
//20ms计数器
//Clk 50_000_000Hz
//一个时钟周期为20ns
//需要计数20_000_000 / 20 = 1_000_000次//计数寄存器
reg [19:0]cnt;//使能计数寄存器
reg en_cnt;//计数满标志信号
reg cnt_full;//计数满寄存器always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)cnt <= 20'd0;else if(en_cnt)cnt <= cnt + 1'b1;elsecnt <= 20'd0;
endalways@(posedge Clk or negedge Rst_n)beginif(!Rst_n)cnt_full <= 1'b0;else if(cnt == 999_999)cnt_full <= 1'b1;elsecnt_full <= 1'b0;
end

状态机主程序:

//------<状态机主程序>------	
//状态机主程序
always@(posedge Clk or negedge Rst_n)beginif(!Rst_n)begincurr_st <= IDLE;en_cnt <= 1'b0;key_flag <= 1'b0;key_state <= 1'b1;endelse begincase(curr_st)IDLE:beginkey_flag <= 1'b0;if(nedge)begincurr_st <= FILTER1;en_cnt <= 1'b1;endelsecurr_st <= IDLE;endFILTER1:beginif(cnt_full)beginkey_flag <= 1'b1;key_state <= 1'b0;curr_st <= DOWN;en_cnt <= 1'b0;end	else if(pedge)begincurr_st <= IDLE;en_cnt <= 1'b0;endelsecurr_st <= FILTER1;endDOWN:beginkey_flag <= 1'b0;if(pedge)begincurr_st <= FILTER2;en_cnt <= 1'b1;endelsecurr_st <= DOWN;endFILTER2:beginif(cnt_full)beginkey_flag <= 1'b1;key_state <= 1'b1;curr_st <= IDLE;en_cnt <= 1'b0;end	else if(nedge)begincurr_st <= DOWN;en_cnt <= 1'b0;endelsecurr_st <= FILTER2;enddefault:begincurr_st <= IDLE;en_cnt <= 1'b0;key_flag <= 1'b0;key_state <= 1'b1;endendcaseend
end

三、仿真测试

3.1 常规编写

`timescale 1ns/1ns
`define clock_period 20module KeyFilter_tb;reg Clk;reg Rst_n;reg key_in;wire key_flag;wire key_state;KeyFilter KeyFilter0(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in),.key_flag(key_flag),.key_state(key_state));initial Clk = 1;always#(`clock_period/2) Clk = ~Clk;initial beginRst_n = 1'b0;key_in = 1'b1;#(`clock_period*10);Rst_n = 1'b1;#(`clock_period*10 + 1);key_in = 0;#1000;key_in = 1;#2000;key_in = 0;#1400;key_in = 1;#2600;key_in = 0;#1300;key_in = 1;#200;key_in = 0;#20000100;#50000000;key_in = 1;#2600;key_in = 0;#1000;key_in = 1;#2000;key_in = 0;#1400;key_in = 1;#2600;key_in = 0;#1300;key_in = 1;#200;key_in = 1;#20000100;#50000000;$stop;endendmodule

仿真结果:
结果1


3.2 task编写

`timescale 1ns/1ns
`define clock_period 20module KeyFilter_tb;reg Clk;reg Rst_n;reg key_in;wire key_flag;wire key_state;KeyFilter KeyFilter0(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in),.key_flag(key_flag),.key_state(key_state));initial Clk = 1;always#(`clock_period/2) Clk = ~Clk;initial beginRst_n = 1'b0;key_in = 1'b1;#(`clock_period*10);Rst_n = 1'b1;#(`clock_period*10 + 1);#30000;PressKey; #10000;PressKey; #10000;PressKey; #10000;$stop;endreg [15:0]myrand;task PressKey;begin//50次随机时间按下抖动repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key_in = ~key_in;endkey_in = 0;#50_000_000;//按下稳定//50次随机时间释放抖动repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key_in = ~key_in;endkey_in = 1;#50_000_000;//释放稳定endendtaskendmodule

注意$random随机函数的用法:

$random这一系统函数可以产生一个有符号的32bit随机整数。一般的用法是 $random%b,其中 b>0。这样就会生成一个范围在(-b+1):(b-1)中的随机数。如果只得到正数的随机数,可采用{$random}%b 来产生。

myrand = {$random}%65536;//0~65535

上述语句的作用即是产生了0~65535之间的随机数。

通过repeat语句循环50次,就产生了50次不同的延时效果:

repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key_in = ~key_in;
end

仿真结果:
结果2


四、仿真模型

编写key_model并添加到测试激励文件中:

`timescale 1ns/1nsmodule key_model(key);output reg key;reg [15:0]myrand;initial beginkey = 1'b1;PressKey; #10000;PressKey; #10000;PressKey; #10000;$stop;endtask PressKey;begin//50次随机时间按下抖动repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key = ~key;endkey = 0;#50_000_000;//按下稳定//50次随机时间释放抖动repeat(50)beginmyrand = {$random}%65536;//0~65535#myrand key = ~key;endkey = 1;#50_000_000;//释放稳定endendtask	endmodule

修改KeyFilter_tb:

`timescale 1ns/1ns
`define clock_period 20module KeyFilter_tb;reg Clk;reg Rst_n;wire key_in;wire key_flag;wire key_state;KeyFilter KeyFilter0(.Clk(Clk),.Rst_n(Rst_n),.key_in(key_in),.key_flag(key_flag),.key_state(key_state));key_model key_model0(.key(key_in));initial Clk = 1;always#(`clock_period/2) Clk = ~Clk;initial beginRst_n = 1'b0;#(`clock_period*10);Rst_n = 1'b1;#(`clock_period*10 + 1);endendmodule

整个激励文件的内部结构:
仿真模型
仿真结果:
结果3

csdn

🧸结尾


  • ❤️ 感谢您的支持和鼓励! 😊🙏
  • 📜您可能感兴趣的内容:
  • 【FPGA】串口通信讲解-状态机判断数据值
  • 【Python】串口通信-与FPGA、蓝牙模块实现串口通信(Python+FPGA)
  • 【Arduino TinyGo】【最新】使用Go语言编写Arduino-环境搭建和点亮LED灯
  • 【全网首发开源教程】【Labview机器人仿真与控制】Labview与Solidworks多路支配关系-四足爬行机器人仿真与控制
    遇见未来
http://www.lryc.cn/news/120566.html

相关文章:

  • 【EI复现】基于阶梯碳交易的含P2G-CCS耦合和燃气掺氢的虚拟电厂优化调度(Matlab代码实现)
  • 高防cdn和高防服务器有什么不一样?
  • ppt怎么压缩?试试这样压缩文件
  • stm32 cubemx ps2无线(有线)手柄
  • 【TI毫米波雷达笔记】sdk传参时的type避坑
  • 【算法挨揍日记】day02——双指针算法_快乐数、盛最多水的容器
  • 【Hilog】鸿蒙系统日志源码分析
  • keil下载程序具体过程4:flash下载算法
  • 如何快速的让自己从月入2000变成月入两万?
  • 使用 CycleGAN 进行图像到图像转换
  • Svg使用和注册components文件夹内部全部为全局组件
  • 解决idea编辑application.yml文件或properties文件没有提示问题
  • 前端懒加载
  • 【手动配置ip地址后,电脑仍自动分配ip的问题】
  • 移远RM500U-CN模块直连嵌入式ubuntu实现拨号上网
  • 【JavaWeb】MySQL基础操作
  • 【Tool】虚拟机安装与调试与设置与主机共享文件
  • Spring中的接口使用
  • 爬虫017_urllib库_get请求的quote方法_urlencode方法_---python工作笔记036
  • Http、SSE、Websocket的区别
  • 【资料分享】全志科技T507工业核心板硬件说明书(一)
  • JavaScript类
  • One-4-All: Neural Potential Fields for Embodied Navigation 论文阅读
  • 【ES】笔记-函数参数默认值
  • 安装harbor
  • kube-prometheus 使用 blackbox-exporter 进行icmp 监控
  • 【python技巧】文本文件的读写操作
  • SpringBoot项目(验证码整合)——springboot整合email springboot整合阿里云短信服务
  • 缓存穿透,击穿,雪崩之间的区别与联系
  • Vue项目npm run dev 启动报错TypeError: Cannot read property ‘upgrade‘ of undefined