STM32 中断控制LED流水灯(HAL库,Proteus仿真)

一、实验目的

  1. 深入理解STM32中断系统的工作原理,包括外部中断的触发机制及中断服务程序执行流程。
  2. 熟练掌握STM32CubeMX软件的使用,能够完成GPIO端口(输出/输入)及外部中断的配置,生成Keil工程代码。
  3. 掌握基于HAL库的STM32开发方法,实现GPIO输出控制LED流水灯,以及外部中断控制流水灯启停功能。
  4. 观察并分析机械抖动(杜邦线模拟开关插拔)对中断响应的影响,理解软件消抖的原理与实现。
  5. 掌握Proteus 8.17版本的使用,完成STM32F103C8T6核心板相关电路的绘制、程序加载与仿真验证。

二、实验环境

类别 具体内容
硬件设备 STM32F103C8T6核心板、3只LED灯(红/绿/蓝)、杜邦线若干
软件工具 STM32CubeMX、Keil MDK-ARM 5、Proteus 8.17、Windows 111操作系统

三、实验原理

1. GPIO输出控制原理

STM32的GPIO(通用输入/输出)端口可配置为推挽输出模式,通过向GPIO输出数据寄存器(ODR)写入高低电平,控制外部负载(如LED)的通断。当GPIO引脚输出低电平时,LED回路导通,LED点亮;输出高电平时,回路断开,LED熄灭。本实验中3只LED分别连接到STM32的3个GPIO输出引脚,通过循环切换引脚电平实现流水灯效果。

2. 外部中断原理

中断是CPU暂停当前任务、优先处理紧急事件的机制,外部中断由硬件电平变化触发。STM32F103的GPIO引脚可配置为外部中断源(如PA5对应EXTI5),支持上升沿、下降沿或双边沿触发。当引脚电平满足触发条件时,会触发中断请求,CPU响应后跳转到中断服务程序(ISR)执行,执行完毕后返回原任务。

在HAL库中,外部中断的处理通过重写HAL_GPIO_EXTI_Callback()函数实现,该函数是中断服务程序的核心回调接口,用于编写自定义中断逻辑(如控制流水灯启停标志)。

3. 按键抖动与消抖原理

杜邦线模拟开关插拔时,金属触点会因物理接触不稳定产生“抖动”,导致引脚电平在短时间内快速切换(如高→低→高→低),进而触发多次中断。为解决该问题,本实验采用软件消抖:在中断回调函数中,触发中断后延时20ms,跳过抖动期,再读取引脚稳定电平,确保中断响应的有效性。

四、实验步骤

任务一:基于STM32硬件的LED流水灯与中断控制实现

1. STM32CubeMX配置(工程创建与引脚配置)
  1. 打开STM32CubeMX,点击“New Project”,搜索并选择“STM32F103C8T6”芯片,点击“Start Project”。

  2. 配置系统时钟:进入“RCC”界面,选择“HSE”为“Crystal/Ceramic Resonator”,配置时钟为72MHz。
    在这里插入图片描述在这里插入图片描述

  3. GPIO输出配置(LED控制):

    • 选择PB9、PA2、PC15引脚,配置为“GPIO_Output”模式;
    • 引脚参数设置:输出类型为“Push-Pull”,上拉/下拉为“Pull-Up”,速度为“Low”;
      在这里插入图片描述
  4. GPIO中断配置(开关控制):

    • 选择PB5引脚,配置为“GPIO_EXTI0”模式;

    • 引脚参数设置:上拉/下拉为“No Pull-Down and no pull-up”(默认低电平,接高电平时触发);

    • 进入“NVIC Settings”界面,勾选“EXTI0 line interrupt”,使能中断,优先级默认(抢占优先级0,子优先级0);

    • 进入“GPIO”界面,点击PA0引脚“GPIO mode and configuration”,设置“Trigger Edge”为“Rising and Falling Edge”(双边沿触发,检测高低电平切换)。

      在这里插入图片描述

  5. 其他配置:

    在这里插入图片描述
    在这里插入图片描述

  6. 生成工程:

    • 进入“Project Manager”界面,设置工程名称(如“STM32_Interrupt_LED”),选择工程路径,工具链/IDE选择“MDK-ARM”;
    • 点击“Generate Code”,生成Keil工程并打开。
2. Keil MDK代码编写与编译
  1. 代码编写(main.c文件):

    • 在“/* USER CODE BEGIN PV /”与“/ USER CODE END PV */”之间定义流水灯控制标志:
      // 1. 定义一个全局标志位,用于从中断传递“暂停”指令给主循环
      volatile uint8_t flag = 0; // 0: 不暂停, 1: 暂停
      
      // 2. 使用一个变量来记录当前点亮的LED,实现“从暂停处继续”
      uint8_t c_led = 0; // 0: PB9, 1: PC15, 2: PA2
      
    • 在“/* USER CODE BEGIN WHILE */”的while(1)循环中,添加流水灯控制逻辑:
      /* USER CODE BEGIN WHILE */
      while (1)
        {
          /* USER CODE END WHILE */
      
          /* USER CODE BEGIN 3 */
          // 检查中断设置的暂停标志
          if (!flag)
          {
            // 如果不暂停,执行流水灯逻辑
      
            // 熄灭当前点亮的LED
            switch(c_led)
            {
              case 0: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET); break;
              case 1: HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_RESET); break;
              case 2: HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET); break;
            }
      
            // 更新到下一个LED
            c_led = (c_led + 1) % 3;
      
            // 点亮新的LED
            switch(c_led)
            {
              case 0: HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET); break;
              case 1: HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_SET); break;
              case 2: HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET); break;
            }
      
            // 延时,控制流水灯速度
            HAL_Delay(1000);
          }
          // 如果暂停标志为1,则什么也不做,LED状态保持不变
        }
      /* USER CODE END 3 */
      
    • 在“/* USER CODE BEGIN 4 /”与“/ USER CODE END 4 */”之间,重写外部中断回调函数:
      /* USER CODE BEGIN 4 */
      void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
      {
        // 确保中断来自我们想要的引脚 (PB5)
        if (GPIO_Pin == GPIO_PIN_5)
        {
          // 读取PB5的当前电平
          // 假设插入时为高电平 (GPIO_PIN_SET),不插入时为低电平 (GPIO_PIN_RESET)
          if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_5) == GPIO_PIN_SET)
          {
            // PB5为高电平:设置暂停标志
            flag = 1;
          }
          else
          {
            // PB5为低电平:清除暂停标志,允许继续
            flag = 0;
          }
        }
      }
      /* USER CODE END 4 */
      
  2. 代码编译:

    • 点击Keil工具栏“Build”按钮,编译工程;
    • 编译无错误后,点击“Rebuild”生成Hex文件(路径:工程目录→Objects→工程名.hex)。

在这里插入图片描述

3. 硬件连接与测试
  1. 硬件连接:

    • LED与STM32连接:3只LED的阳极连接PB9、PA2、PC15,阴极接地(GND);
    • 开关(杜邦线)与STM32连接:杜邦线一端接PB5,另一端可切换连接3.3V(高电平)或GND(低电平)。
  2. 程序下载:通过ST-Link串口工具,将Keil生成的Hex文件下载到STM32F103核心板。

  3. 功能测试:

    • 将PA0杜邦线接3.3V:观察到LED按“PB9→PC15→PA2”顺序周期闪烁(流水灯运行);

    • 将PA0杜邦线接GND:观察到LED立即熄灭(流水灯停止);

    • 快速插拔PA0杜邦线:观察抖动对中断的影响,验证软件消抖效果。

    在这里插入图片描述

任务二:Proteus电路绘制与仿真

1. Proteus新建项目与元器件添加
  1. 打开Proteus 8.15,点击“New Project”,设置项目名称(如“STM32_Interrupt_LED_Simulation”),选择保存路径,点击“Next”;
  2. 选择“Create a schematic from scratch”,点击“Next”;
  3. 元器件添加:点击“Pick Devices”,在搜索框中输入元器件名称并添加,所需元器件如下:
元器件名称 Proteus搜索关键词 数量
STM32F103C8 STM32F103C8 1
LED LED 各1
限流电阻 RES 3
电源 POWER 1
接地 GROUND 1
单刀单掷开关 SWITCH 1
2. 电路绘制
  1. 放置元器件:将添加的元器件拖拽到Proteus原理图界面,按合理布局摆放;

  2. 电路连接:

    • 电源与接地:将POWER(3.3V)和GROUND放置到界面,用于提供电源和接地;
    • LED回路:LED阳极→RES→STM32的PB9/PC15/PA2,LED阴极→GROUND;
    • 中断控制回路:SWITCH-SPDT的中间引脚接STM32的PB6,两端引脚接POWER(3.3V
      在这里插入图片描述
  3. 电路检查:确认所有连接无误,无悬空引脚(除STM32未使用引脚外)。

    3. 仿真设置与运行
  4. 加载Hex文件:双击STM32F103C8元器件,在弹出的“Edit Component”窗口中,点击“Program File”后的“…”,选择Keil生成的Hex文件,点击“OK”;

  5. 仿真运行:

    • 点击Proteus工具栏“Start Simulation”按钮,启动仿真;
    • 切换SWITCH-SPDT开关:
      • 开关接POWER(PB5低电平):观察LED流水灯运行;
      • 开关接GROUND(PB5高电平):观察LED停止并熄灭;
    • 记录仿真现象,验证功能符合预期。

五、实验现象与分析

1. 硬件实验现象

操作 实验现象
PB5接3.3V LED按“PB9(1000ms亮)→PC15(1000ms亮)→PA2(1000ms亮)”循环,形成流水灯效果
PB5接空 所有LED立即熄灭,流水灯停止运行
快速插拔PB5杜邦线 未加消抖时:LED频繁启停、闪烁异常;加20ms消抖后:LED启停稳定,无异常闪烁

2. Proteus仿真现象

仿真现象与硬件实验完全一致:开关接3.3V时流水灯运行,接GND时流水灯停止,消抖后无抖动干扰,功能符合设计预期。

3. 现象分析

  1. 流水灯原理验证:通过GPIO输出高低电平切换,成功实现LED周期闪烁,证明GPIO输出配置与HAL库函数(HAL_GPIO_WritePin()HAL_Delay())使用正确;
  2. 中断控制原理验证:PB5电平变化触发外部中断,通过回调函数修改led_run_flag实现流水灯启停,证明中断配置与回调函数重写正确;
  3. 抖动现象分析:未消抖时,杜邦线插拔产生的电平抖动触发多次中断,导致led_run_flag频繁切换;添加20ms延时后,跳过抖动期读取稳定电平,有效解决了抖动问题,验证软件消抖的有效性。

六、实验结论

  1. 本次实验成功实现了基于STM32F103C8T6的中断控制LED流水灯功能:通过STM32CubeMX完成GPIO与外部中断配置,基于HAL库编写流水灯与中断逻辑,硬件测试与Proteus仿真均满足“高电平运行、低电平停止”的需求;
  2. 深入理解了STM32中断系统原理:包括外部中断触发条件、NVIC优先级配置、中断回调函数执行流程;
  3. 掌握了软件消抖的实现方法:通过20ms延时跳过抖动期,有效解决了杜邦线模拟开关的抖动问题,确保中断响应的稳定性;
  4. 熟练掌握了STM32CubeMX、Keil MDK、Proteus三款工具的协同使用流程,为后续STM32项目开发奠定基础。

七、实验心得

  1. 工具协同的重要性:STM32CubeMX简化了寄存器配置流程,Keil提供高效的代码编写与编译环境,Proteus则实现了无硬件依赖的功能验证,三者结合大幅提升了嵌入式开发效率;
  2. 中断与消抖的关键细节:中断回调函数中需严格判断引脚编号,避免误响应;软件消抖的延时时间需合理(20ms),过短无法消抖,过长影响实时性;
  3. 仿真与硬件的互补性:Proteus仿真可提前验证代码逻辑,减少硬件烧录次数,降低硬件损坏风险;硬件测试则能发现仿真中忽略的问题(如实际抖动干扰),两者结合确保实验成功。

简化了寄存器配置流程,Keil提供高效的代码编写与编译环境,Proteus则实现了无硬件依赖的功能验证,三者结合大幅提升了嵌入式开发效率;
2. 中断与消抖的关键细节:中断回调函数中需严格判断引脚编号,避免误响应;软件消抖的延时时间需合理(20ms),过短无法消抖,过长影响实时性;
3. 仿真与硬件的互补性:Proteus仿真可提前验证代码逻辑,减少硬件烧录次数,降低硬件损坏风险;硬件测试则能发现仿真中忽略的问题(如实际抖动干扰),两者结合确保实验成功。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐