zoukankan      html  css  js  c++  java
  • 九.GPIO中断试验3——GPIO中断驱动

    在完成了通用的中断程序编写后,我们就要针对不同的外设进行中断函数的编写了。这一章我们使用的硬件是还是那个按钮,当按钮按下时,触发中断、调用终端函数。

    GPIO中断初始化

    在构造函数前,我们先要对GPIO进行中断初始化,这里要参考I.MX6ULL的参考手册28章GPIO,我们前面做通用GPIO驱动时已经用了DR和GDIR两个寄存器,这里要用到另外几个

    中断信号类型

    GPIO的中断设置是由GPIO_ICR1和GPIO_ICR2决定的。我们看一下这个寄存器的参数

    每个ICR寄存器用2个bit来描述一个IO口的中断信号类型,每组GPIO最有32个IO口,所以就用了2个寄存器(ICR1和ICR2)来描述所有端口的信号类型(高电平、低电平、上升沿和下降沿)。ICR1对应32个IO口的低16位,ICR2对应高16位。

    这里还有另外一个寄存器:EDGE_SEL,设置后可以在对应位IO口上下降沿同时都可以触发。

    使能GPIO对应中断

     GPIO的中断使能是GPIOx_IMR决定的,每个Bit代表一个IO口(1时为使能,0时禁止)

    中断标志位

    GPIO_ISR,中断完成后,需要清除中断标志位,中断对应的bit应该写1.

    代码构成

    这个代码要对以前的通用GPIO驱动进行修改,添加了中断相关的功能

    #include "bsp_gpio.h"
    /*
    * @description            :   GPIO初始化
    * @param-base             :   待初始化的GPIO组
    * @param-pin              :   待初始化的GPIO在组内的编号
    * @param-config           :   GPIO配置结构体
    * @return                 :   None
    */
    void gpio_init(GPIO_Type *base,
                    int pin,
                    gpio_pin_config_t *config)
    {
        if(config->direction == kGPIO_DigitalInput)              //gpio为输入
        {
            base->GDIR &= ~(1 << pin);  
        }
        
        else                                                      //gpio为输出
        {
            base->GDIR |= 1<<pin;
            gpio_pinwrite(base,pin,config->outputLogic);          //设置默认电平
        }
        gpio_intinit(base,pin,config->interruptMode);             //中断功能配置
    }
    
    /*
    * @description            :   读GPIO指定管脚
    * @param-base             :   待初始化的GPIO组
    * @param-pin              :   待初始化的GPIO在组内的编号
    * @retrun                 :   int 0 or 1
    */
    int gpio_pinread(GPIO_Type *base,int pin)
    {
        return (((base->DR >> pin)) &0x1);
    }
    
    /*
    * @description            :   写GPIO指定管脚
    * @param-base             :   待初始化的GPIO组
    * @param-pin              :   待初始化的GPIO在组内的编号
    * @param-value            :   输出电平,1为高电平,0为低电平
    * @retrun                 :   None
    */
    void gpio_pinwrite(GPIO_Type *base,int pin,int value)
    {
        if (value == 0U)            //输出值为0
        {
            base->DR &= ~(1U << pin);
        }
        else                       //输出值为1
        {
            base->DR |= (1U<<pin);
        }
    }
    
    
    /*
    * @description            :   GPIO中断使能
    * @param-base             :   待初始化的GPIO组
    * @param-pin              :   待初始化的GPIO在组内的编号
    * @return                 :   None
    */
    void gpio_enable(GPIO_Type *base,unsigned int pin)
    {
        base->IMR |= 1<<pin;
    }
    
    
    /*
    * @description            :   GPIO中断禁止
    * @param-base             :   待初始化的GPIO组
    * @param-pin              :   待初始化的GPIO在组内的编号
    * @return                 :   None
    */
    void gpio_disable(GPIO_Type *base,unsigned int pin)
    {
        base->IMR &= ~(1<<pin);
    }
    
    /*
    * @description            :   GPIO中断禁止标志清除位
    * @param-base             :   待初始化的GPIO组
    * @param-pin              :   待初始化的GPIO在组内的编号
    * @return                 :   None
    */
    void gpio_clearIntFlags(GPIO_Type *base,unsigned int pin)
    {
        base->ISR |= 1<<pin;
    }
    
    
    /*
    * @description            :   GPIO初始化
    * @param-base             :   待初始化的GPIO组
    * @param-pin              :   待初始化的GPIO在组内的编号
    * @param-pin_int_mode     :   中断信号类型
    * @return                 :   None
    */
    void gpio_intinit(GPIO_Type *base,
                      unsigned int pin,
                      gpio_interrupt_mode_t pin_int_mode)
    {
        volatile uint32_t *icr;         //icr寄存器,icr通过pin的值来判定
        uint32_t icrShift;              //最终使用的pin的值,
    
        icrShift = pin;                 //icrShift值初始化
    
        base->EDGE_SEL &= ~(1<<pin);    //清除边沿触发标志位,该位如果为1,上升、下降沿触发失效
    
        if(pin<16)                      //pin对应低16位时
        {icr = &(base->ICR1);}          //&为取址符,icr为IRC1地址
        
        else                            //pin对应高16为时
        {
            icr = &(base->ICR2);        //icr为ICR2地址
            icrShift -= 16;             //超过16,减16就是对应bit位,前面初始化的icrShift被修改
        }
    
        /*
        根据触发信号类型修改ICR1/ICR2的值
        */
        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_IntRisingOrFallingEdge:
                base->EDGE_SEL |=(1<<pin);
                break;
            default:
                break;
        }
    }

    在上面的代码中,我们修改了原有的通用GPIO驱动,添加了中断的初始化、GPIO对应IO口的中断使能/禁止,标志位清除。要注意点是标志位清除是对应bit置一。整个流程备注的很详细了,最后记得修改头文件,在头文件里声明新建的函数,还要定义新的枚举型数据gpio_interrupt_mode_t

    #ifndef __BSP_GPIO_H
    #define __BSP_GPIO_H
    
    #include "imx6ul.h"
    
    /*枚举类型描述GPIO方向*/
    typedef enum _gpio_pin_direction
    {
        kGPIO_DigitalInput = 0U,                //输入
        kGPIO_DigitalOutput = 1U,               //输出
    } gpio_pin_direction_t;
    
    
    /*定义GPIO中断触发枚举类型*/
    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;
    
    /*GPIO位置结构体*/
    typedef struct _gpio_pin_config
    {
        gpio_pin_direction_t direction;
        uint8_t outputLogic;
        gpio_interrupt_mode_t interruptMode;
    } gpio_pin_config_t;
    
    
    void gpio_init(GPIO_Type *base, int pin, gpio_pin_config_t *config);
    int gpio_pinread(GPIO_Type *base, int pin);
    void gpio_pinwrite(GPIO_Type *base, int pin, int value);
    
    //中断相关
    void gpio_enable(GPIO_Type *base,unsigned int pin);
    void gpio_disable(GPIO_Type *base,unsigned int pin);
    void gpio_clearIntFlags(GPIO_Type *base,unsigned int pin);
    void gpio_intinit(GPIO_Type *base,unsigned int pin,gpio_interrupt_mode_t pin_int_mode);
    #endif
    bsp_gpio.h

    GIC配置

    配置完GPIO相关参数后,要对GIC进行配置

    使能相应中断ID

    GPIO的中断ID要在参考手册第3章查询,要注意的是3.2章节开始说明了,前32个中断ID是不包含在那个给定的表中的

    开发板按键映射的端口是UART1_CTS,可以复用为GPIO1_IO18,所以我们查到的GPIO中断ID应该加上32

    也就是99。这个中断ID在我们移植的头文件里有预定义,可以直接使用

    中断优先级设置

    由于就用了一个中断源,我们这里就不再设置了,具体的设置方法在上一章有介绍。

    外部中断服务函数

    上面关于GIC的在过程放在一个新的文件夹里:

    作为外部中断的服务函数,c文件里只有两个函数,一个用来按照上面说的过程对GPIO和GIC进行初始化,另一个函数用来定义实际中断要实现的效果

    #include "bsp_exti.h"
    #include "bsp_int.h"
    #include "bsp_beep.h"
    #include "bsp_gpio.h"
    #include "bsp_delay.h"
    // 初始化外部中断:GPIO1_IO18
    
    /*
    * @description            :   外部中断初始化
    * @param                  :   None
    * @return                 :   None
    */
    void exti_init(void)
    {
        gpio_pin_config_t key_config;                               //定义GPIO配置
        IOMUXC_SetPinMux(IOMUXC_UART1_CTS_B_GPIO1_IO18,0);          //GPIO复用初始化
        IOMUXC_SetPinConfig(IOMUXC_UART1_CTS_B_GPIO1_IO18,0xf080);  //GPIO电气初始化
    
        key_config.direction = kGPIO_DigitalInput;                  //GPIO定义为输入
        key_config.interruptMode = kGPIO_IntFallingEdge;            //定义中断触发信号类型
    
        gpio_init(GPIO1,18,&key_config);                            //GPIO初始化
    
        GIC_EnableIRQ(GPIO1_Combined_16_31_IRQn);                   //GIC使能中段,
                                                                    //GPIO1_Combined_16_31_IRQn=99,即GPIO01_IO18中断ID
        
        system_register_irqHandler(GPIO1_Combined_16_31_IRQn,
                                  (system_irq_handler_t)gpio1_io18_irqhandler,
                                  NULL);                            //注册中断函数
                                                                        
        gpio_enable(GPIO1,18);                                      //使能GPIO1_IO18中断
    }
    
    /*
    * @description            :   外部中断初始化
    * @param                  :   这里不需要没有实际参数,但是定义函数的时候是按照前面声明函数是定义的,就把直接把样式复制过来了
    * @return                 :   None
    */
    void gpio1_io18_irqhandler(unsigned int gicciar, void *param)
    {
    
        static unsigned char state = ON;
        /*这里要注意:定时器用来进行按键消抖,实际工况下严禁在中断服务中调用延时!!!*/
        delay(10);
        if(gpio_pinread(GPIO1,18)==0)
        {
            state = !state;
            beep_switch(state);
        }
        gpio_clearIntFlags(GPIO1,18);   //清除中断标志位。
    }

    在初始化的过程中,一定要注意最后两行代码:注册中断函数和使能中断的顺序,一定要先定义函数,因为如果是先使能GPIO中断等话,一上电,有可能被GPIO申请进入中断,而此刻没有对应的函数程序可能异常。所以一定要先注册函数。

    程序里也说明了,在实际的中断函数中是严禁使用延时类函数的,因为这里还没有讲到定时器,所以先用了这个delay函数凑合一下实现按键消除抖动的效果,后面会讲到实际的用法。这里是使用前面的蜂鸣器来调试实际中断效果,没什么可讲的。

    记得在头文件里声明用到的函数

    #ifndef __BSP_EXTI_H
    #define __BSP_EXTI_H
    
    #include "imx6ul.h"
    
    void exti_init(void);
    void gpio1_io18_irqhandler(unsigned int gicciar, void *param);
    
    #endif
    bsp_exti.h

    至此,中断就讲完了。这是非常重要的一个知识点,用了3篇去讲解!不了解的话对照注释看看代码就可以了!

  • 相关阅读:
    在Asp.Net头部动态加载css和js文件的方法
    关于Ajax开发中Response的ContentType的一些问题
    C#获取存储过程的返回值
    在填写表单中输入全角数字的解决方案
    Eclipse3.7中搭建Android开发环境
    THinkPHP 获取客户端IP 与IP地址查询
    php 操作数组 (合并,拆分,追加,查找,删除等)
    HTML5 LocalStorage 本地存储
    [javascript] IE与火狐下window.event对象的区别
    今天有人问是否可以使用vs2005开发,回答了一下,记录下来
  • 原文地址:https://www.cnblogs.com/yinsedeyinse/p/15786658.html
Copyright © 2011-2022 走看看