文章目录
- GPIO 中断管理与配置函数
- 1 GPIO中断服务号
- 2 GPIO中断相关的寄存器配置
- 3 具体代码分析
- 3.1 数据结构和类型定义
- 3.2 `gpio_int_enable`
- 3.3 `gpio_int_disable`
- 3.4 `gpio_int_flagClear`
- 3.5 `gpio_int_init`
- 3.6 `gpio_init`
- 4 完整代码
- 本文章结合了正点原子的 i.mx6u嵌入式Linux开发指南和笔者的理解。
- 前面我们进行了总的外部中断处理函数(system_irqhandler)的初始化,在这个函数中同个中断号,选择跳转到了不同的中断服务函数
- 下面我们就来写一个具体的中断服务函数,本文将具体编写GPIO 中断管理与配置函数
GPIO 中断管理与配置函数
以按键开关为例进行编写。
1 GPIO中断服务号
查看原理图,可知按键开关接入了 GPIO1_PIN18。
-
IMX6ULL参考手册
P185
可以看到中断服务号是67,加上前面32个CPU接口的私有中断,真正的中断号应该是67+32=99。 -
查看
MCIMX6Y2.h
IRQn_Type,可以证实这一点
GPIO1_Combined_0_15_IRQn = 98, /**< Combined interrupt indication for GPIO1 signals 0 - 15. */
GPIO1_Combined_16_31_IRQn = 99, /**< Combined interrupt indication for GPIO1 signals 16 - 31. */
2 GPIO中断相关的寄存器配置
IMX6ULL参考手册
P1360
- 以下是这些寄存器的详细解释:
- GPIOx_ICR1 (GPIO Interrupt Configuration Register 1):配置的是pin0-pin15,该寄存器用于配置 GPIO 中断的触发方式,包括上升沿、下降沿、低电平或高电平触发。
- GPIOx_ICR2 (GPIO Interrupt Configuration Register 2):同上,只不过配置的是pin16-pin31
- GPIOx_IMR (GPIO Interrupt Mask Register):该寄存器用于屏蔽或启用 GPIO 中断。 通过设置该寄存器中的相应位,可以屏蔽或启用每个 GPIO 引脚的中断。
- GPIOx_ISR (GPIO Interrupt Status Register):该寄存器用于指示 GPIO 中断状态。 当一个 GPIO 引脚发生中断时,该寄存器中的相应位会被置位。
- GPIOx_EDGE_SEL (GPIO Edge Select Register):该寄存器用于选择 GPIO 中断的触发方式为边沿(上升沿和下降沿都会触发),且一旦设置边沿检测有效,将会屏蔽ICR寄存器的设置
3 具体代码分析
3.1 数据结构和类型定义
-
gpio_interrupt_mode_t
此枚举定义了GPIO中断的可能触发模式:kGPIO_Nointmode
: 无中断模式。kGPIO_IntLowLevel
: 低电平触发。kGPIO_IntHighLevel
: 高电平触发。kGPIO_IntRisingEdge
: 上升沿触发。kGPIO_IntFallingEdge
: 下降沿触发。kGPIO_IntRisingFallingEdge
: 上升沿和下降沿都触发。
-
gpio_pin_direction_t
此枚举定义了GPIO引脚可能的方向:kGPIO_DigitalInput
: 数字输入。kGPIO_DigitalOutput
: 数字输出。
-
gpio_pin_config_t
此结构体用于配置GPIO引脚:direction
: 引脚方向(输入或输出)。outputLogic
: 当方向为输出时的默认电平。interruptMode
: 中断模式。
3.2 gpio_int_enable
void gpio_int_enable(GPIO_Type *base, unsigned int pin)
{
base->IMR |= (1U << pin);
}
使能指定GPIO组的特定引脚的中断功能。
3.3 gpio_int_disable
void gpio_int_disable(GPIO_Type *base, unsigned int pin)
{
base->IMR &= ~(1U << pin);
}
禁用指定GPIO组的特定引脚的中断功能。
3.4 gpio_int_flagClear
void gpio_int_flagClear(GPIO_type *base, unsigned int pin)
{
base->ISR |= (1U << pin);
}
清除指定GPIO组的特定引脚的中断标志位,注意是通过写1清除。
3.5 gpio_int_init
void gpio_int_init(GPIO_Type *base, int pin, gpio_interrupt_mode_t pin_int_mode)
{
volatile uint32_t *icr;//GPIO高16位和低16位使用的ICR寄存器不同,需要判断一下
uint32_t icrShift;
icrShift = pin;
base->EDGE_SEL &= ~(1<<pin);//先把双边沿触发模式关了
if(pin < 16){
icr = &(base->ICR1);
}
else{
icr = &(base->ICR2);
icrShift -= 16;
}
switch(pin_int_mode){
case kGPIO_IntLowLevel:
*icr &= ~(3 << (2*icrShift));
break;
case kGPIO_IntHighLevel:
*icr &= (3 << (2*icrShift));
*icr |= (1 << (2*icrShift));
break;
case kGPIO_IntRisingEdge:
*icr &= ~(3 << (2*icrShift));
*icr |= (2 << (2*icrShift));
break;
case kGPIO_IntFallingEdge:
*icr &= ~(3 << (2*icrShift));
*icr |= (3 << (2*icrShift));
break;
case kGPIO_IntRisingFallingEdge:
base->EDGE_SEL |= (1<<pin);
break;
}
}
配置特定GPIO引脚的中断触发模式。此函数处理边缘和电平触发的配置,并根据引脚号选择正确的寄存器和位偏移。
3.6 gpio_init
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config)
{
if(config->direction == kGPIO_DigitalInput){
base->GDIR &= ~(1 << pin);
}
else{
base->GDIR |= (1 << pin);
gpio_pin_write(base, pin, config->direction);
}
gpio_int_init(base, pin, config->interruptMode);
}
使用提供的配置(方向,输出逻辑,中断模式)初始化指定的GPIO引脚。此函数首先设置引脚方向,然后根据配置设置默认输出逻辑,并调用gpio_int_init
来配置中断功能。
4 完整代码
bsp_gpio.c
#include "bsp_gpio.h"
/**
* @brief 使用给定配置初始化GPIO引脚。
*
* @param base GPIO组。
* @param pin 要配置的引脚号。
* @param config 指向GPIO引脚配置结构的指针。
*
* @return 无。
*/
void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config)
{
if(config->direction == kGPIO_DigitalInput){
base->GDIR &= ~(1 << pin);
}
else{
base->GDIR |= (1 << pin);
gpio_pin_write(base, pin, config->direction);
}
gpio_int_init(base, pin, config->interruptMode);
}
/**
* @brief 读取指定GPIO的电平值。
*
* @param base 要读取的GPIO组。
* @param pin 要读取的引脚号。
*
* @return GPIO的电平值。
*/
int gpio_pin_read(GPIO_Type *base, int pin)
{
return (((base->DR)>>pin)&0x1);
}
/**
* @brief 指定 GPIO 输出高或者低电平。
*
* @param base GPIO组。
* @param pin 要输出的引脚号。
* @param value 要输出的电平,1输出高电平,0输出低电平。
*
* @return 无。
*/
void gpio_pin_write(GPIO_Type *base, int pin, int value)
{
if(value == 0u){
base->DR &= ~(1U << pin);
}
else{
base->DR |= (1U << pin);
}
}
/**
* @brief 使能指定 GPIO 中断。
*
* @param base GPIO组。
* @param pin 要输出的引脚号。
*
* @return 无。
*/
void gpio_int_enable(GPIO_Type *base, unsigned int pin)
{
base->IMR |= (1U << pin);
}
/**
* @brief 失能指定 GPIO 中断。
*
* @param base GPIO组。
* @param pin 要输出的引脚号。
*
* @return 无。
*/
void gpio_int_disable(GPIO_Type *base, unsigned int pin)
{
base->IMR &= ~(1U << pin);
}
/**
* @brief 清除指定 GPIO 中断标志位。
*
* @param base GPIO组。
* @param pin 要输出的引脚号。
*
* @return 无。
*/
void gpio_int_flagClear(GPIO_Type *base, unsigned int pin)
{
base->ISR |= (1U << pin);
}
/**
* @brief GPIO中断初始化函数。
*
* @param base GPIO组。
* @param pin 要输出的引脚号。
* @param pin_int_mode 要设置的触发类型。
*
* @return 无。
*/
void gpio_int_init(GPIO_Type *base, int pin, gpio_interrupt_mode_t pin_int_mode)
{
volatile uint32_t *icr;//GPIO高16位和低16位使用的ICR寄存器不同,需要判断一下
uint32_t icrShift;
icrShift = pin;
base->EDGE_SEL &= ~(1<<pin);//先把双边沿触发模式关了
if(pin < 16){
icr = &(base->ICR1);
}
else{
icr = &(base->ICR2);
icrShift -= 16;
}
switch(pin_int_mode){
case kGPIO_IntLowLevel:
*icr &= ~(3 << (2*icrShift));
break;
case kGPIO_IntHighLevel:
*icr &= (3 << (2*icrShift));
*icr |= (1 << (2*icrShift));
break;
case kGPIO_IntRisingEdge:
*icr &= ~(3 << (2*icrShift));
*icr |= (2 << (2*icrShift));
break;
case kGPIO_IntFallingEdge:
*icr &= ~(3 << (2*icrShift));
*icr |= (3 << (2*icrShift));
break;
case kGPIO_IntRisingFallingEdge:
base->EDGE_SEL |= (1<<pin);
break;
}
}
bsp_gpio.h
typedef enum _gpio_interrupt_mode{
kGPIO_Nointmode = 0U,
kGPIO_IntLowLevel = 1U, /* 低电平触发 */
kGPIO_IntHighLevel = 2U, /* 高电平触发 */
kGPIO_IntRisingEdge = 3U, /* 上升沿触发 */
kGPIO_IntFallingEdge = 4U, /* 下降沿触发 */
kGPIO_IntRisingFallingEdge = 5U, /* 上升沿和下降沿都触发 */
}gpio_interrupt_mode_t;
typedef enum _gpio_pin_direction{
kGPIO_DigitalInput = 0U,
kGPIO_DigitalOutput = 1U,
} gpio_pin_direction_t;
/**
* @brief 方向,默认电平
*/
typedef struct _gpio_pin_config{
//方向
gpio_pin_direction_t direction;
//方向为输出时,默认输出电平
uint8_t outputLogic;
//中断模式
gpio_interrupt_mode_t interruptMode;
} gpio_pin_config_t;