普冉(PUYA)单片机开发笔记(12): 获取外部中断

概述

将单片机的 GPIO 引脚作为外部按键的输入端是单片机较为常用的方式,例如把这颗 MCU 部署在一块控制面板的触点底板,使用者按压按钮(按键)对产品进行控制。本着学以致用的原则,使用 PY32F003 对外部中断如何处理是一项必须完成的学习内容了。今天我们就来操练一把。

参考厂家的例程,使用 PA12 作为外部中断的输入管脚,掌握一下 PY32F003 对外部中断处理的代码。PA12 设置为突变检测,当 PA12 上产生高低电平的变化时,翻转一次板载 LED 可以验证 MCU 是否正确捕捉了外部中断。

在我的这一个系列的实验,Keil 的文件组织都是沿用下来的,请参见我的任意一期《开发笔记》,这里就不罗嗦了,直接上代码。

实现代码

1. 在 main.h 中加一个预定义,然后定义和外部中断相关的函数原型

代码片段如下。

#define TIM_PWM_TEST    0
#define ADC_SAMPLE_TEST 0
#define FLASH_WR_TEST   0
#define I2C_COMM_TEST   0
#define I2C_SLAVE       0
#define EXTI_TEST       1

...
...

#if(EXTI_TEST)
void EXTI_Config(void);
void EXTI_Demo(void);
#endif

...
...

文件新定义了预编译开关: EXTI_TEST,其它的开关全部设置为 0(关掉其它功能,免得产生冲突)。然后声明了 Config 和 Demo 两个函数。

2. app_exti.c 中实现相关函数

这一次我们将获取外部中断的相关函数都写到 app_exti.c 这一个文件中,包括 main.h 中预定义的 EXTI_Config() 函数,HAL_EXTI_MspInit() 函数和 EXTI4_15_IRQHandler() 函数,酱紫就不需要再“麻烦” 去修改 py32_f0xx_hal_msp.c 文件了。

完整代码如下。

/**
 ******************************************************************************
 * @file    app_exti.c
 * @brief   External Interrupt test functions.
 ******************************************************************************
 * @copyright
 *
 * Copyright (c) 2023 CuteModem Intelligence.
 * All rights reserved.
 *
 * This software is licensed under terms that can be found in the LICENSE file
 * in the root directory of this software component.
 * If no LICENSE file comes with this software, it is provided AS-IS.
 *
 ******************************************************************************
 */
 
#include "main.h"

#if(EXTI_TEST)
/** ----------------------------------------------------------------------------
* @name   : void EXTI_Config(void)
* @brief  : EXTI 初始化, 配置引脚 PA12 为下降沿中断
* @param  : 
* @retval : void
* @remark : 
*** ----------------------------------------------------------------------------
*/
void EXTI_Config(void)
{
    GPIO_InitTypeDef  GPIO_InitStruct;
    
    __HAL_RCC_GPIOA_CLK_ENABLE();                  // 使能GPIOA时钟
    GPIO_InitStruct.Mode  = GPIO_MODE_IT_FALLING;  // 下降沿中断
    GPIO_InitStruct.Pull  = GPIO_PULLUP;           // 上拉
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;  // 速度为高速
    GPIO_InitStruct.Pin   = GPIO_PIN_12;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);             // 使能EXTI中断
    HAL_NVIC_SetPriority(EXTI4_15_IRQn, 0, 0);     // 配置中断优先级
}

/** ----------------------------------------------------------------------------
* @name   : void EXTI4_15_IRQHandler(void)
* @brief  : EXTI 初始化, 配置事件引脚 PA12
* @param  : 每产生一次下降沿, LED翻转一次
* @retval : void
* @remark : 
*** ----------------------------------------------------------------------------
*/
void EXTI4_15_IRQHandler(void)
{
    BSP_LED_Toggle(LED3);
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);//处理EXTI中断请求
}

/** ----------------------------------------------------------------------------
* @name   : void EXTI_Demo(void);
* @brief  : 由 main() 调用的外部中断实验程序
* @param  : 
* @retval : void
* @remark : 
*** ----------------------------------------------------------------------------
*/
void EXTI_Demo(void)
{
    EXTI_Config();
}


#endif
  • 使用 EXTI_Config() 函数分配 PA12 为外部中断源,内部上拉,下降沿中断,GPIO 高速。
  • 使用 EXTI4_15 中断,设置成 0 组最高优先级。
  • 在 EXT4_15中断服务程序中,翻转板载 LED 一次,然后调用 HAL_GPIO_EXTI_IRQHandler(GPIO_Pinx) 函数清除这个 GPIO 的中断标志,以便下一次捕获。
  • 定义了一个非常简单的 Demo 函数。

3. 修改一下 py32_f0xx_hal_it.c

已经在 app_exti.c 中实现了 EXTI_15_IRQ4_Handler,所以在 py32_f0xx_hal_it.c 中把那个函数关掉。

请注意这里不能重复定义其为 __weak 类型,这么做不会导致编译错误,但程序运行时会卡死。

/**
  ******************************************************************************
  * @file    py32f0xx_it.c
  * @author  MCU Application Team
  * @Version V1.0.0
  * @Date    2020-10-19
  * @brief   Interrupt Service Routines.
  ******************************************************************************

  */

...
...

#if(!EXTI_TEST)
void EXTI4_15_IRQHandler(void)
{
}
#endif

...
...

4. 在 main() 函数中调用

/**
 ******************************************************************************
 * @file    main.c
 * @brief   Main program entry.
 ******************************************************************************

...
...
 *
 ******************************************************************************
 */

#include "main.h"
#include 

/**
* -------------------------------------------------------------------------
* @file   : int main(void)
* @brief  : main函数
* @param  : 无
* @retval : 无限循环,无返回值
* @remark : 
* -------------------------------------------------------------------------
*/
int main(void)
{
    HAL_Init();             // systick初始化
    SystemClock_Config();   // 配置系统时钟
    
    if(USART_Config() != HAL_OK) Error_Handler();         
    printf("\r\n\r\n\r\n"
           "[SYS_INIT] Debug port initilaized.\r\n");

    printf("\r\n+---------------------------------------+"
           "\r\n|        PY32F003 MCU is ready.         |"
           "\r\n+---------------------------------------+"
           "\r\n         10 digits sent to you!          "
           "\r\n+---------------------------------------+"
           "\r\n");
           
    HAL_Delay(0);
    if (DBG_UART_Start() != HAL_OK) Error_Handler();
    HAL_Delay(0);

#if(EXTI_TEST)
    EXTI_Config();
#endif

    while (1)
    { 
#if(!EXTI_TEST)
    BSP_LED_Toggle(LED3);
    HAL_Delay(500);
#endif
    }
}

程序很短,如果不考虑打印的话,只需要 HAL_Init(); SystemClock_Config(); 和 EXTI_Config(); 三个函数。主循环用 #if(!EXTI_TEST)关掉了,实际上是空的。

实验结果

编译烧录后,使用一根杜邦线连接 PA12 引脚,杜邦线的另一头接上 GND,反复插拔,观察板载 LED 的明灭。可以发现板载 LED 随着杜邦线的插拔一明一灭,说明实验是成功了的。

总结

  • 使用 PY32F003 的外部中断功能的配置很简单。
  • 如果使用 GPIO 的高速模式,对捕获外部中断有好处,但要避免插拔抖动造成的多次触发。实测使用 GPIO_SPEED_FREQ_VERY_HIGH 时,一次接触会产生 LED 的多次明灭;使用 GPIO_SPEED_FREQ_MEDIUM 时,好像又有点丢失的感觉。在实用设计中,在使用 HIGH 或者 VERY_HIGH 的同时,应该加一套时间常数较为合适的 RC 低通电路用于防抖。

本实验只是完成了一个管脚作为外部中断源的捕获,如果有多个的话,想来可能大同小异吧,毕竟芯片的 NVIC 构架是相同的。

谬误之处,欢迎在评论区讨论指正。

你可能感兴趣的:(PY,MCU,嵌入式硬件,单片机,物联网)