十四、外部中断按键点灯
1. 配置流程
一般使用GPIO的外部中断功能,都需要有以下几个步骤。
- 关闭寄存器写保护
- 配置GPIO的模式
- 配置GPIO外部中断
- 配置IRQ中断
- 配置NVIC
- 编写中断服务函数
HC32单片机的任意引脚都可以配置为外部中断触发,开发板的按键使用的是KEY_UP,接到了单片机的PA0引脚上,因此我们可以使能PA0的外部中断功能。
1.1 关闭寄存器写保护
开启外部中断之前要将寄存器的写保护关闭。 代码为:
c
/* 为一些必要的寄存器打开写功能. */
LL_PERIPH_WE(LL_PERIPH_EFM | LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_PWC_CLK_RMU | LL_PERIPH_SRAM);
1
2
2
1.2 配置GPIO模式
代码如下:
c
stc_gpio_init_t stcGpioInit;
/* GPIO初始化 */
(void)GPIO_StructInit(&stcGpioInit);
stcGpioInit.u16PinDir = PIN_DIR_IN; // 输入模式
stcGpioInit.u16ExtInt = PIN_EXTINT_ON; // 中断开启
stcGpioInit.u16PullUp = PIN_PU_OFF; // 上拉关闭
(void)GPIO_Init(GPIO_PORT_A, GPIO_PIN_00, &stcGpioInit);
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
这段代码是用于初始化 GPIO。
(void)GPIO_StructInit(&stcGpioInit);
:调用了GPIO_StructInit
函数来对stcGpioInit
结构体进行初始化,确保结构体中的各个成员都被正确设置。stcGpioInit.u16PinDir = PIN_DIR_IN;
:将u16PinDir
成员设置为PIN_DIR_IN
,表示配置GPIO
引脚为输入模式。stcGpioInit.u16ExtInt = PIN_EXTINT_ON;
:将u16ExtInt
成员设置为PIN_EXTINT_ON
,表示开启中断功能。stcGpioInit.u16PullUp = PIN_PU_OFF;
:将u16PullUp
成员设置为PIN_PU_OFF
,表示关闭上拉电阻功能。- 最后一行
(void)GPIO_Init(GPIO_PORT_A, GPIO_PIN_00, &stcGpioInit);
:调用GPIO_Init
函数,将上述配置应用到指定的GPIO
引脚上(此处为A
组的第0
号引脚)。
1.3 配置GPIO外部中断
c
stc_extint_init_t stcExtIntInit;
/* 外部中断 */
(void)EXTINT_StructInit(&stcExtIntInit);
stcExtIntInit.u32Filter = EXTINT_FILTER_ON; // 外部中断过滤开启
stcExtIntInit.u32FilterClock = EXTINT_FCLK_DIV8; // 8分屏
stcExtIntInit.u32Edge = EXTINT_TRIG_RISING; // 上升沿触发
(void)EXTINT_Init(EXTINT_CH00, &stcExtIntInit); // 因为是PA0所以通道是0
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
这段代码是在配置外部中断。首先,定义了一个名为 stcIrqSignConfig 的外部中断签入配置结构体。 接下来的代码进行了初始化设置:
这段代码是在配置外部中断。首先,定义了一个名为 stcIrqSignConfig
的外部中断签入配置结构体。 接下来的代码进行了初始化设置:
(void)EXTINT_StructInit(&stcExtIntInit);
:调用了EXTINT_StructInit
函数对stcExtIntInit
结构体进行初始化,确保结构体中的各个成员都被正确设置。stcExtIntInit.u32Filter = EXTINT_FILTER_ON;
:将u32Filter
成员设置为EXTINT_FILTER_ON
,表示开启外部中断过滤功能。stcExtIntInit.u32FilterClock = EXTINT_FCLK_DIV8;
:将u32FilterClock
成员设置为EXTINT_FCLK_DIV8
,表示使用 8 分频作为外部中断滤波时钟。stcExtIntInit.u32Edge = EXTINT_TRIG_RISING;
:将u32Edge
成员设置为EXTINT_TRIG_RISING
,表示选择上升沿触发外部中断。- 最后一行
(void)EXTINT_Init(EXTINT_CH00, &stcExtIntInit);
:调用EXTINT_Init
函数,将上述配置应用到指定的外部中断通道上(此处使用通道0
,因为是针对PA0
引脚的外部中断配置)。
1.4 配置IRQ中断
代码如下:
c
stc_irq_signin_config_t stcIrqSignConfig;
/* IRQ 配置 */
stcIrqSignConfig.enIntSrc = INT_SRC_PORT_EIRQ0; // 因为是PA0所以外部中断号是0
stcIrqSignConfig.enIRQn = INT005_IRQn; // 可以是任意未设置的中断号定义
stcIrqSignConfig.pfnCallback = &KEY_IRQ_Handler; // 中断处理函数
(void)INTC_IrqSignIn(&stcIrqSignConfig);
1
2
3
4
5
6
7
2
3
4
5
6
7
这段代码是用于配置IRQ(中断请求)的相关参数。
stcIrqSignConfig.enIntSrc = INT_SRC_PORT_EIRQ0;
:将enIntSrc
成员设置为INT_SRC_PORT_EIRQ0
,表示选择外部中断源为EIRQ0
(对应PA0
引脚)。stcIrqSignConfig.enIRQn = INT005_IRQn;
:将enIRQn
成员设置为INT005_IRQn
,表示将中断号定义为INT005_IRQn
。这里的中断号可以是任意未设置的中断号定义。stcIrqSignConfig.pfnCallback = &KEY_IRQ_Handler;
:将pfnCallback
设置为指向KEY_IRQ_Handler
中断处理函数的指针。- 最后一行
(void)INTC_IrqSignIn(&stcIrqSignConfig);
:调用INTC_IrqSignIn
函数,将上述配置应用到中断控制器中,以便在满足中断条件时执行相应的中断处理函数。
1.5 配置NVIC
代码如下:
c
/* NVIC 初始化 */
NVIC_ClearPendingIRQ(stcIrqSignConfig.enIRQn);
NVIC_SetPriority(stcIrqSignConfig.enIRQn, DDL_IRQ_PRIO_DEFAULT); // 优先级
NVIC_EnableIRQ(stcIrqSignConfig.enIRQn);
1
2
3
4
2
3
4
这段代码是用于初始化 NVIC(Nested Vectored Interrupt Controller)。
NVIC_ClearPendingIRQ(stcIrqSignConfig.enIRQn);
:该行代码用于清除指定中断的挂起状态,确保中断可以被触发。NVIC_SetPriority(stcIrqSignConfig.enIRQn, DDL_IRQ_PRIO_DEFAULT);
:调用NVIC_SetPriority
函数设置指定中断的优先级。在此处,使用了DDL_IRQ_PRIO_DEFAULT
作为默认的中断优先级。NVIC_EnableIRQ(stcIrqSignConfig.enIRQn);
:最后一行代码调用NVIC_EnableIRQ
函数,用于使能指定的中断,以便在满足中断条件时执行相应的中断服务程序。
1.6 写中断服务函数
中断服务函数需要自己进行编写,自行命名,这里我们就命名为KEY_IRQ_Handler。
因为上一节在配置IRQ中断的时候已经将KEY_IRQ_Handler作为中断处理函数初始化了,所以我们只需要实现一下这个函数的处理逻辑就好。
在中断处理函数里需要检测中断标志位是否被置位。 en_flag_status_t EXTINT_GetExtIntStatus(uint32_t u32ExtIntCh)
- u32ExtIntCh:表示外部中断通道,使用 @ref EXTINT_Channel_Sel 中定义的值。
- en_flag_status_t:表示返回值为枚举类型 en_flag_status_t,用于表示外部中断的触发源状态(0或1)。 这个函数是用于获取指定外部中断的触发源状态。只有一个参数就是中断线。有一个返回值,返回值的状态为SET和RESET。需要注意的是每次中断执行完毕之后都需要清除一下中断标志位等待下一次中断发生。
中断服务函数编写代码如下:
c
// KEY中断处理函数
void KEY_IRQ_Handler(void)
{
if( SET == EXTINT_GetExtIntStatus(EXTINT_CH00) )
{
if( flag )
{
// 如果灯是亮着的,就熄灭LED
GPIO_ResetPins(GPIO_PORT_B, GPIO_PIN_02);
flag = 0;
}
else
{
// 如果灯是灭着的,就点亮LED
GPIO_SetPins(GPIO_PORT_B, GPIO_PIN_02);
flag = 1;
}
printf("Key Press!!!\r\n");
}
// 清除标志位
EXTINT_ClearExtIntStatus(EXTINT_CH00);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2. 实验现象
烧写我们的代码之后,按下一次按键松开,开启LED,再按下一次按键松开,关闭LED,以此类推。每次按下按键松开都会打印Key!!。如图2-1-1所示。