产品选型

单片机检查 详解单片机的按键检测与控制

小编 2024-10-25 产品选型 23 0

详解单片机的按键检测与控制

按键在单片机控制系统中起到人机交互的作用,通过按键可以输入数据、命令和各种参数,按键侧键测处理是单片机系统设计和开发中一个重要的内容,关系到整个系统的交互性能和稳定性。按键处理形式在单片机系统中有两种形式:直接按键和矩阵编码键盘,下面分别对这两种按键检测电路的特点及编程思路和方法进行分析和介绍。

按键抖动问题产生的原因及解决方法

按键的抖动问题是指按键的触点在闭合和断开瞬间由于接触情况不稳定,从而导致电压信号的抖动现象(由按键的机械特性造成,不可避免)。图8-1所示为一次按键的抖动过程,在按键的前沿和后沿都会有5~10ms的抖动。

图8-1 按键抖动示意图

对于时钟是微秒级的单片机而言,键盘的抖动有可能造成单片机对一次按键的多次处理。为了提高系统的稳定性,我们必须采用有效的方式消除抖动。

去除抖动可以采用硬件方式和软件方式。硬件方式一般是在按键与单片机的输入通道上安装硬件去抖电路(如RS触发器)。软件方式的实现方法是:当查询到电路中有按键按下时,先不进行处理,而是先执行10~20ms的延时程序,延时程序结束后,再次查询按键状态,若此时按键仍为按下状态,则视为按键被按下。

按键检测电路及应用

1. 独立式按键

独立式键盘相互独立,每个按键占用一根I/O口线,每根I/O口线上的按键工作状态不会影响其他按键的工作状态,CPU可直接读取该I/O线的高/低电平状态。这种按键硬件、软件结构简单,判键速度快,使用方便,但占用I/O口线较多,适用于按键数量较少的系统中。

独立连接式键盘连接如图8-2所示。当没有键被按下时,所有的数据输入线均为高电平;当任意一个按键被按下时,与之相连的数据输入线将变为低电平;通过相应指令,可以判断是否有键被按下。

图8-2 独立式键盘接口设计

【例8-1】 利用单片机的P1.0~P1.34个I/O口检测4个按键的触发信息,以实现不同功能的控制。

硬件电路参见图8-2所示,C51参考程序如下:

2. 矩阵式按键

在单片机系统中,当按键数量较多时,为了减少IO口的使用,通常将按键排列成矩阵型式。例如下例中的16个按键,被排列成了如图8-3所示的4X4矩阵方式。该矩阵式键盘由4根行线和4根列线组成,每个行线和列线的交叉点是一个按键。

【例8-2】 将矩阵式键盘的按键值通过数码管显示出来。

电路连接如图8-3所示(电源和震荡电路未标出)。

图8-3 矩阵式键盘按键显示电路图

如何判断被按下的键值呢?

分析如下:

根据下面的电路图,如果已知P1.0端口被置为低电平“0”,那么当按键K0被按下时,可以肯定P1.4端口的电平也变为了低电平“0”。基于这个原理,总结矩阵键盘识别按键的步骤如下:

a)首先判断是否有按键被按下

本例中矩阵键盘中P1端口低4位连接的是列线,高4位连接的是行线。将全部行线置为低电平“0”,全部列线置为高电平“1”。然后检测列线的状态。只要有一根列线的电平为低,则表示有按键被按下。否则没有按键按下。

b)按键消抖

当判断到有按键被按下后,还要进行消抖处理,以确认真正有按键被按下。

c)按键识别

当确认有键被按下后,采用逐行扫描的方法来确定是哪一个按键被按下。先扫描第一行,即将第一行输出低电平“0”,然后读取列值,那一列出现低电平“0”,则说明该列与第一行交叉处的按键被按下。如果读入的列值全部为"1",说明与第一行连接的按键均没有被按下。那么接下来开始扫描第二行,以此类推。直到完成全部行线的扫描。

C51程序如下:

矩阵式键盘与I/O接口应用

【例8-3】 四位数字密码锁

四位数字密码锁功能:通过键盘输入密码,当输入密码与内置密码相同时,继电器动作,表示密码锁解开。为了简化功能,该密码锁只使用12个键(即4x3键盘),其中S1~S9为1~9数字键,S10为0数字键,S11为“*”键作为确认键使用,而S12为“#”键作为复位键,键盘接口电路如图8-4所示,键值布局如表8-1所示。

图8-4 行列式键盘电路连接图

表8-1 键盘布局表

单片机上电时,数码管显示“0000”,此时输入数字,数码管将显示按键值,数字逐个向左递增,四次输入完毕,四个数码管显示输入的数,此时按“*”号按键将启动比较,若输入数字与内设密码相同,继电器动作。如不同,则系统复位等待重新输入密码。当按“#”号键,系统复位,数码管显示“0000”。

在上面键扫描程序基础上增加的源程序如下:

(建议收藏)关于单片机检查变量的方法,你会几种?

这些单片机调试方法你真的知道吗?

导读:授人以鱼不如授人以渔,为什么那些前辈们能快捷定位问题,这个系列的文章将揭秘 KEIL 调试那些不为人知的事。

以下内容更适用于 STM32 单片机(51 也支持局部)。掌握了它们将加速你的调试速度,不信吗?试试看咯。

程序中最重要的是什么,数据。很多时候程序运行有问题和你的数据密切相关,假如你能实时观察程序中的数据,你觉得怎么样?

///插播一条:我自己在今年年初录制了一套还比较系统的入门单片机教程和毕业设计指导,想要的同学找我拿就行了免費的,私信我就可以哦~点我头像白色字体加我也能领取哦,记得口令一哥///

数据分为两种,一种是可变的,一种为不可变的。假如 RAM 数据为可变的,FLASH 数据为不可变的(实际上也能变更,不然你怎么把程序烧写到 FLASH 中呢),还有一种极其特殊的存在:寄存器数据。

1、变量查看

首先说说可变数据的查看方式,比如你声明的一些变量,可以通过 Watch 窗口查看。

通过以下方式可打开 Watch 窗口(任选一个窗口打开即可):

在这里可以查看变量(这里选择 Watch 1):

是否发现上面的显示不太对劲?,这是啥意思?这个是说明 KEIL 无法找到这个变量。就我所知,有两种情况会出现这种现象:

1)、这个变量不存在:有可能你之前声明过这个变量,后来发现没用到,删除了。

2)、使用 static 声明的变量。

比如像这样的:

如果是第二种情况,那么可以通过将程序运行到使用该变量的地方,然后停止就可以查看了。

添加变量:

那么如何添加你需要查看的变量呢?通常可以使用如下方法:

1)、光标处于变量位置,然后右击会出现一个界面,最后选择添加到你需要的窗口:

2)、直接将你的变量拖到你的 Watch 窗口(前提是你已经打开了 Watch 窗口):

3)、复制变量名,然后将变量名粘贴在窗口里面就可以了。

移除变量

能添加,也就能移除,可以通过以下方法移除你的变量(注意程序应该处于停止状态):

当然还有一种方法就是直接删除这个变量名,这也可以达到移除的效果。

如果你希望使用十进制的方式显示你的数据,那么试试去掉上面的 Hexadecimal Display 勾选吧。

2、内存查看

如果你想查看 FLASH 的数据怎么办?那么试试这个窗口:

比如说你想看看 FLASH 地址开始处是什么数据,只要把 0x0800 0000 输入进去后按回车键就可以了(注意数字中间没有空格,只是为了看起来方便才用空格分开的):

四字节显示不爽?那试试改变显示格式吧,无符号,有符号,char、int、float……任你选(如果不想用十进制表示,必须去掉 Decimal 的勾选):

如果需要修改某个地址的数据,也可以通过上面的方式在某个数据上右击后选择修改(Modify)。

事实上,除了 FLASH 数据,RAM 数据也是可以通过它观察的:

从这里能够看到,Memory 在数据显示上比 Watch 窗口更壮大,它能够对单片机上的所有数据进行查看,缺少点就是你不知道谁是谁了(没有变量名显示,只能靠地址分辨了)。

对于以上知识可能很多人都了解过,下面说一说一般人不知道的点:

对于单片机来说,片上外设决定了你单片机的功能,所以多数情况下都须要查看外设寄存器的值,那么该怎么样查看呢?

通过 Watch 窗口就能够了。怎么做?

以最为常用的串口外设为例说明:

之后你就可以看到寄存器的内容了:

有没有很方便啊。那到底添加什么标志符才能显示出来呢?实际上这个标志符就是那些外设宏定义了。怎么看?前面一哥说过搜索也算一个调试功能,那你在工程内搜索之后就会发现这个定义:

明白了吧,你输写的 USART1 其实就是一个指针,然后 KEIL 就会从这个地址里读出数据并依照你的指针构造体显示出来。知道了这个,你应该也就知道该怎么样查看 GPIO、SPI 等外设了。

其实这里还有一个额外的益处,不知道你是否发现了。我们都知道,使用宏定义虽好,但它有一个很麻烦的地方,就是不能很直观的知道这个值到底是多少,那么通过这个你也就能够知道 USART1 的值就是 0x4001 3800 了,也就是 USART1?外设基地址就是它:

事实上通过 Memory 窗口也是可以的:

只是没有 Watch 窗口那么直观而已。

那么为什么须要支持这两种方式呢?我们知道有些变量空间非常大,假如串口缓存数组,可能有好几 K,假如你通过 Watch 窗口查看的话,你会发现它会严重干扰你的程序运行,表现情况就是数据刷新缓慢,但是通过 Memory 就不一样了,相当流畅。所以假如你要看大数据的话,用 Memory 效果最好。

还有一个益处就是,它能随时更变更量的显示方式,假如说你把一个浮点数据放在了四个字节数组变量中,那么我想查看这个浮点数据是什么怎么办,我不可能通过浮点数据的存储格式手工计算一下吧?假如你能计算出来还好,说明你很厉害,但是万一不懂存储格式或者计算错了呢?使用 Memory 就不同了,你只有把这个数组的地址给它,然后设置显示方式为浮点型就能够了,相当方便。还有就是当使用宏定义时,查看这个宏定义的值非常不方便,使用 Memory 就能够轻松查看。

假如查看 USART1 的 DR 寄存器地址,在 Watch 窗口显示是这样的:

如果你要知道 DR 的地址,你就需要通过基地址 0x400 13800 和偏移地址 0x04 知道它的地址为 0x400 13804,即使用 Watch 单独查看 DR 也是一样:

但是通过 Memory 就是这样的:

这里千万要注意的是要使用取地址符 &,否则它就变成了这样:

外设地址怎么可能是 0,所以肯定错了。

事实上你用 Watch 也是可以的,但显得比较诡异,会让你觉得这是一个指针变量:

实际上它只是一个常量而已,并不是指针变量。

在这里你会发现,这些窗口支持运算符,看这个:

还有这个:

变量的查看也是如此,是不是特别方便啊。须要注意的是,Watch 窗口和 Memory 都支持在线修改数据,对于须要临时更改数据情况下非常有用。

3、临时变量查看

以上数据查看都有一个特点,那就是数据的地址都是固定的,这样通过地址就能知道你的数据是什么,但还有一种数据,只会在函数运行的时候才会创建,一旦函数运行完,变量空间也就消失了,这就是局部变量。

局部变量使用的空间是栈,在进入函数时分配,离开函数的时候就消失了,所以你没法确定一个局部变量的地址(事实上你能得到局部变量的地址,但这个地址是随时变化的,所以即便你得到了也没用,由于你只能得到这一次的内存地址,下一次又会变化的)。

那么该怎么样观察局部变量的值呢?

假如一个简略延时函数,我想知道传入函数的参数是什么,那么通过窗口 Call Stack + Locals 就能够了。这个是专门查看局部变量的,当然也能够在函数中查看局部静态变量(关于这个你能够看 【C语言之static】)。

当你把断点设置在函数内部,当程序停下在函数内部时,就能通过这个窗口查看了。

当程序停止在上面的第一个断点时,就可以在窗口上看到这个:

不知道你发现没有,nms 变量显示为,用有道词典取词后你就知道这是说变量不在范围内。什么意思?这是由于你的断点在函数的初始处,程序运行到这里时这个空间的值还没有意义,所以并没有显示出来(事实上由于 nms 为函数的第一个参数,所以这个 nms 其实是寄存器的值,而不是内存变量),但是当你的程序运行到第二个断点处你就会发现窗口变成了这样:

这是因为后面的代码将函数的参数传入到变量 nms 中了,导致这个变量有初始值了,并且可以看到这值为 0x00 0A,即传入参数为 10,事实上它传入的就是这个值:

但是你也能够看到,变量 Osprey 的值是能够看到的,为什么?由于它是局部静态变量,意味着它有固定地址,在没有初始化的时候就会被默认初始化为 0。

所以使用 Call Stack + Locals 窗口能够很方便的查看局部变量的数据。

下面再说一点关于这个窗口少有人知道的点:

1、能够查看函数的调用顺序:

为了说明这个,我构造几个函数出来:

Osprey_fun3 被 main 函数调用,而 Osprey_fun3 本身调用 Osprey_fun2,Osprey_fun2 调用函数 Osprey_fun1。

如何知道这个关系呢?

看这个:

最新调用的函数在最下面(所谓的压栈),从下往上看就是,Osprey_fun3 调用 Osprey_fun2,Osprey_fun2 调用函数 Osprey_fun1,而主函数 main 这个最上层调用者却并不显示在这里(假如你使用操作系统,假如 uCOS,你是没办法在任务函数中观察到这个的,由于任务函数的调用由操作系统负责)。

2、显示调用关系:

这个功能能够查看当前函数的上层调用函数位置,通过选中某一个函数后右击选择第一个就能够进入上一层调用者的函数内部了(在这里就会跳到 Osprey_fun2 的函数内部)。而第二个是进入你选中函数的内部。

这个功能有什么用?在这里你可能觉得很鸡肋,由于函数之间的调用关系很明显啊,但是在中断处理函数中却非常有用。假如说 USART1_IRQHandler 处理函数,由于这个中断可能在主程序运行的任何时候发生,所以可能在普通函数的任何位置中断它,进而进入到中断处理函数里面,而通过这个功能你就能知道是哪个函数被中断了。

实际上,你可能并不关怀被串口中断的代码位置在哪,但是对于一些错误中断就不一样了,一旦进入错误中断,你就必需找到错误代码位置才行,怎么找?假如常见的硬件错误中断?HardFault_Handler,假如进入这个中断,你该怎么定位?就是使用这个功能了(关于错误中断的处理我会单独用一小节详细介绍)。

4、寄存器变量查看

在单片机中,有一种及其特殊的变量,就是寄存器(不是那些外设寄存器),而能和 CPU 直接打交道的其实就是这些寄存器(所谓的变量操作其实都要首先通过这些寄存器才能进行的,有一个比喻是:CPU 是君王,寄存器就是君王身边的太监,而内存变量就是那些官员了,官员要和君主说话,首先要通过太监传话才行)。这些寄存器没有所谓的地址,所以你没有办法通过取址符&获取一个申明为 register 的变量(寄存器的存取速度超快,所以假如一个变量的使用得非常频繁,那么申明为 register 是一个明智之举,但这只是建议编译器去这么做罢了,编译器听不听就不知道了,所以即便你声明一个变量为 register,它还可能是内存变量),假如这个错误:

那么通过什么方法查看呢?看左边窗口:

所有的寄存器都在这显示,当寄存器的值在发生变化后(与上一次停下时的值比较),就会变更背景颜色(Watch 窗口也是如此)。

这些寄存器的值在一般情况下根本没啥用,但是对于汇编层面的调试却很有用。假如说一条代码,没有提示任何语法错误,但就是和你想要运行的结果不同,那么假如你懂点汇编,再配合这个寄存器调试,你就能很快的定位问题。

5、注意

这里要注意的一个问题是,为了显示窗口的变量能够实时更新数据,须要在View?里勾选这个:

为了更好的观察变量,这些窗口是可以单独关闭或打开的,当然也可以通过鼠标按住窗口后拖动到你想要的地方去(可以看到这里有多个选择的位置):

有的时候窗口弄得比较乱,怎么办?通过这个就可以复位窗口到默认状态:

接下来就是外设窗口展现局部,我把它也归为根底调试,由于它很常用,很有必要进修。敬请期待!

但是当我在后期查找BUG的时候,一哥发现自己主要的调试伎俩已经变成了它,其次才是Watch之类根本调试功能。

其次就是ITM,这个也是调试利器,一定程度上能够缓解 KEIL 命令行的缺少点之处。

所以既然各位在茫茫文章中看到了这篇文章,不如把我认为比较好的技能一起进修了吧。

切记一点,假如时长充裕的话,别收藏了,直接看完,收藏后很大可能你是不会再去看的。

但是看完之后,肯定会对你今后的软件开发提供非常大的帮助。

想要学习单片机的朋友 ,做毕业设计的同学,关注我们,口令一哥,与导师一起学习成长,共同进步,还有更多资料领取。

说了这么多,大家记得留意下方评论第一条(或者私信我)有干货~

相关问答

怎么检测 单片机 的好坏?

...1、视觉判断机械损坏;2、外用表测量电源及各个端口的对地电阻,具体阻值与好的芯片对比;3、烧录一个使用相关IO口的程序,在线测试,最好是做一块每个IO口...

如何判断 单片机 的好坏?

...1、视觉判断机械损坏;2、外用表测量电源及各个端口的对地电阻,具体阻值与好的芯片对比;3、烧录一个使用相关IO口的程序,在线测试,最好是做一块每个IO口...

单片机 上电后整个系统不能正常运行如何 检查 ?

1、检查供电:直接用万用表测量VCC和GND的电平,是否符合要求。如果VCC偏离5V或3.3V过多,检查7805或其他稳压、滤波电路的输出。2、检查晶振:一般是多换几个晶...

单片机 AD检测,电压不共地单片机自带AD检测,但是检测电压和单...

[最佳回答]1.最简单共地测量(有些场合,可以共地的);2.若不想共地既然隔离,必须用数字通信方式传递模拟量,必定目标板有将电压信号转换为数字信号装置;目标板增...

单片机 好坏判断?

...1、视觉判断机械损坏;2、外用表测量电源及各个端口的对地电阻,具体阻值与好的芯片对比;3、烧录一个使用相关IO口的程序,在线测试,最好是做一块每个IO口...

单片机 上的lcd不亮怎么测试?

要测试单片机上的LCD是否亮,可以按照以下步骤进行测试。首先,检查LCD的电源是否正常连接,确保电源线没有松动或损坏。然后,检查LCD的背光是否正常工作,可...

单片机 怎么检测上升沿与下降沿?

可以通过单片机的引脚来检测,通过设置引脚的外部中断,检测引脚的边沿信号就可以检测上升沿或者下降沿,以stm32单片机为例,先将定时器设定为外部触发,然后配...

单片机 串口检测不到怎么办?

单片机串口检测不到可能有以下几种原因:1.串口线接触不良或者损坏。您可以检查一下串口线是否连接牢固,或者更换一条新的串口线来测试。2.串口驱动程序没...

单片机 怎么检测同一个按键多次按下?

定义一个变量x=0检测到按键按下,且又被释放,变量x=x+1这样就可以知道按键按下多次定义一个变量x=0检测到按键按下,且又被释放,变量x=x+1这样就可以知道按键...

为什么STC-isp检测不到我的 单片机 ?

检测不到单片机有以下几个原因:1、串口有问题。2、串口线有问题。3、单片机有问题。4、单片机电路板可能有问题。比较综合,比如串口电路、复位电路等可能...

猜你喜欢