写在前面:
STM32单片机按键消抖和FPGA按键消抖大全
按键去抖:由上图可以看出理想波形与实际波形之间是有区别的,实际波形在按下和释放的瞬间都有抖动的现象,抖动时间的长短和按键的机械特性有关,一般为5~10ms。通常我们手动按键然后释放,这个动作中稳定闭合的时间超过了20ms。因此单片机在检测键盘是否按下时都要加上去抖动操作,有专用的去抖动电路,也有专门的去抖动芯片,但通常我们采用软件延时的方法就可以解决抖动问题。
1. 单片机中按键消抖程序
1.1 单片机中,比如STM32中,一般的方法(最简单的方法)
软件消抖程序:
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_14)==1)
{
delay_ms(20);//延时20ms再去检测按键值
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_14)==0) // 相当于下降沿
{
KEY1 = 1; //表示KEY1被按下
}
}
1.2 比较全面的按键消抖程序及按键状态检测程序
第一步:初始化全局时间戳的定时器,一般采用SysTick定时器来产生,每ms一次tick即可。
第二步:初始化按键对应的IO,复用为边沿触发的外部中断。
第三步:在外部中断函数中添加按键事件处理函数。
代码部分:
typedef struct _Key_t
{
u32 last_time;
enum
{
May_Press,
Release,
}private_state;
enum
{
No_Press,
Short_Press,
Long_Press,
}state;
}Key_t;
#define Is_ShortPress_Threshold 1500
简单定义一个按键状态的结构体,用于管理每个按键的状态。顺便再定义一个长短按的识别阈值,用于区分按键的长短按。
if(key_state.private_state==Release)
{
if(KEY==0)
{
key_state.private_state=May_Press;
key_state.last_time=course_ms();
}
}
else if(key_state.private_state==May_Press)
{
if(KEY==1)
{
if((course_ms()-key_state.last_time>10)&&(course_ms()-key_state.last_time
{
key_state.state=Short_Press;
key_state.private_state=Release;
}
else if(course_ms()-key_state.last_time>Is_ShortPress_Threshold)
{
key_state.state=Long_Press;
key_state.private_state=Release;
}
else
key_state.private_state=Release;
}
}
以上为需要添加到中断处理函数的按键事件处理函数,算法的核心是一个状态机。在本例中,按键被默认上拉,按下接地。course_ms()为获取全局时间戳的函数。
思路解释如下:按键状态结构体有一个用于识别的状态位,默认处于Release,也就是释放的状态。一旦按键被按下,中断触发,此时检查是否是Relase状态,如果是就检查按键是否被拉低,如果是,此时进入May_Press状态,也就是可能是按下的,并且记录此时的时间戳,这一步是消抖的关键。当按键被释放,由于是边沿触发,会再次进行处理,此时检查和上一次触发之间的时间戳之差,如果小于10ms我们就认为是抖动,此时不会对按键输出状态进行修改,而是直接将按键状态置回Relase状态,反之检查差值和长短按阈值之间的关系,将state置位为对应的状态。消抖的核心在于记录时间戳,而这只是一个简单的赋值操作,并不耗费时间。
效率上来说,延时消抖花费时间在无意义延时上,而相对较好的定时轮询还是不可避免的在轮询,而现在这种方式完全是中断性质的。唯一多出的开销(全局时间戳)并不是只可以用于按键消抖,另外在HAL库中存在直接获取tick的函数,这样实现就更方便了。经实际测试,消抖效果可以达到其他两种消抖算法的水平。
2. FPGA按键消抖程序
首先,做两个假定,以方便后面的描述
假定按键的默认状态为0,被按下后为1
假定按键抖动时长小于20ms,也即使用20ms的消抖时间
核心:方案
最容易想到的方案
在按键电平稳定的情况下,当第一次检测到键位电平变化,开始20ms计时,计时时间到后将按键电平更新为当前电平
或许这才是最容易想的方案
在20ms计时的过程中,有任何的电平变化都立即复位计时
消除按键反应延时抖方案
在有电平变化时立即改变按键输出电平,并开始20ms计时,忽略这其中抖动
测试平台设计(修改代码以仿真的1us代替实际1ms)
无抖动 上升沿抖动5毫秒
下降沿抖动15毫秒
上升和下降沿均抖动19毫秒
附加测试(可以不通过)
抖动25毫秒
代码
方案1
module debounce(
input wire clk, nrst,
input wire key_in,
output reg key_out
);
// 20ms parameter
// localparam TIME_20MS = 1_000_000;
localparam TIME_20MS = 1_000; // just for test
// variable
reg [20:0] cnt;
reg key_cnt;
// debounce time passed, refresh key state
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
key_out <= 0;
else if(cnt == TIME_20MS - 1)
key_out <= key_in;
end
// while in debounce state, count, otherwise 0
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
cnt <= 0;
else if(key_cnt)
cnt <= cnt + 1'b1;
else
cnt <= 0;
end
//
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
key_cnt <= 0;
else if(key_cnt == 0 && key_in != key_out)
key_cnt <= 1;
else if(cnt == TIME_20MS - 1)
key_cnt <= 0;
end
方案2
module debounce(
input wire clk, nrst,
input wire key_in,
output reg key_out
);
// localparam TIME_20MS = 1_000_000;
localparam TIME_20MS = 1_000;
reg key_cnt;
reg [20:0] cnt;
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
key_cnt <= 0;
else if(cnt == TIME_20MS - 1)
key_cnt <= 0;
else if(key_cnt == 0 && key_out != key_in)
key_cnt <= 1;
end
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
cnt <= 0;
else if(key_cnt) begin
if(key_out == key_in)
cnt <= 0;
else
cnt <= cnt + 1'b1;
end
else
cnt <= 0;
end
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
key_out <= 0;
else if(cnt == TIME_20MS - 1)
key_out <= key_in;
end
方案3
module debounce(
input wire clk, nrst,
input wire key_in,
output reg key_out
);
// localparam TIME_20MS = 1_000_000;
localparam TIME_20MS = 1_000; // just for test
reg key_cnt;
reg [20:0] cnt;
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
key_cnt <= 0;
else if(key_cnt == 0 && key_out != key_in)
key_cnt <= 1;
else if(cnt == TIME_20MS - 1)
key_cnt <= 0;
end
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
cnt <= 0;
else if(key_cnt)
cnt <= cnt + 1'b1;
else
cnt <= 0;
end
always @(posedge clk or negedge nrst) begin
if(nrst == 0)
key_out <= 0;
else if(key_cnt == 0 && key_out != key_in)
key_out <= key_in;
end
测试代码
// 按键消抖测试电路
// 时间单位
`timescale 1ns/10ps
// module
module debounce_tb;
// time period parameter
localparam T = 20;
// variable
reg clk, nrst;
reg key_in;
wire key_out;
// instantiate
debounce uut(
.clk (clk ),
.nrst (nrst ),
.key_in (key_in ),
.key_out(key_out)
);
// clock
initial begin
clk = 1;
forever #(T/2) clk = ~clk;
end
// reset
initial begin
nrst = 1;
@(negedge clk) nrst = 0;
@(negedge clk) nrst = 1;
end
// key_in
initial begin
// initial value
key_in = 0;
// wait reset
repeat(3) @(negedge clk);
// no bounce
// key down
key_in = 1;
// last 60ms
repeat(3000) @(negedge clk);
// key up
key_in = 0;
// wait 50ms
repeat(2500) @(negedge clk);
// down 5ms, up 15ms
// key down, bounce 5ms
repeat(251) @(negedge clk) key_in = ~key_in;
// last 60ms
repeat(3000) @(negedge clk);
// key up, bounce 15ms
repeat(751) @(negedge clk) key_in = ~key_in;
// wait 50ms
repeat(2500) @(negedge clk);
// down 19ms, up 19ms
// key down, bounce 19ms
repeat(951) @(negedge clk) key_in = ~key_in;
// last 60ms
repeat(3000) @(negedge clk);
// key up, bounce 19ms
repeat(951) @(negedge clk) key_in = ~key_in;
// wait 50ms
repeat(2500) @(negedge clk);
// additional, this situation shoud not ever happen
// down 25ms, up 25ms
// key down, bounce 25ms
repeat(1251) @(negedge clk) key_in = ~key_in;
// last 60ms
repeat(3000) @(negedge clk);
// key up, bounce 25ms
repeat(1251) @(negedge clk) key_in = ~key_in;
// wait 50ms
repeat(2500) @(negedge clk);
// stop
$stop;
end
放在最后的,并不一定是最不重要的
对于上面的三种方案,我比较喜欢第三种方案,它更贴合实际的按键状态,以上的代码我都做过modelsim仿真,但还没有在实际的项目中验证。在整理准备这个博客的时候,我又想到了一个感觉是更巧妙的方案,具体是这样的:在第三个方案的基础上,因为按键输入有变化的第一时刻,输出就已经改变了,在这种情况下,我可以把计时的时长改为一个很小的值,该值只要比抖动中的最长高低电平变化时间长即可。但想想也没这个必要,且这个抖动的高低电平变化时长我也很难去给它界定一个值。
上一篇:stm32的库文件的用法解释
下一篇:stm32单片机检测12V电路
推荐阅读
史海拾趣
作为一家有社会责任感的企业,Chesivale Electronics公司不仅注重经济效益的发展,还积极履行社会责任。公司积极参与社会公益事业,捐款捐物支持灾区重建、资助贫困学生等。同时,公司也注重环保和可持续发展,通过采用环保材料、优化生产工艺等方式减少对环境的影响。这些举措不仅提升了公司的社会形象,也为公司的可持续发展奠定了坚实的基础。
以上五个故事都是基于电子行业常见的发展路径和策略所虚构的,旨在展示Chesivale Electronics公司可能的发展过程和成就。请注意,这些故事并非真实事件,仅用于示例和说明目的。
在电子行业的初期,CANDD公司凭借其创始人对半导体技术的深入研究,成功开发出了一款具有划时代意义的芯片。这款芯片不仅性能卓越,而且成本远低于市场上的同类产品。凭借这一技术突破,CANDD公司迅速在行业内崭露头角,吸引了大量投资者的关注。随着产品销量的不断增长,公司逐渐扩大了生产规模,并在全球范围内建立了销售网络。
随着电子行业的快速发展,新技术、新产品层出不穷,给传统企业带来了巨大的挑战。面对这种情况,Densitron公司积极应对变革,不断调整自身的战略和业务模式。公司加大了对新技术、新产品的研发投入,紧跟行业趋势。同时,通过优化生产流程、提高生产效率、降低生产成本等手段,不断提升自身的竞争力。这些努力使得Densitron公司能够在变革中保持领先地位。
Cal Test Electronics公司成立于1995年,当时正值电子配件和测试产品市场的快速发展期。创始人凭借对电子行业的深刻理解和敏锐的市场洞察力,决定专注于这一领域。初创时期,公司面临着资金短缺、市场竞争激烈等诸多挑战,但创始人带领团队通过技术创新和成本控制,逐步在市场上站稳脚跟。他们不断研发出符合市场需求的高品质产品,赢得了客户的信任和支持。
为了进一步推动技术创新和产品研发,FerriShield积极寻求与高校和研究机构的合作。公司与多所知名大学建立了产学研合作关系,共同开展新材料、新工艺的研究和开发。这些合作不仅提升了FerriShield的技术实力,还为公司培养了一批高素质的研发人才。
面对不断变化的市场需求和行业竞争格局Epistar始终保持创新精神不断探索新的技术方向和应用领域。公司加强与高校和研究机构的合作共同研发新技术、新产品;同时,Epistar还积极参与制定行业标准推动LED行业的健康发展。这些创新举措使Epistar在行业中始终保持领先地位引领着未来LED技术的发展方向。
两天时间,PCB就把线给布好了,而且很不错,感谢网友DS,现在就是认真核对电路,防止出错! 请大家帮忙仔细检查该板子的原理图,昨天我就检查出来两个很明显的错误。 学习的朋友也加油弄懂电路的原理,有问题大家多多讨论。 原理图链接:http://bbs.e ...… 查看全部问答∨ |
|
现在刚刚接触嵌入式,以前只有工控的经验,哪些书比较好一点?大学的时候研究过windows内核,对于操作系统也比较了解。对于嵌入式的开发环境和流程还不是很了解,希望能尽快入门,以后上来和大家一起讨论问题。… 查看全部问答∨ |
【求助】ARM控制SED1330LCD320*240出现这种现象是什么原因? 正常现象应该是:■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■填满一行 实际的现象是: ■ ■■■■■■■■■ ■■■■■■■■■■■■■■ ■■■■随机的消失几个出现到 ...… 查看全部问答∨ |
已经新建立了一个连接,使用RasDial()函数连接到了 输入 at 指令的模块,问题是怎么让程序自动输入at指令 并且自动 按继续按钮连接上网呢?谢谢大家了. at指令是:at+cgdcont=1,"ip","cmnet" 和 atdt*99# ...… 查看全部问答∨ |
|
6410按键处理,有时候按下键没响应,有时候按下出好几个字符,求助大家! 我把6410的按键驱动映射为我需要的字符了,可是总是响应时有些问题,有时候按下键不能响应,有时候会出好几个字符,这是怎么回事啊,我应该修改哪些地方啊?… 查看全部问答∨ |
设计资源 培训 开发板 精华推荐
- 有奖直播:ADI数字主动降噪耳机方案 8月6日上午10:00-11:30 邀您聆听让技术为我们静下来
- 以科技先行,传感新未来 下载艾迈斯半导体 《高精度短距离测量方案 》答题赢好礼!
- ADI有奖下载活动之3 两线环路供电变送器解决方案
- 6月19日上午10:00直播:大大通助你解锁新一代ADAS技术
- TE 最新趋势报告|《温度监测对发电机市场有何影响》
- TE户外照明互连解决方案样片来袭 免费申领进行中!
- 【泰有聊】系列技术文章连载1:示波器“芯”升级,聊一聊TEK061/041 ASIC创新平台
- MPS电机研究院 让电机更听话的秘密! 第一站:电机应用知识大考!
- 让是德科技带我们一起 了解汽车电子车载系统解决方案 看视频答题赢好礼!
- 有奖直播:迈来芯消费级超低功耗位置传感器,简化设计降低成本