草庐IT

03按键

Lavender-edgar 2023-03-28 原文

一、IO口输入内容

在学习按键之前先学习一下如何往单片机的IO口输入内容。

其实输入的本质就是往单片机的一个端口在外部给一个电平,然后单片机中的程序去读取那个端口的电平即可完成一次输入。

51单片机的输入电平是非常简单的,不需要像stm32一样,需要调节端口的模式才能读取端口的电平,51单片机只需要读取端口的电平就可以了,非常的简单。

比如说现在我给我的单片机的P2组中第2个引脚一个电平,那么接收的代码如下:

int main(){
    int a = P2_2;
}

这样变量a中的值就是我给P2组中第二个引脚的电平值了。

二、什么是按键

按键在生活中很常见,一些嵌入式的设备都是通过按键来就行控制的(除了一些触摸显示屏),按键是使用是比较常用的。

在51单片机中通常使用的按键就这两种

第一种:独立按键,这种按键每一个是对应每一个引脚的,相当于一个按键控制一个引脚

第二种:矩阵按键,这种按键是一个矩阵,能控制的东西比较多,使用的引脚也比较多,如果每一个按键控制一个引脚,那么需要16个引脚,也就是需要2个引脚组,这样的方法很耗费引脚,那么这里使用到一种方法了。

就是将这些按键的一个引脚全部接在一起,另一个引脚按照行的方式了解在一起,就变成这种样子了

这样只需要一组IO口就可以得到16个按钮输入的内容。

三、按键分析

1.独立按键

从原理图可以知道

这些独立按键的一段引脚全部接到GND上的,而另一个接P3组中的0,1,2,3引脚。当点击任意按键,比如说K2按键,那么就会给P30这个端口一个低电平。

如果这些按键的一端全部接高电平,那么当点击任意按键,比如说K2按键,那么就会给P30一个高电平。

但是如果你是自己设置按键的话,让按键的一端全部接低电平,因为在使用单片机的时候,我们会给所有的IO组一个高电平,这个过程叫做充能,所以如果你接高电平的时候在判断的时候会产生误判。

现在简单写一个按键输入判断输入电平的一个程序

void main(){
    char a = P3_0;
    if (a == 0){
        ;//执行的内容
    }
}

如果用户按下了K2这个按键,那么P3_0的引脚应该会接收到一个低电平,然后再判断执行其它的内容。

2.矩阵按键

首先先看原理图

这个按键是一行连接到一个端口,列连接到一个端口,就比如第一行的左引脚全部都连接到P1_7这个引脚,第一列的右引脚都连接到P1_3这个引脚,这个是需要注意的。

而控制这个按键需要更复杂的方式,因为这个按键的一端并没有连接到电平,所以我们需要先让一端为低电平,另一端来接收用户按下按键时输入的电平,然后再发过来让之前接收的一端变成输出的,然后输出的变成输入,然后将两次按下的电平进行相加,这样就可以通过相加和判断按下的是哪一个按键了。这种方法叫做按键扫描。

不用担心上面的方法导致接收不了,因为内部运行的速度非常快,当我们做按键扫描的时候,可能第一个键还没有弹回到原来的地方就已经扫描完毕了。

那么代码可以这样写:

void main(){
    char row, col; //定义两个变量来接收端口输入
    P1 = 0x0F;
    col = P1;
    P1 = 0xF0;
    row = P1;
}

然后两值一相加判断一下结果为多少就可以知道按下的按键是哪一个了,每个按键所对应相加的值如下:

char btnNumber[] = {0x77, 0x7B, 0x7D, 0x7E, 0xB7, 0xBB, 0xBD, 0xBE, 0xD7, 0xDB, 0xDD, 0xDE, 0xE7, 0xEB, 0xED, 0xEE};

上面的按键是按照行来排列的,0x77, 0x7B, 0x7D, 0x7E是第一行中的第一个按键一直到第四个。剩下的以此类推。

在使用的时候可以添加一个for循环来判断:

void main(){
    char btnNumber[] = {0x77, 0x7B, 0x7D, 0x7E, 0xB7, 0xBB, 0xBD, 0xBE, 0xD7, 0xDB, 0xDD, 0xDE, 0xE7, 0xEB, 0xED, 0xEE};
    char row, col, i; //定义两个变量来接收端口输入
    P1 = 0x0F;
    col = P1;
    P1 = 0xF0;
    row = P1;
    for (i = 0; i < 16; i++){
        if (col + row == btnNumber[i]){
            ;
        }
    }
}

上面的方法是判断是不是按下了矩形按键中的一个内容,下面的方法是判断是不是按下了一个具体的按键:

void main(){
    char btnNumber[] = {0x77, 0x7B, 0x7D, 0x7E, 0xB7, 0xBB, 0xBD, 0xBE, 0xD7, 0xDB, 0xDD, 0xDE, 0xE7, 0xEB, 0xED, 0xEE};
    char row, col, i; //定义两个变量来接收端口输入
    P1 = 0x0F;
    col = P1;
    P1 = 0xF0;
    row = P1;
    if (col + row == btnNumber[1]){
        ;
    }
    else if (col + row == btnNumber[2]){
        ;
    }
    else if (col + row == btnNumber[0]){
        ;
    }
}

3.抖动

抖动这个问题是在使用按键的时候经常会出现,因为按键中有一个金属弹片,当按下按键时会挤压金属弹片来导通电流,而松手时金属弹片会弹回去。

按下和松手都会让金属弹片有抖动,这个抖动的电流是不稳定的,会影响程序对按键的判断

如上图就可以知道按键的抖动,那我们如何消除这个按键的抖动呢?

第一种方法直接从硬件方面下手,使用另一种没用抖动或者抖动频率小的硬件。

第二种方法是使用程序来超过这个抖动,也就是用一个延迟函数来将这个按下的抖动时间消除。

这里我们选择第二种方法,毕竟咱使用的已经是封装好的板子,如果自己搭建的话可以使用第一种方法,代码如下:

void delay(unsigned int time){
    unsigned char i, j;
    while(time--){
        i = 15;
        j = 90;
        do
        {
            while (--j);
        } while (--i);
    }
}
void main(){
    delay(10);
    char a = P3_0;
    if (a == 0){
        ;//执行的内容
    }
}

延迟10毫秒后得到的就是一个稳定的电平了。这个抖动时间一般都是5毫秒到10毫秒的样子,可能有些按键的抖动时间更多,那么就得查看手册了决定了。

四、按键案例

1.按下按键点亮一个LED灯

#include <at89x51.h>
void delay(unsigned int time){
    unsigned char i, j;
    while(time--){
        i = 15;
        j = 90;
        do
        {
            while (--j);
        } while (--i);
    }
}
void main(){
    char row;
    while(1){
        delay(15);
        row = P3_1;
        while (row == 0){
            P2_2 = 0;
        }
    }
}

但按下了K1这个按键后,就让P22口控制的LED灯点亮。

这里还可以扩展,就是当按下后LED亮,再按下后LED灭

#include <at89x51.h>
void delay(unsigned int time){
    unsigned char i, j;
    while(time--){
        i = 15;
        j = 90;
        do
        {
            while (--j);
        } while (--i);
    }
}
void main(){
    char row, x = 0;
    while(1){
        row = P3_1;
        delay(15);
        if (row == 0){
            P2_2 = ~P2_2;
            delay(15);
        }
    }
}

这里是将P22口进行取反操作然后再将这个取反后的值赋值给P22端口。

2.按键模拟二进制

这个其实也是比较简单,就是按下一个按键相当于+1,然后使用LED灯进行显示值。

#include <at89x51.h>
void delay(unsigned int time){
    unsigned char i, j;
    while(time--){
        i = 15;
        j = 90;
        do
        {
            while (--j);
        } while (--i);
    }
}
void main(){
    char row, x = 0;
    while(1){
        row = P3_1;
        delay(15);
        if (row == 0){
            x++;
            delay(15);
        }
        P2 = ~x;
    }
}

可能会有同学提出一个问题,就是当这个x一直叠加到0xFF后需不需要进行判断来为x清0?

其实不需要的,因为我的x是char类型只有8个字节,也就是从0x00一直到0xFF,如果到达了0xFF后再进行只增一那么就会得到0x100,但是这个1已经超过了范围了,所以就会被截断变成0x00。

3.矩形按键控制LED灯的亮灭

这里使用矩形按键来进行控制

#include <at89x51.h>
char btnNumber[] = {0x77, 0x7B, 0x7D, 0x7E, 0xB7, 0xBB, 0xBD, 0xBE, 0xD7, 0xDB, 0xDD, 0xDE, 0xE7, 0xEB, 0xED, 0xEE};
void delay(unsigned int time){
    unsigned char i, j;
    while(time--){
        i = 15;
        j = 90;
        do
        {
            while (--j);
        } while (--i);
    }
}
void main(){
    char row, col, x = 0;
    while(1){
        P1 = 0x0F;
        col = P1;
        P1 = 0xF0;
        row = P1;
        delay(10);
        if (col + row == btnNumber[0]){
            P2_0 = ~P2_0;
            delay(10);
        }
        else if (col + row == btnNumber[1]){
            P2_1 = ~P2_1;
            delay(10);
        }
    }
}

总结

按键的使用地方还是比较多的,一般是用于用户对单片机的控制。

这里要注意,在使用按键的时候如果你是使用的是模拟器,那么就可以不用考虑消抖,如果你是使用硬件环境,那么就需要考虑一下消抖问题。

有关03按键的更多相关文章

  1. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  2. ruby - 按键数组中的顺序对 Ruby 哈希进行排序 - 2

    我有一个散列:sample={bar:200,foo:100,baz:100}如何使用sort_order中的键顺序对sample进行排序:sort_order=[:foo,:bar,:baz,:qux,:quux]预期结果:sample#=>{foo:100,bar:200,baz:100}我能想到的就是new_hash={}sort_order.each{|k|new_hash[k]=sample[k]unlesssample[k].nil?}sample=new_hash必须有更好的方法。提示?不应该出现没有值的键,即键的数量保持不变,SortHashKeysbasedonord

  3. ruby - 在 Ruby 数组中(按键)删除重复项的最快/单行方法? - 2

    根据特定的键:值或方法返回的结果,删除对象数组中重复项的最快/单行方法是什么?例如,我有20个名称相同的XML元素节点,但它们具有不同的“文本”值,其中一些是重复的。我想通过说“ifelement.text==previous_element.text,删除它”来删除重复项。我如何用最短的代码在Ruby中做到这一点?我已经看到如何为简单的字符串/整数值执行此操作,但不是为对象执行此操作。 最佳答案 这是标准的散列方式。注意||=运算符的使用,这是一种更方便的(a||=b)方式来编写a=bunlessa.array.inject({}

  4. ruby-on-rails - Rails 未定义方法 `strftime' 为 "2013-03-06":String - 2

    我遇到了错误“2013-03-06”的未定义方法`strftime':String当尝试使用strftime从字符串2013-03-06正常显示日期(2013年6月3日或类似日期)时。在我的index.html.erb中执行此操作的行看起来像这样我只是在学习Rails,所以我确信这只是一个愚蠢的初学者错误,我们将不胜感激。谢谢 最佳答案 当strftime是时间/日期类的方法时,您的截止日期看起来是一个字符串。你可以试试这个:Date.parse(task.duedate).strftime("%B%e,%Y")

  5. ruby - 按键对哈希数组进行分组 - 2

    我有一个由以下形式的哈希组成的数组:[{:user=>"mike"etc},{:user=>"mike"etc},{:user=>"peter"etc},{:user=>"joe"etc}]有什么方便的方法可以根据userkey的值进行分组?最终结果应该是这样的:[[{:user=>"mike"etc},{:user=>"mike"etc}],[{:user=>"peter"etc}],[{:user=>"joe"etc}]] 最佳答案 使用group_by。array.group_by{|h|h[:user]}.values

  6. ruby - 在 Ruby 中检测按键(非阻塞)w/o getc/gets - 2

    我有一个简单的任务需要等待文件系统上的某些更改(它本质上是一个原型(prototype)编译器)。所以我有一个简单的无限循环,在检查更改的文件后休眠5秒。loopdo#iffileschanged#processfiles#andputsresultsleep5end而不是Ctrl+C敬礼,我宁愿能够测试并查看是否按下了某个键,而不会阻塞循环。本质上,我只需要一种方法来判断是否有传入的按键,然后是一种获取它们直到遇到Q的方法,然后退出程序。我想要的是:defwait_for_Qkey_is_pressed&&get_ch=='Q'endloopdo#iffileschanged#pro

  7. ruby - 如何按键按字母顺序对 Ruby 哈希进行排序 - 2

    我正在尝试按键按字母顺序对哈希进行排序,但如果不创建我自己的排序类,我似乎无法找到一种方法来做到这一点。如果它是一个整数,我发现下面的代码按值排序,我试图修改它但没有任何运气。temp["ninjas"]=36temp["pirates"]=12temp["cheese"]=222temp.sort_by{|key,val|key}我的目标是按键排序哈希,然后输出值。我将不得不使用不同的哈希顺序但相同的值多次执行此操作。 最佳答案 假设您希望输出是一个散列,它将按排序顺序遍历键,那么您就快完成了。Hash#sort_by返回一个Ar

  8. ruby - 按键对散列进行分组并对值求和 - 2

    我有一个哈希数组:[{"Vegetable"=>10},{"Vegetable"=>5},{"DryGoods"=>3>},{"DryGoods"=>2}]我想我需要在这里使用inject,但我真的很挣扎。我想要一个新的散列来反射(reflect)先前散列的重复键的总和:[{"Vegetable"=>15},{"DryGoods"=>5}]我控制着输出这个散列的代码,所以我可以在必要时修改它。结果主要是散列,因为这最终可能会嵌套任意数量的层级,然后很容易在数组上调用展平但不会展平散列的键/值:defrecipe_pl(parent_percentage=nil)ingredients.

  9. ruby - 按键排序散列,在 Ruby 中返回散列 - 2

    这是否是对散列进行排序并返回散列对象(而不是数组)的最佳方式:h={"a"=>1,"c"=>3,"b"=>2,"d"=>4}#=>{"a"=>1,"c"=>3,"b"=>2,"d"=>4}Hash[h.sort]#=>{"a"=>1,"b"=>2,"c"=>3,"d"=>4} 最佳答案 在Ruby2.1中很简单:h.sort.to_h 关于ruby-按键排序散列,在Ruby中返回散列,我们在StackOverflow上找到一个类似的问题: https://st

  10. javascript - iPhone 为 (hash and 3) 和 (Asterisk and 8) 返回相同的按键事件 - 2

    我正在处理电话验证,需要使用电话号码自动格式化输入,并且只允许添加数字字符。但是,当我尝试使用keydown和keypress限制输入时,iPhone允许我输入#和*。当我检查keydown值时,它们分别与3和8相同(键码51和56)。这在Android浏览器中完美运行,但在iPhone中失败。任何人都遇到过类似的问题。$(formSelector+'input[name^="phone"]').on('keydownkeypress',function(e){//Allow:backspace,delete,tab,escape,andenterif(e.keyCode==46||e

随机推荐