【嵌入式Linux】i.MX6ULL GPIO 中断管理与配置函数

  • 本文章结合了正点原子的 i.mx6u嵌入式Linux开发指南和笔者的理解。
  • 前面我们进行了总的外部中断处理函数(system_irqhandler)的初始化,在这个函数中同个中断号,选择跳转到了不同的中断服务函数
  • 下面我们就来写一个具体的中断服务函数,本文将具体编写GPIO 中断管理与配置函数

GPIO 中断管理与配置函数

以按键开关为例进行编写。

1 GPIO中断服务号

查看原理图,可知按键开关接入了 GPIO1_PIN18。

  • IMX6ULL参考手册P185
    在这里插入图片描述
    可以看到中断服务号是67,加上前面32个CPU接口的私有中断,真正的中断号应该是67+32=99。

  • 查看MCIMX6Y2.hIRQn_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;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__Witheart__

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值