经典的verilog键盘扫描程序

合集下载

(整理)经典的verilog键盘程序

(整理)经典的verilog键盘程序
经典的verilog键盘扫描程序
2.环境影评价工作等级的划分依据
2.规划环境影响评价的内容
报告内容有:建设项目基本情况、建设项目所在地自然环境社会环境简况、环境质量状况、主要环境保护目标、评价适用标准、工程内容及规模、与本项目有关的原有污染情况及主要环境问题、建设项目工程分析、项目主要污染物产生及预计排放情况、环境影响分析、建设项目拟采取的防治措施及预期治理效果、结论与建议等。2.环境影响评价工程师职业资格制度
3.评估环境影响的价值(最重要的一步):采用环境经济学的环境经济损益分析方法,对量化后的环境功能损害后果进行货币化估价,即对建设项目的环境费用或环境效益进行估价。
(1)生产力变动法

基于FPGA的键盘扫描程序的设计

基于FPGA的键盘扫描程序的设计

摘要在现代电子工业的控制电路中,键盘扫描和显示电路对系统的调试和设置有着重要的作用。

随着EDA技术的发展,基于FPGA的扫描键盘因其结构简单,能有效防止机械键盘按键抖动带来的数据错误等优点在许多电子设备中都得到了广泛的应用。

本文主要是设计一个基于FPGA的键盘扫描程序,该设计在EDA工具Quarutus II9.0上开发完成,以Creat-SOPC2000实验箱上的4*4矩阵键盘为硬件实体,设计键盘扫描程序,将程序划分为时序产生模块、键盘扫描模块、弹跳消除模块、键值译码模块四个模块,时序产生模块为键盘扫描和弹跳消除模块产生时钟信号,键盘扫描模块采用行扫描法对4*4矩阵键盘进行扫描,键值译码模块将所按键值译码为共阳极8位7段数码管的显示码,几个模块组合起来实现键盘扫描的设计要求。

最后对程序进行仿真分析和硬件验证。

仿真结果表明,该系统具有集成度高、稳定性好、设计灵活和设计效率高等优点。

关键词: FPGA,Quartus II,VHDL,键盘扫描ABSTRACTIn the modern electronics industry controlling-circuit, the keyboard scanning and display circuit plays an important role in debugging and setting the system. With the development of EDA technology, FPGA-based scanning keyboard have been widely used in many electronic devices because of its simple structure, and it also can effectively prevent mechanical keyboard jitter caused by data errors.This article primarily designed an FPGA-based keyboard scan procedures, this design is developed on the EDA tools—— Quarutus II9.0 and designed the keyboard scan program, using the Creat-SOPC2000 experimental box 4 * 4 matrix keyboard as the hardware entity .the program is divided into four modules as the timing generation module, a keyboard scanning module, bounce cancellation module and the decoding module. The timing generation module generates the clock signal for the keyboard scanning and bounce elimination module, the keyboard scanning module using the line scanning method to sweep the 4* 4 matrix keyboard, key decoder module decodes the key value for the common anode eight 7-segment display code. Several modules assembles together to meet the keyboard scanning design requirements. Finally, conducting simulation analysis by the program and verifying the hardware.Simulation results show that the system has many advantages such as high integration, good stability, high efficiency, flexible design and high design efficiency.Keywords: FPGA,Quartus II,VHDL,keyboard scanning目录摘要 (I)ABSTRACT .......................................................................................................... I I 第1章绪论 (1)1.1 课题的研究背景 (1)1.2 课题的研究意义 (2)1.3 本文的主要工作 (2)第2章FPGA开发工具简介 (3)2.1 FPGA概述 (3)2.2 VHDL语言以及Quartus II应用 (3)2.3 本章小结 (4)第3章基于FPGA的键盘扫描程序的设计 (3)3.1 键盘扫描程序的总体电路设计 (5)3.1.1 矩阵式键盘扫描的工作原理 (6)3.1.2 数码管的显示原理 (7)3.2 键盘扫描电路各主要功能模块的设计 (8)3.2.1 时序产生模块 (8)3.2.2 键盘扫描模块 (9)3.2.3 弹跳消除模块 (11)3.2.4 键盘译码电路 (13)3.2.5 键盘扫描程序的顶层文件设计 (15)3.3本章小结 (16)第4章键盘扫描程序的波形仿真及硬件验证 (17)4.1 系统仿真 (17)4.1.1 消抖电路仿真 (17)4.1.2 键盘时钟信号仿真 (18)4.1.3 键盘扫描信号仿真 (18)4.1.4 键盘译码电路仿真 (19)4.1.5 键盘扫描总体电路仿真 (21)4.2引脚的锁定 (22)4.3硬件验证 (23)4.4本章小结 (25)结论 (26)参考文献 (27)附录 (28)致谢 (32)第1章绪论1.1 课题的研究背景在现代计算机与电子系统中,一般都采用通用式的标准键盘将所需的数据和指令等信息通过键盘输入到计算机和电子系统,以此来实现人机之间的接口交互。

51单片机键盘扫描程序7294详细介绍

51单片机键盘扫描程序7294详细介绍

51单片机键盘扫描程序7294详细介绍1. 前言随着人们对电子产品的需求不断增加,微处理器成为现代电子设备中必不可少的一个组成部分。

其中,单片机作为一种嵌入式微处理器,由于其功能强大、价格低廉和易于编程等优点,被广泛应用于各个领域。

在单片机应用中,键盘扫描技术是常见的一种应用技术。

它既可以用于普通键盘,也可以用于定制键盘或遥控器等输入设备。

在51单片机中,键盘扫描程序7294是最为常用和经典的键盘扫描程序之一。

本文将详细介绍7294程序的原理、功能和实现方法。

2. 概述7294程序是一种基于矩阵扫描的键盘输入程序,也称为键盘扫描程序。

该程序主要由两个部分组成,即扫描部分和解码部分。

扫描部分是通过读取每个行端口和列端口的状态,得出当前按下的键位信息。

在实现过程中,通常采用交错式扫描,即先扫描行端口再扫描列端口。

这种方式可以避免多个键同时按下时无法识别的情况。

解码部分负责将扫描部分得到的行列矩阵转换成对应的键位信息。

这里我们可以采用查表法或者位运算法来实现。

3. 原理主要原理7294程序的主要工作原理如下:(1)行列扫描首先,程序将所有行端口设为输出,所有列端口设为输入。

接着,程序对每个行端口进行扫描,每次只将一个行端口输出高电平,同时读取所有列端口的状态。

如果任意一个列端口检测到低电平,说明该列与当前行对应的键被按下,程序将该键位信息保存下来。

反之,如果所有列端口均输出高电平,说明该行无按键按下。

(2)解码当扫描部分输出一个键位矩阵后,解码部分即开始工作。

首先,程序将每个键位的状态 (按下或放开)保存到一个矩阵中。

接着,程序通过查表法或者位运算法将该矩阵转换成对应的键位信息,然后提交给主程序进行后续处理。

4. 具体实现为了更好地理解7294程序的实现步骤,我们这里以4x4矩阵键盘为例展示其具体实现过程。

(1)硬件连接首先,将4个行端口 (行1~行4)和4个列端口 (列1~列4)分别连接到51单片机的IO口,如图所示:(2)扫描过程接着,通过编写程序实现键盘的扫描过程。

PS2键盘接口Verilog程序

PS2键盘接口Verilog程序

之前探讨过PS/2键盘编解码以及数据传输协议,这次自己动手实现了利用FPGA FPGA现场可编程逻辑门阵列(FPGA, Field Programmable Gate Array),是一个含有可编辑元件的半导体设备,可供使用者现场程式化的逻辑门阵列元件。

FPGA是在PAL、GAL、CPLD 等可编辑器件的基础上进一步发展的产物。

[全文]接收键盘编码,然后通过串口串口串口是计算机上一种非常通用的设备通信协议,大多数计算机包含两个基于RS232的串口。

串口同时也是仪器仪表设备的通信协议,并可用于获取远程采集设备的数据。

[全文]传输到PC。

做的比较简单,只是通过FPGAFPGA现场可编程逻辑门阵列(FPGA, Field Programmable Gate Array),是一个含有可编辑元件的半导体设备,可供使用者现场程式化的逻辑门阵列元件。

FPGA是在PAL、GAL、CPLD等可编辑器件的基础上进一步发展的产物。

把大写字母A-Z转换成相应的ASCII码,只要字母按键被按下,就能在串口串口串口是计算机上一种非常通用的设备通信协议,大多数计算机包含两个基于RS232的串口。

串口同时也是仪器仪表设备的通信协议,并可用于获取远程采集设备的数据。

调试助手里显示相应大写字母。

下面就共享代码吧!除了顶层模块,三个底层模块分别为PS/2传输处理模块、串口传输模块以及串口波特率选择模块(下面只给出顶层模块和PS/2传输处理模块的verilog代码)。

module ps2_key(clk,rst_n,ps2k_clk,ps2k_data,RS232_tx);input clk; //50M时钟信号input rst_n; //复位信号input ps2k_clk; //PS2接口时钟信号input ps2k_data; //PS2接口数据信号output rs232_tx; // RS232发送数据信号wire[7:0] ps2_by te; // 1byte键值wire ps2_state; //按键状态标志位wire bps_start; //接收到数据后,波特率时钟启动信号置位wire clk_bps; // clk_bps的高电平为接收或者发送数据位的中间采样点ps2scan ps2scan( .clk(clk), //按键扫描模块.rst_n(rst_n),.ps2k_clk(ps2k_clk),.ps2k_data(ps2k_data),.ps2_byte(ps2_byte),.ps2_state(ps2_state));speed_select speed_select( .clk(clk),.rst_n(rst_n),.bps_start(bps_start),.clk_bps(clk_bps));my_uart_tx my_uart_tx( .clk(clk),.rst_n(rst_n),.clk_bps(clk_bps),.rx_data(ps2_byte),.rx_int(ps2_state),.rs232_tx(rs232_tx),.bps_start(bps_start));Endmodulemodule ps2scan(clk,rst_n,ps2k_clk,ps2k_data,ps2_byte,ps2_state); input clk; //50M时钟信号input rst_n; //复位信号input ps2k_clk; //PS2接口时钟信号input ps2k_data; //PS2接口数据信号output[7:0] ps2_byte; // 1byte键值,只做简单的按键扫描output ps2_state; //键盘当前状态,ps2_state=1表示有键被按下//------------------------------------------reg ps2k_clk_r0,ps2k_clk_r1,ps2k_clk_r2; //ps2k_clk状态寄存器//wire pos_ps2k_clk; // ps2k_clk上升沿标志位wire neg_ps2k_clk; // ps2k_clk下降沿标志位always @ (posedge clk or negedge rst_n) beginif(!rst_n) beginps2k_clk_r0 <= 1'b0;ps2k_clk_r1 <= 1'b0;ps2k_clk_r2 <= 1'b0;endelse begin //锁存状态,进行滤波ps2k_clk_r0 <= ps2k_clk;ps2k_clk_r1 <= ps2k_clk_r0;ps2k_clk_r2 <= ps2k_clk_r1;endendassign neg_ps2k_clk = ~ps2k_clk_r1 & ps2k_clk_r2; //下降沿//------------------------------------------reg[7:0] ps2_byte_r; //PC接收来自PS2的一个字节数据存储器存储器存储器是用来存储程序和数据的部件,有了存储器,计算机才有记忆功能,才能保证正常工作。

Verilog HDL 编写 4.4 3.3键盘程序

Verilog HDL 编写 4.4  3.3键盘程序

这里有一段4*4的,哪位大哥能帮我改成3*3的,谢谢!!! module Verilog1(clk,//50MHZreset,row,//行col,//列key_value//键值);input clk,reset;input [3:0] row;output [3:0] col;output [3:0] key_value;reg [3:0] col;reg [3:0] key_value;reg [5:0] count;//delay_20msreg [2:0] state;//状态标志reg key_flag;//按键标志位reg clk_500khz;//500KHZ时钟信号reg [3:0] col_reg;//寄存扫描列值reg [3:0] row_reg;//寄存扫描行值always @(posedge clk or negedge reset)if(!reset)begin clk_500khz<=0; count<=0;endelsebeginif(count>=50)begin clk_500khz<=~clk_500khz;count<=0;endelse count<=count+1;endalways @(posedge clk_500khz or negedge reset) if(!reset)begin col<=4'b0000;state<=0;endelsebegincase (state)0:begincol[3:0]<=4'b0000;key_flag<=1'b0;if(row[3:0]!=4'b1111)begin state<=1;col[3:0]<=4'b1110;end //有键按下,扫描第一行else state<=0;end1:beginif(row[3:0]!=4'b1111)begin state<=5;end//判断是否是第一行elsebegin state<=2;col[3:0]<=4'b1101;end//扫描第二行end2:beginif(row[3:0]!=4'b1111)begin state<=5;end//判断是否是第二行elsebegin state<=3;col[3:0]<=4'b1011;end//扫描第三行end3:beginif(row[3:0]!=4'b1111)begin state<=5;end//判断是否是第三一行elsebegin state<=4;col[3:0]<=4'b0111;end//扫描第四行end4:beginif(row[3:0]!=4'b1111)begin state<=5;end//判断是否是第一行else state<=0;end5:beginif(row[3:0]!=4'b1111)begincol_reg<=col;//保存扫描列值row_reg<=row;//保存扫描行值state<=5;key_flag<=1'b1;//有键按下endelsebegin state<=0;endendendcaseendalways @(clk_500khz or col_reg or row_reg) beginif(key_flag==1'b1)begincase ({col_reg,row_reg})8'b1110_1110:key_value<=0;8'b1110_1101:key_value<=1;8'b1110_1011:key_value<=2;8'b1110_0111:key_value<=3;8'b1101_1110:key_value<=4;8'b1101_1101:key_value<=5;8'b1101_1011:key_value<=6;8'b1101_0111:key_value<=7;8'b1011_1110:key_value<=8;8'b1011_1101:key_value<=9;8'b1011_1011:key_value<=10;8'b1011_0111:key_value<=11;8'b0111_1110:key_value<=12;8'b0111_1101:key_value<=13;8'b0111_1011:key_value<=14;8'b0111_0111:key_value<=15;endcaseendendendmodule提问者:roddyni - 二级3*3module Verilog1(clk,//50MHZreset,row,//行col,//列key_value//键值);input clk,reset;input [2:0] row;output [2:0] col;output [3:0] key_value;reg [2:0] col;reg [3:0] key_value;reg [5:0] count;//delay_20msreg [2:0] state;//状态标志reg key_flag;//按键标志位reg clk_500khz;//500KHZ时钟信号reg [2:0] col_reg;//寄存扫描列值reg [2:0] row_reg;//寄存扫描行值always @(posedge clk or negedge reset)if(!reset)begin clk_500khz<=0; count<=0;endelsebeginif(count>=50)begin clk_500khz<=~clk_500khz;count<=0;endelse count<=count+1;endalways @(posedge clk_500khz or negedge reset) if(!reset)begin col<=3'b000;state<=0;endelsebegincase (state)0:begincol[2:0]<=3'b000;key_flag<=1'b0;if(row[2:0]!=3'b111)begin state<=1;col[2:0]<=3'b110;end //有键按下,扫描第一行else state<=0;end1:beginif(row[2:0]!=3'b111)begin state<=4;end//判断是否是第一行elsebegin state<=2;col[2:0]<=3'b101;end//扫描第二行end2:beginif(row[2:0]!=3'b111)begin state<=4;end//判断是否是第二行elsebegin state<=3;col[2:0]<=3'b011;end//扫描第三行end3:beginif(row[2:0]!=3'b111)begin state<=4;end//判断是否是第一行else state<=0;end4:beginif(row[2:0]!=3'b111)begincol_reg<=col;//保存扫描列值row_reg<=row;//保存扫描行值state<=4;key_flag<=1'b1;//有键按下endelsebegin state<=0;endendendcaseendalways @(clk_500khz or col_reg or row_reg) beginif(key_flag==1'b1)begincase ({col_reg,row_reg})6'b110_110:key_value<=0;6'b110_101:key_value<=1;6'b110_011:key_value<=2;6'b101_110:key_value<=3;6'b101_101:key_value<=4;6'b101_011:key_value<=5;6'b011_110:key_value<=6;6'b011_101:key_value<=7;6'b011_011:key_value<=8;endcaseendendendmodule3。

经典的verilog键盘扫描程序

经典的verilog键盘扫描程序

经典的verilog键盘扫描程序作者:ilove314拿到威百仕( VibesIC )的板子后就迫不及待的开始我的学习计划,从最基础的分频程序开始,但看到这个键盘扫描程序后,直呼经典,有相见恨晚的感觉,还想说一句:威百仕( VibesIC ),我很看好你!WHY?待我慢慢道来,这个程序的综合后是0error,0warning。

想想自己编码的时候那个warning是满天飞,现在才明白HDL设计有那么讲究了,代码所设计的不仅仅是简单的逻辑以及时序的关系,更重要的是你要在代码中不仅要表现出每一个寄存器,甚至每一个走线。

想想我写过的代码,只注意到了前者,从没有注意过后者,还洋洋自得以为自己也算是个高手了,现在想来,实在惭愧啊!学习学习在学习,这也重新激发了我对HDL设计的激情,威百仕给了我一个方向,那我可要开始努力喽!废话说了一大堆,看程序吧:(本代码经过ise7.1i综合并下载到SP306板上验证通过)//当三个独立按键的某一个被按下后,相应的LED被点亮;再次按下后,LED熄灭,按键控制LED亮灭module key_debounce(clk,rst_n,s1_n,s2_n,s3_n,s4_n,s5_n,led_d1,led_d2,led_d3,led_d 4,led_d5);input clk; //主时钟信号,10MHzinput rst_n; //复位信号,低有效input s1_n,s2_n,s3_n,s4_n,s5_n;output led_d1,led_d2,led_d3,led_d4,led_d5;reg[4:0] s_rst;always @(posedge clk or negedge rst_n)if (!rst_n) s_rst <= 5'b11111;else s_rst <= {s5_n,s4_n,s3_n,s2_n,s1_n};reg[4:0] s_rst_r;always @ ( posedge clk or negedge rst_n )if (!rst_n) s_rst_r <= 5'b11111;else s_rst_r <= s_rst;wire[4:0] s_an = s_rst_r & ( ~s_rst);reg[19:0] cnt; //计数寄存器always @ (posedge clk or negedge rst_n)if (!rst_n) cnt <= 20'd0; //异步复位else if(s_an) cnt <=20'd0;else cnt <= cnt + 1'b1;reg[4:0] low_s;always @(posedge clk or negedge rst_n)if (!rst_n) low_s <= 5'b11111;else if (cnt == 20'h30D40)low_s <= {s5_n,s4_n,s3_n,s2_n,s1_n};reg [4:0] low_s_r;always @ ( posedge clk or negedge rst_n )if (!rst_n) low_s_r <= 5'b11111;else low_s_r <= low_s;wire[4:0] led_ctrl = low_s_r[4:0] & ( ~low_s[4:0]);reg d1,d2,d3,d4,d5;always @ (posedge clk or negedge rst_n)if (!rst_n) begind1 <= 1'b0;d2 <= 1'b0;d3 <= 1'b0;d4 <= 1'b0;d5 <= 1'b0; endelse begin //if ( led_ctrl[0] ) d1 <= ~d1;if ( led_ctrl[1] ) d2 <= ~d2;if ( led_ctrl[2] ) d3 <= ~d3;if ( led_ctrl[3] ) d4 <= ~d4;if ( led_ctrl[4] ) d5 <= ~d5; endassign led_d1 = d1 ? 1'b1 : 1'b0; //LED翻转输出assign led_d2 = d2 ? 1'b1 : 1'b0;assign led_d3 = d3 ? 1'b1 : 1'b0;assign led_d4 = d4 ? 1'b1 : 1'b0;assign led_d5 = d5 ? 1'b1 : 1'b0;endmodule也许初看起来这段代码似乎有点吃力,好多的always好多的wire啊,而我们通常用得最多的判断转移好像不是主流。

4×4矩阵键盘扫描

4×4矩阵键盘扫描

矩阵键盘(Verilog)module matrixKeyboard_drive(input i_clk,input i_rst_n,input [3:0] row, // 矩阵键盘行output reg [3:0] col, // 矩阵键盘列output reg [3:0] keyboard_val // 键盘值);//++++++++++++++++++++++++++++++++++++++// 分频部分开始//++++++++++++++++++++++++++++++++++++++reg [19:0] cnt; // 计数子always @ (posedge i_clk, negedge i_rst_n)if (!i_rst_n)cnt <= 0;elsecnt <= cnt + 1'b1;wire key_clk = cnt[19]; // (2^20/50M = 21)ms //--------------------------------------// 分频部分结束//--------------------------------------//++++++++++++++++++++++++++++++++++++++// 状态机部分开始//++++++++++++++++++++++++++++++++++++++// 状态数较少,独热码编码parameter NO_KEY_PRESSED = 6'b000_001; // 没有按键按下parameter SCAN_COL0 = 6'b000_010; // 扫描第0列parameter SCAN_COL1 = 6'b000_100; // 扫描第1列parameter SCAN_COL2 = 6'b001_000; // 扫描第2列parameter SCAN_COL3 = 6'b010_000; // 扫描第3列parameter KEY_PRESSED = 6'b100_000; // 有按键按下reg [5:0] current_state, next_state; // 现态、次态always @ (posedge key_clk, negedge i_rst_n)if (!i_rst_n)current_state <= NO_KEY_PRESSED;elsecurrent_state <= next_state;// 根据条件转移状态always @ *case (current_state)NO_KEY_PRESSED : // 没有按键按下if (row != 4'hF)next_state = SCAN_COL0;elsenext_state = NO_KEY_PRESSED;SCAN_COL0 : // 扫描第0列if (row != 4'hF)next_state = KEY_PRESSED;elsenext_state = SCAN_COL1;SCAN_COL1 : // 扫描第1列if (row != 4'hF)next_state = KEY_PRESSED;elsenext_state = SCAN_COL2;SCAN_COL2 : // 扫描第2列if (row != 4'hF)next_state = KEY_PRESSED;elsenext_state = SCAN_COL3;SCAN_COL3 : // 扫描第3列if (row != 4'hF)next_state = KEY_PRESSED;elsenext_state = NO_KEY_PRESSED;KEY_PRESSED : // 有按键按下if (row != 4'hF)next_state = KEY_PRESSED;elsenext_state = NO_KEY_PRESSED;endcasereg key_pressed_flag; // 键盘按下标志reg [3:0] col_val, row_val; // 列值、行值// 根据次态,给相应寄存器赋值always @ (posedge key_clk, negedge i_rst_n)if (!i_rst_n)begincol <= 4'h0;key_pressed_flag <= 0;endelsecase (next_state)NO_KEY_PRESSED : // 没有按键按下begincol <= 4'h0;key_pressed_flag <= 0; // 清键盘按下标志endSCAN_COL0 : // 扫描第0列col <= 4'b1110;SCAN_COL1 : // 扫描第1列col <= 4'b1101;SCAN_COL2 : // 扫描第2列col <= 4'b1011;SCAN_COL3 : // 扫描第3列col <= 4'b0111;KEY_PRESSED : // 有按键按下begincol_val <= col; // 锁存列值row_val <= row; // 锁存行值 key_pressed_flag <= 1; // 置键盘按下标志endendcase//--------------------------------------// 状态机部分结束//--------------------------------------//++++++++++++++++++++++++++++++++++++++// 扫描行列值部分开始//++++++++++++++++++++++++++++++++++++++always @ (posedge key_clk, negedge i_rst_n)if (!i_rst_n)keyboard_val <= 4'h0;elseif (key_pressed_flag)case ({col_val, row_val})8'b1110_1110 : keyboard_val <= 4'h0;8'b1110_1101 : keyboard_val <= 4'h4;8'b1110_1011 : keyboard_val <= 4'h8;8'b1110_0111 : keyboard_val <= 4'hC;8'b1101_1110 : keyboard_val <= 4'h1;8'b1101_1101 : keyboard_val <= 4'h5;8'b1101_1011 : keyboard_val <= 4'h9;8'b1101_0111 : keyboard_val <= 4'hD;8'b1011_1110 : keyboard_val <= 4'h2;8'b1011_1101 : keyboard_val <= 4'h6;8'b1011_1011 : keyboard_val <= 4'hA;8'b1011_0111 : keyboard_val <= 4'hE;8'b0111_1110 : keyboard_val <= 4'h3;8'b0111_1101 : keyboard_val <= 4'h7;8'b0111_1011 : keyboard_val <= 4'hB;8'b0111_0111 : keyboard_val <= 4'hF;endcase//--------------------------------------// 扫描行列值部分结束//--------------------------------------endmodule。

FPGA矩阵键盘扫描

FPGA矩阵键盘扫描

------------------------------------------------- --功能:4×4键盘扫描和获得键盘值--接口:clk -时钟输入-- lie -列值输入-- hang-行扫描输出-- qout-键盘值BCD码输出-------------------------------------------------library ieee;use ieee.std_logic_1164.all;use ieee.std_logic_unsigned.all;use ieee.std_logic_arith.all;entity keyboard isport(clk :in std_logic;lie :in std_logic_vector(3 downto 0);hang :out std_logic_vector(3 downto 0);qout :out std_logic_vector(3 downto 0));end keyboard;architecture behave of keyboard issignal cnt:std_logic_vector(1 downto 0);signal hang_tem:std_logic_vector(3 downto 0); signal tem:std_logic_vector(3 downto 0);signal reg:std_logic_vector(3 downto 0);begintem<=lie;process(clk)beginif clk'event and clk='1' thenif cnt="11" thencnt<="00";elsecnt<=cnt+1;end if;case cnt iswhen "00"=>hang_tem<="1101";if tem="1110" thenreg<="0000";elsif tem="1101" thenreg<="0001";elsif tem="1011" thenreg<="0010";elsif tem="0111" then reg<="0011";elsereg<=reg;end if;when "01"=>hang_tem<="1011";if tem="1110" thenreg<="0100";elsif tem="1101" then reg<="0101";elsif tem="1011" then reg<="0110";elsif tem="0111" then reg<="0111";elsereg<=reg;end if;when "10"=>hang_tem<="0111";if tem="1110" thenreg<="1000";elsif tem="1101" then reg<="1001";elsif tem="1011" then reg<="1010";elsif tem="0111" then reg<="1011";elsereg<=reg;end if;when "11"=>hang_tem<="1110";if tem="1110" thenreg<="1100";elsif tem="1101" then reg<="1101";elsif tem="1011" then reg<="1110";elsif tem="0111" then reg<="1111";elsereg<=reg;end if;when others=>hang_tem<="1111"; reg<=reg;end case;end if;hang<=hang_tem; qout<=reg;end process;end behave;。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

经典的verilog键盘扫描程序作者:ilove314拿到威百仕( VibesIC )的板子后就迫不及待的开始我的学习计划,从最基础的分频程序开始,但看到这个键盘扫描程序后,直呼经典,有相见恨晚的感觉,还想说一句:威百仕( VibesIC ),我很看好你!WHY?待我慢慢道来,这个程序的综合后是0error,0warning。

想想自己编码的时候那个warning是满天飞,现在才明白HDL设计有那么讲究了,代码所设计的不仅仅是简单的逻辑以及时序的关系,更重要的是你要在代码中要表现出每一个寄存器,甚至每一个走线。

想想我写过的代码,只注意到了前者,从没有注意过后者,还洋洋自得以为自己也算是个高手了,现在想来,实在惭愧啊!学习学习在学习,这也重新激发了我对HDL设计的激情,威百仕给了我一个方向,那我可要开始努力喽!废话说了一大堆,看程序吧:(本代码经过ise7.1i综合并下载到SP306板上验证通过)//当三个独立按键的某一个被按下后,相应的LED被点亮;再次按下后,LED熄灭,按键控制LED亮灭module key_debounce(clk,rst_n,s1_n,s2_n,s3_n,s4_n,s5_n,led_d1,led_d2,led_d3,led_d 4,led_d5);input clk; //主时钟信号,10MHzinput rst_n; //复位信号,低有效input s1_n,s2_n,s3_n,s4_n,s5_n;output led_d1,led_d2,led_d3,led_d4,led_d5;reg[4:0] s_rst;always @(posedge clk or negedge rst_n)if (!rst_n) s_rst <= 5'b11111;else s_rst <= {s5_n,s4_n,s3_n,s2_n,s1_n};reg[4:0] s_rst_r;always @ ( posedge clk or negedge rst_n )if (!rst_n) s_rst_r <= 5'b11111;else s_rst_r <= s_rst;wire[4:0] s_an = s_rst_r & ( ~s_rst);reg[19:0] cnt; //计数寄存器always @ (posedge clk or negedge rst_n)if (!rst_n) cnt <= 20'd0; //异步复位else if(s_an) cnt <=20'd0;else cnt <= cnt + 1'b1;reg[4:0] low_s;always @(posedge clk or negedge rst_n)if (!rst_n) low_s <= 5'b11111;else if (cnt == 20'h30D40)low_s <= {s5_n,s4_n,s3_n,s2_n,s1_n};reg [4:0] low_s_r;always @ ( posedge clk or negedge rst_n )if (!rst_n) low_s_r <= 5'b11111;else low_s_r <= low_s;wire[4:0] led_ctrl = low_s_r[4:0] & ( ~low_s[4:0]);reg d1,d2,d3,d4,d5;always @ (posedge clk or negedge rst_n)if (!rst_n) begind1 <= 1'b0;d2 <= 1'b0;d3 <= 1'b0;d4 <= 1'b0;d5 <= 1'b0; endelse begin //if ( led_ctrl[0] ) d1 <= ~d1;if ( led_ctrl[1] ) d2 <= ~d2;if ( led_ctrl[2] ) d3 <= ~d3;if ( led_ctrl[3] ) d4 <= ~d4;if ( led_ctrl[4] ) d5 <= ~d5; endassign led_d1 = d1 ? 1'b1 : 1'b0; //LED翻转输出assign led_d2 = d2 ? 1'b1 : 1'b0;assign led_d3 = d3 ? 1'b1 : 1'b0;assign led_d4 = d4 ? 1'b1 : 1'b0;assign led_d5 = d5 ? 1'b1 : 1'b0;endmodule也许初看起来这段代码似乎有点吃力,好多的always好多的wire啊,而我们通常用得最多的判断转移好像不是主流。

的确是这样,一个好的verilog代码,用多个always语句来分摊一个大的always来执行,会使得综合起来更快,这也是接前两篇日志说到代码优化的一个值得学习的方面。

其次是wire连线很多,你要是仔细研究代码,不难发现所有的锁存器的连线关系编程者都考虑到了,这样就不会平白无故的生成意想不到的寄存器了,这也是一个优秀代码的必备要素。

上面说的是代码风格,下面就看程序的编程思想吧。

前两个always语句里其实是做了一个20ms的计数,每隔20ms 就会读取键值,把这个键值放到寄存器low_sw中,接下来的一个always语句就是把low_sw的值锁存到low_sw_r里,这样以来,low_sw和low_sw_r就是前后两个时钟周期里的键值了,为什么要这样呢?看下一个语句吧:wire [2:0] led_ctrl = low_sw_r[2:0] & ( ~low_sw[2:0]);仔细分析,你会发现当没有键按下时,low_sw=low_sw_r=3’b111,此时的led_ctrl=3’b000;只有当low_sw和low_sw_r的某一位分别为0和1时,才可能使led_ctrl的值改变(也就是把led_ctrl的某一位拉高)。

那么这意味着当键值由1跳变到0时才可能把led_ctrl拉高。

回顾前面的20ms赋键值,也就是说每20ms内如果出现按键被按下,那么有一个时钟周期里led_ctrl是会被拉高的,而再看后面的程序,led_ctrl的置高就使得相应的LED灯的亮灭做一次改变,这就达到了目的。

verilog 键盘扫描程序之debug 作者:ilove314: EDN China上次的日志《经典的verilog 键盘扫描程序》承蒙厚爱,已成博客精华,在EDN 博客主页置顶多日。

但是我发现那个经典程序还是存在一点点小bug ,且听我慢慢道来。

先放上仿真波形来说明一下问题吧:仿真说明:由于20ms 检测一次按键值对于仿真来说太长了,所以只假定16个主时钟周期就做一次检测(也就是cnt[3]的下降沿锁存键值)。

图1,sw1_n 被按下(拉底)大约5个时钟周期(<16),而此时与其相应的led_d5却改变状态了。

说明的问题是,大多数时候按键消抖其实是到不了20ms 的。

其实这个小bug 通常在下载后,测试键盘是不会有什么感觉的。

但是问题是,如果真的出现那种抖动在20ms 以内(甚至远小于20ms )的外部干扰存在时,这个bug 就不可忽视了。

因此,在原程序的基础上,做了如下的改进。

其思想是在每个主时钟(50MHz )周期里都进行一次按键检测,如果前后两次键值改变了,说明有可能键盘被按下了,此时,在下一个时钟周期将复位20ms 计数值,然后20ms 后重新锁存键值,其它的和原程序基本相同,这样就达到了真正意义上的20ms 消抖。

重新修改代码后的仿真波形如下:点击看原图图2,可以看到此时在不满16个时钟周期的键值变化是不会然led 做出变化的。

图3,按键sw3_n 的按下时间明显超过了16个时钟周期,那么在cnt 重新记到16个时钟周期后,led_d4就做出了改变。

重新修改后的代码如下://当三个独立按键的某一个被按下后,相应的LED 被点亮;再次按下后,LED 熄灭,按键控制LED 亮灭key_led.vmodule key_led(input CLOCK_50,input Q_KEY,input [4:1] KEY,output reg [4:1] LED);//++++++++++++++++++++++++++++++++++++++// 获取键值开始//++++++++++++++++++++++++++++++++++++++wire [4:1] key_val; // 键值key_debounce u0(.i_clk (CLOCK_50),.i_rst_n (Q_KEY),.i_key (KEY),.o_key_val (key_val) // 按下为0,松开为1 );//--------------------------------------// 获取键值结束//--------------------------------------//++++++++++++++++++++++++++++++++++++++// 按下键后开关LED 开始//++++++++++++++++++++++++++++++++++++++always @ (posedge CLOCK_50, negedge Q_KEY)if (!Q_KEY)LED <= 4'hF; // 0灭1亮elsecase (1'b0)key_val[1] : LED[1] <= ~LED[1];key_val[2] : LED[2] <= ~LED[2];key_val[3] : LED[3] <= ~LED[3];key_val[4] : LED[4] <= ~LED[4];default : LED <= LED ; // 缺省亮灭情况不变 endcase//--------------------------------------// 按下键后开关LED 结束//--------------------------------------endmodulekey_debounce.vmodule key_debounce(input i_clk,input i_rst_n,input [4:1] i_key, // 按下为0,松开为1 output reg [4:1] o_key_val // 键值);//++++++++++++++++++++++++++++++++++++++reg [4:1] key_samp1, key_samp1_locked;// 将i_key采集至key_samp1always @ (posedge i_clk, negedge i_rst_n)if(!i_rst_n)key_samp1 <= 4'hF;elsekey_samp1 <= i_key;// 将key_samp1锁存至key_samp1_lockedalways @ (posedge i_clk, negedge i_rst_n)if(!i_rst_n)key_samp1_locked <= 4'hF;elsekey_samp1_locked <= key_samp1;//++++++++++++++++++++++++++++++++++++++wire [4:1] key_changed1;// 当key_samp1由1变为0时// key_changed1由0变为1,只维持一个时钟周期assign key_changed1 = key_samp1_locked & (~key_samp1); //++++++++++++++++++++++++++++++++++++++reg [19:0] cnt;// 一旦有按键按下,cnt立即被清零always @ (posedge i_clk, negedge i_rst_n)if(!i_rst_n)cnt <= 20'h0;else if(key_changed1)cnt <= 20'h0;elsecnt <= cnt + 1'b1;//++++++++++++++++++++++++++++++++++++++reg [4:1] key_samp2, key_samp2_locked;// 只有当按键不变化(不抖动),且维持20ms以上时// 才将i_key采集至key_samp2always @ (posedge i_clk, negedge i_rst_n)if(!i_rst_n)key_samp2 <= 4'hF;else if(cnt == 20'hF_FFFF) // 0xFFFFF/50M = 20.9715ms key_samp2 <= i_key;// 将key_samp2锁存至key_samp2_lockedalways @ (posedge i_clk, negedge i_rst_n)if(!i_rst_n)key_samp2_locked <= 4'hF;elsekey_samp2_locked <= key_samp2; //++++++++++++++++++++++++++++++++++++++wire [4:1] key_changed2;// 当key_samp2由1变为0时// key_changed2由0变为1,只维持一个时钟周期assign key_changed2 = key_samp2_locked & (~key_samp2); //++++++++++++++++++++++++++++++++++++++// 每次按键稳定后,输出键值// 按下为0,松开为1always @ (posedge i_clk, negedge i_rst_n)if(!i_rst_n)o_key_val <= 4'hF;elseo_key_val <= ~key_changed2;//--------------------------------------endmodule。

相关文档
最新文档