I.MX6U 裸机开发15.IRQ中断——GPIO中断处理
一、向GPIO驱动添加中断处理函数
1. GPIO 相关寄存器
(1)GPIOx_ICRn 按键GPIO设置中断模式
在《IMX6ULL参考手册》中可以看到,GPIOx_ICR1
和 GPIOx_ICR2
是用于配置 GPIO 引脚中断触发模式的寄存器。每个寄存器控制 16 个 GPIO 引脚的中断配置。
GPIOx_ICR1
:控制 GPIO 引脚 0 到 15 的中断配置。GPIOx_ICR2
:控制 GPIO 引脚 16 到 31 的中断配置。
每个引脚的中断配置占用 2 位,可以配置为以下几种模式:
00
:低电平触发01
:高电平触发10
:上升沿触发11
:下降沿触发
具体配置方法如下:
// 配置 GPIO 引脚 0 为下降沿触发
GPIOx->ICR1 &= ~(3 << (0 * 2)); // 清除原有配置
GPIOx->ICR1 |= (2 << (0 * 2)); // 设置为下降沿触发
// 配置 GPIO 引脚 16 为上升沿触发
GPIOx->ICR2 &= ~(3 << ((16 - 16) * 2)); // 清除原有配置
GPIOx->ICR2 |= (3 << ((16 - 16) * 2)); // 设置为上升沿触发
通过设置 GPIOx_ICR1
和 GPIOx_ICR2
寄存器,可以灵活配置每个 GPIO 引脚的中断触发模式。
开发板按下时,UART1_CTS端口接地,所以设置为下降沿触发 。
(2)使能GPIO对应的中断
GPIO_MIR
寄存器用于配置 GPIO 引脚的中断屏蔽。每个 GPIO 引脚对应 GPIO_MIR
寄存器中的一位,通过设置或清除这些位,可以使能或屏蔽相应引脚的中断。
设置示例:
// 屏蔽 GPIO 引脚 0 的中断
GPIOx->MIR |= (1 << 0);
// 使能 GPIO 引脚 0 的中断
GPIOx->MIR &= ~(1 << 0);
// 屏蔽 GPIO 引脚 16 的中断
GPIOx->MIR |= (1 << 16);
// 使能 GPIO 引脚 16 的中断
GPIOx->MIR &= ~(1 << 16);
(3)GPIOx_EDGE_SEL
GPIOx_EDGE_SEL 寄存器用于配置 GPIO 引脚的边沿选择。它允许选择 GPIO 引脚是对上升沿、下降沿还是双边沿(上升沿和下降沿)触发中断。
(4)GPIO_ISR寄存器
处理完中断后,通过 GPIO_ISR
寄存器清除中断标志位,写1清零。
2. GIC 设置
(1)使能相应的中断ID
开发板使用的按键是接在 GPIO1_IO18引脚,在 《IMX6ULL参考手册》P185:
可以看到GPIO1_IO18对应的中断ID是67+32=99。
d MCIMX6X6Y2.h 里有定义:
GPIO1_Combined_16_31_IRQn = 99
(2)中断优先级设置
(3)注册GPIO1_IO18的中断处理函数
二、GPIO 中断代码
1. 在 bsp_gpio.h 里定义中断触发类型枚举
/**
* @brief 中断触发类型枚举
*/
typedef enum _gpio_interrupt_mode{
kGPIO_NoIntmode = 0U, // 无中断
kGPIO_IntLowLevel = 1U, // 低电平触发
kGPIO_IntHighLevel = 2U, // 高电平触发
kGPIO_IntRisingEdge = 3U, // 上升沿触发
kGPIO_IntFallingEdge = 4U, // 下降沿触发
kGPIO_IntRisingOrFallingEdge = 5U, // 上升沿或下降沿触发
} gpio_interrupt_mode_t;
2. 修改配置结构体
/**
* @brief GPIO 配置结构体
*/
typedef struct _gpio_pin_config
{
gpio_pin_direction_t direction; // 输入输出
uint8_t outputLogic; // 输出电平
gpio_interrupt_mode_t interruptMode; // 中断触发类型
} gpio_pin_config_t;
3. 使能、禁用GPIO中断及清除标志位
/**
* @brief 使能GPIO中断
*/
void gpio_enableint(GPIO_Type *base, int pin)
{
base->IMR |= 1 << pin;
}
/**
* @brief 禁止GPIO中断
*/
void gpio_disableint(GPIO_Type *base, int pin)
{
base->IMR &= ~(1 << pin);
}
/**
* @brief 清除中断标志位
*/
void gpio_clearintflags(GPIO_Type *base, int pin)
{
base->ISR |= 1 << pin;
}
4. 触发方式
/**
* @brief GPIO中断初始化
*/
void gpio_interrupt_init(GPIO_Type *base, unsigned int pin, gpio_interrupt_mode_t mode) {
volatile uint32_t *icr;
uint32_t icrShift;
icrShift = pin;
// 清0防止 gpio_icr 寄存器设置无效
base->EDGE_SEL &= ~(1 << pin);
if (pin < 16) {
icr = &(base->ICR1);
} else {
icr = &(base->ICR2);
icrShift -= 16;
}
switch (mode) {
case kGPIO_IntLowLevel: // 低电平触发
*icr &= ~(3 << (icrShift * 2)); // 每个IO占用2位,即0b11,移动次数为icrShift * 2
break;
case kGPIO_IntHighLevel: // 高电平触发
*icr = (*icr & ~(3 << (icrShift * 2))) | (1 << (icrShift * 2));
break;
case kGPIO_IntRisingEdge: // 上升沿触发
*icr = (*icr & ~(3 << (icrShift * 2))) | (2 << (icrShift * 2));
break;
case kGPIO_IntFallingEdge: // 下降沿触发
*icr = (*icr & ~(3 << (icrShift * 2))) | (3 << (icrShift * 2));
break;
case kGPIO_IntRisingOrFallingEdge: // 上升沿或下降沿触发
base->EDGE_SEL |= 1 << pin;
break;
}
}
三、按键中断驱动
1. bsp_esti.h
//
// Created by Xundh on 2024/11/19.
//
#ifndef LEARN_I_MX6U_BSP_ESTI_H
#define LEARN_I_MX6U_BSP_ESTI_H
/**
* @brief 初始化外部中断, GPIO1_IO18
*/
void exti_init(void);
/**
* @brief GPIO1_IO18 中断服务函数
*/
void gpio1_io18_irqhandler(unsigned int gicciar, void *param);
#endif //LEARN_I_MX6U_BSP_ESTI_H
2. bsp_esti.c
//
// Created by Xundh on 2024/11/19.
//
#include "bsp_exti.h"
#include "bsp_clk.h"
#include "bsp_gpio.h"
#include "bsp_delay.h"
#include "bsp_int.h"
#include "led.h"
#include "imx6u.h"
/**
* @brief 初始化外部中断, GPIO1_IO18
*/
void exti_init(void) {
gpio_pin_config_t key_config;
// 1. 设置复用为GPIO1_IO18
IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0);
// 2. 设置电气属性
IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18, 0xF0B0);
// 3. 设置方向为输入
key_config.direction = kGPIO_DigitalInput;
// 4. 设置中断触发类型为下降沿触发
key_config.interruptMode = kGPIO_IntFallingEdge;
// 5. 初始化GPIO
gpio_init(GPIO1, 18, &key_config);
// 6. 使能GPIO1_IO18中断
GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);
// 7. 中断服务函数注册
sys_irq_handle_register(GPIO1_Combined_16_31_IRQn, (system_irq_handler_t)gpio1_io18_irqhandler, NULL);
// 8. GPIO 使能
gpio_enableint(GPIO1, 18);
}
/**
* @brief GPIO1_IO18 中断服务函数
*/
void gpio1_io18_irqhandler(unsigned int gicciar, void *param) {
static unsigned char state = 0;
// 临时使用定时,生产环境不能这样用
delay(10);
if (gpio_pinread(GPIO1, 18) == 0) {
state = !state;
led_switch(LED0, state);
}
// 清除中断标志位
gpio_clearintflags(GPIO1, 18);
}
上面程序编译后,查看反汇编文件:
可以看到 0x87800000 放的并不是中断向量表,修改 start.S,将bss段定义移到清除BSS段前面。
3. main.c
#include "inc/main.h"
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "led.h"
#include "beep.h"
#include "key.h"
#include "bsp_int.h"
#include "bsp_exti.h"
int main(void)
{
bsp_int_init(); /* 初始化中断 */
imx6u_clkinit(); /* 初始化系统时钟 */
clk_enable(); /* 使能外设时钟 */
led_init(); /* 初始化LED */
beep_init(); /* 初始化蜂鸣器 */
exti_init(); /* 初始化外部中断 */
while(1) {
// led_on();
// delay(1000);
// led_off();
// delay(1000);
}
return 0;
}
本代码实现按key0, LED0切换亮灭显示。