基于FPGA的 矩阵键盘按键识别 【原理+源码】

这篇具有很好参考价值的文章主要介绍了基于FPGA的 矩阵键盘按键识别 【原理+源码】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

fpga矩阵按键使用,FPGA-Verilog技术专栏,Verilog,FPGA,矩阵键盘


目录

引言

原理阐述

实现方法

源码分享

板级调试演示


引言

最近了解了矩阵键盘扫描的原理,动手实现了一下,在这里做一个简单的总结。



原理阐述

矩阵键盘典型电路:

fpga矩阵按键使用,FPGA-Verilog技术专栏,Verilog,FPGA,矩阵键盘

FPGA的应用电路:

fpga矩阵按键使用,FPGA-Verilog技术专栏,Verilog,FPGA,矩阵键盘

其中,行信号为FPGA输入信号,列信号为FPGA输出信号。 

原理解释: 

  1. 起始状态,FPGA的列信号输出 全0 低电平;
  2. 没有任何按键按下时,FPGA接收到的 行信号 为 全1 高电平;
  3. 当有按键按下时,被按下的按键所在行变为低电平,此时便可以开启一次检测行为;
  4. 由于机械按键固有的振动特性,需要延迟约20毫秒后再次确认是否有按键按下;
  5. 如果20毫秒延迟后,依然检测到有按键按下,则认为按键按下有效,开始逐列扫描;
  6. 逐列扫描时,当前正在扫描的列,FPGA需输出低电平,其他列则输出高电平;
  7. 列扫描完毕就可以确定按键所在行列,进一步确定按键的数值;
  8. 列输出信号输出全0低电平,等待按键释放(条件是行输入信号全为高电平)。释放后回归空闲状态。完成一次按键检测。

实现方法

对于这种程序化的检测流程,状态机很适合做这个事情。我选择的状态机实现。代码贴在下面,可以仔细阅读~

源码分享

// | ===================================================---------------------------===================================================
// | ---------------------------------------------------   矩阵键盘按键检测设计	   ---------------------------------------------------
// | ===================================================---------------------------===================================================
// | 创建时间 : 2022-12-19
// | 完成时间 : 2022-12-19
// | 作    者 :Xu Y. B.(CSDN 用户名:在路上,正出发)
// | 功能说明 :
// |			-1- 输出数据按照 16进制 0~F编码
// |			-2- 时钟频率可变更
// |
// | ================================= 		模块修改历史纪录 	  =================================
// | 修改日期:
// | 修改作者:
// | 修改注解:

`timescale 1ns/1ps


module MATRIX_KEYBOARD_DETECT_MDL#(
// | ====================================  模块可重载参数声明  ====================================
parameter		P_CLK_FREQ				=				32'd50_000_000 //时钟频率 单位 :Hz

)(

// | ==================================== 模块输入输出端口声明 ==================================== 
input 												   	I_SYS_CLK  ,
input												   	I_SYS_RSTN ,

input 				[3:0]								I_ROW	   ,
output 	reg			[3:0]								O_COL      ,

output 	reg												O_KEYBOARD_VAL,
output 	reg			[3:0]								O_KEYBOARD_DATA

    );
// | ====================================   模块内部参数声明   ====================================
localparam 			LP_DLY_20MS_CNT_MAX		=			FUNC_CAL_DLY_20MS_CNT_MAX(P_CLK_FREQ);
localparam          LP_20MS_CNT_WIDTH		=			$clog2(LP_DLY_20MS_CNT_MAX);
// | 状态编码
localparam 			LP_ST_IDLE				=			5'b00001;//空闲
localparam 			LP_ST_DLY				=			5'b00010;
localparam 			LP_ST_ROW				=			5'b00100;//行
localparam			LP_ST_COL				=			5'b01000;//列
localparam 			LP_ST_RLS				=			5'b10000;//按键释放检测

// | ====================================   模块内部信号声明   ====================================
// 20ms计数
reg 				[LP_20MS_CNT_WIDTH-1:0] 			R_20MS_CNT;
// 状态信号
reg 				[4:0]								R_CS;

// 下降边沿
reg					[1:0]								R_NDG_DETECT;
wire													W_NDG_ROW;
// 寄存
reg					[3:0]								R_ROW;	 
reg					[3:0]								R_COL;
// wire													W_ROW_0; 
reg 													R_ROW_0; 
reg 				[2:0]								R_CNT_4;							



// | ====================================   模块内部逻辑设计   ====================================
// | 边沿
always @ (posedge I_SYS_CLK)
begin
	if(~I_SYS_RSTN)
	begin
		R_NDG_DETECT <= 2'b00;
	end
	else
	begin
		R_NDG_DETECT[0] <= &I_ROW ;
		R_NDG_DETECT[1] <= R_NDG_DETECT[0];
	end
end
assign W_NDG_ROW = !R_NDG_DETECT[0] & R_NDG_DETECT[1];


/*assign 	W_ROW_0 = (R_ROW == 4'b1110) ? I_ROW[0] : 
				  (R_ROW == 4'b1101) ? I_ROW[1] : 
				  (R_ROW == 4'b1011) ? I_ROW[2] :
				  (R_ROW == 4'b0111) ? I_ROW[3] : 1'b1;*/
always @ (*)
begin
	if(~I_SYS_RSTN)
	begin
		R_ROW_0 = 1'b0;
	end
	else
	begin
		case(R_ROW)
			4'b1110:
			begin
				R_ROW_0 = I_ROW[0];
			end
			4'b1101:
			begin
				R_ROW_0 = I_ROW[1];
			end
			4'b1011:
			begin
				R_ROW_0 = I_ROW[2];
			end
			4'b0111:
			begin
				R_ROW_0 = I_ROW[3];
			end
			default:
			begin
				R_ROW_0 = 1'b1;
			end
		endcase
	end
end

// | 状态机
always @ (posedge I_SYS_CLK)
begin
	if(~I_SYS_RSTN)
	begin
		R_20MS_CNT 		  <= {(LP_20MS_CNT_WIDTH){1'b0}};
		O_COL			  <= 4'b0000;
		R_ROW			  <= 4'b0000;
		R_COL			  <= 4'b0000;
		R_CNT_4           <= 3'd0;
		R_CS              <= LP_ST_IDLE;
	end
	else
	begin
		case(R_CS)
			LP_ST_IDLE:
			begin
				R_20MS_CNT 		  <= {(LP_20MS_CNT_WIDTH){1'b0}};
				O_COL			  <= 4'b0000;
				R_ROW			  <= 4'b0000;
				R_COL			  <= 4'b0000;
				R_CNT_4           <= 3'd0;

				if(W_NDG_ROW)
				begin
					R_CS <= LP_ST_DLY;
				end
				else
				begin
					R_CS <= LP_ST_IDLE;
				end
			end
			LP_ST_DLY:
			begin
				if(R_20MS_CNT == LP_DLY_20MS_CNT_MAX)
				begin
					R_20MS_CNT   <= {(LP_20MS_CNT_WIDTH){1'b0}};
					R_CS         <= LP_ST_ROW;
				end
				else
				begin
					R_20MS_CNT   <= R_20MS_CNT + 1;
					R_CS         <= LP_ST_DLY;
				end
			end
			LP_ST_ROW:
			begin
				if(!(&I_ROW))
				begin
					R_ROW 	   		  <= I_ROW;
					O_COL			  <= 4'b1110;
					R_CS 			  <= LP_ST_COL;
				end
				else
				begin
					R_ROW	   		  <= 4'b0000;
					O_COL			  <= 4'b0000;
					R_CS              <= LP_ST_IDLE;
				end

				R_COL			  <= 4'b0000;
			end
			LP_ST_COL://暂不考虑列扫描失败 , 即列扫描 4 次结束后,无法定位列索引
			begin
				if(~R_ROW_0)
				begin
					O_COL			  <= 4'b0000;
					R_COL			  <= O_COL;
					R_CNT_4           <= 3'd0;
					R_CS              <= LP_ST_RLS;
				end
				else if(R_CNT_4 == 3'd4)
				begin
					O_COL			  <= 4'b0000;
					R_CNT_4			  <= 3'd0;
					R_CS              <= LP_ST_IDLE;
				end
				else
				begin
					O_COL			  <= {O_COL[2:0],O_COL[3]};
					R_COL			  <= 4'b0000;
					R_CNT_4           <= R_CNT_4 + 1;
					R_CS              <= LP_ST_COL;
				end

				R_20MS_CNT 		  <= {(LP_20MS_CNT_WIDTH){1'b0}};
				R_ROW			  <= R_ROW;
			end
			LP_ST_RLS:
			begin
				if(&I_ROW)
				begin
					R_CS              <= LP_ST_IDLE;
				end
				else
				begin
					R_CS              <= LP_ST_RLS;
				end

				R_20MS_CNT 		  <= {(LP_20MS_CNT_WIDTH){1'b0}};
				O_COL			  <= 4'b0000;
				R_ROW			  <= R_ROW;
				R_COL 			  <= R_COL;
			end
			default:
			begin
				R_20MS_CNT 		  <= {(LP_20MS_CNT_WIDTH){1'b0}};
				O_COL			  <= 4'b0000;
				R_CS              <= LP_ST_IDLE;
			end
		endcase
	end
end

always @ (posedge I_SYS_CLK)
begin
	if(~I_SYS_RSTN)
	begin
		O_KEYBOARD_VAL  <= 1'b0;
		O_KEYBOARD_DATA <= 4'd0;
	end
	else
	begin
		if(R_CS[4] & (&I_ROW))
		begin		
			case(R_ROW)
				4'b1110:
				begin
					O_KEYBOARD_VAL <= 1'b1;

 					if(((~R_COL) >> 1) == 4'b0100)
 					begin
 						O_KEYBOARD_DATA <= 4'd3;
 					end
 					else
 					begin
 						O_KEYBOARD_DATA <= (~R_COL) >> 1;
 					end
				end
				4'b1101:
				begin
					O_KEYBOARD_VAL <= 1'b1;

 					if(((~R_COL) >> 1) == 4'b0100)
 					begin
 						O_KEYBOARD_DATA <= 4'd3 + 4'd4;
 					end
 					else
 					begin
 						O_KEYBOARD_DATA <= ((~R_COL) >> 1) + 4'd4;
 					end
				end
				4'b1011:
				begin
					O_KEYBOARD_VAL <= 1'b1;

 					if(((~R_COL) >> 1) == 4'b0100)
 					begin
 						O_KEYBOARD_DATA <= 4'd3 + 4'd8;
 					end
 					else
 					begin
 						O_KEYBOARD_DATA <= ((~R_COL) >> 1) + 4'd8;
 					end
				end
				4'b0111:
				begin
					O_KEYBOARD_VAL <= 1'b1;

 					if(((~R_COL) >> 1) == 4'b0100)
 					begin
 						O_KEYBOARD_DATA <= 4'd3 + 4'd12;
 					end
 					else
 					begin
 						O_KEYBOARD_DATA <= ((~R_COL) >> 1) + 4'd12;
 					end
				end
				default:
				begin
					O_KEYBOARD_VAL  <= 1'b0;
					O_KEYBOARD_DATA <= 4'd0;
				end
			endcase
		end
		else
		begin
			O_KEYBOARD_VAL  <= 1'b0;
			O_KEYBOARD_DATA <= 4'd0;			
		end
	end
end
// | ====================================   模块内部函数设计   ====================================
// | 函数功能:根据时钟频率计算20ms延迟对应计数器计数的峰值
function integer FUNC_CAL_DLY_20MS_CNT_MAX ;
	input integer I_ITG_CLK_FREQ;
	integer ITG_1G ,ITG_20MS ,ITG_CLK_PERIOD;
	begin
		ITG_1G   = 10**9;
		ITG_20MS = 20*(10**6);
		ITG_CLK_PERIOD            = ITG_1G / I_ITG_CLK_FREQ;
		FUNC_CAL_DLY_20MS_CNT_MAX = ITG_20MS / ITG_CLK_PERIOD;
	end
endfunction 

endmodule

一般来说,矩阵键盘通常没有上拉电阻,所以需要在FPGA的约束中对行输入引脚添加上拉约束。例:

fpga矩阵按键使用,FPGA-Verilog技术专栏,Verilog,FPGA,矩阵键盘

 

以上的代码中,没有对按键释放的过程做震动延迟处理,稳妥起见还是在按键松起释放时,增加一个20毫秒的延迟,读者可以自行改进。 

板级调试演示

点击链接进入观看:

矩阵键盘扫描+三线制数码管驱动显示https://live.csdn.net/v/264596



有问题可以在评论区留言交流~~文章来源地址https://www.toymoban.com/news/detail-813325.html

到了这里,关于基于FPGA的 矩阵键盘按键识别 【原理+源码】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA学习——按键控制LED流水灯(附源码 无按键消抖版本)

    在博主的cyclone4开发板上一共有4个按键,本次实验的目的是为了实现每按下一次按键,都会使开发板上的4个LED灯切换一次状态,博主一共设计了四种状态,分别是: 按键 状态 按键1按下 自右向左的流水灯 按键2按下 自左向右的流水灯 按键3按下 四灯常亮 按键4按下 四灯闪烁

    2024年02月06日
    浏览(47)
  • 单片机原理及应用:独立式键盘控制LED与多功能按键识别

    今天来介绍另一个外设——按键与LED的配合工作,与开关不同,按键需要注意消除抖动带来的影响,代码逻辑也会更复杂一写,下面先为大家介绍独立式键盘的相关知识。 单片机的独立式键盘指的是一种不依赖于计算机或其他外部设备的键盘输入方式,由若干按键按照一定的

    2024年01月21日
    浏览(44)
  • 基于FPGA的键盘扫描程序的设计

    在现代电子「业的控制电路中,键盘扫描和显示电路时系统的调试和设置有着重要 的作用。随着EDA技术的发展,基于FPGA的扫描键盘因其结构简单,能有效防止机 械键盘按键抖动带来的数据错误等优点在许多电子设备中都得到了广泛的应用。 本文主耍是设计 个基于FPGA的键•盘

    2024年02月05日
    浏览(44)
  • FPGA | Verilog 实现矩阵乘法(附源码)

    使用 for语句实现,后续继续做并行优化… 最近需要用 verilog写一个矩阵乘法的简单模块,本来想着网上随便搜一个复制粘贴一下,却发现居然找不到有源码的(好多还上传到了CSDN资源),罢了罢了,照着Github的自己写一个吧。 我写的是 3 * 3 的、数值位宽为 [3:0] (0-15)的矩

    2024年02月11日
    浏览(39)
  • 基于FPGA的四位数码管显示按键计数器设计(verilog编程)

    软件:Vivado 2022.2 硬件:BASYS 3 设计描述:通过开发板上的四个按键,按下一次记数加一,分别由四个数码管从左到右分别显示四个按键记数情况。 例: 1.初始值为0000,意为无记数 2.当按下第一个按键,记数加一,数码管显示1000 3.再次按下一第一个按键,记数加一,数码管显

    2024年02月08日
    浏览(49)
  • 基于FPGA的车牌识别

    基于FPGA进行车牌识别 1. 文件说明 2. 程序移植说明 3. 小小的编程感想 本项目的原理讲解视频已经上传到B站“基于FPGA进行车牌识别”。 本项目全部开源,见我本人的Github仓库“License-Plate-Recognition-FPGA”。 小技巧:下载整个Github文件夹:http://tool.mkblog.cn/downgit/#/home 1. 工程及源

    2024年01月20日
    浏览(38)
  • 基于FPGA的手势识别

    使用正点原子开拓者开发板,预定义三种手势:石头(0)、剪刀(2)、布(5)。通过 OV5640 摄像头套件对手势图像进行采集,LCD 显示屏(显示屏用的正点原子的 7 寸 RGB_LCD,分辨率为 1024×600)对系统处理后的手势进行实时显示,根据预定义手势的面积周长比判断手势,最终

    2024年02月11日
    浏览(41)
  • FPGA高端项目:FPGA帧差算法图像识别+目标跟踪,提供9套工程源码和技术支持

    FPGA高端项目:FPGA帧差算法图像识别+目标跟踪,提供9套工程源码和技术支持 本设计使用Xilinx系列FPGA实现帧差算法图像识别+目标跟踪,提供vivado2019.1版本的工程源码共计9套,详情见下表: 视频输入源由多种方案可供选择,既有廉价的OV5640、OV7725摄像头,也有高分辨率的HDM

    2024年04月15日
    浏览(50)
  • 基于FPGA的车牌识别,其中包括常规FPGA图像处理算法

    基于FPGA的车牌识别,其中包括常规FPGA图像处理算法:         rgb转yuv,        sobel边缘检测,        腐蚀膨胀,        特征值提取与卷积模板匹配。 有bit流可以直接烧录实验。 保证无错误,完好,2018.3vivado版本,正点达芬奇Pro100t,板卡也可以自己更改移植一下。 所

    2024年04月14日
    浏览(52)
  • FPGA高端项目:FPGA帧差算法多目标图像识别+目标跟踪,提供11套工程源码和技术支持

    FPGA高端项目:FPGA帧差算法多目标图像识别+目标跟踪,提供11套工程源码和技术支持 本设计使用Xilinx系列FPGA实现帧差算法的多目标运动物体图像识别+目标跟踪,可实时识别多个目标的运动物体,并将其在画面中框出来实时锁定,可模拟无人机空中侦查,发现目标并实时锁定

    2024年04月29日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包