蓝桥杯单片机第二周第二课:按键控制入门
学习目标
-
认识按键的硬件原理(就像看懂开关的接线图)
-
学会用代码“听”按键的“说话”(按下和松开)
-
掌握用按键控制LED的几种方式(点灯、流水灯、调速)
认识按键:单片机的“耳朵”
按键是什么?
按键就像是单片机的耳朵或开关。当它被按下时,会给单片机一个信号:“嘿,我被按了!”
原理图解读(看图说话)
-
按键按下 = 低电平 = 引脚读到
0 -
按键松开 = 高电平 = 引脚读到
1
独立按键(4个独立开关)
P3.4 引脚 → 按键1
P3.5 引脚 → 按键2
P3.6 引脚 → 按键3
P3.7 引脚 → 按键4
就像你家墙上的4个独立电灯开关
矩阵按键(4×4键盘)
用4个行线 + 4个列线,组成16个按键的键盘。
-
行线:控制哪一行“通电”
-
列线:检测哪一列“被接通”
就像在地图上找坐标:先确定行,再确定列
模块化编程:把代码装进“盒子”
为什么要模块化?
想象你要做菜:
-
糟糕的方式:每次做菜都从头种菜、养鸡、榨油… -
聪明的方式:把常用的油、盐、酱、醋装在调料盒里,随取随用
模块化编程就是把常用功能装进“代码盒子”。
函数定义(制作调料盒)
返回值类型 函数名(参数) // 盒子标签
{
// 函数体(盒子里的内容)
// 实现特定功能的代码
}
示例:延时函数(就像“等一会儿”)
void Delay(unsigned char xms) // 要等多长时间?(单位:毫秒)
{
unsigned char i, j;
while(xms--) // 数够指定的次数
{
i = 2;
j = 259;
do {
while(--j); // 空循环,消耗时间
} while(--i);
}
}
实战编程:让按键“活”起来
3.1 独立按键控制LED(一按一亮)
/* 按键读取函数:检查谁被按了 */
unsigned char Key_Read()
{
unsigned char temp = 0; // 临时记分牌,记录哪个键被按
// 检查每个引脚,就像老师点名
if(P3_4 == 0) temp = 1; // 按键1被按
if(P3_5 == 0) temp = 2; // 按键2被按
if(P3_6 == 0) temp = 3; // 按键3被按
if(P3_7 == 0) temp = 4; // 按键4被按
return temp; // 返回“点名结果”
}
/* 主程序:不断检查按键并控制LED */
void main()
{
while(1) // 永不停止的循环
{
// 关键技巧:检测按键的“按下瞬间”和“松开瞬间”
Key_Val = Key_Read(); // 读取当前按键状态
Key_Down = Key_Val & (Key_Val ^ Key_Old); // 检测“按下瞬间”
Key_Up = ~Key_Val & (Key_Val ^ Key_Old); // 检测“松开瞬间”
Key_Old = Key_Val; // 记住这次的状态,下次对比用
// 控制LED
if(Key_Down == 1) // 按键1按下瞬间
P1_0 = 0; // LED1亮(0代表亮,1代表灭)
if(Key_Up == 2) // 按键2松开瞬间
P1_0 = 1; // LED1灭
// 按键3:按住就亮,松开就灭(像手电筒)
if(Key_Old == 3) // 按键3正被按着
P1_1 = 0; // LED2亮
else
P1_1 = 1; // LED2灭
}
}
理解键值检测
想象侦探查案:
-
Key_Old:上次现场照片 -
Key_Val:这次现场照片 -
Key_Down:通过对比发现新出现的指纹(按下瞬间) -
Key_Up:通过对比发现消失的指纹(松开瞬间)
3.2 按键控制流水灯(开关和调速)
/* 变量声明 */
bit System_Flag = 0; // 系统标志:0=停止,1=运行
unsigned int time = 500; // 流水速度:数字越大越慢
/* 主程序逻辑 */
void main()
{
while(1)
{
// 1. 检测按键(同上)
Key_Val = Key_Read();
Key_Down = Key_Val & (Key_Val ^ Key_Old);
Key_Old = Key_Val;
// 2. 如果系统正在运行,就让灯“流水”
if(System_Flag == 1)
{
ucLed = _crol_(ucLed, 1); // 将灯的状态向左旋转1位
P1 = ucLed; // 更新LED显示
Delay(time); // 等待一段时间(控制流速)
}
// 3. 根据按键控制
switch(Key_Down) // switch比多个if更清晰
{
case 1: // 按键1:开始流水
System_Flag = 1;
break;
case 2: // 按键2:停止流水
System_Flag = 0;
break;
case 3: // 按键3:减速(时间+100)
time += 100;
break;
case 4: // 按键4:加速(时间-100)
if(time > 100) // 防止过快
time -= 100;
break;
}
}
}
3.3 矩阵按键扫描(16键键盘)
/* 矩阵按键扫描:像在表格里找位置 */
unsigned char Key_Read()
{
unsigned char temp = 0;
// 第一行:让P3.0=0,其他行=1,然后检查列
P3_0 = 0; P3_1 = 1; P3_2 = 1; P3_3 = 1;
if(P3_4 == 0) temp = 1; // (1,1)
if(P3_5 == 0) temp = 2; // (1,2)
if(P3_6 == 0) temp = 3; // (1,3)
if(P3_7 == 0) temp = 4; // (1,4)
// 第二行:让P3.1=0,其他行=1
P3_0 = 1; P3_1 = 0; P3_2 = 1; P3_3 = 1;
if(P3_4 == 0) temp = 5; // (2,1)
// ... 以此类推
return temp; // 返回按键编号1-16
}
矩阵按键原理
就像在Excel表格里找单元格:
-
先选中第一行(让第一行接地)
-
检查哪一列被接通(读列线状态)
-
得到坐标(行,列) → 按键编号
-
重复扫描所有行
核心要点总结
| 概念 | 比喻 | 关键代码 | 作用 |
|---|---|---|---|
| 按键检测 | 侦探对比现场变化 | Key_Down = Key_Val & (Key_Val ^ Key_Old) |
捕捉按下瞬间 |
| 独立按键 | 一排独立开关 | if(P3_4 == 0) temp=1; |
简单控制 |
| 矩阵按键 | 坐标网格查找 | 行扫描 + 列检测 | 节省引脚(16键只用8线) |
| 模块化 | 调料盒分类存放 | unsigned char Key_Read() |
代码复用、清晰 |
| 流水灯控制 | 传送带搬运 | _crol_(ucLed, 1) |
LED循环点亮 |
学习建议
-
先理解原理:把按键想象成开关,把单片机想象成聪明的管家
-
从简单开始:先搞定1个按键控制1个灯,再逐步增加复杂度
-
多用比喻:技术概念往往有生活对应物,找到它就容易理解
-
动手实验:代码要自己敲、自己改参数,看实际效果
记住:按键是人机交互的基础,掌握了它,你就能让单片机“听懂”你的指令了!![]()
完整代码:
/*头文件区域*/
#include <REGX52.H>
#include <intrins.h>
/*延时函数*/
void Delay(unsigned char xms)
{
unsigned char i,j;
while(xms--)
{
i=2;
j=259;
do
{
while(--j);
}while(--i);
}
}
/*变量声明*/
unsigned char ucLed=0xFE;
unsigned char Key_Val,Key_Down,Key_Up,Key_Old;
unsigned int time = 500;
bit System_Flag;
/*按键读取函数*/
unsigned char Key_Read()
{
unsigned char temp=0;//选中行扫描列
P3_0 = 0;P3_1 = 1;P3_2 = 1;P3_3 =1;
if(P3_4 == 0) temp=1;
if(P3_5 == 0) temp=2;
if(P3_6 == 0) temp=3;
if(P3_7 == 0) temp=4;
P3_0 = 1;P3_1 = 0;P3_2 = 1;P3_3 =1;
if(P3_4 == 0) temp=5;
if(P3_5 == 0) temp=6;
if(P3_6 == 0) temp=7;
if(P3_7 == 0) temp=8;
P3_0 = 1;P3_1 = 1;P3_2 = 0;P3_3 =1;
if(P3_4 == 0) temp=9;
if(P3_5 == 0) temp=10;
if(P3_6 == 0) temp=11;
if(P3_7 == 0) temp=12;
P3_0 = 1;P3_1 = 1;P3_2 = 1;P3_3 =0;
if(P3_4 == 0) temp=13;
if(P3_5 == 0) temp=14;
if(P3_6 == 0) temp=15;
if(P3_7 == 0) temp=16;
return temp;
}
/*Main*/
void main()
{
while(1)
{
Key_Val = Key_Read();//读取键码值
Key_Down = Key_Val & (Key_Val ^ Key_Old);//检测下降沿
Key_Up= ~Key_Val & (Key_Val ^ Key_Old);//检测上升沿
Key_Old= Key_Val;//扫描辅助变量
if(System_Flag == 1)
{
ucLed = _crol_(ucLed,1);
P1 = ucLed;
Delay(time);
}
switch(Key_Down)
{
case 1:
System_Flag = 1;//开始
break;
case 2:
System_Flag = 0;//关闭
break;
case 3:
time += 100;//减速
break;
case 4:
time -= 100;//加速
break;
}
}


