当前位置: 首页 > news >正文

STM32 外部中断 和 定时器中断

Overivew

外部中断

功能

外部中断是由芯片外部引脚的电平变化(上升沿、下降沿或双边沿)触发的中断,主要用于响应外部硬件事件(如按键触发、传感器信号变化、外部设备状态改变等)。

特点

  1. 触发源
    由 GPIO 引脚的电平变化触发,每个 GPIO 引脚可通过配置映射到相应的外部中断线(EXTI)。STM32 中外部中断线数量有限(通常为 16 条),多个 GPIO 引脚可复用同一条中断线(需通过 AFIO 配置)。

  2. 响应场景
    适用于处理异步外部事件,如按键按下、传感器触发、外部设备中断请求等,实时响应外部信号变化的场景。

  3. 触发方式
    可配置为上升沿触发、下降沿触发或双边沿触发。

  4. 优先级
    支持中断优先级配置,可通过 NVIC(嵌套向量中断控制器)设置抢占优先级和子优先级,确保高优先级事件优先响应。

定时器中断

功能

定时器中断由芯片内部定时器模块触发,通过配置定时器的计数溢出、比较匹配等事件产生中断,主要用于实现定时任务、周期性操作或精确计时。

特点

  1. 触发源
    由内部定时器(如 TIM1-TIM17 等)的计数事件触发,例如计数器溢出(更新事件)、比较通道匹配、输入捕获等。定时器的时钟源可来自内部时钟(APB 总线时钟)、外部时钟或其他定时器触发信号。

  2. 响应场景
    适用于需要周期性执行的任务,如定时采样数据、刷新显示、产生 PWM 信号、延时控制等,依赖精确时间间隔的场景。

  3. 定时精度
    定时精度高,可通过配置定时器的预分频系数和自动重装载值,实现从微秒级到秒级的精确定时(例如:定时时间 = (预分频值 + 1) × (自动重装载值 + 1) / 定时器时钟频率)。

  4. 灵活性
    支持多种工作模式(向上计数、向下计数、中心对齐计数),可配合比较输出、输入捕获等功能实现复杂时序控制,如电机调速、频率测量等。

  5. 资源特性
    每个定时器独立工作,可同时运行多个定时器中断,互不干扰;中断优先级同样通过 NVIC 配置,便于管理多个定时任务的执行顺序。

核心区别与应用场景

特性外部中断定时器中断
触发源外部 GPIO 引脚电平变化内部定时器计数事件(溢出、比较等)
主要用途响应外部异步事件(按键、传感器)实现精确定时、周期性任务
精度依赖外部信号,无固定时间间隔高精度,可精确控制时间间隔
典型应用按键检测、外部设备唤醒、异常报警定时采样、PWM 生成、系统滴答定时器

程序设计

外部中断

我用的是STM32 F103的芯片,它的外部中断引脚映射(将 GPIO 引脚连接到 EXTI 线)由 AFIO(辅助功能 IO)外设控制,具体通过GPIO_EXTILineConfig()函数配置 SYSCFG_EXTICR 寄存器实现。所以需要使能AFIO时钟。

GPIO_EXTILineConfig()是 F1 系列的函数,而 STM32F4 系列已将引脚映射功能整合到 SYSCFG 外设中,对应函数为SYSCFG_EXTILineConfig(),且需要使能 SYSCFG 时钟(RCC_APB2Periph_SYSCFG)。

外部输入引脚的配置

  1. 开启GPIOD和AFIO时钟(我用的是PD0引脚)
  2. 设置PD0为下拉输入(因为会配置为上升沿触发,所以默认应该为低电平)
  3. 关联PD0到外部中断线0
  4. 配置中断线0为上升沿触发
  5. 配置并使能EXTI0中断的NVIC优先级
void EXTI_PD0_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;EXTI_InitTypeDef EXTI_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;// 使能 GPIOD 和 AFIO 时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);// 配置 PD0 为下拉输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 修改为下拉输入GPIO_Init(GPIOD, &GPIO_InitStructure);// 将 PD0 映射到外部中断线 0(F103使用GPIO_EXTILineConfig)GPIO_EXTILineConfig(GPIO_PortSourceGPIOD, GPIO_PinSource0);// 配置外部中断线 0EXTI_InitStructure.EXTI_Line = EXTI_Line0;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);// 配置 NVICNVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);}

中断服务函数

简单打印一个信息即可,注意函数名必须用这个,不能用其他的。

PD0识别到有上升沿到来,就会打印这个消息


void EXTI0_IRQHandler(void)
{if(EXTI_GetITStatus(EXTI_Line0) != RESET){// 处理中断事件,例如打印信息printf("PD0 Rising Edge Interrupt Detected!\r\n");// 清除中断标志位EXTI_ClearITPendingBit(EXTI_Line0);}
}

触发中断

连接到3.3V触发变化
  1. PD0接杜邦线接到3.3V去,接通的瞬间,就会触发一个上升沿,中断就被触发,打印消息。
  2. 当断开时,由于下拉电阻,电平会恢复到低电平,下一次再接通3.3V时,又会触发上升沿
通过按键控制输入

我们可以设置按键,按键触发另一个引脚PD1(或者其他引脚),PD1是作为GPIO输出的,使用按键来触发PD1的输出高或低电平,然后PD1也用杜邦线连接到PD0上。

按键可以参考STM32 按键输入检测 轮询和中断

按键引脚初始化代码
void KEY_Init(void) //IO初始化
{ GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);//使能PORTA,PORTE时钟GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_4|GPIO_Pin_3;//KEY0-KEY1GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE4,3//初始化 WK_UP-->GPIOA.0	  下拉输入GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉	  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.0}
按键扫描代码
void Key_task()
{vu8 key=0;	printf("Key_task start\r\n");while(1){key=KEY_Scan(0);	//得到键值if(key){						   switch(key){			 case WKUP_PRES:	//控制蜂鸣器printf("Task 2 WKUP_PRES\r\n");break; case KEY1_PRES:printf("Task 2 KEY1_PRES\r\n");break;case KEY0_PRES:printf("Task 2 KEY0_PRES\r\n");break;}}else{delay_ms(10); }}
}

按键触发PD1引脚的输出,从而影响PD0输入值

  • 按下KEY1,PD1输出高电平,PD0就会输入高电平
  • 按下KEY0,PD1输出低电平,PD0就会输入低电平
void Key_task(void *pvParameters)
{vu8 key=0;	printf("Key_task start\r\n");while(1){key=KEY_Scan(0);	//得到键值if(key){						   switch(key){			 case WKUP_PRES:	//控制蜂鸣器BEEP=!BEEP;break; case KEY1_PRES:printf("Task 2 KEY1_PRES\r\n");GPIO_SetBits(GPIOD, GPIO_Pin_1);break;case KEY0_PRES:printf("Task 2 KEY0_PRES\r\n");GPIO_ResetBits(GPIOD, GPIO_Pin_1);break;}}else{delay_ms(10); }}
}

定时器中断

定时器初始化函数

配置定时器时基参数

  • TIM_Prescaler = 7200 - 1:预分频器设置为 7199,将定时器时钟(72MHz)分频为 72MHz / 7200 = 10kHz(即计数频率为 10kHz,每计数 1 次耗时 0.1ms)。
  • TIM_Period = 10000 - 1:自动重载值为 9999,计数器从 0 计数到 9999 后溢出(产生更新事件),耗时 10000 × 0.1ms = 1000ms = 1秒。

  • TIM_CounterMode = TIM_CounterMode_Up:向上计数模式(从 0 到重载值循环)。
  • TIM_ITConfig使能 TIM2 的更新中断(计数器溢出时触发中断)。
void TIM_Configuration(void) 
{TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;NVIC_InitTypeDef NVIC_InitStructure;// 使能定时器时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);// 假设系统时钟频率为 72MHz,APB1 时钟频率为 36MHz,定时器时钟频率为 72MHz// 配置定时器 1 秒定时TIM_TimeBaseStructure.TIM_Period = 10000 - 1; // 自动重载值TIM_TimeBaseStructure.TIM_Prescaler = 7200 - 1; // 预分频器值,72MHz / 7200 = 10kHzTIM_TimeBaseStructure.TIM_ClockDivision = 0;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);// 使能定时器中断TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);// 配置 NVICNVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);// 使能定时器TIM_Cmd(TIM2, ENABLE);
}

中断服务函数

每次定时器时间到,触发中断服务函数,打印信息

// 定时器 2 中断服务函数
void TIM2_IRQHandler(void) 
{if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {printf("TIM2_IRQHandler() \r\n");// 清除定时器中断标志TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}
}

http://www.lryc.cn/news/604289.html

相关文章:

  • 第六章第一节 TIM 定时中断
  • (RedmiBook)上禁用触摸板或自带键盘
  • mybatis-入门
  • 《Vuejs设计与实现》第 12 章(组件实现原理 下)
  • 量子图灵机 Quantum Turing Machine, QTM
  • 【从基础到实战】STL string 学习笔记(上)
  • 如何在出售Windows11/10/8/7前彻底清除电脑数据
  • Python 使用 asyncio 包处理并 发(使用asyncio包编写服务器)
  • Linux的小程序——进度条
  • 重生之我在10天内卷赢C++ - DAY 1
  • 红绿多空策略
  • 华为昇腾×绿算全闪存缓存释放澎湃潜能
  • 【C++详解】深入解析多态 虚函数、虚函数重写、纯虚函数和抽象类、多态原理、重载/重写/隐藏的对⽐
  • 基于 Hadoop 生态圈的数据仓库实践 —— OLAP 与数据可视化(六)
  • ‌CASE WHEN THEN ELSE END‌
  • 分布式系统:一致性
  • Linux常用基础命令
  • 【大语言模型入门】—— Transformer 如何工作:Transformer 架构的详细探索
  • 【C语言】指针深度剖析(一)
  • LeetCode 11 - 盛最多水的容器
  • VUE进阶案例
  • RabbitMQ 消息持久化的三大支柱 (With Spring Boot)
  • Hyperchain账本数据存储机制详解
  • C++:stack与queue的使用
  • AI应用:电路板设计
  • [mcp: JSON-RPC 2.0 规范]
  • Excel文件批量加密工具
  • 【LeetCode 随笔】
  • flask使用celery通过数据库定时
  • 【C语言进阶】题目练习