中断(按键、SYSTICK、串口)

目录

按键中断

SYSTICK中断

串口中断


类型 典型代表 归属 核心配置逻辑
外设级中断 EXTI、USART、TIM STM32 外设模块 1. 配置外设自身参数(如 EXTI 的触发方式、USART 的中断源);2. 配置 NVIC(使能中断 + 设优先级)
内核级中断 SysTick、SVCall、PendSV Cortex-M4 内核 1. 配置内核自身寄存器(如 SysTick 的 LOAD/VAL/CTRL);2. 配置 NVIC(仅设优先级,无需外设参数)

按键中断

做开发一般不用子优先级,也就是4个bit抢占优先级,0个子优先级

目的:通过两个按键中断控制两个灯的亮灭

配置EXTI然后再配置NVIC

cpp 复制代码
#include <stdint.h>
#include <string.h>
#include "stm32f4xx.h"

/**
 * @brief  主函数:初始化硬件(LED、按键中断),程序入口
 * @note   硬件对应关系:
 *         - LED1 → PB0(高电平灭,低电平亮)
 *         - LED2 → PB1(高电平灭,低电平亮)
 *         - KEY2 → PC4(下降沿触发中断,控制LED1翻转)
 *         - KEY3 → PC5(下降沿触发中断,控制LED2翻转)
 * @retval int 程序返回值(嵌入式中无实际意义)
 */
int main(void)
{
    // 1. 使能外设时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);  // 使能GPIOB时钟(LED1/LED2挂载在GPIOB)
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);  // 使能GPIOC时钟(KEY2/KEY3挂载在GPIOC)
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); // 使能SYSCFG时钟(EXTI中断必须依赖此时钟)

    // 2. 配置NVIC优先级分组:分组4(仅抢占优先级有效,0-15级,子优先级无效)
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);	
	
    // 3. 初始化LED引脚(PB0、PB1)为推挽输出模式
    GPIO_InitTypeDef GPIO_InitStruct;  // 定义GPIO初始化结构体
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;        // 模式:输出模式
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;       // 输出类型:推挽输出(适合驱动LED)
    GPIO_InitStruct.GPIO_Speed = GPIO_Medium_Speed;   // 输出速度:中等速度(50MHz,LED无高速需求)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; // 配置引脚:PB0(LED1)、PB1(LED2)
    GPIO_Init(GPIOB, &GPIO_InitStruct);               // 将配置写入GPIOB寄存器,使配置生效
    GPIO_WriteBit(GPIOB, GPIO_Pin_0 | GPIO_Pin_1, Bit_SET); // 初始状态:LED1/LED2灭(高电平)

    // 4. 初始化按键引脚(PC4、PC5)为输入模式
    GPIO_StructInit(&GPIO_InitStruct);                // 初始化GPIO结构体为默认值(避免随机值)
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;         // 模式:输入模式
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;     // 上下拉:无上下拉(依赖硬件外部上拉/下拉)
    GPIO_InitStruct.GPIO_Speed = GPIO_Medium_Speed;   // 输入模式下速度无实际作用,仅规范配置
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; // 配置引脚:PC4(KEY2)、PC5(KEY3)
    GPIO_Init(GPIOC, &GPIO_InitStruct);               // 将配置写入GPIOC寄存器,使配置生效

    // 5. 将按键引脚绑定到EXTI中断线
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource4); // PC4 → EXTI4中断线
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOC, EXTI_PinSource5); // PC5 → EXTI5中断线

    // 6. 配置EXTI外部中断参数
    EXTI_InitTypeDef EXTI_InitStruct;   // 定义EXTI初始化结构体
    EXTI_StructInit(&EXTI_InitStruct);  // 初始化EXTI结构体为默认值
    EXTI_InitStruct.EXTI_Line = EXTI_Line4 | EXTI_Line5; // 中断线:EXTI4(KEY2)、EXTI5(KEY3)
    EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;      // 模式:中断模式(非事件模式)
    EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;  // 触发方式:下降沿触发(按键按下时电平从高→低)
    EXTI_InitStruct.EXTI_LineCmd = ENABLE;                // 使能该中断线
    EXTI_Init(&EXTI_InitStruct);                          // 将配置写入EXTI寄存器,使配置生效

    // 7. 配置NVIC(嵌套向量中断控制器),使能中断并设置优先级
    NVIC_InitTypeDef NVIC_InitStruct;  // 定义NVIC初始化结构体
    memset(&NVIC_InitStruct, 0, sizeof(NVIC_InitStruct)); // 结构体清零(避免未初始化的随机值)
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 5; // 抢占优先级:5(0-15可选,数值越小优先级越高)
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;       // 子优先级:0(分组4下无实际作用)
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;          // 使能该中断通道

    // 配置KEY2对应的中断通道(EXTI4_IRQn)
    NVIC_InitStruct.NVIC_IRQChannel = EXTI4_IRQn;
    NVIC_Init(&NVIC_InitStruct);

    // 配置KEY3对应的中断通道(EXTI9_5_IRQn:EXTI5~EXTI9共用的中断通道)
    NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn;
    NVIC_Init(&NVIC_InitStruct);		

    // 8. 主循环:程序常驻此处,等待中断触发
    while(1)
    {
            
    }
}

/**
 * @brief  EXTI4中断服务函数(KEY2(PC4)触发)
 * @note   功能:翻转LED1(PB0)的状态,执行后清除中断标志
 */
void EXTI4_IRQHandler(void)
{
    GPIO_ToggleBits(GPIOB, GPIO_Pin_0); // 翻转PB0电平(LED1亮/灭切换)
    EXTI_ClearITPendingBit(EXTI_Line4); // 清除EXTI4中断挂起标志(必须清除,否则会重复触发中断)
}

/**
 * @brief  EXTI9_5中断服务函数(KEY3(PC5)触发)
 * @note   功能:翻转LED2(PB1)的状态,执行后清除中断标志
 * @warning 该中断通道为EXTI5~EXTI9共用,原代码未判断具体中断源,快速按键易串灯(需加消抖+中断源判断)
 */
void EXTI9_5_IRQHandler(void)
{
    GPIO_ToggleBits(GPIOB, GPIO_Pin_1); // 翻转PB1电平(LED2亮/灭切换)
    EXTI_ClearITPendingBit(EXTI_Line5); // 清除EXTI5中断挂起标志(必须清除,否则会重复触发中断)
}

SYSTICK中断

目的:通过systick内核中断,实现灯500ms闪一次,不用像按键那样外部触发

配置systick,然后再配置NVIC,因为systick是内部中断,不需要像外部中断那样配置exti,exti是针对外部中断

SysTick->LOAD=SystemCoreClock/1000 -1; SystemCoreClock是内核主频,因为我用的芯片主频168MHz,其实啥主频都是这么写,SystemCoreClock如果是168MHz,也就是每秒能数 1.68 亿次(168000000Hz,1MHz=1000KHz=1000000Hz),数完也就是过了1s,1s/1000=1ms,减1的原因是(比如10到1,就正好10个,但是现在是10到0,最终到0,所以多了一次要减1),所以SysTick->LOAD=SystemCoreClock/1000 -1这个表示load减到0就过去了1ms。

SysTick 是向下递减计数器 :从LOAD值开始,每 1 个时钟周期减 1,减到 0 就触发中断

cpp 复制代码
#include <stdint.h>
#include <string.h>
#include "stm32f4xx.h"
#include "led.h"


int main(void)
{ 
		//开启时钟
		RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
		RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
	
		//配置中断优先级组
		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	
		//初始化GPIO 实现LED闪烁
		led_init();
		led_all_off();
		
		//配置SYSTICK 
		SysTick->LOAD=SystemCoreClock/1000 -1;  //1ms
		SysTick->VAL=0; //当前计数器清0
		//设定 SysTick 的时钟源、开启中断
		SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk ;
		SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;//最终启动计数器
		
	
		
		//配置SYSTICK中断优先级
		NVIC_InitTypeDef NVIC_InitStruct;
		NVIC_InitStruct.NVIC_IRQChannel = (uint8_t)SysTick_IRQn;
		NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 5 ;
		NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0 ;
		NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
		NVIC_Init(&NVIC_InitStruct);	
	
		while(1)
		{
			
		}
}

void SysTick_Handler(void)
{
		static bool led_state = false ;
		static int count = 0 ;
		if(++count >= 500)
		{
				count = 0;
				led_state = !led_state;
				led_set(1,led_state);
		}		
}

这种NVIC也可以

NVIC_SetPriority(SysTick_IRQn,5); 等价于原来的 NVIC 结构体配置,只是更简洁

cpp 复制代码
#include <stdint.h>
#include <string.h>
#include "stm32f4xx.h"
#include "led.h"


int main(void)
{ 
		//开启时钟
		RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
		RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
	
		//配置中断优先级组
		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
	
		//初始化GPIO 实现LED闪烁
		led_init();
		led_all_off();
		
		//配置SYSTICK 
		SysTick->LOAD=SystemCoreClock/1000 -1;  //1ms
		SysTick->VAL=0; //当前计数器清0
		//设定 SysTick 的时钟源、开启中断
		SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk ;
		SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;//最终启动计数器
		
		NVIC_SetPriority(SysTick_IRQn,5);
		
		while(1)
		{
			
		}
}

void SysTick_Handler(void)
{
		static bool led_state = false ;
		static int count = 0 ;
		if(++count >= 500)
		{
				count = 0;
				led_state = !led_state;
				led_set(1,led_state);
		}		
}

串口中断

口中断的触发源是 USART 外设内部,而非 GPIO 引脚,USART 外设本身就内置了中断逻辑,当满足中断条件时,会直接向 NVIC 发送中断请求(对应 USART1_IRQn 通道),完全不需要 EXTI 做中转。不需要像按键中断那样写EXTI

下个这个串口中断,只能回显一个字符,多个字符要用到ringbuffer

cpp 复制代码
#include <stdint.h>
#include <stdint.h>
#include <stdio.h>
#include "stm32f4xx.h"

volatile uint8_t rxdata = 0;

int main(void)
{
		//串口用PA9 PA10   
		RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
		RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
		RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);	
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
		
		NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
		
		GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
		GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
		
	
	
		//初始化串口
		GPIO_InitTypeDef GPIO_InitStruct;
		GPIO_StructInit(&GPIO_InitStruct);
		GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
		GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
		GPIO_InitStruct.GPIO_OType =GPIO_OType_PP;
		GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
		GPIO_Init(GPIOA,&GPIO_InitStruct);
		
		//配置串口
		USART_InitTypeDef USART_InitStruct;
		USART_StructInit(&USART_InitStruct);
		USART_InitStruct.USART_BaudRate = 115200;
		USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
		USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
		USART_InitStruct.USART_Parity = USART_Parity_No; //0校验位
		USART_InitStruct.USART_StopBits = USART_StopBits_1;
		USART_InitStruct.USART_WordLength = USART_WordLength_8b;
		USART_Init(USART1,&USART_InitStruct);
		USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
		
		//使能串口
		USART_Cmd(USART1,ENABLE);
		
		NVIC_InitTypeDef NVIC_InitStruct;
		NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn;
		NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 5;
		NVIC_InitStruct.NVIC_IRQChannelSubPriority=0;
		NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
		NVIC_Init(&NVIC_InitStruct);
		
		char msg[64];
		while(1)
		{
				if(rxdata!=0)
				{
						int len=snprintf(msg, sizeof(msg), "receive : %c\r\n", (char)rxdata);
						for(int i=0;i<len;i++)
						{ 
								while(!USART_GetFlagStatus(USART1,USART_FLAG_TXE));
								USART_SendData(USART1,(uint16_t)msg[i]);
						}
						rxdata = 0;
				}			
		}
}

void USART1_IRQHandler(void)
{
		if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET)
		{
				
				rxdata =USART_ReceiveData(USART1);
		}
}
相关推荐
yuanmenghao18 小时前
CAN系列 — (8) 为什么 Radar Object List 不适合“直接走 CAN 信号”
网络·数据结构·单片机·嵌入式硬件·自动驾驶·信息与通信
xu_wenming18 小时前
物联网Wi-Fi 6(802.11ax)和 Wi-Fi 5(802.11ac)的差异
嵌入式硬件·mcu·物联网·iot
尼喃18 小时前
24V过压过流保护电路芯片PW1605,60V耐压5A大电流,硬件设计选型优选
单片机·51单片机·芯片
chem411118 小时前
STM32 ISP下载
stm32·单片机·接口隔离原则
say_fall19 小时前
微机原理:微型计算机基础
服务器·网络·单片机·微机原理
BreezeJuvenile19 小时前
ADC_案例练习:独立模式单通道转换
stm32·单片机·adc·hal·寄存器·单通道采集
zd84510150019 小时前
stm32f407 电机多轴联动算法
stm32·单片机·算法
电子工程师成长日记-C5119 小时前
基于51单片机的乒乓球计分器
单片机·嵌入式硬件·51单片机
weixin_6695452019 小时前
单通道 2.7-12.0V 持续电流 2.3A H 桥驱动芯片 智能锁马达驱动IC XR8313
单片机·嵌入式硬件·硬件工程·信息与通信