PIR 人体红外控制板载 LED — 保姆级笔记

PIR 人体红外控制板载 LED --- 保姆级笔记

配套硬件 :DshanMCU-F407(STM32F407ZGT6)+ 人体红外感应模块(PIR)
前置知识 :已完成 LED 闪烁实验(见 HAL快速入门笔记 第二章)
学习理念:每个知识点都亲手敲一遍,不只看不练


文章目录

  • [PIR 人体红外控制板载 LED --- 保姆级笔记](#PIR 人体红外控制板载 LED — 保姆级笔记)
    • [1. 实验概述](#1. 实验概述)
      • [1.1 我们在做什么?](#1.1 我们在做什么?)
      • [1.2 涉及的知识点](#1.2 涉及的知识点)
      • [1.3 和 LCD 实验的关系](#1.3 和 LCD 实验的关系)
    • [2. 认识 PIR 人体红外传感器](#2. 认识 PIR 人体红外传感器)
      • [2.1 什么是 PIR?](#2.1 什么是 PIR?)
      • [2.2 PIR 模块引脚](#2.2 PIR 模块引脚)
      • [2.3 PIR 和按键的本质区别](#2.3 PIR 和按键的本质区别)
    • [3. 新知识点:GPIO 输入](#3. 新知识点:GPIO 输入)
      • [3.1 输出和输入的区别](#3.1 输出和输入的区别)
      • [3.2 HAL_GPIO_ReadPin --- 读电平](#3.2 HAL_GPIO_ReadPin — 读电平)
    • [4. 新知识点:上拉与下拉电阻](#4. 新知识点:上拉与下拉电阻)
      • [4.1 为什么要上拉/下拉?](#4.1 为什么要上拉/下拉?)
      • [4.2 上拉电阻(Pull-up)](#4.2 上拉电阻(Pull-up))
      • [4.3 下拉电阻(Pull-down)](#4.3 下拉电阻(Pull-down))
      • [4.4 我们的 PIR 用哪种?](#4.4 我们的 PIR 用哪种?)
    • [5. 硬件接线](#5. 硬件接线)
      • [5.1 接线表](#5.1 接线表)
      • [5.2 板载 LED](#5.2 板载 LED)
      • [5.3 接线实物对应](#5.3 接线实物对应)
    • [6. CubeMX 配置](#6. CubeMX 配置)
      • [6.1 新建工程](#6.1 新建工程)
      • [6.2 引脚配置](#6.2 引脚配置)
      • [6.3 PE0 配置为下拉](#6.3 PE0 配置为下拉)
      • [6.4 时钟配置(时钟树)](#6.4 时钟配置(时钟树))
      • [6.5 生成工程](#6.5 生成工程)
    • [7. 代码逐行解读](#7. 代码逐行解读)
      • [7.1 完整代码](#7.1 完整代码)
      • [7.2 HAL_GPIO_ReadPin 详解](#7.2 HAL_GPIO_ReadPin 详解)
      • [7.3 if-else 控制逻辑](#7.3 if-else 控制逻辑)
      • [7.4 LED 亮灭控制(复习)](#7.4 LED 亮灭控制(复习))
      • [7.5 HAL_Delay 的作用](#7.5 HAL_Delay 的作用)
    • [8. 实验现象](#8. 实验现象)
    • [9. 扩展思考](#9. 扩展思考)
      • [9.1 如果接反了上下拉会怎样?](#9.1 如果接反了上下拉会怎样?)
      • [9.2 如何让两个 LED 同时受控?](#9.2 如何让两个 LED 同时受控?)
      • [9.3 如果换成按键,代码几乎一样](#9.3 如果换成按键,代码几乎一样)
      • [9.4 下一步](#9.4 下一步)

1. 实验概述

1.1 我们在做什么?

用 PIR 人体红外传感器检测是否有人,有人时点亮板载 LED1,无人时熄灭。

1.2 涉及的知识点

知识点 类型 说明
GPIO 输出 复习 控制板载 LED(PF9 低电平亮)
GPIO 输入 新学 读取 PIR 模块的电平信号
上拉/下拉电阻 新学 让输入引脚有确定的默认电平
HAL_GPIO_ReadPin 新函数 读取 GPIO 引脚电平

1.3 和 LCD 实验的关系

LCD 实验你学会了 SPI 发送 (往屏幕写数据)。

这个实验让你学会 GPIO 读取(从传感器读信号)。

一个是"写",一个是"读"。以后绝大多数传感器都是这两种操作的组合。


2. 认识 PIR 人体红外传感器

2.1 什么是 PIR?

PIR = Passive InfraRed(被动式红外),检测人体发出的红外线。

只要有人进入感应范围,OUT 脚输出高电平(3.3V) ;人离开后,OUT 脚恢复低电平(0V)

复制代码
  人进入范围 ───────┬────── 人离开范围
                  │
  OUT  ──────┐    │    ┌───────── 低电平(无人)
             │    │    │
             └────┴────┘
                  高电平(有人)

2.2 PIR 模块引脚

你的 PIR 模块引出 3 根线:

线色 信号 接 F407
🔴 红 VCC 3.3V
⚫ 黑 GND GND
🟢 绿 OUT PE0(GPIO 输入)

记住这个配色:红线=电源、黑线=地、绿线=信号。这是传感器模块最常见的线色。

2.3 PIR 和按键的本质区别

复制代码
按键:            你用手按 → 接通 → 高电平
PIR 传感器:    有人进入 → 内部感应 → 高电平

对 STM32 来说,两者都是"GPIO 读到高电平",
区别只在于:按键需要人主动去按,PIR 被动检测。

所以你用 PIR 代替按键,代码几乎一模一样------这就是"传感器替换"的思想。


3. 新知识点:GPIO 输入

⭐⭐⭐ MUST MASTER

3.1 输出和输入的区别

之前学 LED 时用的是 GPIO 输出------F407 主动控制引脚高低电平去驱动 LED。

GPIO 输入反过来------F407 读取外部设备送到引脚上的电平(高 or 低),做出判断。

复制代码
输出:F407 ──→ 外设(F407 发出信号)
输入:F407 ←── 外设(F407 接收信号)

3.2 HAL_GPIO_ReadPin --- 读电平

HAL 库读取 GPIO 输入电平的函数:

c 复制代码
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);

参数

参数 含义 例子
GPIOx 哪一组 GPIO GPIOE(E 组)
GPIO_Pin 哪一个引脚 GPIO_PIN_0(第 0 号引脚)

返回值

返回值 含义
GPIO_PIN_SET 引脚当前为高电平(= 1)
GPIO_PIN_RESET 引脚当前为低电平(= 0)

用法示例

c 复制代码
// 读取 PE0 引脚电平
GPIO_PinState state = HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_0);

if (state == GPIO_PIN_SET) {
    // PE0 是高电平
} else {
    // PE0 是低电平
}

对比记忆

操作 函数 方向
写高电平 HAL_GPIO_WritePin(GPIOx, Pin, GPIO_PIN_SET) 输出
写低电平 HAL_GPIO_WritePin(GPIOx, Pin, GPIO_PIN_RESET) 输出
读取电平 HAL_GPIO_ReadPin(GPIOx, Pin) 输入

4. 新知识点:上拉与下拉电阻

⭐⭐⭐ MUST MASTER

4.1 为什么要上拉/下拉?

GPIO 配置为输入时,引脚处于"高阻态"------它只是在"听"外部信号,自己不主动输出电平。

问题来了:如果外部什么信号都没有(模块没接、导线断开),引脚读到的是什么?

答案是不确定 ------可能是 0,可能是 1,随机的,这就是"浮空"状态。

复制代码
浮空引脚的问题:
  ┌────────────┐
  │  引脚浮空  │──→ 读到的值随机跳变:0 1 0 1 1 0 ...
  │  (高阻态)  │
  └────────────┘
  STM32 内部 → 不确定

你的程序用这个随机值做判断 → 就会出现"没人但 LED 偶尔自己亮"的诡异现象。

4.2 上拉电阻(Pull-up)

在引脚和 VCC(3.3V)之间接一个电阻,让引脚默认拉到高电平。

复制代码
             3.3V
               │
              ┌┴┐
              │ │ 上拉电阻(~40KΩ 内部)
              └┬┘
               │
               └── GPIO 引脚 → PIR 没信号时默认读到 1

适用场景:外设默认高电平有效,或者外设用"拉低"来表示触发。

4.3 下拉电阻(Pull-down)

在引脚和 GND 之间接一个电阻,让引脚默认拉到低电平。

复制代码
              GPIO 引脚 → PIR 没信号时默认读到 0
               │
              ┌┴┐
              │ │ 下拉电阻(~40KΩ 内部)
              └┬┘
               │
              GND

适用场景:外设默认低电平,或者外设用"拉高"来表示触发。

4.4 我们的 PIR 用哪种?

PIR 模块:无人=低电平,有人=高电平。

所以应该用 下拉(Pull-down)------没人时引脚稳定读到 0,有人时读到 1。

复制代码
  没人时:PIR 输出 0V + 下拉电阻 → 引脚保持 0 ✓
  有人时:PIR 输出 3.3V > 下拉 → 引脚读到 1 ✓

在 CubeMX 里配成 Pull-down,就是让 STM32 内部自动接上下拉电阻,不需要外接。

经验法则

  • 外设空闲时低电平、触发时高电平 → 用 Pull-down
  • 外设空闲时高电平、触发时低电平 → 用 Pull-up
  • 不确定的时候优先用 Pull-down,大多数传感器是"高电平触发"

5. 硬件接线

5.1 接线表

PIR 模块 F407 引脚 说明
VCC(红) 3.3V 供电
GND(黑) GND 共地
OUT(绿) PE0 信号输入

5.2 板载 LED

LED F407 引脚 控制逻辑
LED1 PF9 输出低电平亮,高电平灭

无需外接,LED1 在开发板上已经和 PF9 连好了。

5.3 接线实物对应

复制代码
┌──────────────────────────────────────┐
│          PIR 模块                      │
│  ┌────┐  ┌────┐  ┌────┐              │
│  │ VCC│  │ GND│  │ OUT│              │
│  └──┬─┘  └──┬─┘  └──┬─┘              │
│     │       │       │                 │
└─────┼───────┼───────┼─────────────────┘
      │       │       │
      │ 红线   │ 黑线  │ 绿线
      │       │       │
      ▼       ▼       ▼
    F407    F407    F407
    3.3V    GND     PE0

6. CubeMX 配置

6.1 新建工程

  1. 打开 STM32CubeMX → New Project
  2. 搜索 STM32F407ZGTx → 双击选中

6.2 引脚配置

找到以下引脚,左键点击设置功能:

引脚 设置 说明
PF9 GPIO_Output 控制板载 LED1
PE0 GPIO_Input 读取 PIR 信号

6.3 PE0 配置为下拉

  1. 点菜单栏 Pinout & Configuration
  2. 左边选 GPIO
  3. 在引脚列表中找到 PE0
  4. GPIO Pull-up/Pull-down 改为 Pull-down

6.4 时钟配置(时钟树)

  1. 进入 Clock Configuration 标签页
  2. HCLK 输入框输入 168,回车
  3. 确认时钟树无红色警告

6.5 生成工程

进入 Project Manager

配置项 设置
Project Name pir_led(或其他名字)
Toolchain/IDE MDK-ARM
Minimum Firmware Version 选高版本

点击 GENERATE CODE → 弹窗选 Yes


7. 代码逐行解读

7.1 完整代码

c 复制代码
int main(void)
{
    /* HAL 库初始化 */
    HAL_Init();
    /* 系统时钟配置 */
    SystemClock_Config();
    /* GPIO 初始化 */
    MX_GPIO_Init();

    /* USER CODE BEGIN WHILE */
    while (1)
    {
        // 读取 PE0(PIR 输出脚)的电平
        if (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_0) == GPIO_PIN_SET)
        {
            // PIR 检测到人 → LED1 亮
            HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_RESET);
        }
        else
        {
            // 无人 → LED1 灭
            HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET);
        }
        HAL_Delay(100);  // 每 100ms 检测一次
    }
}

7.2 HAL_GPIO_ReadPin 详解

c 复制代码
if (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_0) == GPIO_PIN_SET)

这行干了什么:

复制代码
① STM32 内部读取 PE0 引脚的输入数据寄存器(IDR)
② 判断该位的值是 1(高电平)还是 0(低电平)
③ 如果是 1 → 返回 GPIO_PIN_SET(=1)
④ 如果是 0 → 返回 GPIO_PIN_RESET(=0)

对应硬件原理:

复制代码
PIR_OUT=3.3V → PE0 读到 1 → GPIO_PIN_SET     → 有人
PIR_OUT=0V   → PE0 读到 0 → GPIO_PIN_RESET   → 无人

7.3 if-else 控制逻辑

c 复制代码
if (条件为真) {
    // 执行这里
} else {
    // 否则执行这里
}

这是 C 语言最基础的分支结构。配合 GPIO 读引脚,就是"传感器判断 + 动作执行"的基本模式。

几乎所有传感器驱动都可以归结为:

复制代码
if (传感器触发条件满足) {
    执行响应动作
} else {
    不动作 / 恢复默认
}

7.4 LED 亮灭控制(复习)

c 复制代码
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_RESET);  // PF9 低 → LED1 亮
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET);    // PF9 高 → LED1 灭

DshanMCU-F407 的板载 LED 是低电平点亮(LED 正极接 3.3V,负极通过 PF9 接地):

复制代码
                  LED1
                 ↑(电流)
   3.3V ───────┬─<LED>───┬─── PF9
               │         │
               R(限流)   └── PF9=0 → 电流流过 → LED 亮
                         PF9=1 → 无压差 → LED 灭

7.5 HAL_Delay 的作用

c 复制代码
HAL_Delay(100);  // 延时 100 毫秒

不加延时会怎样?

复制代码
while (1) 每纳秒执行一次
  → 每次都在读 PE0
  → 读到啥就立刻写 PF9
  → CPU 跑满,白白发热

加了延时 100ms 后:
  → 每 100ms 才检查一次 PIR
  → CPU 利用率大幅降低
  → 对人类的反应来说,100ms 和"即时"没有区别

延时 vs 实时性

  • 延时太长(如 1000ms)→ 手晃过去了但 LED 还没亮,体验差
  • 延时太短(如 1ms)→ CPU 占用率高,但没必要
  • 100ms 是个经验值,对"人检测"场景足够

后续会学到,延时可以用定时器中断替代,让 CPU 在等待时做别的事------那是 06-5 的内容。


8. 实验现象

正常现象:

复制代码
手在 PIR 前晃动 → LED1 立刻亮
手离开           → LED1 亮几秒后熄灭
                   (PIR 模块内部有保持时间)

可能遇到的问题

现象 原因 解决方法
LED 一直不亮 接线不对 检查 VCC/GND/OUT 接线
LED 一直不亮 PE0 没配 Pull-down 检查 CubeMX 配置
LED 一直亮从不灭 PIR 模块检测范围有人 人离开范围测试
LED 闪烁不停 PIR 受到干扰 远离空调/暖气风口
编译报错 undefined identifier 代码写在 USER CODE 外面 确保代码在 BEGIN 3 / END 3 之间

9. 扩展思考

9.1 如果接反了上下拉会怎样?

如果 PE0 配了 Pull-up 而不是 Pull-down:

复制代码
没人时:PIR_OUT=0V,但内部上拉电阻把 PE0 拉到 3.3V → 读到 1 → 认为"有人"
有人时:PIR_OUT=3.3V,上拉也是 3.3V → 还是读到 1
→ 结果:LED 一直亮,永远检测不到"无人"

9.2 如何让两个 LED 同时受控?

PF9 和 PF10 各有一个板载 LED:

c 复制代码
// 有人 → 两个都亮
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9 | GPIO_PIN_10, GPIO_PIN_RESET);

// 无人 → 两个都灭
HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9 | GPIO_PIN_10, GPIO_PIN_SET);

9.3 如果换成按键,代码几乎一样

c 复制代码
// 按键(假设接在 PE0,按键按下为高电平)
if (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_0) == GPIO_PIN_SET) {
    // 按键按下 → 亮灯
} else {
    // 按键松开 → 灭灯
}

PIR 和按键,对 STM32 来说都是 GPIO 输入,区别只在于"触发源"不同。这就是硬件抽象的思想------不管你用按键、PIR、还是其他传感器,只要输出数字信号,代码框架都是一样的。

9.4 下一步

现在你已经学会了:

  • ✅ GPIO 输出(控制 LED)
  • ✅ GPIO 输入(读 PIR 信号)
  • ✅ 上拉/下拉电阻的概念

下一节 05-12 光敏传感器控制蜂鸣器 ,你会学到 GPIO 模拟输入(ADC)------不仅能读"有没有"(0 或 1),还能读"有多少"(0~4095 的数值)。


编写日期 :2026年5月25日
适用硬件 :DshanMCU-F407(STM32F407ZGT6)+ PIR 人体红外模块
配套视频:05-11 按键控制LED(用 PIR 替代按键实现)

相关推荐
IT199517 小时前
Dify笔记-一种知识库文件上传失败报错500解决方法
笔记
IronMurphy17 小时前
AI Agent 学习笔记 Day 1:大模型基础、API 调用与 Prompt 工程
人工智能·笔记·学习
爱学习的章鱼哥18 小时前
AI编程学习笔记(I)
人工智能·笔记·学习·ai编程
·醉挽清风·18 小时前
学习笔记—MySQL—索引
笔记·学习·mysql
数智工坊18 小时前
《我看见的世界:李飞飞自传》第7-12章阅读笔记:从ImageNet到以人为本的AI
人工智能·笔记
数智工坊18 小时前
钱钟书《围城》第1-5章阅读笔记:一场关于人生困境的提前预演
笔记·生活
sbjdhjd18 小时前
03(中)| K8s控制器:DaemonSet+Job+CronJob 逐行解析与生产落地
运维·笔记·docker·云原生·容器·kubernetes·开源
AI视觉网奇19 小时前
3d打印资料笔记
笔记·学习
sheeta199819 小时前
LeetCode 每日一题笔记 日期:2026.05.25 题目:1871. 跳跃游戏 VII
笔记·leetcode·游戏