你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32经验分享 第13章 GPIO—按键中断

[复制链接]
STMCU小助手 发布时间:2022-8-30 18:46
13.1 关于STM32的EXTI
前面“第10章 基础重点—中断系统”介绍了STM32的中断和中断优先级,知道了所有外设中断都由NVIC管理,比如USART、ADC、I2C、SPI等。GPIO产生的中断也不例外,但在给NVIC管理之前,还有一个EXTI(External interrupt/event controller,外部中断/事件控制器)先处理一下,如图 13.1.1 所示。
}VMV7JY631V${X]NZZO9MB7.png


图 13.1.1 STM32中断处理机制



STM32F103系列的EXTI支持19个外部中断/事件请求(互联型系列的STM32支持20个),每个中断/事件都有独立的触发和屏蔽设置,支持中断模式和事件模式。

中断模式是指外部信号产生电平变化时,EXTI将该信号给NVIC处理,从而触发中断,执行中断服务函数,完成对应操作。

事件模式是指外部信号产生电平变化时,EXTI根据配置,联动ADC或TIM执行相关操作。

中断和事件的产生源是一样的,中断需要软件实现相应功能,而事件是由硬件触发后执行相应操作。前者需要CPU参与功能实现,可以实现的功能更多,后者无需CPU参与,具有更高的响应速度。

EXTI的结构如图 13.1.2 所示,图中画斜线“/”的信号线表示这样的线共有19根。外部信号输入后,首先经过边缘检测电路,可以实现对上升沿或下降沿信号进行检测,从而得到硬件触发,也可由软件中断事件寄存器产生软件触发信号。无论是硬件触发还是软件触发,如果中断屏蔽寄存器允许,则产生中断给NVIC处理(绿色路线);如果事件屏蔽寄存器允许,则产生事件,脉冲发生器产生脉冲供其它模块使用(黄色路线)。

STM32F103的GPIO挂载APB总线上,如果要使用GPIO引脚作为外部中断/事件功能,则必须使能APB总线上该引脚对应端口的时钟和AFIO复用功能。

I$PM7K%0YD`_L7CL7O0A857.png


图 13.1.2 STM32F103系列EXTI结构框图

STM32F103C8T6有2组GPIO,每组16个引脚,即32个GPIO引脚,但EXTI只支持19个外部中断/事件请求,因此需要将多个GPIO合成一组,共用一个中断线,STM32F103系列中断线分组如表 13.1.1 所示。

3LGEH(R9{{Z}3P4J{{NFMOW.png


表 13.1.1 EXIT中断线分组



结合图 13.1.1 所示,EXTI0~EXTI15作为GPIO中断线使用,同组的GPIO共享一条中断线,比如EXTI0组,PA0作为了中断源,则此时PB0~PG0不能作为中断源。



【总结】

STM32有众多异常和中断,其中内部中断源(USART、ADC等)直接由NVIC处理。GPIO引脚可以产生外部中断或事件,如是中断则交由NVIC处理,如果是事件则产生脉冲信号联动其它模块工作。

无论是内部中断源,还是GPIO产生的中断,都由NVIC管理分组,然后根据中断优先级分组确定抢占优先级级数和子优先级级数。

GPIO引脚众多,将引脚数字相同的作为一组,共享一个中断线。



13.2 硬件设计

同“12.2 硬件设计”小结内容。


13.3 软件设计
13.3.1 软件设计思路
实验目的:本实验通过使用外部中断功能去判断按键的状态,通过中断的形式能够更加灵敏的读取到GPIO的电平,让用户更加直观的感受到STM32F103的中断,并学会如何使用和开发其中断功能。

1) 按键初始化:GPIO端口时钟使能、AFIO复用功能时钟使能、GPIO引脚设置为下降沿触发中断(PA0);

2) 填充按键中断处理函数:读取按键GPIO状态,操作对应LED灯亮灭;

3) 主函数调用LED和按键初始化后,无需任何操作;

本实验配套代码位于“5_程序源码\6_GPIO—按键中断\”。



13.3.2 软件设计讲解
1) GPIO宏定义与接口宏定义

代码段 13.3.1 引脚宏定义(driver_key.h)

  1. /*********************
  2. * 按键引脚状态定义
  3. **********************/
  4. #define PUSH_DOWN                       GPIO_PIN_RESET
  5. #define SPRING_UP                       GPIO_PIN_SET

  6. /*********************
  7. * 引脚宏定义
  8. **********************/
  9. #define KEY_GPIO_PIN                 GPIO_PIN_0
  10. #define KEY_GPIO_PORT                GPIOA
  11. #define KEY_GPIO_CLK_EN()            __HAL_RCC_GPIOA_CLK_ENABLE()

  12. /*********************
  13. * 函数宏定义
  14. **********************/
  15. /*
  16. * 按键状态读取函数宏定义
  17. */
  18. #define KEY                           HAL_GPIO_ReadPin(KEY_GPIO_PORT, KEY_UP_GPIO_PIN)
复制代码

此处与上一实验一致,不再赘述。



2) GPIO初始化

代码段 13.3.2 按键初始化(driver_key.c)

  1. /*
  2. *  函数名:void KeyInit(void)
  3. *  输入参数:无
  4. *  输出参数:无
  5. *  返回值:无
  6. *  函数作用:初始化按键的引脚,配置为下降沿触发外部中断

  7. */

  8. void KeyInit(void)
  9. {
  10.     // 定义GPIO的结构体变量
  11.     GPIO_InitTypeDef GPIO_InitStruct = {0};

  12.     // 使能按键的GPIO对应的时钟
  13.     KEY_GPIO_CLK_EN();

  14.     GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;        // 设置为下降沿触发外部中断
  15.     GPIO_InitStruct.Pull = GPIO_PULLUP;                 // 默认上拉
  16.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;       // 引脚反转速度设置为快

  17.     // 初始化按键引脚配置
  18.     GPIO_InitStruct.Pin = KEY_GPIO_PIN;                 // 选择按键的引脚
  19.     HAL_GPIO_Init(KEY_GPIO_PORT, &GPIO_InitStruct);

  20.     /* EXTI interrupt init*/
  21.     HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
  22.     HAL_NVIC_EnableIRQ(EXTI0_IRQn);

  23. }
复制代码

13行:使能按键对应GPIO端口时钟;

15行:设置为下降沿触发外部中断,即按键按下瞬间触发中断。可根据需求设置为上升沿触发,即松开按键触发中断,双边缘触发,即按下松开都触发中断;

16~21行:初始化按键对应的GPIO,“HAL_GPIO_Init()”里会判断该引脚是否为EXTI模式,如果是则调用“__HAL_RCC_AFIO_CLK_ENABLE()”使能AFIO时钟;

25~25行:设置EXTI中断线0的优先级和使能,即PA0所在中断线;

3) 中断处理函数

当中断发生后,则自动跳到代码段 10.2.1 中所对应的中断函数所在位置,执行中断函数的内容,因此我们需要编写中断函数的内容,如代码段 13.3.3 所示。

代码段 13.3.3 按键中断处理函数(driver_key.c)

  1. /*
  2. *  函数名:void EXTI0_IRQHandler(void)
  3. *  输入参数:无
  4. *  输出参数:无
  5. *  返回值:无
  6. *  函数作用:外部中断0的中断处理函数
  7. */

  8. void EXTI0_IRQHandler(void)
  9. {
  10.     HAL_GPIO_EXTI_IRQHandler(KEY_GPIO_PIN);
  11. }
复制代码

注意这里的中断处理函数的实现,可以放在“stm32f1xx_it.c”中,同时注意在“stm32f1xx_it.h”中声明。本示例放在“driver_key.c”中,方便读者理解。

每个中断处理函数里,都调用的“HAL_GPIO_EXTI_IRQHandler()”准备后续处理,传入参数为外部中断的引脚号,该函数原型如代码段 13.3.4 所示。

代码段 13.3.4 外部中断处理函数(stm32f1xx_hal_gpio.c)

  1. /**
  2.   * @brief  This function handles EXTI interrupt request.
  3.   * @param  GPIO_Pin: Specifies the pins connected EXTI line
  4.   * @retval None
  5.   */

  6. void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
  7. {
  8.   /* EXTI line interrupt detected */
  9.   if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
  10.   {
  11.     __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
  12.     HAL_GPIO_EXTI_Callback(GPIO_Pin);
  13.   }
  14. }
复制代码

该函数先判断传入引脚是否产生了外部中断,确认该引脚产生了中断,则清理中断标志,再调用“HAL_GPIO_EXTI_Callback()”回调函数。在该回调函数,通过判断输入的引脚,完成对应的用户操作,如代码段 13.3.5 所示。

代码段 13.3.5 外部中断处理函数回调函数(driver_key.c)

  1. /*
  2. *  函数名:void HAL_GPIO_EXTI_Callback(void)
  3. *  输入参数:无
  4. *  输出参数:无
  5. *  返回值:无
  6. *  函数作用:外部中断处理函数的回调函数,用以处理不同引脚触发的中断服务最终函数
  7. */
  8. static volatile bool  key_flag = false;           // 定义一个全局静态标志,用以判断按键按下的次数
  9. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
  10. {
  11.     switch(GPIO_Pin)                    // 判断是哪个按键
  12.     {
  13.         case KEY_GPIO_PIN:              // 如果是用户按键
  14.         {
  15.             key_flag = !key_flag;       // 按下一次标志翻转一次
  16.             BLED(key_flag?OFF:ON);      // 根据标志控制蓝灯的亮灭
  17.             break;
  18.         }
  19.         default:break;
  20.     }
  21. }
复制代码

8行:定义了全局变量标志位,用于记录按键按下状态;

9~21行:根据传入的引脚号,得知是哪一个按键按下,从而控制对应LED灯亮灭;

4) 主函数测试

代码段 13.3.6 按键中断主函数(main.h)

  1. // 初始化按键
  2. KeyInit();

  3. //初始化LED
  4. LedGpioInit();

  5. while(1)
  6. {
  7. }
复制代码

  主函数只需初始化LED和按键,无需任何操作。一旦按键按下产生中断,将自动跳转到对应中断向量位置,调用该位置的中断处理函数。读者需要自行填充中断处理函数内容,这里设置中断处理函数最终都调用外部中断回调函数“HAL_GPIO_EXTI_Callback()”,在该函数里判断是哪个引脚按下,执行相应操作。

13.4 实验效果
本实验对应配套资料的“5_程序源码\6_GPIO—按键中断\”。打开工程后,编译,下载。按下按键,用户LEDd灯亮/灭。

作者:攻城狮子黄


收藏 评论0 发布时间:2022-8-30 18:46

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版