一、 实验效果展示

在深入代码之前,我们先看看这个“按键计数密码锁”是如何工作的。
功能描述

  1. 系统设定了一个密码(例如按 12 次)。
  2. 用户通过按键输入次数。
  3. 如果次数正确,LED慢闪2次表示验证成功
  4. 如果次数超过限制(例如超过10次),LED快闪5次表示报警/锁定
    在这里插入图片描述

二、 硬件准备与接线

1. 实验材料

  • Arduino Uno 开发板 x 1
  • 按键开关 (微动开关) x 1
  • LED发光二极管 x 1
  • 220Ω 电阻 x 1 (用于LED限流)
  • 杜邦线 若干

2. 接线原理

根据代码 pinMode(KEY_PIN, INPUT_PULLUP); 分析,我们使用的是内部上拉模式

  • LED:正极接 Pin 9,负极串联电阻接 GND。
  • 按键:一脚接 Pin 4,另一脚直接接 GND。(注意:虽然代码使用的是内部上拉,但如果你参考的图片中使用了外部下拉电阻接法,请确保代码逻辑与接线电平匹配。本代码逻辑是:按下=LOW)。

3. 接线示意图

在这里插入图片描述


三、 实验代码实现

以下是本实验的核心代码。该程序通过计算按键按下的次数来与预设密码进行比对。

#define INPUT_MAX_TIME 10   // 最大输入次数限制
const int PASSWORD = 12;    // 设置的密码值(按键按下12次)
const int LED_PIN = 9;      // LED指示灯引脚
const int KEY_PIN = 4;      // 按键输入引脚

int current_password = 0;   // 当前输入的密码值(按键累计次数)
int press_count = 0;        // 按键按下次数计数器(用于判断最大输入次数)

// 函数声明
void led_flash(int times, int interval);  // LED闪烁函数
void reset_system();                      // 系统重置函数

void setup() {
  pinMode(LED_PIN, OUTPUT);        // 设置LED引脚为输出模式
  pinMode(KEY_PIN, INPUT_PULLUP);  // 按键使用内部上拉电阻(按下时为LOW)
  digitalWrite(LED_PIN, LOW);      // 初始状态:关闭LED
  Serial.begin(9600);              // 初始化串口通信
  
  Serial.println("密码验证系统就绪");
  Serial.print("设定密码:");
  Serial.println(PASSWORD);
  Serial.println("请按按键输入密码(累计按下次数=密码值)");
}

void loop() {
  // 检测按键是否被按下(内部上拉电阻,按下时为LOW)
  if (digitalRead(KEY_PIN) == LOW) {
    delay(20);  // 延时消抖
    
    // 等待按键松开(死循环等待,直到松手)
    while (digitalRead(KEY_PIN) == LOW);
    
    // 按键有效,计数增加
    current_password++;
    press_count++;  
    
    // 串口打印当前状态
    Serial.print("当前输入密码(按键次数):");
    Serial.println(current_password);
    Serial.print("剩余输入次数:");
    Serial.println(INPUT_MAX_TIME - press_count);

    // --- 逻辑判断区域 ---
    
    // 1. 密码正确判断
    if (current_password == PASSWORD) {
      Serial.println("=== 密码正确! ===");
      led_flash(2, 500);  // 成功:慢闪2次
      reset_system();     // 重置
    }
    // 2. 超过最大输入次数判断
    else if (press_count >= INPUT_MAX_TIME) {
      Serial.println("=== 错误:输入次数已达上限! ===");
      led_flash(5, 500);  // 失败:快闪5次
      reset_system();     // 重置
    }
  }
}

/**
 * LED闪烁工具函数
 */
void led_flash(int times, int interval) {
  for (int i = 0; i < times; i++) {
    digitalWrite(LED_PIN, HIGH);
    delay(interval);
    digitalWrite(LED_PIN, LOW);
    delay(interval);
  }
}

/**
 * 系统重置函数
 */
void reset_system() {
  current_password = 0;
  press_count = 0;
  Serial.println("系统已重置,请重新输入密码");
  Serial.println("------------------------");
}

四、 拓展一:代码逻辑缺陷与优化

仔细观察上面的代码,细心的同学可能会发现一个致命的逻辑冲突

1. 存在的问题

  • 设定密码const int PASSWORD = 12;
  • 最大限制#define INPUT_MAX_TIME 10
  • 冲突点:用户想要输入密码12,必须按12次。但是,当用户按到第10次时,代码会触发 else if (press_count >= INPUT_MAX_TIME),直接报错重置系统。
  • 后果这个密码永远无法解开! 这是一个典型的逻辑设计漏洞。

2. 优化方案

我们需要修改最大输入次数,使其大于密码值,或者修改密码值使其小于最大次数。同时,我们可以增加一个“超时重置”或者“确认键”的功能,让交互更人性化。

修改建议:

// 修正逻辑错误
#define INPUT_MAX_TIME 15   // 将最大次数改为15,大于密码12
const int PASSWORD = 12;    

五、 拓展二:C语言 if-else 配对原则详解

在代码编写中,if-else 的嵌套使用非常频繁,初学者很容易搞混配对关系。

1. 配对原则(就近原则)

C语言规定:else 总是与它上面最近的、未配对的 if 进行配对。
这与代码的缩进无关!缩进只是为了给人看,编译器只认逻辑。

2. 经典错误示例

int a = 1, b = 2;
if (a == 0)
    if (b == 2)
        printf("b is 2");
else
    printf("a is not 0"); // 你的意图可能是这个else对应第一个if

实际运行结果:什么都不输出。
原因

  • 因为 a=1,第一个 if 不成立。
  • 根据就近原则,那个 else 其实是属于第二个 if (b==2) 的。
  • 既然第一个 if 都没进去,里面的 if-else 整体都不会执行。

3. 如何避免?

永远使用大括号 {} 即使只有一行代码。

if (a == 0) {
    if (b == 2) {
        printf("b is 2");
    }
} else {
    printf("a is not 0"); // 现在逻辑正确了
}

六、 拓展三:折纸挑战(While 与 For 循环实战)

问题描述
有一张厚度为 0.1毫米 的纸。假设纸张足够大,可以无限次折叠。问:需要折叠多少次,其厚度才能超过 珠穆朗玛峰 的高度(8848.86米)?

单位换算

  • 纸张厚度:0.1 mm
  • 珠峰高度:8848.86 m = 8848860 mm

1. 使用 while 循环实现

while 循环适用于**“不知道要循环多少次,只知道停止条件”**的场景。

#include <stdio.h>

int main() {
    double paper = 0.1;      // 纸张初始厚度 (mm)
    double everest = 8848860.0; // 珠峰高度 (mm)
    int count = 0;           // 折叠次数

    // 当 纸张厚度 < 珠峰高度 时,继续循环
    while (paper < everest) {
        paper = paper * 2;   // 每次折叠,厚度翻倍
        count++;             // 次数加1
    }

    printf("使用while循环计算:需要折叠 %d 次\n", count);
    printf("最终厚度为:%.2f mm\n", paper);
    return 0;
}

2. 使用 for 循环实现

for 循环通常用于已知次数,但也可以通过省略条件或配合 break 来实现未知次数的循环。

#include <stdio.h>

int main() {
    double paper = 0.1;
    double everest = 8848860.0;
    int count;

    // for(初始化; 条件; 迭代)
    // 这里我们将厚度计算放在循环体内,条件放在中间
    for (count = 0; paper < everest; count++) {
        paper *= 2; // 厚度翻倍
    }

    printf("使用for循环计算:需要折叠 %d 次\n", count);
    return 0;
}

计算结果剧透
如果你运行上述代码,答案应该是 27次。这告诉我们指数增长的恐怖力量!


结语

通过这个实验,我们不仅实现了一个简单的密码锁,还发现了代码中隐藏的逻辑陷阱,并复习了C语言中重要的控制结构。编程不仅仅是写代码,更是逻辑思维的锻炼。

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐