说明
本人使用的是清翔的51单片机开发板,如果型号相同最方便,但是如果型号不同也可以参考,因为芯片都是一样的,只是外设不同而已,使用时只需要对照自己的开发板原理图稍微修改下引脚即可。
本次笔记对应清翔视频教程的第21、22集 矩阵按键(理论+编程)
目录
2.1 switch-case语句和if-else语句的区别
一、矩阵按键理论部分
矩阵按键有4行4列,可以通过列扫描和行扫描来确定哪个键被按下。
1.1 原理图
1.2 扫描方法
先进行列扫描列扫描时把所有列的IO口拉高,行IO口拉低,这样当检测到某列的IO口是低,那么就知道这一列有按键被按下;
再进行行扫描,把所有行的IO口拉高,列的IO口拉低,这样当检测到某行的IO口是0,那么就证明这一行有按键被按下。
经过这一行一列的扫描,就能够把具体的按键确定下来
例如:假设s11被按下,在进行列扫描时,我们让P3 = b0000 1111,结果读取到的P3 是b0000 1011,那么就说明第2列有按键被按下了
然后进行行扫描,我们让P3 = b1111 0000,结果读取到的P3是b1011 0000,说明第2行有按键被按下,第2列第2行,那么就可以唯一确定这个按键是s11。
总结分析一下,可以发现,如果在列扫描中发现某列有按键被按下
①假设在行扫描中第1行被按下:那么被按下的按键顺序=列顺序
②假设在行扫描中第2行被按下:那么被按下的按键顺序=列顺序+4;
③假设在行扫描中第3行被按下:那么被按下的按键顺序=列顺序+8;
④假设在行扫描中第4行被按下:那么被按下的按键顺序=列顺序+12;
二、switch-case语句
2.1 switch-case语句和if-else语句的区别
switch-case语句适用于多个固定选项的条件判断。它的语法结构是由一个switch关键字和多个case语句组成。在switch关键字后面,你需要提供一个表达式或变量,这个表达式的值会与每个case语句后的值进行比较。当表达式的值与某个case语句的值相等时,程序将执行该case语句后的代码块。如果没有匹配的case语句,你可以使用default关键字来指定一个默认的代码块。switch-case语句的优点是可以清晰地展示多个选项的处理逻辑,使代码更易读。
相比之下,if-else语句则更加灵活,适用于多种条件的判断。if-else语句的语法结构由一个if关键字和一个或多个条件语句组成。当条件语句的结果为真时,程序将执行if语句后的代码块;当条件语句的结果为假时,程序将执行else语句后的代码块。if-else语句的优点是可以根据具体的条件灵活地控制程序的执行流程。
总结来说,switch-case语句适用于多个固定选项的条件判断,而if-else语句则更适用于多种条件的判断。
2.2 switch-case语句使用方法
一般格式:
switch (表达式)
{
case 常量表达式1: 语句1;break;
case 常量表达式2: 语句2;break;
case 常量表达式3: 语句3;break;
case 常量表达式4: 语句4;break;
...
}
当表达式与case后面的常量相等时,就执行对应的语句
三、编程
3.1创建工程
复制一份工程模板,修改名称为“7.矩阵按键”,进入项目文件夹,打开工程文件
3.2 创建一个main.h头文件
在Source Group 1上右键,选择Add New Itemto Group'Source Group 1'..,
选择.h文件,名字为main.h,Add,这样在左侧边栏就会有一个main.h文件,里面可以放一些函数的声明,数组,宏定义等,以此来简化main.c函数,在我以后的代码中,我将同时给出我的main.h和main.c中的代码。可以直接在工程模板中创建一个,这样以后复制的工程模板改名文件里面都有main.h了,也可以在工程模板中直接写入
#include <reg52.h>
#include <main.h> //只有创建并使用了main.h才需要写这一行代码
void main()
{
}
上面这段内容,因为这是每个工程都要有的,也算模板的一部分
在main.h文件中写入
#ifndef __MAIN_H
#define __MAIN_H
#endif
然后我们就可以在中间空白处放入内容了
比如一个显示三位数的程序,有了main.h,main.h中写入
#ifndef __MAIN_H
#define __MAIN_H
typedef unsigned char uchar;
typedef unsigned int uint;
void delay(unsigned int i);
void SEG_DIS(uchar position, uchar number);
void SEG_DIS3(uchar i);
sbit DU = P2^6; //数码管段选
sbit WE = P2^7; //数码管位选
sbit key_s2 = P3^0;
sbit key_s3 = P3^1;
uchar we[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};
uchar du[]={
0x3F, //"0"
0x06, //"1"
0x5B, //"2"
0x4F, //"3"
0x66, //"4"
0x6D, //"5"
0x7D, //"6"
0x07, //"7"
0x7F, //"8"
0x6F, //"9"
0x77, //"A"
0x7C, //"B"
0x39, //"C"
0x5E, //"D"
0x79, //"E"
0x71, //"F"
0x76, //"H"
0x38, //"L"
0x37, //"n"
0x3E, //"u"
0x73, //"P"
0x5C, //"o"
0x40, //"-"
0x00, //熄灭
0x00 //自定义
};
#endif
main.c写入
#include <reg52.h>
#include <main.h>
void main()
{
while(1)
{
SEG_DIS3(137);
}
}
void SEG_DIS(uchar position, uchar number)
{
DU = 0;
WE = 0;
P0 = we[position - 1];
WE = 1;
WE = 0;
P0 = du[number];
DU = 1;
DU = 0;
delay(1);
}
void SEG_DIS3(uchar i)
{
SEG_DIS(1, i / 100);
SEG_DIS(2, i % 100 / 10);
SEG_DIS(3, i % 10);
}
void delay(unsigned int i)
{
unsigned int j;
for (; i > 0; i--)
for (j = 114; j > 0; j--);
}
这样main.c函数就会简洁很多
3.3 矩阵按键程序
由于刚开始使用main.h,初学者可能难以把握,所以在本次笔记中,我将每次都给出完整的main.c和main.h中的程序,在以后的笔记中,我将只给出变化的部分,以此减小笔记的长度,也便于识别出程序变化的部分,有利于学习51的编程
3.3.1 main.c
#include <reg52.h>
#include <main.h>
uchar keyval = 22; //初始值是22,这样在第11行的程序SEG_DIS(1,keyval);就会显示du[22]的数值‘-’
void main()
{
while(1)
{
key_scan();
SEG_DIS(1,keyval);
}
}
void key_scan()
{
P3 = 0xF0; //列扫描b1111 0000
if (P3 != 0xF0)
{
delay(10); //软件消抖
if (P3 != 0xF0)
{
switch (P3)
{
case 0xE0: keyval = 0;break; //11100000 第1列按下
case 0xD0: keyval = 1;break; //11010000 第2列按下
case 0xB0: keyval = 2;break; //10110000 第3列按下
case 0x70: keyval = 3;break; //01110000 第4列按下
}
P3 = 0x0F; //行扫描 b0000 1111
switch (P3)
{
case 0x0E: keyval = keyval;break; //11100000 第1行按下
case 0x0D: keyval = keyval + 4;break; //11010000 第2行按下
case 0x0B: keyval = keyval + 8;break; //10110000 第3行按下
case 0x07: keyval = keyval + 12;break; //01110000 第4行按下
}
while (P3 != 0x0F); //松手检测
}
}
}
void SEG_DIS(uchar position, uchar number)
{
DU = 0;
WE = 0;
P0 = we[position - 1];
WE = 1;
WE = 0;
P0 = du[number];
DU = 1;
DU = 0;
delay(1);
}
//void SEG_DIS3(uchar i) //注释掉是因为在本次矩阵按键扫描程序中未使用到
//{
// SEG_DIS(1, i / 100);
// SEG_DIS(2, i % 100 / 10);
// SEG_DIS(3, i % 10);
//}
void delay(unsigned int i)
{
unsigned int j;
for (; i > 0; i--)
for (j = 114; j > 0; j--);
}
3.3.2 main.h
#ifndef __MAIN_H
#define __MAIN_H
typedef unsigned char uchar;
typedef unsigned int uint;
void delay(unsigned int i);
void SEG_DIS(uchar position, uchar number);
//void SEG_DIS3(uchar i);
void key_scan();
sbit DU = P2^6; //数码管段选
sbit WE = P2^7; //数码管位选
sbit key_s2 = P3^0;
sbit key_s3 = P3^1;
uchar we[] = {0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f};
uchar du[]={
//0 1 2 3 4 5 6 7 8
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F,
//9 A B C D E F H L
0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x76, 0x38,
//n u - 熄灭
0x37, 0x3E, 0x40, 0x00 };
#endif
3.4 在矩阵按键程序中添加独立按键
只需要在key_scan函数末尾添加上这段代码
//加入独立按键扫描
P3 = 0XFF; //b1111 1111
if (P3 != 0xFF)
{
delay(10); //软件消抖
if (P3 != 0xFF)
{
switch (P3)
{
case 0xFE: keyval = 16;break; //1111 1110 s2按下
case 0xFD: keyval = 17;break; //1111 1101 s3按下
case 0xFB: keyval = 18;break; //1111 1011 s4按下
case 0xF7: keyval = 19;break; //1111 0111 s5按下
}
while (P3 != 0XFF);
}
}
本次笔记对应清翔视频教程的第21、22集 矩阵按键(理论+编程),到此结束,下一篇笔记将对应视频教程的23,24,25 8x8点阵屏(理论+实践)