51 单片机按键控制 LED 笔记(独立键 + 矩阵键)
1. 独立按键:单个按键控制 LED
先搞懂 “4 个独立按键” 怎么用
1.1 硬件咋连接?
看原理图就能明白:
- 独立按键(比如 S2-S5)的一端接 “地(GND)”,另一端接单片机的 P3 口(P3_4~P3_7);
- 单片机 P3 口默认是 “高电平(可以理解为‘有电’)”,按下按键时,对应 P3 引脚会被 “拉到地”,变成 “低电平(没电)”。
大白话总结:
→ 按键按下 → 对应 P3 引脚变 “0”;
→ 按键松开 → 对应 P3 引脚变 “1”。
1.2 先写 “读按键” 的函数
我们需要一个函数(叫Key_Read),帮我们 “看当前哪个按键被按下了”,代码长这样:
// 先定义几个按键相关的变量(全局的,哪里都能用)
unsigned char Key_Val, Key_Down, Key_Up, Key_Old;
// 按键读取函数:返回“当前按下的按键编号(1-4)”
unsigned char Key_Read()
{
unsigned char temp = 0; // 存按键编号,默认0(没按键)
// 检测P3_4(对应按键1)是否按下(变0)
if(P3_4 == 0) temp = 1;
// 检测P3_5(对应按键2)
if(P3_5 == 0) temp = 2;
// 检测P3_6(对应按键3)
if(P3_6 == 0) temp = 3;
// 检测P3_7(对应按键4)
if(P3_7 == 0) temp = 4;
return temp; // 把按键编号返回出去
}
解释:这个函数会挨个查 P3_4 到 P3_7,哪个引脚是 0(按键按下),就返回对应的数字(1-4);没按键就返回 0。
1.3 关键:区分 “按下瞬间” 和 “松开瞬间”
有 4 行 “死记” 的代码,但得知道它是干嘛的:
Key_Val = Key_Read(); // 步骤1:读“现在哪个键按了”
Key_Down = Key_Val & (Key_Val ^ Key_Old); // 步骤2:找“刚按下”的瞬间
Key_Up = ~Key_Val & (Key_Val ^ Key_Old); // 步骤3:找“刚松开”的瞬间
Key_Old = Key_Val; // 步骤4:把当前按键值存起来,下次对比用
用大白话翻译:
Key_Down:只有 “手指刚按下去” 的时候,这个变量才等于按键编号(比如按了按键 1,Key_Down=1);Key_Up:只有 “手指刚松开” 的时候,这个变量才等于按键编号;- 这俩变量能帮我们实现 “按一下就触发,不是一直触发”(比如按一下亮灯,不是按住一直闪)。
1.4 用独立键控制 LED
举 2 个常用例子:
例子 1:按键 1 点亮 LED1,按键 2 松开熄灭 LED1
代码片段:
// 按键1“刚按下”→ P1_0=0(LED1点亮)
if(Key_Down == 1)
P1_0 = 0;
// 按键2“刚松开”→ P1_0=1(LED1熄灭)
if(Key_Up == 2)
P1_0 = 1;
例子 2:按键控制流水灯 “启动 / 停止”
先定义一个 “开关标志”(System_Flag):1 = 流水灯跑,0 = 流水灯停。
代码片段:
unsigned char System_Flag = 0; // 流水灯状态:默认“停”
unsigned char ucLed = 0xfe; // 流水灯初始值(第一个LED亮)
void main()
{
while(1) // 死循环,一直运行
{
// 先执行“读按键+判断按下/松开”的4行代码
Key_Val = Key_Read();
Key_Down = Key_Val & (Key_Val ^ Key_Old);
Key_Up = ~Key_Val & (Key_Val ^ Key_Old);
Key_Old = Key_Val;
// 1. 流水灯运行(只有System_Flag=1时才跑)
if(System_Flag == 1)
{
ucLed = __crol__(ucLed, 1); // 循环左移(LED依次亮)
P1 = ucLed; // 把值给P1口,控制LED
Delay(100); // 延时100ms,让流水灯能看清
}
// 2. 按键控制“启停”
switch(Key_Down)
{
case 1: System_Flag = 1; break; // 按按键1→流水灯启动
case 2: System_Flag = 0; break; // 按按键2→流水灯停止
}
}
}
效果:按按键 1,流水灯开始跑;按按键 2,流水灯立刻停下。
2. 矩阵按键:16 个按键控制更多功能
独立键只能接 4 个,想接更多按键(比如 16 个),就用 “矩阵按键”(4 行 4 列)。
2.1 矩阵键的硬件 & 原理
看原理图:
- 矩阵键是4 行 + 4 列(比如 S6-S21),行接 P3_0~P3_3,列接 P3_4~P3_7;
- 原理是 “逐行扫描”:先把某一行设为 0(其他行设 1),然后看 “列” 哪个是 0,就能知道 “哪一行哪一列的按键按下了”。
比如:
→ 先把第 1 行(P3_0)设 0,其他行设 1 → 看列 P3_4~P3_7 哪个是 0 → 对应第 1 行的按键;
→ 再把第 2 行(P3_1)设 0,其他行设 1 → 看列哪个是 0 → 对应第 2 行的按键;
→ 扫完 4 行,就能识别 16 个按键。
2.2 矩阵键的 “读按键” 函数
代码逻辑就是 “逐行扫”,返回按键编号(1-16):
unsigned char Key_Read()
{
unsigned char temp = 0;
// 扫第1行:P3_0=0,其他行=1
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;
// 扫第2行:P3_1=0,其他行=1
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;
// 扫第3行:P3_2=0,其他行=1
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;
// 扫第4行:P3_3=0,其他行=1
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;
}
2.3 矩阵键控制流水灯 “速度”
加个 “延时时间变量(Time)”:Time越大,流水灯越慢;Time越小,流水灯越快。
代码片段:
unsigned char Time = 100; // 初始延时100ms(流水灯速度中等)
void main()
{
while(1)
{
// 先执行“读按键+判断按下/松开”的4行代码(和之前一样)
Key_Val = Key_Read();
Key_Down = Key_Val & (Key_Val ^ Key_Old);
Key_Up = ~Key_Val & (Key_Val ^ Key_Old);
Key_Old = Key_Val;
// 1. 流水灯运行(用Time控制速度)
if(System_Flag == 1)
{
ucLed = __crol__(ucLed, 1);
P1 = ucLed;
Delay(Time); // 延时时间是Time,控制流水灯快慢
}
// 2. 按键控制:启停+速度
switch(Key_Down)
{
case 1: System_Flag = 1; break; // 按键1→启动流水灯
case 2: System_Flag = 0; break; // 按键2→停止流水灯
case 3: Time += 100; break; // 按键3→Time+100(流水灯变慢)
case 4: Time -= 100; break; // 按键4→Time-100(流水灯变快)
}
}
}
效果:
→ 按按键 3:流水灯 “顿一下” 再变,跑得更慢;
→ 按按键 4:流水灯 “唰唰唰” 地跑,速度变快。