ZZZ-08-GPIO输入

学习 GPIO 输入 是迈向“人机交互”的关键一步。现在我们将以 PA0 引脚连接按键,PC13 引脚控制 LED 为例。学习两种最常用的方法:轮询扫描(Polling) 和 外部中断(EXTI)

准备工作:硬件连接

  1. LED: 最小开发板自带,连接在 PC13(低电平点亮)。
  2. 按键: 找一个轻触按键。
    • 一端连接到 PA0。
    • 另一端连接到 GND。
    • 原理:平时 PA0 是高电平,按键按下时 PA0 变成低电平。

实验一:轮询扫描(Button Scanning)

这种方法就像警察“查岗”,在 while(1) 循环里不断地看 PA0 的状态。

1. CubeIDE 配置 (ioc 文件)

  • PC13: 保持 GPIO_Output【最小开发板上的灯,低电平点亮】
  • PA0: 设置为 GPIO_Input
  • 关键配置 (GPIO 选项卡):
    • 点击 PA0,将 GPIO Pull-up/Pull-down 设置为 Pull-up(上拉)【PA0 默认为高电平,当按键按下时,将 PA0 拉低到 GND】
    • 原因:这样当按键没按下时,内部电阻会把 PA0 稳定在 3.3V(高电平),防止引脚“浮空”导致电平乱跳。

2. 代码实现

main.cwhile(1) 中编写:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
/* USER CODE BEGIN WHILE */
while (1)
{
  // 1. 读取 PA0 的电平状态
  if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) // 如果读到低电平(按键按下)
  {
    // 2. 软件消抖:按键是机械结构,按下时会产生几毫秒的杂波
    HAL_Delay(20); 
    
    // 3. 再次确认是否按下
    if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
    {
      // 4. 执行动作:翻转 LED
      HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
      
      // 5. 等待按键松开(死循环直到变回高电平),防止长按导致灯疯狂闪烁
      while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);
    }
  }
}

实验二:外部中断(EXTI)

轮询扫描很占 CPU 资源。中断 则像“报警器”:CPU 平时去干别的事,按键一按下,硬件自动触发一个信号通知 CPU 来处理。

1. CubeIDE 配置 (ioc 文件)

  • PA0: 修改为 GPIO_EXTI0
  • GPIO 设置:
    • GPIO mode: 选择 External Interrupt Mode with Falling edge trigger(下降沿触发,即电平从 1 变 0 的瞬间触发)。
    • GPIO Pull-up/Pull-down: 保持 Pull-up。
  • NVIC 设置 (最重要!):
    • 点击左侧 System Core -> NVIC。
    • 勾选 EXTI line0 interrupt 后面的 Enabled 框。如果不勾,中断永远不会发生!
    • ![[Pasted image 20251220204306.png]]

为什么勾选 EXTI line 0? “EXTI line 0” 其实是 STM32 中断分配机制中的一个“频道”。STM32 的 GPIO 引脚非常多(PA0PA15, PB0PB15…),但中断线(Line)资源是有限的

  • 对应关系:引脚的编号决定了它属于哪条线。
    • PA0, PB0, PC0, PD0 … 全部连接到 EXTI Line 0。
    • PA1, PB1, PC1, PD1 … 全部连接到 EXTI Line 1。
  • 排他性(重要):虽然 PA0 和 PB0 都能连到 Line 0,但同一时刻 Line 0 只能选择其中一个引脚。你不能同时把 PA0 和 PB0 都设为中断。

2. 代码实现

中断不需要写在 while(1) 里。HAL 库会自动调用一个名为“回调函数”的地方。你只需要在 main.c 的末尾(USER CODE BEGIN 4 之后)加上这个函数:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
/* USER CODE BEGIN 4 */
// 当 PA0 触发中断时,系统会自动跳到这个函数执行
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if (GPIO_Pin == GPIO_PIN_0) // 确保是 PA0 产生的中断
  {
    // 翻转 LED
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
  }
}
/* USER CODE END 4 */

对比

两种方法的深度对比

特性 轮询扫描 (Polling) 外部中断 (EXTI)
CPU 占用 高(一直在跑判断语句) 极低(平时 CPU 可以休眠)
实时性 取决于 while 循环里其他代码的长短 极高(信号一到立刻响应)
消抖处理 简单(用 HAL_Delay 复杂(中断里不建议用 Delay,通常用定时器)
适用场景 简单按键、对实时性要求不高 紧急停止、脉冲计数、低功耗唤醒

常见坑点(避坑指南)

  1. 忘记开 NVIC 中断开关:这是 EXTI 实验失败的 90% 原因
  2. 中断里加了很长的延时:中断服务函数要求“快进快出”,如果在里面写 HAL_Delay(1000),系统可能会卡死或错过其他重要任务。
  3. 引脚悬空:如果不配置内部上拉(Pull-up),你的灯可能会因为你手指靠近板子而自己乱闪。

扩展资料:常见中断类型

STM32 的中断分为三大类:系统异常外部中断 (EXTI)外设中断

A. 系统异常 (System Exceptions)

这些是内核自带的,优先级极高,通常用于处理“紧急事故”。

  • Reset: 复位中断。
  • HardFault: 硬故障(程序崩溃、访问非法内存)。
  • SysTick: 系统滴答定时器(HAL_Delay 的基础)。

B. 外部中断 (EXTI)

主要处理引脚的电平变化。Line 0 到 Line 15: 对应 GPIO 的 16 个引脚编号。

C. 外设中断 (Peripheral Interrupts)

每个功能模块都有自己的“报警器”。

  • TIM (Timer): 定时器时间到、捕获到信号。
  • UART/USART: 收到数据了、数据发完了。
  • ADC: 模数转换完成了。
  • DMA: 大批量数据搬运完了。

中断结构树图

graph TD subgraph "中断源 (Interrupt Sources)" subgraph "外部中断 EXTI" EXTI0[EXTI Line 0
PA0/PB0/PC0...] EXTI1[EXTI Line 1
PA1/PB1/PC1...] EXTIn[EXTI Line 10-15] end subgraph "通信外设" UART[UART1/2
接收完成/发送空] I2C[I2C/SPI
传输错误/完成] end subgraph "定时与控制" TIM[TIM1/2/3/4
更新/比较/捕获] ADC[ADC
转换结束] end subgraph "系统异常 (内核)" SysTick[SysTick
1ms 节拍] Fault[HardFault
系统崩溃] end end EXTI0 --> NVIC EXTI1 --> NVIC EXTIn --> NVIC UART --> NVIC I2C --> NVIC TIM --> NVIC ADC --> NVIC SysTick --> NVIC Fault --> NVIC subgraph "NVIC (分诊台)" NVIC{NVIC 控制器
判断优先级/屏蔽} end NVIC -->|打断当前任务| CPU[Cortex-M3 内核] style CPU fill:#f96,stroke:#333,stroke-width:2px style NVIC fill:#bbf,stroke:#333

常见的 EXTI 线路“拼车”现象

STM32F103C8T6 中,为了节省硬件资源,有些中断线路是合并在一起的。你在 CubeIDE 的 NVIC 列表里会发现:

  1. EXTI0, EXTI1, EXTI2, EXTI3, EXTI4:这 5 条线是独立的,每个都有自己的中断服务函数。
  2. EXTI9_5_IRQHandler:Line 5 到 Line 9 的中断共享这一个函数。
  3. EXTI15_10_IRQHandler:Line 10 到 Line 15 的中断共享这一个函数。

这意味着:如果 PA10 和 PA11 都开启了中断,它们会跳进同一个函数。你需要在这个函数里通过 if 语句判断到底是哪条线触发的。

Licensed under CC BY-NC-SA 4.0