好久不见,因为博主最近两个月有点事情,加上接着考试,考完试也有点事情要处理,最近才稍微闲了一些,这才赶紧记录分享一篇博文。FPGA驱动4x4矩阵键盘。这个其实原理是十分简单,但是由于博主做的时候遇到了一些有意思的情况,所以我个人觉得值得记录分享一下。

首先找了本书看了下矩阵键盘的驱动原理,一般来说4x4矩阵键盘的原理图如下,有四根行线和四根列线,行选通和列选通可以确定键盘上的一个位置。从原理图上看出,在没有操作的情况下,行线上接了一个10K的上拉电阻接vcc,这使得键盘在没有按下时,四根行线始终是高电平。

列线是由处理器输入给矩阵键盘,空闲状态下保持为0。也就是行空闲时输出给处理器为四个1,列空闲时由处理器输入给四个0。

当按下按键时,比如第一行第一个按键,对应的那一行导通输出为0,即row_data = 0111,此时由处理器逐渐输入列扫描信号由col_data = 0111——1110,当所按下按键为对应的那一行列的按键,矩阵键盘的行才会导通输出为0,否则会回到1111。其他按键类似,就是利用这个原理来驱动矩阵键盘。

最后FPGA部分模块引脚设计如图,我们需要对按键进行消抖,和普通按键一样,采用20ms的延时对按键进行消抖,分为按下消抖和松开消抖,中间的状态转移,因为列信号需要输出判断行信号的变化,所以状态机状态转移用两个系统时钟周期跳转。采用状态机进行描述,状态转移图如下。

代码如下:(点击阅读原文查看博客)

 `timescale      1ns/1ps
// *********************************************************************************
// Project Name :
// Author : NingHeChuan
// Email : ninghechuan@foxmail.com
// Blogs : http://www.cnblogs.com/ninghechuan/
// File Name : Matrix_Key_Scan.v
// Module Name :
// Called By : // Abstract :
//
// CopyRight(c) 2018, NingHeChuan Studio..
// All Rights Reserved
//
// *********************************************************************************
// Modification History:
// Date By Version Change Description
// -----------------------------------------------------------------------
// 2018/7/28 NingHeChuan 1.0 Original
//
// ********************************************************************************* module Matrix_Key_Scan(
input clk, //50Mhz
input rst_n,
input [:] row_data,
output key_flag,
output reg [:] key_value,
output reg [:] col_data
); //FSM state
parameter SCAN_IDLE = 'b0000_0001;
parameter SCAN_JITTER1 = 'b0000_0010;
parameter SCAN_COL1 = 'b0000_0100;
parameter SCAN_COL2 = 'b0000_1000;
parameter SCAN_COL3 = 'b0001_0000;
parameter SCAN_COL4 = 'b0010_0000;
parameter SCAN_READ = 'b0100_0000;
parameter SCAN_JITTER2 = 'b1000_0000;
//
parameter DELAY_TRAN = ;
parameter DELAY_20MS = 1000_000;
//parameter DELAY_20MS = 100;//just test
reg [:] delay_cnt;
wire delay_done;
//
reg [:] pre_state;
reg [:] next_state;
reg [:] tran_cnt;
wire tran_flag;
//
reg [:] row_data_r;
reg [:] col_data_r;
// //-------------------------------------------------------
//delay 20ms
always @(posedge clk or negedge rst_n)begin
if(rst_n == 'b0)begin
delay_cnt <= 'd0;
end
else if(delay_cnt == DELAY_20MS)
delay_cnt <= 'd0;
else if(next_state == SCAN_JITTER1 | next_state == SCAN_JITTER2) begin
delay_cnt <= delay_cnt + 'b1;
end
else
delay_cnt <= 'd0;
end assign delay_done = (delay_cnt == DELAY_20MS - 'b1)? 1'b1: 'b0; //-------------------------------------------------------
//delay 2clk
always @(posedge clk or negedge rst_n)begin
if(rst_n == 'b0)begin
tran_cnt <= 'd0;
end
else if(tran_cnt == DELAY_TRAN)begin
tran_cnt <= 'd0;
end
else
tran_cnt <= tran_cnt + 'b1;
end assign tran_flag = (tran_cnt == DELAY_TRAN)? 'b1: 1'b0; //-------------------------------------------------------
//FSM step1
always @(posedge clk or negedge rst_n)begin
if(rst_n == 'b0)begin
pre_state <= SCAN_IDLE;
end
else if(tran_flag)begin
pre_state <= next_state;
end
else pre_state <= pre_state;
end //FSM step2
always @(*)begin
next_state = SCAN_IDLE;
case(pre_state)
SCAN_IDLE:
if(row_data != 'b1111)
next_state = SCAN_JITTER1;
else
next_state = SCAN_IDLE;
SCAN_JITTER1:
if(row_data != 'b1111 && delay_done == 1'b1)
next_state = SCAN_COL1;
else
next_state = SCAN_JITTER1;
SCAN_COL1:
if(row_data != 'b1111)//如果row_data是全1,说明不是列扫描没有对应到该行
next_state = SCAN_READ;
else
next_state = SCAN_COL2;
SCAN_COL2:
if(row_data != 'b1111)
next_state = SCAN_READ;
else
next_state = SCAN_COL3;
SCAN_COL3:
if(row_data != 'b1111)
next_state = SCAN_READ;
else
next_state = SCAN_COL4;
SCAN_COL4:
if(row_data != 'b1111)
next_state = SCAN_READ;
else
next_state = SCAN_IDLE;
SCAN_READ:
if(row_data != 'b1111)
next_state = SCAN_JITTER2;
else
next_state = SCAN_IDLE;
SCAN_JITTER2:
if(row_data == 'b1111 && delay_done == 1'b1)
next_state = SCAN_IDLE;
else
next_state = SCAN_JITTER2;
default:next_state = SCAN_IDLE;
endcase
end //FSM step3
always @(posedge clk or negedge rst_n)begin
if(rst_n == 'b0)begin
col_data <= 'b0000;
row_data_r <= 'b0000;
col_data_r <= 'b0000;
end
else if(tran_flag) begin
case(next_state)
SCAN_COL1:col_data <= 'b0111;
SCAN_COL2:col_data <= 'b1011;
SCAN_COL3:col_data <= 'b1101;
SCAN_COL4:col_data <= 'b1110;
SCAN_READ:begin
col_data <= col_data;
row_data_r <= row_data;
col_data_r <= col_data;
end
default: col_data <= 'b0000;
endcase
end
else begin
col_data <= col_data;
row_data_r <= row_data_r;
col_data_r <= col_data_r;
end
end //这个状态表明是扫开消完抖动的那一瞬间
assign key_flag = (next_state == SCAN_IDLE && pre_state == SCAN_JITTER2 && tran_flag)? 'b1: 1'b0; //-------------------------------------------------------
//decode key_value
always @(posedge clk or negedge rst_n)begin
if(rst_n == 'b0)begin
key_value <= 'd0;
end
else if(key_flag == 'b1)begin
case({row_data_r, col_data_r})
'b0111_0111: key_value <= 4'h1;
'b0111_1011: key_value <= 4'h2;
'b0111_1101: key_value <= 4'h3;
'b0111_1110: key_value <= 4'ha;
'b1011_0111: key_value <= 4'h4;
'b1011_1011: key_value <= 4'h5;
'b1011_1101: key_value <= 4'h6;
'b1011_1110: key_value <= 4'hb;
'b1101_0111: key_value <= 4'h7;
'b1101_1011: key_value <= 4'h8;
'b1101_1101: key_value <= 4'h9;
'b1101_1110: key_value <= 4'hc;
'b1110_0111: key_value <= 4'hf;
'b1110_1011: key_value <= 4'h0;
'b1110_1101: key_value <= 4'he;
'b1110_1110: key_value <= 4'hd;
default : key_value <= key_value;
endcase
end
else
key_value <= key_value;
end endmodule

Matrix_Key_Scan

代码部分其实没啥好说的,有意思的是博主连接硬件做调试的时候,博主的矩阵键盘模块如图,薄膜键盘。某宝客服连原理图都没有,有一家给的我原理图还是错的。问题在于代码第一次下载到板子上的时候,没有出结果,不知道是代码问题还是硬件电路连接问题,我也是试了好久才猜出来正确的连接顺序。

这是,某宝客服给的错的原理图,先拿这个看一下,和这个矩阵键盘的构造差不多,从图中可以看到这个图和文章开头的原理图中少了点什么,上拉电阻。这里比较迷惑的是,如果没有上拉电阻,在空闲状态下,row_data怎么能保持输出高电平呢。问了几个客服,有说不懂的有说不用加上拉电阻的。

  我直接上板调试,在键盘的基础上加了个数码管显示按下的数值。按下时发现是可以显示正确的数字的,但是奇怪的是过一会儿数码管显示会清零。最开始以为是代码的问题,检查后从仿真和逻辑看,按键后译码的数值其实是一直保持不变的,没有操作是不会发生变化的,但实际情况不太符合。

  这样奇怪的情况发生,这个时候我们要相信科学。这种仿真发现不了问题,但实际运行却又bug,这个没法猜出来。在线逻辑分析仪就可以看到你的代码在开发板上运行的情况,这里引出Xilinx的Chipscope,用在线逻辑分析仪几乎可以抓到你的代码内部的所有信号,这个时候抓到的是你的电路实际运行的情况,配置流程如下。

新建New source界面,选择如图Chipscope,next。

然后会自动生成一个后缀为.cdc的文件,双击打开。

这一步点击next

同样next

这里选择,触发信号的数量和位宽,我这里选择了三个触发信号,两个位宽为4,对应矩阵键盘的行和列,一个位宽为1,为复位信号。最后边的滚轮下拉可以看到全部信号。

这里设置抓取的信号深度,选择上升沿采样信号。完成后点击next

这里选择时钟信号clk

选择后点击make connection,OK。

同样的选择其他触发信号,加入行和列和复位信号。

添加完成OK

点击完成退出。

保存

这里点击这里就会启动Chipscope了,这个时候板子就可以上电了。

点击链接板子

照图点击下载板子。

配置相关文件

然后会弹出这个窗口,这里可以设置触发类型和触发方式,添加的信号都会显示出来

设置触发方式为M2,即复位信号。

点击上面的按钮开始运行,复位键释放,就可以抓取到一部分信号了。

按下一个按键会看到对应的行列变换。

这是Chipscope的调用流程,通过在线逻辑分析仪,博主发现了问题,在空闲无操作时,触发复位抓取信号,抓到的row_data有时候是1111。有时候是0000或其他,但是理论上矩阵键盘在无操作下应该一直row_data输出1111。就是这个奇怪的问题导致的错误。我们要相信科学。应该是硬件电路的问题,检查了与开发板连接的杜邦线没问题后,应该就是矩阵键盘自己的问题,上拉电阻这块的原理,我所使用的矩阵键盘没有上拉电阻,但是实际上这样的驱动,如果row_data线上没有上拉电阻,它很难保持为高电平,而这个地方加不加其实和驱动开发板的构造有关,据我了解,有些单片机的I/O引脚会内置上拉电阻,默认情况下是高电平,所以用这些单片机驱动是不需要加上拉电阻的。

我这里使用FPGA驱动,FPGA的引脚特性来说,还是需要加的,使矩阵键盘的信号输出稳定,对于Xilinx FPGA来说有意思的是,通过综合工具添加引脚约束可以启动同样的效果,比如在ucf文件的引脚电平约束中加上pullup就可以了。

由于我使用的Spartan-3E系列的开发板,从它手册上可以得到。在引脚约束在电平为3.3v时加上pull up,可以等下出相当于10.8k欧姆的电阻这和矩阵键盘的驱动原理是完全相符。

这篇博文主要分享的是硬件的一个调试过程,Chipscope还是很好用的。对于硬件来说,你没办法确定他的状态,所以使用工具抓取他的实际信号,帮助我们更好调试。

转载请注明出处:NingHeChuan(宁河川)

个人微信订阅号:开源FPGA

如果你想及时收到个人撰写的博文推送,可以扫描左边二维码(或者长按识别二维码)关注个人微信订阅号

知乎ID:NingHeChuan

微博ID:NingHeChuan

原文地址:https://www.cnblogs.com/ninghechuan/p/9401744.html

基于FPGA的4x4矩阵键盘驱动调试的更多相关文章

  1. Win10 IoT C#开发 6 - 4x4矩阵键盘扫描

    Windows 10 IoT Core 是微软针对物联网市场的一个重要产品,与以往的Windows版本不同,是为物联网设备专门设计的,硬件也不仅仅限于x86架构,同时可以在ARM架构上运行. 上一章我 ...

  2. 4x4矩阵键盘扫描

    4x4矩阵键盘扫描 Windows 10 IoT Core 是微软针对物联网市场的一个重要产品,与以往的Windows版本不同,是为物联网设备专门设计的,硬件也不仅仅限于x86架构,同时可以在ARM架 ...

  3. MCU软件最佳实践——矩阵键盘驱动

    1.矩阵键盘vs独立按键 在mcu应用开发过程中,独立按键比较常见,但是在需要的按键数比较多时,使用矩阵键盘则可以减少io占用,提高系统资源利用率.例如,某mcu项目要求有16个按钮,如果采用独立按键 ...

  4. 4X4矩阵键盘扫描程序

    4X4矩阵键盘扫描: 1. 4根行线的GIO均设为Output,根列线的GIO均设为Input: 2. 4根行线的GIO分别置为0111.1011.1101.1110,读逐一读取列线GIO的值,可确定 ...

  5. 「雕爷学编程」Arduino动手做(26)——4X4矩阵键盘模块

    37款传感器与模块的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止37种的.鉴于本人手头积累了一些传感器和模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的,这里 ...

  6. 基于 FPGA 的 PCIE 总线 Linux 驱动设计

    硬件平台 Kintex ®-7 family of FPGAs Intel X86 软件平台 Linux 4.15.0-36-generic #39~16.04.1-Ubuntu Xilinx xap ...

  7. 4x4矩阵键盘 扫描程序

    一:不排除第四位异常处理 uchar JuzhenkeyScan() { // P3=0xfe; // temp=P3; // while(temp!=0xfe) // { // temp=P3; / ...

  8. 芯航线FPGA学习套件之4*4矩阵键盘模块测试手册

    芯航线FPGA学习套件之4*4矩阵键盘模块测试手册   本手册以简明扼要的方式介绍芯航线FPGA学习套件提供的矩阵键盘模块的测试方法:   连接开发板,如下所示: 2.将矩阵键盘模块与开发板按如下图所 ...

  9. ARM开发(3)基于STM32的矩阵键盘控制蜂鸣器

    一 矩阵键盘控制蜂鸣器原理:  1.1 本实验实现8*7矩阵键盘上按键控制蜂鸣器响.  1.2 实验思路:根据电路图原理,找出矩阵键盘行列所对应的引脚,赋予对应的按键值,然后控制蜂鸣器响.  1.3 ...

随机推荐

  1. adb的一些常用的命令

    如果在dos界面想要直接用adb的话,需要将anroidsdk安装目录下的tools和platform-tools以及加入到环境变量path中. 查看当前的设备(包括真机和模拟器):adb devic ...

  2. one by one 项目 part 6

    package Controllerservlet; import java.io.IOException; import java.io.PrintWriter; import java.util. ...

  3. ArcGIS案例学习笔记2_2

    ArcGIS案例学习笔记2_2 联系方式:谢老师,135_4855_4328,xiexiaokui#qq.com 时间:第二天下午 2018年8月12日 案例1:模型构建器,山顶点提取 背景:数据量大 ...

  4. [图解tensorflow源码] 入门准备工作

     tensorflow使用了自动化构建工具bazel.脚本语言调用c或cpp的包裹工具swig.使用EIGEN作为矩阵处理工具.Nvidia-cuBLAS GPU加速计算库.结构化数据存储格式prot ...

  5. django通过url传递参数(编辑操作页面)

    在做到编辑部分时,想到的办法是在编辑上跳转到页面时给他一个包含唯一标识id的url,然后通过这个url中的id去查询出该条数据,将数据内容显示在编辑页面.   1.编辑按钮 <button on ...

  6. 第六章 图(a)概述

  7. SqlServer把日期转换成不同格式的字符串的函数大全

    SQL语句                                        结果SELECT CONVERT(varchar(100), GETDATE(), 0)-- 05 03 20 ...

  8. 关于block元素和inline元素

    呃...这个会不会太基础了.最近在复习,所以基础知识也不能够忽略. 根据HTML 4.01 规范,其描述如下(http://www.w3.org/TR/html401/struct/global.ht ...

  9. php.ini memory_limit引起的问题

    故障现象    在运行PHP程序,通常会遇到“Fatal Error: Allowed memory size of xxxxxx bytes exhausted”的错误, 这个意味着PHP脚本使用了 ...

  10. php多进程 防止出现僵尸进程

    对于用PHP进行多进程并发编程,不可避免要遇到僵尸进程的问题. 僵尸进程是指的父进程已经退出,而该进程dead之后没有进程接受,就成为僵尸进程(zombie)进程.任何进程在退出前(使用exit退出) ...