[VHDL]

玩转VHDL013-4x4按键扫描

[复制链接]
830|1
手机看帖
扫描二维码
随时随地手机跟帖
ucx|  楼主 | 2017-11-4 00:04 | 显示全部楼层 |阅读模式
4x4按键扫描
前几贴介绍了时钟使用与反跳处理,本帖以4x4按键扫描一个具体的简单示例展示如何运用时钟与反跳处理。
4x4按键电路简介
4x4按键排列成44列结构共16个按键,8根引线。这8根引线分别是行线4根和列线4根。在FPGA连接电路中可以把行线作为输入,列线作为输出,或者相反。本示例选用列作为输入,行作为输出。作为输入的4条列线应该分别接10K上拉电阻,或者在FPGA内部设置列输入为弱上拉。
VHDL定义行和列引脚如下:
        col_in               : in std_vector(0 to 3);    --列输入
        ln_out              : out std_vector(0 to 3); --行输出
按键扫描方法
ln_out循环输出0111, 1011, 1101, 1110,每次输出只有1行为0其他3行为1。同时每改变一次输出,读取col_in一次。那么一轮循环共读取16个比特的数值。这16个比特中值为0的表示对应按键被按下,如果全1说明没有按键按下。
具体实现
按键实现模块命名为Key4x4_scan,系统时钟采用50M
Entity Key4x4_scan is Port(
        clk50M                     : in std_logic;
        col_in                       : in std_vector(0 to 3);
        ln_out                       : out std_vector(0 to 3);
        qKey                                 : out std_vector(0 to 3);
        kPress                       : out std_logic
        );
End entity Key4x4_scan;
引脚功能描述:
clk50M:系统50M时钟。
kPress:无按键按下时值为0,有按键按下时值为1
qKey:按键号的4比特编码,值0~15kPress=0时不不关心qKey数值。
模块实现全部代码
Architecture myFavor of Key4x4_scan is
        attributealtera_attribute       : string;
        alias clock                                 : std_logic isclk50M;
        signal C1K                                 : std_vector(0to 15);
        signal kDn                                 : std_vector(0to 15);
        signal scan                                : std_vector(0to 1);
        signal P1K,smp                         : std_logic;
        signal k_rd,k_same          : std_logic;
        signal kCode                     : std_vector(0 to 4);
        signal cn_bounce             : std_vector(0 to 4); --128ms
        signal vKey,kBuf               : std_vector(0 to 4);
        attributealtera_attribute of vKey : signal is "power_up_level=high";
Begin
Process(clock) begin        
        if rising_edge(clock)then
1.              RstIncDec(C1K,C1K=50e3-1, true);
2.              P1K <=b_nor(C1K);
3.              smp <=STDZ(C1K=40e3-1);
4.              IncDec(scan,P1K);
        end if;      
End process;
With scan select ln_out
<=    "0111"     when "00",
        "1011"     when "01",
        "1101"     when "10",
        "1110"     when others;
Process(clock) begin        
        if rising_edge(clock)then
5.              LoadValue(kDn(0to 3),   col_in, smp and scan=0);
6.              LoadValue(kDn(4to 7),   col_in, smp and scan=1);
7.              LoadValue(kDn(8to 11), col_in, smp and scan=2);
8.              LoadValue(kDn(12to 15),col_in, smp and scan=3);
9.              k_rd <= P1Kand scan=3;
10.           kCode <=pri_encoder(not kDn);
11.           LoadValue(kBuf,kCode, k_rd);
12.           LoadValue(k_same,STDZ(kCode=kCode), k_rd);
13.           RstIncDec(cn_bounce,not k_same, k_rd);
14.           LoadValue(vKey,kBuf, b_and(cn_bounce&k_rd&k_same));
        end if;      
End process;
15.   kPress <= not vKey(0);
16.   qKey <= vKey(1 to 4);
End myFavor;
11ms计数器,用于定时每1ms扫描一行。
2P1K1KHz周期脉冲,010贴中提到用作使能下一行扫描,等效为1KHz时钟。
3smp为扫描一行时采样列数据时刻。
4scan扫描行计数,03循环。控制ln_out输出。
5~8:读取16个按键是否按下状态放入kDn
9k_rd指示4行扫描完毕,可以一次读取16键按下状态。k_rd周期是4ms
10pri_encoder(v)优先编码函数,返回从左到右v中第一个比特为1的位置,v0时返回v的比特个数。由于kDn中比特为0表示按键按下,所以采用notkDn来判断第一个0的位置。所以kCode=16时表示无按键按下,kCode=0~15表示对应按键被按下。
11~14:对kCode值进行反跳处理,kCode持续128ms值保持不变则装载到vKey中。关于并行反跳处理已在012贴中论述。
1516,最后输出。行15 not vKey(0)取反目的是用1表示有键按下,更加符合常规思维习惯。

相关帖子

ucx|  楼主 | 2017-11-4 00:05 | 显示全部楼层
小结
本示例中,用P1K来控制1ms时间,而没有用C1K的最高位作为时钟已在贴010和011中论述。
行11~14是对kCode进行反跳处理,而不是直接对按键输入col_in进行反跳处理是基于如下考虑。如果col_in进行100ms反跳处理,那么处理后col_in至少带来100ms时延。这使得扫描一行的时间不能小于100ms,一轮扫描4行共需要400ms时间,则明显感觉按键输入迟钝。而小于100ms时间的反跳处理又不能完全滤除反跳。
本帖以4x4按键扫描为例,展示了时钟问题和反跳处理,并重点对多个反跳输入情况加以分析论述。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

ucx

28

主题

85

帖子

5

粉丝