本人初学单片机,前几天看到论坛里"qinshuai"发的“堪称一绝的“IO口扫键”法-单片机端口不够用时键盘扫描方法”一文,发现真是把IO口的复用玩到了极致!按照原文的说明,如果在电路中借助二极管,那么N个IO口最多能扫描N^2个按键!一般8脚的单片机有6个IO口,在不用其他IC的情况下最多能扫描36个按键!
下面是该方法的示意图:
首先,我们将IO1~5全部置1,扫描IO1~5,如果有0就说明最下面K61~K65中对应的按键被触发。接下来,将IO1置0,IO2~5置1,扫描IO2~5;将IO2置0,IO1,IO3~5置1并扫描;······;将IO5置0,IO1~4置1并扫描。以上便是一个完整的扫描周期,25个键都能够被扫描到。
我自己仿真了用6个IO口扫描30个按键的程序,效果还不错,代码、hex烧录文件和仿真文件都打包放在文章末尾了。不过暂时还没有在实际中实验,等我焊好按键键盘之后再试试!
下面是我的仿真电路图,理论上6个IO口最多可以扫描36个按键,不过我只需要30个就够了(6行5列),所以接地的六个按键我没有接(写程序也省事)。按键按下后,程序需要把对应的数字显示在数码管上。
按键对应的数字:
第1行 01 02 03 04 05
第2行 06 07 08 09 10
第3行 11 12 13 14 15
第4行 16 17 18 19 20
第5行 21 22 23 24 25
第6行 26 27 28 29 30
实现该功能的代码如下:
- #include <reg52.h>
- //CTRL1与CTRL2控制两位数码管的使能
- sbit CTRL1 = P3^0;
- sbit CTRL2 = P3^1;
- //BUTTON是按键端口,本次使用P0的0~5这6个io口。(P0:0x80, P1:0x90, P2:0xA0, P3:0xB0)
- sfr BUTTON = 0x80;
- //点亮数码管(共阳)的函数
- void dig_num(unsigned int x)
- {
- unsigned int i;
- unsigned int y;
- //共阳数码管0~9对应的编码
- unsigned char code num[] = {0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90};
- y = x % 10;
- x = x / 10;
- CTRL1 = 0;
- CTRL2 = 1;
- P2 = num[y];
- for(i=0;i<20;i++);
- CTRL2 = 0;
- CTRL1 = 1;
- P2 = num[x];
- for(i=0;i<20;i++);
- P2 = 0xFF;
- CTRL1 = 0;
- }
- void main()
- {
- unsigned int i, j;
- //key_map用于定义每个按键的功能
- unsigned int key_map[6][5] = {{1, 2, 3, 4, 5},
- {6, 7, 8, 9, 10},
- {11, 12, 13, 14, 15},
- {16, 17, 18, 19, 20},
- {21, 22, 23, 24, 25},
- {26, 27, 28, 29, 30}};
- unsigned char mask = 0x01;
-
- BUTTON = 0xFF;
- while (1)
- {
- //逐行扫描
- for (i=0; i<6; i++)
- {
- BUTTON = 0xFF ^ mask;
- //逐列扫描
- for (j=0; j<6; j++)
- {
- //检测到某个io口低电平,可能是按钮被按下
- if (((BUTTON >> j) & 0x01) == 0x00)
- {
- //i == j时的低电平io口恰好是逐行扫描设定的io口,不用管
- if (i < j) dig_num(key_map[i][5-j]);
- else if (i > j) dig_num(key_map[i][4-j]);
- }
- }
- mask = mask << 1;
- }
- mask = 0x01;
- }
- }
复制代码
下面是仿真的效果展示:
https://v.youku.com/v_show/id_XNTg5NTk3OTU2OA==.html
proteus仿真文件、hex文件、c源码文件在这个压缩包里:
6个引脚控制30个开关.zip
(40.1 KB, 下载次数: 13)
|