5、SCM 按键消抖 vs FPGA 按键消抖

2024-03-03 15:32
文章标签 vs fpga 按键 scm 消抖

本文主要是介绍5、SCM 按键消抖 vs FPGA 按键消抖,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

;SCM(使用的锐志实验板) 时钟周期为11.0592Mhz FPGA (Altrea BJ-EPM240)时钟周期为50Mhz(C语言和Verilog语言的语法不做详细讲解,可以查看相关资料以下基础实验都是基于两个原理图(http://pan.baidu.com/s/1sl56yc9))

这里写图片描述

  • 按键在闭合和断开时,触点会存在抖动现象。在按键按下或者是释放的时候都会出现一个不稳定的抖动时间,如果不处理好这个抖动时间,我们就无法处理好按键编码;首先介绍串行代码的描述思想,然后介绍硬件并行代码的思想

因为 指令周期>(~4) 机器周期>状态周期>(~12) 时钟周期,因此对于按键的延迟要根据执行一条指令的执行时间;理论的计算是 一条指令执行时间=m个机器周期xn1个时钟周期+p个状态状态周期xn2个机器周期

  • 时钟周期,是晶振频率的倒数。
  • 状态周期,是时钟周期的二倍。
  • 机器周期,是时钟周期的 12 倍。
  • 指令周期,执行一条指令所需要的时间,一般由若干个机器周期组成。指令不同,所需的机器周期也不同。
#include<reg52.h>
#define  uint unsigned int
sbit clk_div=P3^5; //按键
sbit led=P0^7;
void delay(uint div_delay)
{uint i,j;for(i=div_delay;i>0;i--)for(j=110;j>0;j--);	 //延迟1ms  
}void main()
{ led=1;//可省略
while(1)
{if(clk_div==0){ delay(20);	//按键延迟20msif(clk_div==0) while(!clk_div); led=~led;}}}

上述代码为一个简单的SCM按键点亮小灯;

  • (个人见解,还望前辈批评)首先FPGA 的硬件编程语言是并行执行的,但是每一个always
    语句的触发的逻辑操作一般都是有逻辑的制约关系的,即下一个always
    语句的逻辑操作成立条件,一般都是建立在上一个always语句的逻辑操作的寄存器的数值变化,这是阻碍初学者晦涩难懂的串行代码转并行硬件电路代码思维主要障碍。verilog
    的基本语法不做讲解,可以查看相关资料;还是以源码促进学习;
`timescale 1ps/1ns 
module div
( clk,rst_n,sw,led );input clk;
input rst_n;
input [2:0] sw;
output [2:0] led ;reg [2:0]clk_div;
reg [2:0]clk_div_t;always@(posedge clk or negedge rst_n)
if(!rst_n) clk_div<=3'b111;
else clk_div<={sw[0],sw[1],sw[2]};         always@(posedge clk or negedge rst_n)
if(!rst_n) clk_div_t<=3'b111;
else  clk_div_t<=clk_div;wire [2:0] key_an=(~clk_div) & clk_div_t;reg [20:0] cnt;always@(posedge  clk or negedge rst_n)  
if(!rst_n) cnt<=20'h0;
else if(key_an) cnt<=20'h0;
else cnt<=cnt+1'b1;reg [2:0] key_clk_div;
reg [2:0] key_clk_div_t;always@(posedge clk or negedge rst_n)
if(!rst_n) key_clk_div<=3'b111;
else if(cnt==20'hfffff)  key_clk_div<={sw[0],sw[1],sw[2]};always@(posedge clk or negedge rst_n)
if(!rst_n) key_clk_div_t<=3'b111;
else key_clk_div_t<=key_clk_div;wire [2:0] key_an_clk= (~key_clk_div) &(key_clk_div_t);  reg [2:0] d;always@(posedge clk or negedge rst_n)
if(!rst_n) 
begin
d[0]<=1'b0;
d[1]<=1'b0;
d[2]<=1'b0;
end 
else 
begin
if(key_an_clk[0]) d[0]<=~d[0];
if(key_an_clk[1]) d[1]<=~d[1];
if(key_an_clk[2]) d[2]<=~d[2]; 
end assign led[0]=d[0]?1'b1:1'b0;
assign led[1]=d[1]?1'b1:1'b0;
assign led[2]=d[2]?1'b1:1'b0;endmodule

上面是源代码,下面详细分析一下代码的含义;

reg [2:0]clk_div;
reg [2:0]clk_div_t;always@(posedge clk or negedge rst_n)
if(!rst_n) clk_div<=3'b111;
else clk_div<={sw[0],sw[1],sw[2]};        always@(posedge clk or negedge rst_n)
if(!rst_n) clk_div_t<=3'b111;
else  clk_div_t<=clk_div;wire [2:0] key_an=(~clk_div) & clk_div_t;

首先假设在按键未按下状态时,sw[0],sw[1],sw[2] 都为高电平 , 在每一个时钟周期上升沿过程中,clk_div 和clk_div_t 、key_an寄存器的数值分别会有下面的状态(每一列的一组数据代表一个上升沿的此刻寄存器中的数值)

regposedge clkposedge clkposedge clkposedge clkposedge clk
clk_div111111111111111
clk_div_t111111111111111
key_an000000000000000

当按键在第二个时钟周期上升沿之前按下sw[1]按键的时候,各个寄存器的状态如下

regposedge clkposedge clkposedge clkposedge clkposedge clk
clk_div111101101101101
clk_div_t111111101101101
key_an000000010000000

当在第三个时钟周期时候检测到有按键按下去(主要是因为非阻塞电路),即key_an 状态为 010

reg [20:0] cnt;always@(posedge  clk or negedge rst_n)  
if(!rst_n) cnt<=20'h0;
else if(key_an) cnt<=20'h0;
else cnt<=cnt+1'b1;reg [2:0] key_clk_div;
reg [2:0] key_clk_div_t;always@(posedge clk or negedge rst_n)
if(!rst_n) key_clk_div<=3'b111;
else if(cnt==20'hfffff)  key_clk_div<={sw[0],sw[1],sw[2]};always@(posedge clk or negedge rst_n)
if(!rst_n) key_clk_div_t<=3'b111;
else key_clk_div_t<=key_clk_div;wire [2:0] key_an_clk= (~key_clk_div) &(key_clk_div_t);  

首先因为按键的抖动原因 ,需要延迟20ms 在检测一下按键 是否真呈按下状态; 因为假设时钟周期为50Mhz ; 1/50Mhz=0.02us 若要延迟20ms 则首先让让寄存器cnt数值累加到20’HFFFFF 也就等同于累计了20’HFFFFF 时钟周期;计算为 0.02220=0.021000000=20ms (代码里面的20’HFFFFFF ${\approx}$220) 这样就完成了延迟20ms 时间,然后在锁定此时的按键状态,进行按键小灯点亮或者关闭;

reg [2:0] d;always@(posedge clk or negedge rst_n)
if(!rst_n) 
begin
d[0]<=1'b0;
d[1]<=1'b0;
d[2]<=1'b0;
end 
else 
begin
if(key_an_clk[0]) d[0]<=~d[0];
if(key_an_clk[1]) d[1]<=~d[1];
if(key_an_clk[2]) d[2]<=~d[2]; 
end assign led[0]=d[0]?1'b1:1'b0;
assign led[1]=d[1]?1'b1:1'b0;
assign led[2]=d[2]?1'b1:1'b0;

xdc文件

set_property PACKAGE_PIN K17 [get_ports clk]
set_property PACKAGE_PIN E17 [get_ports rst_n]
set_property PACKAGE_PIN M15 [get_ports {led[0]}]
set_property PACKAGE_PIN G14 [get_ports {led[1]}]
set_property PACKAGE_PIN M17 [get_ports {led[2]}]
set_property PACKAGE_PIN G15 [get_ports {led[3]}]
set_property PACKAGE_PIN M19 [get_ports {key[0]}]
set_property PACKAGE_PIN M20 [get_ports {key[1]}]
set_property PACKAGE_PIN L16 [get_ports {key[2]}]
set_property PACKAGE_PIN F16 [get_ports {key[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {key[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {key[1]}]
set_property IOSTANDARD LVCMOS33 [get_ports {key[2]}]
set_property IOSTANDARD LVCMOS33 [get_ports {key[3]}]
set_property IOSTANDARD LVCMOS33 [get_ports clk]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]

源代码

`timescale 1ns / 1ps
module led_demo(input  clk ,
input  rst_n ,
input  [3:0]key,
output [3:0]led );//50MHZ 1/50MHZ=0.02us  1s=1000000/0.02us=1000 000 00/2=50 000 000
//key 20ms/0.02us=2000000/2=1000000reg [3:0] key_first;always@(posedge clk or negedge rst_n)
if(!rst_n)key_first<=4'b1111;
elsekey_first<=key;wire [3:0]key_an=key_first&~key;reg [20:0] cnt ;
always@(posedge clk or negedge rst_n)if(!rst_n)cnt <= 0 ;
else if(key_an)cnt <= 0 ;
elsecnt<=cnt+1'b1;// 当加法超过24位位数之后,又变成0reg [3:0] key_second;  always@(posedge clk or negedge rst_n)
if(!rst_n)key_second<=4'b1111;
else if(cnt==20'd1_000_000)key_second<=key;reg [3:0] key_three;
always@(posedge clk or negedge rst_n)
if(!rst_n)key_three<=4'b1111;
else key_three<=key_second;wire [3:0]key_an_real=key_three&~key_second;
//wire [3:0]key_an_real=key_second&~key; //不能这样写 这样写会在按键之后, 这里会立刻记录按键bitreg [3:0] ledreg;
always@(posedge clk or negedge rst_n)
if(!rst_n)ledreg <=4'b1111 ;
else 
begin
if(key_an_real[0])  ledreg[0] <= ~ledreg[0]; 		
if(key_an_real[1])  ledreg[1] <= ~ledreg[1];   		
if(key_an_real[2])  ledreg[2] <= ~ledreg[2]; 	
if(key_an_real[3])  ledreg[3] <= ~ledreg[3]; 	
endassign led[0]=ledreg[0]?1'b1:1'b0;
assign led[1]=ledreg[1]?1'b1:1'b0;
assign led[2]=ledreg[2]?1'b1:1'b0;
assign led[3]=ledreg[3]?1'b1:1'b0;
endmodule

这篇关于5、SCM 按键消抖 vs FPGA 按键消抖的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/770070

相关文章

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

独立按键单击检测(延时消抖+定时器扫描)

目录 独立按键简介 按键抖动 模块接线 延时消抖 Key.h Key.c 定时器扫描按键代码 Key.h Key.c main.c 思考  MultiButton按键驱动 独立按键简介 ​ 轻触按键相当于一种电子开关,按下时开关接通,松开时开关断开,实现原理是通过轻触按键内部的金属弹片受力弹动来实现接通与断开。  ​ 按键抖动 由于按键内部使用的是机

FPGA编译与部署方法全方位介绍

FPGA编译与部署是FPGA开发中的核心环节,涉及从代码编写、调试到将设计部署到FPGA硬件的全过程。这个流程需要经过创建项目、编写FPGA VI、模拟调试、编译生成比特流文件,最后将设计部署到硬件上运行。编译的特点在于并行执行能力、定制化硬件实现以及复杂的时钟管理。通过LabVIEW的FPGA模块和NI硬件,可以快速完成开发和部署,尤其适用于复杂控制与高性能数据处理系统。 1. FPG

VS Code 调试go程序的相关配置说明

用 VS code 调试Go程序需要在.vscode/launch.json文件中增加如下配置:  // launch.json{// Use IntelliSense to learn about possible attributes.// Hover to view descriptions of existing attributes.// For more information,

FPGA开发:条件语句 × 循环语句

条件语句 if_else语句 if_else语句,用来判断是否满足所给定的条件,根据判断的结果(真或假)决定执行给出的两种操作之一。 if(表达式)语句; 例如: if(a>b) out1=int1; if(表达式)         语句1; else         语句2; 例如: if(a>b)out1=int1;elseout1=int2; if(表达式1) 语句1; els

FPGA开发:模块 × 实例化

模块的结构 对于C语言,其基本单元为函数。与此类似,Verilog的基本设计单元称之为"模块"(block)。对于整个项目的设计思想就是模块套模块。 一个模块由两个部分组成:一部分描述接口,一部分描述逻辑功能。 每个Verilog模块包含4个部分:端口定义、IO说明、内部信号声明、功能定义。且位于module和endmodule之间,如下: module block(a,b,c);inpu

LabVIEW环境中等待FPGA模块初始化完成

这个程序使用的是LabVIEW环境中的FPGA模块和I/O模块初始化功能,主要实现等待FAM(Field-Programmable Gate Array Module,FPGA模块)的初始化完成,并处理初始化过程中的错误。让我们逐步分析各部分的功能: 1. Wait for FAM Initialization框架 此程序框架用于等待I/O模块成功初始化。如果在5秒钟内模块没有完成配

FPGA随记——小说 可综合和不可综合

当然我在网络上找到了些可综合和不可综合的解释 感觉也很有参考价值: https://wenda.so.com/q/1378362174074040 综合就是把你写的rtl代码转换成对应的实际电路。 比如你写代码assign a=b&c; EDA综合工具就会去元件库里拿一个二输入与门出来,然后输入端分别接上b和c,输出端接上a 假如你写了很多这样的语句 assign a=b&c; assig

Xilinx系FPGA学习笔记(五)ROM的IP核学习

系列文章目录 文章目录 系列文章目录前言ROM IP分布式ROM生成ROM配置创建COE文件 块ROM生成如何快速生成Example Design 两种ROM对比 前言 最近在学习小梅哥的xilinx型FPGA开发板,一边学习一边记录,简化整理一下笔记 ROM IP 在 Memories &Storage Elements 下可以看到有两个与 ROM 相关的

3.门锁_STM32_矩阵按键设备实现

概述 需求来源: 门锁肯定是要输入密码,这个门锁提供了两个输入密码的方式:一个是蓝牙输入,一个是按键输入。对于按键输入,采用矩阵按键来实现。矩阵按键是为了模拟触摸屏的按键输入,后续如果项目结束前还有时间就更新为触摸屏按键输入。 矩阵按键开发整体思路: 由于矩阵按键就是GPIO的控制,所以不进行芯片和设备的分层编写,控制写在同一个文件中,最终向应用层提供一个接口。 代码层级关系: