I.MX6U 裸机开发15.IRQ中断——GPIO中断处理
- 一、向GPIO驱动添加中断处理函数
- 1. GPIO 相关寄存器
- (1)GPIOx_ICRn 按键GPIO设置中断模式
- (2)使能GPIO对应的中断
- (3)GPIOx_EDGE_SEL
- (4)GPIO_ISR寄存器
- 2. GIC 设置
- (1)使能相应的中断ID
- (2)中断优先级设置
- (3)注册GPIO1_IO18的中断处理函数
- 二、GPIO 中断代码
- 1. 在 bsp_gpio.h 里定义中断触发类型枚举
- 2. 修改配置结构体
- 3. 使能、禁用GPIO中断及清除标志位
- 4. 触发方式
- 三、按键中断驱动
- 1. bsp_esti.h
- 2. bsp_esti.c
- 3. main.c
一、向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 * 2break;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_IO18IOMUXC_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. 初始化GPIOgpio_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切换亮灭显示。