ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
>[success] **技术支持说明** > 1.**客服**提供简单的技术支持,一般自主学习为主 > 2.可到官方问答社区中提问:[**去提问**](https://bbs.csdn.net/forums/nb-iot) > 3.工程师**会尽快**解答社区问题,但他们是一线开发,【**难以保证**】解答时效,解答辛苦,感谢理解! <br/> ## **什么是中断** 通俗地说,中断是指程序在常规运行时突然遇到一个紧急事件,需要先放下当前的工作去处理这个紧急的事件。我们通过一张逻辑图来理解一下这个过程: ![](https://img.kancloud.cn/ff/c2/ffc282b8834b748834c917e6a43c027d_832x496.png =500x) ### 如图所示,CPU在执行主程序的过程中,在断点处遇到一个紧急事件了。这个紧急事件请求CPU中断主程序,并且去处理这个紧急事件。CPU执行中断处理程序来处理这个紧急事件,这个过程称为中断响应。处理完这个紧急事件后,返回至主程序中并且继续执行主程序。 <br/> **中断优先级**与**中断向量** * **中断优先级**:当有多个中断同时发生时,应该优先处理哪个中断呢?我们可以给每个中断定义一个优先级,以解决这个问题。 * **中断向量**:不同类型的中断需要不同类型的中断处理程序来处理,例如用户突然按下一个按键、计时结束等都需要使用不同的中断处理程序来处理。中断向量的作用是保证当发生不同类型的中断时能够进入对应的中断处理程序。 <br/> ## **重新设计按键 HAL API** 一般地,用户按下按键时,对于CPU来说就是一个紧急事件,因为CPU需要放下当前工作去处理它。因此,这非常使用使用中断来处理按键事件。 ### 接下来只需简单修改一下上节课的工程代码,便可使其支持中断机制。打开本节课配套工程代码的 hal_button.h文件,代码如下: ``` #ifndef __HAL_BUTTON_H__ #define __HAL_BUTTON_H__ /*定义按键连接的GPIO口PA0*/ #define HAL_BUTTON_PORT GPIOA #define HAL_BUTTON_PIN GPIO_Pin_0 /*定义GPIOA对应的时钟 */ #define HAL_BUTTON_CLOCK RCC_AHBPeriph_GPIOA #define HAL_BUTTON_TRIGGER_LEVEL 0 /*如果定义了HAL_BUTTON_USE_IRQ,表示开启了中断功能*/ #ifdef HAL_BUTTON_USE_IRQ /*定义按键连接的GPIO口PA0*/ #define HAL_BUTTON_EXTI_PORT EXTI_PortSourceGPIOA #define HAL_BUTTON_EXTI_PIN EXTI_PinSource0 #endif /* #ifndef HAL_BUTTON_USE_IRQ */ /* * 按键初始化 * @param onClick - 按键回调函数,当按键被按下时执行此函数 */ void halButtonInit(void (*onClick)(void)); /*如果没有定义HAL_BUTTON_USE_IRQ,表示关闭了中断功能*/ #ifndef HAL_BUTTON_USE_IRQ /* * 如果没有开启中断功能,那么仍使用上节课的方式检测按键是否被按下 */ void halButtonPoll(void); #endif /* #ifndef HAL_BUTTON_USE_IRQ */ #endif /* #ifndef __HAL_BUTTON_H__ */ ``` <br/> hal_button.c文件的代码如下: ### ``` #include "hal_button.h" #include "hal_system.h" #include "stm32f0xx_gpio.h" static void (*halButtonOnClick)(void) = 0; /* * 按键初始化 * @param onClick - 按键回调函数,当按键被按下时执行此函数 */ void halButtonInit(void (*onClick)(void)) { halButtonOnClick = onClick;//保存回调函数 GPIO_InitTypeDef buttonGPIO;//定义GPIO配置 /*如果定义了HAL_BUTTON_USE_IRQ,表示开启了中断功能*/ #ifdef HAL_BUTTON_USE_IRQ EXTI_InitTypeDef buttonEXTI; NVIC_InitTypeDef buttonNVIC; #endif buttonGPIO.GPIO_Mode = GPIO_Mode_IN;//配置为输入模式 buttonGPIO.GPIO_PuPd = GPIO_PuPd_UP,//配置为上拉 buttonGPIO.GPIO_Speed = GPIO_Speed_2MHz;//速率为2MHz buttonGPIO.GPIO_Pin = HAL_BUTTON_PIN;//引脚 RCC_AHBPeriphClockCmd(HAL_BUTTON_CLOCK, ENABLE);//使用按键时钟 GPIO_Init(HAL_BUTTON_PORT, &buttonGPIO);//初始化PA0 /*如果定义了HAL_BUTTON_USE_IRQ,表示开启了中断功能*/ /*以下代码的主要作用是配置PA0为低电平输入触发中断,其中涉及多方面内容,可暂时无须深入理解*/ #ifdef HAL_BUTTON_USE_IRQ buttonEXTI.EXTI_Line = EXTI_Line0; buttonEXTI.EXTI_Mode = EXTI_Mode_Interrupt; buttonEXTI.EXTI_Trigger = EXTI_Trigger_Rising_Falling; buttonEXTI.EXTI_LineCmd = ENABLE; buttonNVIC.NVIC_IRQChannel = EXTI0_1_IRQn, buttonNVIC.NVIC_IRQChannelPriority = 0, buttonNVIC.NVIC_IRQChannelCmd = ENABLE, EXTI_Init(&buttonEXTI); NVIC_Init(&buttonNVIC); SYSCFG_EXTILineConfig(HAL_BUTTON_EXTI_PORT, HAL_BUTTON_EXTI_PIN);//配置PA0 #endif /* #ifndef HAL_BUTTON_USE_IRQ */ } /*如果没有定义HAL_BUTTON_USE_IRQ,表示关闭了中断功能*/ #ifndef HAL_BUTTON_USE_IRQ /* * 如果没有开启中断功能,那么仍使用上节课的方式检测按键是否被按下 */ void halButtonPoll(void) { /* 如果按键没有被按下,如果没有则返回*/ if (GPIO_ReadInputDataBit(HAL_BUTTON_PORT, HAL_BUTTON_PIN) != HAL_BUTTON_TRIGGER_LEVEL) return; /* 延迟10ms,去除机械按键抖动 */ halSystemDelayUs(1000 * 10); /* 再次检测按键是否被按下,如果没有则返回*/ if (GPIO_ReadInputDataBit(HAL_BUTTON_PORT, HAL_BUTTON_PIN) != HAL_BUTTON_TRIGGER_LEVEL) return; /* 等待按键松开 */ while(GPIO_ReadInputDataBit(HAL_BUTTON_PORT, HAL_BUTTON_PIN) == HAL_BUTTON_TRIGGER_LEVEL) halSystemDelayUs(1000); /* 如果定义回调函数,则调用回调函数 */ if (halButtonOnClick != 0) halButtonOnClick(); } /* 使用中断*/ #else /*中断事件处理函数,用于处理中断事件。如果发生了中断,则自动进入此函数*/ void EXTI0_1_IRQHandler() { if (EXTI_GetITStatus(EXTI_Line0) != RESET && halButtonOnClick != 0) { /* 延迟10ms,去除机械按键抖动 */ halSystemDelayUs(1000 * 10); /* 再次检测按键是否被按下*/ if (GPIO_ReadInputDataBit(HAL_BUTTON_PORT, HAL_BUTTON_PIN) == HAL_BUTTON_TRIGGER_LEVEL) halButtonOnClick();//执行按键回调函数 } EXTI_ClearFlag(EXTI_Line0);//清楚中断标志 } #endif /* #ifndef HAL_BUTTON_USE_IRQ */ ``` <br/> ## **使用按键 HAL API** 编写好按键 HAL API后,按键的使用非常简单。在配套工程的main.c文件中添加如下代码: ### ``` /* * 按键回调函数,当按键被按下后即执行此函数 */ static void buttonOnClick() { halLedToggle(); } int main(void) { halSystemInit();//系统初始化 halLedInit();//LED初始化 halButtonInit(buttonOnClick);//按键初始化 while (1) { /*如果没有定义HAL_BUTTON_USE_IRQ,表示关闭了中断功能*/ #ifndef HAL_BUTTON_USE_IRQ /* 如果没有开启中断功能,那么仍使用上节课的方式检测按键是否被按下 */ halButtonPoll(); #endif } } ``` 增加了中断功能后,上层应用的修改非常少,代码仍然非常简洁。 <br/> ## **全局宏定义** 前述代码多次使用到HAL_BUTTON_USE_IRQ,这是一个全局宏定义。其定义方式如下: * 选择工程魔法棒; * 选择C/C++选项卡; * Define 位置加入宏宏定义如图所示。 ![](https://img.kancloud.cn/14/9b/149b18c41c1030015bfc8d33420f034e_835x663.png =500x) >[danger] 注意,宏与宏之间需要用英文的逗号隔开 <br/> <br/> ## **商务合作** 如有以下需求,可扫码添加管理员好友,注明“**商务合作**” * 项目定制开发,技术范围:**NB-IoT**、**CATn(4G)**、**WiFi**、**ZigBee**、**BLE Mesh**以及**STM32**、**嵌入式Linux**等; * 入驻平台,成为讲师; * 接项目赚外快; * 善学坊官网:[www.sxf-iot.com](https://www.sxf-iot.com/) ![](https://img.kancloud.cn/ca/73/ca739f92cab220a3059378642e3bd502_430x430.png =150x) (非商务合作**勿扰**,此处**非**技术支持)