STM32按键中断案例(HAL库实现)

目录

引言

一、案例需求描述

二、硬件电路设计

三、软件设计

[3.1 STM32CubeMX配置](#3.1 STM32CubeMX配置)

[3.1.1 工程创建](#3.1.1 工程创建)

[3.1.2 调试配置](#3.1.2 调试配置)

[3.1.3 时钟配置](#3.1.3 时钟配置)

[3.1.4 GPIO端口配置](#3.1.4 GPIO端口配置)

[3.1.5 NVIC配置](#3.1.5 NVIC配置)

[3.1.6 工程管理配置](#3.1.6 工程管理配置)

[3.2 keil配置](#3.2 keil配置)

[3.3 完善中断处理程序](#3.3 完善中断处理程序)

四、最终效果

五、结语


引言

前面我们基于寄存器实现了按键中断案例,在实践中深入理解了中断在STM32中的基本使用方法。现在,我们再来基于HAL库对该案例进行二次实现,关于HAL库的基本操作流程,如果有朋友还不熟悉的话,可以参考我前面最初关于HAL库的基本介绍以及基于HAL库实现流水灯的详细讲解,这里我们就直接开始实现了。

一、案例需求描述

二、硬件电路设计

由于本次案例和上一次基于寄存器的按键中断案例 一模一样,因此这里就不再赘述案例相关需求以及硬件电路分析了,不太清楚的朋友可以点击此处跳转至寄存器开发实现的文章中,我们这里就不再赘述了。

STM32按键中断案例(基于寄存器)-CSDN博客https://blog.csdn.net/2301_79475128/article/details/145037646?spm=1001.2014.3001.5501

三、软件设计

好,了解完本次案例的相关需求以及硬件电路设计后,我们就来进行基于HAL的案例实现了。当然,我们首先回忆一下,HAL库开发的基本操作流程是什么?没错,就是以下几步,首先打开STM32CubeMX软件,接着如下步骤:

  1. 选择对应的芯片类型并创建新的工程;

  2. 根据实际情况选择合适的调试工具;

  3. 配置外部时钟以及系统时钟;

  4. 选择需求的GPIO端口,并根据实际的硬件电路情况进行相应配置;

  5. 在工程管理进行工程的基础配置(名称、工具链等);

  6. 生成代码并打开工程;

  7. 根据所需完善核心功能代码。

那么,我们再思考一下,对于本次案例,可能需要多什么步骤呢?因为**要使用中断,所以可能不免要和NVIC产生关系,所以在STM32CubeMX中进行配置时,可能会进入NVIC配置一下优先级等;同时对于按键相应GPIO的配置,此时我们需要的是复用功能,**因此可能比基本操作会需要多一点思考在其中。

好了,说到这,相信大家关于本次基于HAL库的大致思路已经有了,接下来我们就开始真正去做吧!

3.1 STM32CubeMX配置

3.1.1 工程创建

进入STM32CubeMX软件,点击进入【ACCESS TO MCU SELECTOR】,找到自己的芯片,然后双击即可创建工程。

双击芯片后就会自动创建新的工程,然后就会进入一下界面

3.1.2 调试配置

接着,我们开始配置,单击【System Core】,展开该栏,继续选择【SYS】,即可开始配置Debug.

1.配置Debug部分,我们使用的是串行单线调试,所以我选择Serial Wire

3.1.3 时钟配置

随后,我们进入**【RCC】**,配置外部时钟源,如下图左边框中所示

由于我所使用开发板均为石英晶振,因此高速外部时钟和低速外部时钟源均选择**【Crystal/Ceramic Resonator】**

然后,单击上方的【Clock Configuration】,进入系统时钟配置,如下图所示

然后由于开发板外接8MHz的高速晶振,因此我们在左侧往右看的HSE外部高速时钟一条的两路选择器处选择**【HSE】** ,使用外接的高速时钟源,同时继续往右看,PLL锁相环变频器修改为**【x9】** ,即九倍频,以获取到72MHz的频率供给CPU;接着修改三路复用选择器为倍频后的时钟**【PLLCLK】** ;最后,由于APB1低速外设总线最高频率为36MHz,因此在APB1 Prescaler 处修改为半分频**【/2】**

至此,关于时钟的配置就完成了。

3.1.4 GPIO端口配置

接下来,开始配置GPIO端口。根据前面硬件电路分析,我们本次案例需要使用的GPIO端口有两个,分别是PA0和PF10。

其中PA0控制LED,输出低电平则LED亮,使用最高速度的通用推挽输出即可;

PF10控制按键,由按键的硬件电路知,输入高电平表示按键按下,反之没有按下,即PF10端口应当设置为下拉输入,默认低电平。

操作如下:

在图形化芯片上搜索PA0,此时PA0会不断闪烁提示位置,然后单击闪烁的PA0引脚,选择输出模式【GPIO_Output】;

同时,搜索PF10,点击闪烁的引脚,由于按键端口我们需要使用复用功能,而其中选项没有AFIO,但是有作外部中断EXTI10,我们复用就是为了中断处理,所以直接选择【GPIO_EXTI】即可。

接着,单击在单击PA0名字,按照前面分析进行配置即可,如下图

同理,对于PF10也是一样

这样,关于GPIO的配置就完成了。

3.1.5 NVIC配置

接下来,我们进行NVIC的相关配置,首先开启NVIC中断使能

就在上一个界面上,单击上方的【NVIC】,接着勾选【Enabled】即可

然后,单击左侧的NVIC,检查中带你优先级是否能够满足本次案例的中断执行情况

进入发现,其中的系统嘀嗒定时器【system tick timer】中断优先级低于【EXTI15_10】的 ,而我们在中断处理程序中需要使用延时函数进行按键消抖,由于延时函数用到了嘀嗒定时器,因此如果嘀嗒定时器的优先级高于【EXTI15_10】的话,就会导致中断处理程序执行到延时函数时由于不能打断【EXTI15_10】而卡死程序 。所以这里我们一定要手动设置一下这两个的优先级,我这里把嘀嗒定时器【system tick timer】的优先级值设为2,【EXTI15_10】的优先级值设为3,如下图

3.1.6 工程管理配置

最后,进入【Project Manager】,给本次工程起个名字,并指定工程存放路径、应用工具链存放路径以及使用的工具链/IDE,这里我把工具链放在本工程内部,工具链设置为keil的MDK-ARM即可。如下图

然后,进入【Code Generator】,只把必要的库放在工程中,避免所占空间较大;同时外设代码以头文件和源文件的形式生成。即作如下图修改

最后,生成代码,点击【GENERATE CODE】,然后打开工程即可完成在STM32CubeMX中自动生成代码

3.2 keil配置

代码生成以后,在keil中打开,然后我们做一下基本配置,如下图

1、 修改调试工具STLINK

2、进入settings,然后进入勾选【Reset and Run】 即烧录并运行,同时进入【Pack】,**取消勾选【Enable】**打印日志,操作如下

然后如下图看看生成的代码,发现我们寄存器方式编写的大部分代码,如按键和LED的初始化都已经自动生成好了,也就是说我们只需要去编写中断处理程序的代码就行,显然使用HAL库开发的效率简直高了不少。

3.3 完善中断处理程序

现在,我们在VSCode中打开该工程,然后开始编写中断处理函数的代码,首先我们应该知道生成的代码中哪一部分是中断处理函数的文件?实际上就是那个文件名字有"it"的文件,如下图

打开这个文件,我们可以发现,里面有很多之前在汇编文件中看见的中断处理程序的入口名称,也就意味着这里就是放着中断处理程序的函数空实现了。我们往下翻,就能翻到如上图中我们需要完善的EXTI15_10的中断处理函数部分了。如上图所示

当然,我们发现,这里面有一条调用函数的语句,参数是我们按键对应的端口,我们点进去看看,如下图所示

会发现,调用的函数中也主要是俩条语句,实际上551行这个语句时清除中断标志位的意思,参数就是我们传进来的按键对应端口,其底层就是我们寄存器实现时配置的挂起寄存器,不信我们可以看看

进入551行语句实现体,就能看见这里有修改PR寄存器的值,也就是配置挂起寄存器,感兴趣的朋友可以自己去深入了解一下,这里又不在深究了。

这样说,我们中断处理程序实际上就是和这个552行的函数调用有关系咯,所以我们来看看这个函数调用

诶,先看这个名字,我们很容易发现这应该是个Callback回调函数 吧。实际上,这个函数是一个中断回调函数HAL_GPIO_EXTI_Callback 。然后我们发现它的函数头上最前面多了个平时没见过的关键字**__weak** ,实际上这玩意并不是C语言中的关键字,而是C++中的一个关键字,叫做弱实现,就是可以实现一个函数的重写,换句话说,用**__weak**修饰的函数,如果有新的同名函数实现,则执行时会自动调用新的实现函数。因此,中断回调函数HAL_GPIO_EXTI_Callback是一个弱实现函数。

既然是这样,那就意味着我们可以对这个函数进行重写,即我们重新对这个函数在编写一次的话,最后程序执行的中断回调函数就是我们编写那个了,也就是说,我们可以直接对中断处理函数中调用的中断回调函数重新编写我们中断处理逻辑,就能实现我们中断处理了

这里,我们用一张图形象化描述一下这个HAL库中的中断处理函数、中断回调函数以及汇编文件中提供的中断服务函数(也叫中断处理函数)的一个关系(下图仅描述一下关系,并不一定对应上我们本次的名称)

对此时本案例的整个中断处理过程总结一下:

当有按键按下的时候,检测到上升沿会执行中断服务函数:EXTI15_10_IRQHandler,内部又会调用HAL库总的外部中断处理函数HAL_GPIO_EXTI_IRQHandler,然后会调用中断回调函数HAL_GPIO_EXTI_Callback,由于它是一个弱实现函数,所以我们重新实现这个函数就可以了

因此,当务之急,就是重新实现以下这个中断回调函数,在哪重写呢?就在咱【smt32f1xxx_it.c】中重写就行。

所以,返回**【smt32f1xxx_it.c】文件,**写好中断回到函数空实现先

复制代码
/* USER CODE BEGIN 1 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)  // 重写中断回调函数
{

}
/* USER CODE END 1 */

这里我们思考一下具体要写一些什么:根据前面寄存器实现时的思路,中断处理程序我们首先是清除了中断标志位------>关于这个我们这在中断回调函数前就已经做了,因此我们这里不用写;其次是编写延时消抖的语句------>这里还没写,所以我们需要 ;然后是判断按键是否还是按下的状态,也就是对应端口是否还处于高电平,如果是则翻转LED,反之不翻转------>显然这里也没有,需要我们编写,接着应该就结束了。

当然,现在是HAL库实现,所以所用的函数名字会不太一样,比如判断按键端口是否还是高电平的函数,寄存器实现时用的输入数据寄存器给值,而这里是用HAL库提供的读取端口状态的函数HAL_GPIO_ReadPin(GPIOF, GPIO_Pin),并且LED的翻转在HAL库中也已经提供。当然哈,这些都是名字差别,不会影响我们分析的思路。

不过还有一个要注意的地方是:因为中断回调函数原本是不知道他要处理的端口是哪一个,所以会传进来一个参数,而这个参数也是传进HAL库的总中断处理函数的参数,因此,我们需要对其判断一下,如果这个传进来的中断请求是我们此时要进行的中断处理的话,就开始执行下面的程序,否则不执行,避免CPU执行了错误端口的中断处理程序

总结一下逻辑:

1、判断是否是EXTI10(即EXTI15_10)传来的中断请求

2、延时10ms进行按键消抖

3、判断当前按键是否仍处于高电平

4、若处于高电平,则翻转LED,否则不执行

以下是关于中断处理程序的参考代码

复制代码
/**
  * @brief This function handles EXTI line[15:10] interrupts.
  */
void EXTI15_10_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI15_10_IRQn 0 */

  /* USER CODE END EXTI15_10_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(KEY3_Pin);
  /* USER CODE BEGIN EXTI15_10_IRQn 1 */

  /* USER CODE END EXTI15_10_IRQn 1 */
}

/* USER CODE BEGIN 1 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)  // 重写中断回调函数
{
  // 清除中断标志位在这之前已经进行

  // 先判断是否是EXTO10传来的中断请求
  if (GPIO_Pin == KEY3_Pin)
  {
    // 消抖延时
    HAL_Delay(10);

    // 如果还是高电平就翻转LED1
    if (HAL_GPIO_ReadPin(GPIOF, GPIO_Pin) == GPIO_PIN_SET)
    {
      HAL_GPIO_TogglePin(GPIOA, LED1_Pin);
    }    
  }
}
/* USER CODE END 1 */

四、最终效果

我们对代码进行编译看看是否正确

显然编译通过了!

然后我们烧录了看看效果对不对

和前面寄存器实现一样,效果没有问题,说明本次基于HAL库的实现也是不错的。


五、结语

OK,经过本次实验,我们对HAL库开发的流程又有了进一步的了解。同时,通过对按键中断案例在寄存器以及HAL库上的两次实现,相信大家和我都对中断有了更深的了解。也祝愿大家和我都能坚持下去,学有所成吧!加油


以上便是本次文章的所有内容,欢迎各位朋友在评论区讨论,本人也是一名初学小白,愿大家共同努力,一起进步吧!

鉴于笔者能力有限,难免出现一些纰漏和不足,望大家在评论区批评指正,谢谢!

相关推荐
欢乐熊嵌入式编程2 小时前
智能手表 MCU 任务调度图
单片机·嵌入式硬件·智能手表
Mr zhua2 小时前
STM32G474VET6-CAN FD使用经典模式+过滤报文ID
stm32·can·tim
sword devil9002 小时前
将arduino开发的Marlin部署到stm32(3D打印机驱动)
stm32·单片机·嵌入式硬件
GodKK老神灭2 小时前
STM32 变量存储
stm32·单片机·嵌入式硬件
木宁kk3 小时前
51单片机引脚功能概述
单片机·嵌入式硬件
JANYI20183 小时前
嵌入式MCU和Linux开发哪个好?
linux·单片机·嵌入式硬件
sword devil9005 小时前
Arduino快速入门
stm32·单片机·嵌入式硬件
GodKK老神灭5 小时前
STM32实现循环队列
stm32·单片机·嵌入式硬件
不脱发的程序猿7 小时前
从MCU到SoC的开发思维转变
单片机·嵌入式硬件
A-花开堪折8 小时前
OpenMCU(六):STM32F103开发板功能介绍
stm32·单片机·嵌入式硬件