4x4按键扫描 前几贴介绍了时钟使用与反跳处理,本帖以4x4按键扫描一个具体的简单示例展示如何运用时钟与反跳处理。 4x4按键电路简介4x4按键排列成4行4列结构共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~15。kPress=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; 行1:1ms计数器,用于定时每1ms扫描一行。 行2:P1K为1KHz周期脉冲,010贴中提到用作使能下一行扫描,等效为1KHz时钟。 行3:smp为扫描一行时采样列数据时刻。 行4:scan扫描行计数,0到3循环。控制ln_out输出。 行5~8:读取16个按键是否按下状态放入kDn。 行9:k_rd指示4行扫描完毕,可以一次读取16键按下状态。k_rd周期是4ms。 行10:pri_encoder(v)优先编码函数,返回从左到右v中第一个比特为1的位置,v全0时返回v的比特个数。由于kDn中比特为0表示按键按下,所以采用notkDn来判断第一个0的位置。所以kCode=16时表示无按键按下,kCode=0~15表示对应按键被按下。 行11~14:对kCode值进行反跳处理,kCode持续128ms值保持不变则装载到vKey中。关于并行反跳处理已在012贴中论述。 行15和16,最后输出。行15 中not vKey(0)取反目的是用1表示有键按下,更加符合常规思维习惯。 |