外部中断实验演示理论

Tutorial: 电子电路 Category: 嵌入式 Published: 2026-08-29 08:09:36 Views: 0 Likes: 0 Comments: 0

106.3.1外部中断实验演示理论

/* ==================== Core\Inc\main.h ==================== */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.h
  * @brief          : Header for main.c file.
  *                   This file contains the common defines of the application.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "stm32f1xx_hal.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */

/* USER CODE END ET */

/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */

/* USER CODE END EC */

/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */

/* USER CODE END EM */

/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);

/* USER CODE BEGIN EFP */

/* USER CODE END EFP */

/* Private defines -----------------------------------------------------------*/
#define EXIT_KEY1_Pin GPIO_PIN_3
#define EXIT_KEY1_GPIO_Port GPIOE
#define EXIT_KEY1_EXTI_IRQn EXTI3_IRQn
#define EXTI_KEY2_Pin GPIO_PIN_4
#define EXTI_KEY2_GPIO_Port GPIOE
#define EXTI_KEY2_EXTI_IRQn EXTI4_IRQn
#define LED1_Pin GPIO_PIN_5
#define LED1_GPIO_Port GPIOB
#define BEEP_Pin GPIO_PIN_8
#define BEEP_GPIO_Port GPIOB

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

#ifdef __cplusplus
}
#endif

#endif /* __MAIN_H */

/* ==================== Core\Inc\gpio.h ==================== */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    gpio.h
  * @brief   This file contains all the function prototypes for
  *          the gpio.c file
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __GPIO_H__
#define __GPIO_H__

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */

void MX_GPIO_Init(void);

/* USER CODE BEGIN Prototypes */

/* USER CODE END Prototypes */

#ifdef __cplusplus
}
#endif
#endif /*__ GPIO_H__ */


/* ==================== Core\Inc\stm32f1xx_it.h ==================== */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    stm32f1xx_it.h
  * @brief   This file contains the headers of the interrupt handlers.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
 ******************************************************************************
  */
/* USER CODE END Header */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F1xx_IT_H
#define __STM32F1xx_IT_H

#ifdef __cplusplus
 extern "C" {
#endif

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */

/* USER CODE END ET */

/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */

/* USER CODE END EC */

/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */

/* USER CODE END EM */

/* Exported functions prototypes ---------------------------------------------*/
void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void SVC_Handler(void);
void DebugMon_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
void EXTI3_IRQHandler(void);
void EXTI4_IRQHandler(void);
/* USER CODE BEGIN EFP */

/* USER CODE END EFP */

#ifdef __cplusplus
}
#endif

#endif /* __STM32F1xx_IT_H */

/* ==================== Core\Src\main.c ==================== */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	HAL_Delay(10);
	if(HAL_GPIO_ReadPin(EXIT_KEY1_GPIO_Port, EXIT_KEY1_Pin)==0)
	{
		HAL_GPIO_TogglePin(BEEP_GPIO_Port, BEEP_Pin);
	}
	if(HAL_GPIO_ReadPin(EXTI_KEY2_GPIO_Port, EXTI_KEY2_Pin)==0)
	{
		HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
	}
}
/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/* ==================== Core\Src\gpio.c ==================== */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    gpio.c
  * @brief   This file provides code for the configuration
  *          of all used GPIO pins.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "gpio.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/*----------------------------------------------------------------------------*/
/* Configure GPIO                                                             */
/*----------------------------------------------------------------------------*/
/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/** Configure pins as
        * Analog
        * Input
        * Output
        * EVENT_OUT
        * EXTI
*/
void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOE_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(BEEP_GPIO_Port, BEEP_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pins : PEPin PEPin */
  GPIO_InitStruct.Pin = EXIT_KEY1_Pin|EXTI_KEY2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = LED1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LED1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : PtPin */
  GPIO_InitStruct.Pin = BEEP_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(BEEP_GPIO_Port, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI3_IRQn, 5, 0);
  HAL_NVIC_EnableIRQ(EXTI3_IRQn);

  HAL_NVIC_SetPriority(EXTI4_IRQn, 7, 0);
  HAL_NVIC_EnableIRQ(EXTI4_IRQn);

}

/* USER CODE BEGIN 2 */

/* USER CODE END 2 */

/* ==================== Core\Src\stm32f1xx_it.c ==================== */
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    stm32f1xx_it.c
  * @brief   Interrupt Service Routines.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */

/* USER CODE END TD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/* External variables --------------------------------------------------------*/

/* USER CODE BEGIN EV */

/* USER CODE END EV */

/******************************************************************************/
/*           Cortex-M3 Processor Interruption and Exception Handlers          */
/******************************************************************************/
/**
  * @brief This function handles Non maskable interrupt.
  */
void NMI_Handler(void)
{
  /* USER CODE BEGIN NonMaskableInt_IRQn 0 */

  /* USER CODE END NonMaskableInt_IRQn 0 */
  /* USER CODE BEGIN NonMaskableInt_IRQn 1 */
   while (1)
  {
  }
  /* USER CODE END NonMaskableInt_IRQn 1 */
}

/**
  * @brief This function handles Hard fault interrupt.
  */
void HardFault_Handler(void)
{
  /* USER CODE BEGIN HardFault_IRQn 0 */

  /* USER CODE END HardFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_HardFault_IRQn 0 */
    /* USER CODE END W1_HardFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Memory management fault.
  */
void MemManage_Handler(void)
{
  /* USER CODE BEGIN MemoryManagement_IRQn 0 */

  /* USER CODE END MemoryManagement_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
    /* USER CODE END W1_MemoryManagement_IRQn 0 */
  }
}

/**
  * @brief This function handles Prefetch fault, memory access fault.
  */
void BusFault_Handler(void)
{
  /* USER CODE BEGIN BusFault_IRQn 0 */

  /* USER CODE END BusFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_BusFault_IRQn 0 */
    /* USER CODE END W1_BusFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Undefined instruction or illegal state.
  */
void UsageFault_Handler(void)
{
  /* USER CODE BEGIN UsageFault_IRQn 0 */

  /* USER CODE END UsageFault_IRQn 0 */
  while (1)
  {
    /* USER CODE BEGIN W1_UsageFault_IRQn 0 */
    /* USER CODE END W1_UsageFault_IRQn 0 */
  }
}

/**
  * @brief This function handles System service call via SWI instruction.
  */
void SVC_Handler(void)
{
  /* USER CODE BEGIN SVCall_IRQn 0 */

  /* USER CODE END SVCall_IRQn 0 */
  /* USER CODE BEGIN SVCall_IRQn 1 */

  /* USER CODE END SVCall_IRQn 1 */
}

/**
  * @brief This function handles Debug monitor.
  */
void DebugMon_Handler(void)
{
  /* USER CODE BEGIN DebugMonitor_IRQn 0 */

  /* USER CODE END DebugMonitor_IRQn 0 */
  /* USER CODE BEGIN DebugMonitor_IRQn 1 */

  /* USER CODE END DebugMonitor_IRQn 1 */
}

/**
  * @brief This function handles Pendable request for system service.
  */
void PendSV_Handler(void)
{
  /* USER CODE BEGIN PendSV_IRQn 0 */

  /* USER CODE END PendSV_IRQn 0 */
  /* USER CODE BEGIN PendSV_IRQn 1 */

  /* USER CODE END PendSV_IRQn 1 */
}

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}

/******************************************************************************/
/* STM32F1xx Peripheral Interrupt Handlers                                    */
/* Add here the Interrupt Handlers for the used peripherals.                  */
/* For the available peripheral interrupt handler names,                      */
/* please refer to the startup file (startup_stm32f1xx.s).                    */
/******************************************************************************/

/**
  * @brief This function handles EXTI line3 interrupt.
  */
void EXTI3_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI3_IRQn 0 */

  /* USER CODE END EXTI3_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(EXIT_KEY1_Pin);
  /* USER CODE BEGIN EXTI3_IRQn 1 */

  /* USER CODE END EXTI3_IRQn 1 */
}

/**
  * @brief This function handles EXTI line4 interrupt.
  */
void EXTI4_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI4_IRQn 0 */

  /* USER CODE END EXTI4_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(EXTI_KEY2_Pin);
  /* USER CODE BEGIN EXTI4_IRQn 1 */

  /* USER CODE END EXTI4_IRQn 1 */
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

一、外部中断实验演示

本实验实现两项功能:

输入MCU 引脚触发边沿输出MCU 引脚
KEY1PE3下降沿蜂鸣器翻转PB8
KEY0PE4下降沿LED1 翻转PB5

按键松开时由内部上拉保持高电平,按下后接地变低,所以按下瞬间形成下降沿。外部中断让 CPU 不必在主循环中不停询问按键;引脚出现目标边沿后,硬件自动提出中断请求。

flowchart LR
    KEY[按键按下] --> EDGE[PE3/PE4 产生下降沿]
    EDGE --> EXTI[EXTI3/EXTI4 挂起]
    EXTI --> NVIC[NVIC 仲裁优先级]
    NVIC --> ISR[EXTI3/EXTI4_IRQHandler]
    ISR --> HAL[HAL_GPIO_EXTI_IRQHandler]
    HAL --> CB[HAL_GPIO_EXTI_Callback]
    CB --> OUT[翻转蜂鸣器或 LED]

1.1 中断基本概念

中断是处理器对异步事件的硬件响应。主程序正常执行时,某个中断源提出请求;满足使能和优先级条件后,CPU 暂停当前指令流,保存必要现场,跳到对应中断服务程序,处理完成后再返回原位置继续执行。

主程序:A → B → C ─────────────→ D → E
                     ↑          ↓
中断请求             └─ ISR:保存现场 → 处理 → 返回

轮询与中断的区别:

方式CPU 行为优点风险/代价
轮询周期性主动读取状态流程直观,调试简单响应取决于轮询周期,占用执行时间
中断事件到来才跳入 ISR响应及时,主循环可做其他工作存在并发、优先级和 ISR 时长问题

中断不等于多线程,但会打断主程序,也可能被更高优先级中断打断,所以共享变量仍有并发问题。主程序与 ISR 共用的状态通常需要 volatile、临界区或原子操作。

1.2 外部中断

外部中断的信号来自 MCU 引脚或少数内部事件线。STM32F1 的 GPIO 外部中断不是“每个 GPIO 都有独立中断线”,而是同号引脚共享同一条 EXTI 线:

PA0/PB0/PC0/... ──选择一个──> EXTI0
PA1/PB1/PC1/... ──选择一个──> EXTI1
...
PA15/PB15/...   ──选择一个──> EXTI15

端口选择由 AFIO_EXTICR1~4 完成。因此 PE3 使用 EXTI3,PE4 使用 EXTI4;同一时刻不能再把 PB3 也独立映射为另一条 EXTI3。

1.2.1 实验演示按键控制蜂鸣器和灯

开发板按键和输出器件的电路决定了有效电平。

PE3/PE4 按键:

STM32 内部                       开发板
3.3 V ──[弱上拉]──┬── PE3/PE4 ── 按键 ── GND
                   │
                 输入缓冲

松开:GPIO=1
按下:GPIO=0,形成 1→0 下降沿

PB5 LED1:

3.3 V ── LED ──[1 kΩ]── PB5

PB5=0:MCU 吸收电流,LED 亮
PB5=1:LED 两端压差接近 0,LED 灭

PB8 蜂鸣器:

                      3.3 V
                        │
                    有源蜂鸣器
                        │
                        C
PB8 ──[1 kΩ]── B   S8050 NPN
                  E     │
                        ├──[10 kΩ]── GND
                        └─────────── GND

PB8=1:基极有电流,三极管导通,蜂鸣器响
PB8=0:三极管截止,蜂鸣器停

MCU 不直接承担蜂鸣器工作电流。PB8 只提供受限的基极电流,S8050 像受控开关一样接通蜂鸣器到地的电流路径。10 kΩ 下拉使 PB8 上电高阻时三极管保持截止,避免误响。原理图标注 3 kHz 表明它是带内部振荡的有源蜂鸣器,给直流使能电平即可发声;无源蜂鸣器则需要 PWM。

实验初始值为 LED1=高(灭)、BEEP=低(停)。每次按键确认后执行 Toggle,所以输出在开与关之间切换。

1.2.2 中断配置

外部按键中断需要同时打通五层:

  1. GPIOE 时钟和 AFIO 时钟;
  2. PE3/PE4 配为带上拉的输入;
  3. AFIO 将 EXTI3/4 映射到端口 E;
  4. EXTI 选择下降沿并解除中断屏蔽;
  5. NVIC 设置优先级并使能对应 IRQ。

任何一层缺失都会导致 IRQHandler 不执行。

1.2.2.1 芯片电路图分析

经典 8051 的外部中断入口通常是 P3.2/INT0 和 P3.3/INT1。课程 51 开发板的两只按键按下时接地,松开状态利用 P3 准双向口自身的内部上拉保持为高;它们是芯片预先固定的专用复用引脚。

STM32F103 的结构更灵活:几乎任意 Px0~Px15 都可连接对应编号的 EXTIx,但必须经过 AFIO 选择端口。

8051:
按键 ──> 固定的 P3.2/INT0 或 P3.3/INT1 ──> 中断控制器

STM32F1:
PE3 ──> AFIO_EXTICR1 选择端口 E ──> EXTI3 ──> NVIC
PE4 ──> AFIO_EXTICR2 选择端口 E ──> EXTI4 ──> NVIC

看原理图时应依次回答:

  • 按键按下接 VCC 还是 GND?
  • 松开状态由外部电阻还是内部上下拉确定?
  • 输出器件高电平有效还是低电平有效?
  • MCU 是直接驱动负载,还是通过三极管/MOSFET?
  • 原理图网络名与代码宏是否一致?

本实验蜂鸣器是 PB8,不是 PB0;LED1 是 PB5;按键为 PE3 和 PE4。

1.2.2.2 配置界面

CubeMX/CubeIDE 中的配置要点:

引脚功能GPIO Pull-up/Pull-downUser Label
PE3GPIO_EXTI3Pull-upEXIT_KEY1
PE4GPIO_EXTI4Pull-upEXTI_KEY2
PB5GPIO_OutputPull-up,Low speedLED1
PB8GPIO_OutputPull-down,Low speedBEEP

PE3/PE4 选择 External Interrupt Mode with Falling edge trigger detection。随后在 NVIC 中启用 EXTI line3 和 EXTI line4。

标签只影响生成的宏名,不改变硬件功能。工程中 KEY1 宏写成 EXIT_KEY1,KEY2 写成 EXTI_KEY2,虽然拼写不完全一致,但必须在 main.h、gpio.c 与中断文件中保持一致。

1.2.2.3 外部通用中断线

STM32F103 常用 EXTI 线:

EXTI 线来源
0~15GPIO 的 0~15 号引脚,由 AFIO 选择端口
16PVD 输出
17RTC Alarm
18USB Wakeup
19仅互联型器件的 Ethernet Wakeup;普通 STM32F103 不使用

IRQ 向量的组合方式:

线NVIC IRQ
EXTI0~EXTI4各自独立 IRQ
EXTI5~EXTI9共用 EXTI9_5_IRQn
EXTI10~EXTI15共用 EXTI15_10_IRQn

共享 IRQ 中必须检查 PR 挂起位,判断究竟是哪一条线触发。

1.2.2.4 中断和事件控制器

EXTI 同一套边沿检测可走两条输出通路:

flowchart LR
    IN[输入线] --> EDGE[上升/下降沿检测]
    EDGE --> PEND[挂起请求]
    PEND --> IMR{IMR 是否开放}
    IMR -->|是| NVIC[NVIC 中断请求]
    EDGE --> EMR{EMR 是否开放}
    EMR -->|是| EVT[事件脉冲/WFE 唤醒]
    SWIER[软件请求] --> PEND
  • 中断通路经过 IMR 和 NVIC,CPU 跳转到 ISR;
  • 事件通路经过 EMR,可产生事件脉冲或唤醒 WFE,不必执行 ISR;
  • RTSR 选择上升沿,FTSR 选择下降沿,两者可同时开启;
  • PR 记录挂起状态,写 1 清除;
  • SWIER 可由软件模拟提出请求。
1.2.2.5 中断配置示例

本实验 PE3/PE4 的关键寄存器结果:

RCC_APB2ENR.IOPEEN = 1
RCC_APB2ENR.AFIOEN = 1

AFIO_EXTICR1.EXTI3 = 0100  (端口 E)
AFIO_EXTICR2.EXTI4 = 0100  (端口 E)

EXTI_FTSR.TR3 = 1
EXTI_FTSR.TR4 = 1
EXTI_RTSR.TR3 = 0
EXTI_RTSR.TR4 = 0
EXTI_IMR.MR3  = 1
EXTI_IMR.MR4  = 1

NVIC 再分别使能 EXTI3_IRQn 和 EXTI4_IRQn。边沿发生后,硬件置位 PR3/PR4;HAL 中断处理函数写 1 清除对应挂起位,再调用用户回调。若不清 PR,中断会再次进入。

按键机械抖动会产生多个下降/上升沿。可靠设计应让 ISR 只记录“哪一键发生事件”和时间戳,再由主循环确认稳定状态;不应把长延时、串口打印或复杂业务全部放进中断回调。