目录
我们首先来看原理图

可以看到有4条输入线接到FPGA的IO口(最左边四个KEY)上,分两种情况:
1.当按键KEY1按下时,D3V3(也就是电源)通过电阻R(原理图上折线的那一段)然后再通过按键KEY1最终进入GND形成一条通路,那么这条线路的全部电压都加到了R这个电阻上,KEY1(最左边四个IO口)这个引脚就是个低电平。
2.当松开按键后,线路断开,就不会有电流通过,那么KEY1和D3V3就应该是等电位,是一个高电平。我们就可以通过KEY1这个IO口的高低电平来判断是否有按键按下。
由于机械按键的物理特性,按键被按下的过程中,存在一段时间的抖动,同
时在释放按键的过程中也存在抖动,这就导致在识别的时候可能检测为多次的按键按下,而通常检测到一次按键输入信号的状态为低电平,就可以确认按键被按下了,所以我们在使用按键时往往需要消抖,以确保按键被按下一次只检测到一次低电平
按键消抖解决方案1:延迟采样。(图片采用作者stark-lin)
按键消抖解决方案2:信号变化频率平稳后并且持续20ms则采样。

任务:
我们需要在检测到按键抖动的时刻延时20ms再采样
思路:我们首先需要一个模块来检测按键是否抖动,如果抖动,→ 给计时模块一个标志位开始计时,记满20ms,→ 再给输出消抖后按键信号模块一个标志位进行采样。
理清思路,整个程序分为三个模块,模块之间相互关联,关联之处需要一个起到连接作用的器件,也就是标志位。比如将flag作为标志位,检测到按键抖动之后,将flag作为计时开始的条件。下面开始编写代码,之后进行时序以及代码分析。
key_debounce.v
module key_debounce (
input wire clk, //系统时钟 50MHz
input wire rst_n, //复位信号
input wire key, //按键输入信号
output reg key_done //消抖之后的按键信号
);
reg key_r0; //同步信号(滤波作用,滤除小于一个周期的抖动)
reg key_r1; //打拍
reg flag; //标志位
wire nedge; //下降沿检测(检测到下降沿代表开始抖动)
//计时器定义
reg [19:0] cnt;
wire add_cnt; //计时器开启
wire end_cnt; //计时记满
parameter MAX_CNT=20'd1_000_000; //20ms延时
//同步
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
key_r0<=1'b1;
end
else
key_r0<=key;
end
//打拍
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
key_r1<=1'b1;
end
else
key_r1<=key_r0;
end
assign nedge = ~key_r0 & key_r1; //检测到下降沿拉高
//标志位
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
flag<=1'b0;
end
else if (nedge) begin
flag<=1'b1;
end
else if (end_cnt) begin
flag<=1'b0;
end
end
//延时模块
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt<=20'b0;
end
else if (add_cnt) begin
if (end_cnt) begin
cnt<=20'b0;
end
else
cnt<=cnt+1;
end
end
assign add_cnt=flag; //计时器开启
assign end_cnt=add_cnt&&cnt==MAX_CNT-1; //计时器关闭
//key_done输出
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
key_done<=1'b0;
end
else if (end_cnt) begin //延时满20ms采样
key_done<=~key_r0;
end
else
key_done<=1'b0;
end
endmodule
clock:我们的整个程序都是在时钟的控制下运行的,所有的always模块都对时钟的上升沿敏感,本人使用的开发板时钟频率是50MHz,也就是一秒震动50_000_000次,一个周期就是20ns
always @(posedge clk or negedge rst_n)
key_in:这是按键输入信号,低电平有效
key_r0:为了滤除掉小于一个周期的抖动,对key_in推迟一个周期进行同步。看图
方框位置key_in输入的按键信号抖动为低电平,小于一个周期,在时钟上升沿(posedge 代表上升沿敏感)到来的时候信号已经恢复高电平,key_r0还是保持高电平,这样就达到了滤波的效果

key_r1:这个信号是把同步信号再延时一个周期,主要是为了保存key_r0上一个周期的值,来判断是否出现下降沿
nedge:检测key_r0是否出现下降沿,若出现,则将标志位flag设为1,也是计时器开启的条件
cnt:20ms计时器
add_cnt:计时器开启条件,用flag表示
end_cnt:计时器结束条件,记满20ms,也是采样模块的开启条件
key_done:计时器记满,则开始采样,key_done拉高一个周期,代表按键按下
代码:
`timescale 1ns/1ps
module tb_key_debounce ();
reg tb_clk;
reg tb_rst_n;
reg tb_key;
wire tb_key_done;
integer i,j;
defparam u_key_debounce.MAX_CNT=5; //将延时的时间修改为5个周期,也就是100ns
key_debounce u_key_debounce(
.clk (tb_clk) , //系统时钟 50MHz
.rst_n (tb_rst_n) , //复位信号
.key (tb_key) , //按键输入信号
.key_done (tb_key_done) //消抖之后的按键信号
);
always #10 tb_clk=~tb_clk;
initial begin
tb_clk=1'b1;
tb_rst_n=1'b1;
tb_key=1'b1;
#100;
//复位
tb_rst_n=1'b0;
#100;
//恢复
tb_rst_n=1'b1;
//模拟按键抖动
for (i = 0; i<=15; i=i+1) begin
j=({$random}%15);
#(j);
tb_key={$random};
end
//按键按下
tb_key=1'b0;
#150;
tb_key=1'b1;
#2000;
$stop;
end
endmodule
方框标红处延时20ms开始采样,拉高一个周期

任务:
我们需要不停的检测到按键抖动,直到信号稳定之后再延时20ms,之后采样
思路:我们首先需要一个模块来检测按键是否抖动,如果抖动,→ 给计时模块一个标志位开始计时,记满20ms,→ 再给输出消抖后按键信号模块一个标志位进行采样。
计时模块与方式1不同,只要还在抖动就把计数器清零,重新计数20ms,直到抖动稳定
key_debounce.v
module key_debounce(
input wire clk,
input wire rst_n,
input wire key_in,
output reg key_flag, //判断抖动是否消除的标志信号,0为抖动,1为抖动结束
output reg key_value //消抖后稳定的按键值给到蜂鸣器模块
);
//定义20ms延迟计数器,0.2s,1_000_000次
reg [19:0] delay_cnt;
//寄存依次key的值用来判断按键是否消抖成功
reg key_reg;
parameter MAX_CNT=20'd1_000_000; //20ms
//20ms延时计数器
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
begin
key_reg <= 1'b1; //复位信号,设置按键无效
delay_cnt <= 1'b0; //计数器设置为0
end
else
begin
key_reg <= key_in;
if(key_reg == 1 && key_in == 0) //当这一次key值和上一次key值不一样,证明正在抖动
delay_cnt <= MAX_CNT; //延迟时间20ms
else if(delay_cnt > 0)
delay_cnt <= delay_cnt - 1; //没有抖动,开始20ms倒计时
else
delay_cnt <= 1'b0;
end
end
//根据延时计数器获取按键状态以及按键值
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
begin
key_flag <= 1'b0; //复位信号,设置信号标志为抖动
key_value <= 1'b1; //设置抽样值为1
end
else
begin
if(delay_cnt == 20'd1) //倒计时1_000_000到1
begin
key_flag <= 1'b1;
key_value <= key_in; //稳定20ms后将key值给到key_value
end
else
begin
key_flag <= 1'b0;
key_value <= key_value; //20ms内先不取样
end
end
end
endmodule
这里因为没有同步信号,和打拍信号,比较容易理解,就不画时序图做分析,可以对照仿真进行理解
计时模块中此处

key_reg相当于消抖方式1的同步信号,同样检测到下降沿开始计时(即计时器清零,我们这里使用倒计时,效果一样)
红框中的条件也可以换成key_reg != key_in,但是意义就不一样了,更改了之后就是只要前一个周期跟现在的信号有差异就计数,也就是出现下降沿和上升沿都计数,实际上差别不大,因为抖动时间在5-10ms内,有抖动一定会出现上升沿和下降沿
代码:
`timescale 1ns/1ps
module tb_key_debounce ();
reg tb_clk;
reg tb_rst_n;
reg tb_key_in;
wire tb_key_flag;
wire tb_key_value;
integer i,j;
defparam u_key_debounce.MAX_CNT=5; //将延时的时间修改为5个周期,也就是100ns
key_debounce u_key_debounce(
.clk (tb_clk) , //系统时钟 50MHz
.rst_n (tb_rst_n) , //复位信号
.key_in (tb_key_in) , //按键输入信号
.key_flag (tb_key_flag) , //标志位
.key_value (tb_key_value) //消抖之后的按键信号
);
always #10 tb_clk=~tb_clk;
initial begin
tb_clk=1'b1;
tb_rst_n=1'b1;
tb_key_in=1'b1;
#100;
//复位
tb_rst_n=1'b0;
#100;
//恢复
tb_rst_n=1'b1;
//模拟按键抖动
for (i = 0; i<=15; i=i+1) begin
j=({$random}%15);
#(j);
tb_key_in={$random};
end
//按键按下
tb_key_in=1'b0;
#150;
tb_key_in=1'b1;
#2000;
$stop;
end
endmodule
红框处,计时器从我修改的值延时5个周期(100ns),倒计时到0,key_value采样,电平拉低一个周期,代表按键低有效
上面所提到的按键消抖方式我们可以想象:
①验证密码:
假设我们的密码是10101,设置key[1]代表密码1,key[0]代表密码0
我们第一次按下key[1],经过消抖之后,系统只会检测到一个按键低电平,代表只按了一次key[1],否则,不消抖的话,按一次,系统能检测到多次低电平,输入的密码就会出现错误
②按键控制LED灯状态切换
按下一个按键并松开,LED保持对应状态,切换按键,LED切换状态
如果跟①一样,那么按键必须按住不放LED才会显示对应的状态,因为按照我们的按键消抖,按一下只会输出一个周期的低电平,所以需要一直按下按键,才能保持低电平,在不改变按键消抖的前提下,我们可以设置一个标志位,在按键消抖模块输出一个周期低电平的时候给标志位flag赋值为1,让flag作为LED状态的条件
说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时
我有一个散列:sample={bar:200,foo:100,baz:100}如何使用sort_order中的键顺序对sample进行排序:sort_order=[:foo,:bar,:baz,:qux,:quux]预期结果:sample#=>{foo:100,bar:200,baz:100}我能想到的就是new_hash={}sort_order.each{|k|new_hash[k]=sample[k]unlesssample[k].nil?}sample=new_hash必须有更好的方法。提示?不应该出现没有值的键,即键的数量保持不变,SortHashKeysbasedonord
FPGA时钟和时钟域时钟树所谓时钟树为FPGA内部资源,分:全局时钟树,区域时钟树,IO时钟树原则上优先使用全局时钟树,在GT接口上使用IO时钟树,一般工具也会对GT时钟加以限制;时钟树使用方式正确的物理连接FPGA会由物理管脚专门用于全局时钟设置,通过查询数据手册可以在PCB设计阶段进行确认,当外部时钟接入此管脚时,工具会自动占有全局时钟树资源,当接入普通信号时不会分配时钟树资源;恰当的代码描述原语的使用,即BUFG的使用,可以将PLL的输出等内部时钟进行全局时钟资源的分配;IO时钟资源需要参考相应接口手册,以ultrascale的GTH为例,其JESD204的时钟方案针对不同的子类会由不同
外部SPIFLASH:MicronN25Q128A13ESE40G(128Mbit(16MByte))FPGA:XC7A100T CPU:Microblaze第一种情况:Microblaze在简单的应用,比如运行LED,IIC,SPI,UART之类的低俗接口驱动,或做一些简单的辅助型工作时,一般生成的applicationelf文件都不大,在10几KB或者几十,百几KB,此时使用FPGA内部的BRAM资源已经足够。XC7A100T本身就有600几KB的BRAM资源。这种情况下直接将硬件流文件和elf文件合并为download.bit文件,在直接烧录到外部SPIFLAH即可。1.Xilinx--
根据特定的键:值或方法返回的结果,删除对象数组中重复项的最快/单行方法是什么?例如,我有20个名称相同的XML元素节点,但它们具有不同的“文本”值,其中一些是重复的。我想通过说“ifelement.text==previous_element.text,删除它”来删除重复项。我如何用最短的代码在Ruby中做到这一点?我已经看到如何为简单的字符串/整数值执行此操作,但不是为对象执行此操作。 最佳答案 这是标准的散列方式。注意||=运算符的使用,这是一种更方便的(a||=b)方式来编写a=bunlessa.array.inject({}
1FPGA启动流程图1 7SerialsFPGA配置流程1.1DevicePower-Up1.2ClearConfigurationMemory在上电后的任何时间内,可以对Slave-FPGA配置存储器(BlockRAM)进行复位处理。复位方式是将PROGRAM_B信号拉低(下降沿有效)。1.3SampleModePins当复位完成后,INIT_B恢复高电平,Slave-FPGA对M[2:0]模式引脚进行采样,然后开始在CCLK上升沿接收配置数据。1.4Synchronization在接收配置数据前,Slave-FPGA首先进行总线位宽检测。主机发送的配置文件中,“BusWidthAutoDe
我有一个由以下形式的哈希组成的数组:[{:user=>"mike"etc},{:user=>"mike"etc},{:user=>"peter"etc},{:user=>"joe"etc}]有什么方便的方法可以根据userkey的值进行分组?最终结果应该是这样的:[[{:user=>"mike"etc},{:user=>"mike"etc}],[{:user=>"peter"etc}],[{:user=>"joe"etc}]] 最佳答案 使用group_by。array.group_by{|h|h[:user]}.values
我有一个简单的任务需要等待文件系统上的某些更改(它本质上是一个原型(prototype)编译器)。所以我有一个简单的无限循环,在检查更改的文件后休眠5秒。loopdo#iffileschanged#processfiles#andputsresultsleep5end而不是Ctrl+C敬礼,我宁愿能够测试并查看是否按下了某个键,而不会阻塞循环。本质上,我只需要一种方法来判断是否有传入的按键,然后是一种获取它们直到遇到Q的方法,然后退出程序。我想要的是:defwait_for_Qkey_is_pressed&&get_ch=='Q'endloopdo#iffileschanged#pro
我正在尝试按键按字母顺序对哈希进行排序,但如果不创建我自己的排序类,我似乎无法找到一种方法来做到这一点。如果它是一个整数,我发现下面的代码按值排序,我试图修改它但没有任何运气。temp["ninjas"]=36temp["pirates"]=12temp["cheese"]=222temp.sort_by{|key,val|key}我的目标是按键排序哈希,然后输出值。我将不得不使用不同的哈希顺序但相同的值多次执行此操作。 最佳答案 假设您希望输出是一个散列,它将按排序顺序遍历键,那么您就快完成了。Hash#sort_by返回一个Ar
我有一个哈希数组:[{"Vegetable"=>10},{"Vegetable"=>5},{"DryGoods"=>3>},{"DryGoods"=>2}]我想我需要在这里使用inject,但我真的很挣扎。我想要一个新的散列来反射(reflect)先前散列的重复键的总和:[{"Vegetable"=>15},{"DryGoods"=>5}]我控制着输出这个散列的代码,所以我可以在必要时修改它。结果主要是散列,因为这最终可能会嵌套任意数量的层级,然后很容易在数组上调用展平但不会展平散列的键/值:defrecipe_pl(parent_percentage=nil)ingredients.