打印
[新手园地]

我不是凡客,我是白痴--GPIO中断理解

[复制链接]
5325|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
leonbaichi|  楼主 | 2011-9-1 18:20 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 leonbaichi 于 2011-9-1 19:27 编辑

好几天没登论坛。学习还处在原地。在看了神农之前的讲课后,自己对GPIO做个理解。书写如下,我觉得对于像我一样的新手还是比较方便马上看懂的。
程序是BSP中的smpl_drGPIO

神农:其实M系列的芯片,每一个管脚都可以作为中断引脚。他在每一组的PORT中,有一个寄存器,它用来保护一组PORT的中断,出了两个口。


  该寄存器是GPIOA_ISRC(可读可写,具体读写的含义不同,读是查询是否中断,写中断的那位是清中断)具体可以参考数据手册,这边不赘述。
NUC1xx的芯片,讲IO的中断分成了两个源头,将IO的中断分成了两个源头,GPAB_IRQHandlerGPCDE_IRQHandler。也就是说,如果我设置了PA1作为中断输入,实际上我是进入了GPAB_IRQHandler这个源头,同样CDE的管脚中断会进入GPCDE_IRQHandler这个源头。

     void GPAB_IRQHandler(void)
{

volatile uint32_t u32GPAStatus, u32GPBStatus;


    /* Keep the interrupt source */

u32GPAStatus = GPIOA->ISRC;

u32GPBStatus = GPIOB->ISRC;

    /* Avoid to clear EINT0/EINT1 INT flag */
    u32GPBStatus = u32GPBStatus & ~(0x3 << 14);

    /* Clear the interrupt */
    GPIOA->ISRC = u32GPAStatus;
    GPIOB->ISRC = u32GPBStatus;  //进入中断当然要清楚标志位了

    /* Call the callback function of GPIOAB interrupt */
    if ( _pfGPABCallback )
        _pfGPABCallback(u32GPAStatus, u32GPBStatus);   
}

其中在同一个DrvGPIO.c文件中定义了该函数指针

static void (*_pfGPABCallback)(uint32_t u32GPAStatus, uint32_t u32GPBStatus);
   所以if语句判断为真,执行之。

具体执行的是哪个函数呢,请往下看

void GPABCallback(uint32_t u32GpaStatus, uint32_t u32GpbStatus)
{
    printf("GPAB Interrupt ! GPA:0x%04x  GPB:0x%04x. \n", u32GpaStatus, u32GpbStatus);
}

然后它传递的方式呢,是通过以下这个函数在MAIN中的执行来进行传递的

void DrvGPIO_SetIntCallback(GPIO_GPAB_CALLBACK pfGPABCallback,GPIO_GPCDE_CALLBACK pfGPCDECallback);

它的具体实现:

void DrvGPIO_SetIntCallback(GPIO_GPAB_CALLBACK pfGPABCallback, GPIO_GPCDE_CALLBACK pfGPCDECallback)
{
    _pfGPABCallback  = (void (*)(uint32_t, uint32_t))pfGPABCallback;
    _pfGPCDECallback = (void (*)(uint32_t, uint32_t, uint32_t))pfGPCDECallback;   
}

看到了吧

其中红色部分,在DrvGPIO.H的头文件中又定义了一个跟
(*_pfGPABCallback)函数格式相同的typedef
typedef void (*GPIO_GPAB_CALLBACK)(uint32_t u32GPAStatus, uint32_t u32GPBStatus);

就为了传递那个要执行的函数参数。

至此,中断的流程细节应该就交代清楚了,虽然好像有点乱,但是对照那个BSP中的例程还是很清楚的。

另外,经常出现的类似于E_GPA,E_GPB......

  DrvGPIO_EnableDebounce(E_GPA, 11);

定义原型为:

typedef enum
{
        E_GPA = 0,
        E_GPB = 1,
        E_GPC = 2,
        E_GPD = 3,
        E_GPE = 4
} E_DRVGPIO_PORT;

在实际使用中,会做一个相对地址到实际地址的转换。

int32_t DrvGPIO_DisableDebounce(E_DRVGPIO_PORT port, int32_t i32Bit)
{
    volatile uint32_t u32Reg;

    u32Reg = (uint32_t)&GPIOA->DBEN + (port*PORT_OFFSET);     //这边进行了转换。
        
    outpw(u32Reg, inpw(u32Reg) & ~(1<<i32Bit));
   
    GPIO_DBNCECON->DBNCECON.ICLK_ON = 0;

        return E_SUCCESS;
}

添加附件:




Smpl_DrvGPIO.zip

345.2 KB

相关帖子

沙发
leonbaichi|  楼主 | 2011-9-1 20:08 | 只看该作者
本帖最后由 leonbaichi 于 2011-9-1 20:44 编辑

在BSP的GPIO例程中,同时也包含了中断0的段子,下面写写我的理解:

/*---------------------------------------------------------------------------------------------------------*/
/* Function:    DrvGPIO_EnableEINT0                                                                        */
/*                                                                                                         */
/* Parameter:                                                                                                                                                                                   */       
/*                    TriggerType - [in]                                                                         */
/*                  E_DRVGPIO_INT_TYPE, specify the interrupt trigger type.                                */
/*                  It could be E_IO_RISING, E_IO_FALLING or E_IO_BOTH_EDGE and                            */
/*                  it's meaning the interrupt function enable by rising egde/high level,                  */
/*                  falling edge/low level or both riging edge and falling egde.                           */
/*                  If the interrupt mode is E_MODE_LEVEL and interrupt type is                            */
/*                  E_BOTH_EDGEthen calling this API is ignored.  
                                              */
/*                    Mode - [in]                                                                                */
/*                  E_DRVGPIO_INT_MODE, specify the interrupt mode.                                        */
/*                  It could be E_MODE_EDGE or E_MODE_LEVEL to control the interrupt is by                 */
/*                        edge trigger or by level trigger.                                                      */
/*                  If the interrupt mode is E_MODE_LEVEL and interrupt type is                            */
/*                  E_BOTH_EDGEthen calling this API is ignored.  
                                       */
/*                    pfEINT0Callback - [in]                                                                     */
/*                  It's the function pointer of the external INT0 callback function.                      */
/* Returns:                                                                                                */
/*              None                                                                                       */
/* Description:                                                                                            */
/*              Enable the interrupt function for external GPIO interrupt from /INT0(GPB.14) pin.          */
/*---------------------------------------------------------------------------------------------------------*/

GPIO的中断触发模式有两种(由GPIO_IMD控制)分别是电平触发和边沿触发。
当电平触发的时候有高电平触发和低电平触发,而在边沿触发的时候有上升沿触发,下降沿触发
这个函数的两个参数,mode和TriggerType是配合着用的,上图红色部分已经标记说明了。

void DrvGPIO_EnableEINT0(E_DRVGPIO_INT_TYPE TriggerType, E_DRVGPIO_INT_MODE Mode, GPIO_EINT0_CALLBACK pfEINT0Callback)
{
    volatile uint32_t u32Reg;

    if ((TriggerType == E_IO_BOTH_EDGE) && (Mode == E_MODE_LEVEL))
        return ;

    u32Reg = (uint32_t)&GPIOA->IEN + (E_GPB*PORT_OFFSET);
        if (TriggerType == E_IO_RISING)
    {
        outpw(u32Reg, inpw(u32Reg) | (1UL<<(14+16)));         
        }
    else if (TriggerType == E_IO_FALLING)
        {
        outpw(u32Reg, inpw(u32Reg) | (1UL<<(14)));         
        }
    else if (TriggerType == E_IO_BOTH_EDGE)
    {  
        outpw(u32Reg, inpw(u32Reg) | (1UL<<(14))|(1UL<<(14+16)));         

/*inpw(u32Reg) 就是那个地址所代表寄存器的值  之后的(1UL<<(14))|(1UL<<(14+16))就是在IR_EN[14]和IF_EN[14]都使能中断EINT0

        }             

     /* Configure to be level trigger or edge trigger */
    u32Reg = (uint32_t)&GPIOA->IMD + (E_GPB*PORT_OFFSET);
        if (Mode == E_MODE_EDGE)
                outpw(u32Reg, inpw(u32Reg) & ~(1<<14));
        else if(Mode == E_MODE_LEVEL)
                outpw(u32Reg, inpw(u32Reg) | (1<<14));

   _pfEINT0Callback = pfEINT0Callback;   //传递了,同样的 _pfEINT0Callback用于那个INT0Handler.嘿嘿

    NVIC_SetPriority(EINT0_IRQn, (1<<__NVIC_PRIO_BITS) - 2);
    NVIC_EnableIRQ(EINT0_IRQn);
}

使用特权

评论回复
板凳
leonbaichi|  楼主 | 2011-9-1 20:45 | 只看该作者
我说这个编辑帖子的东西怎么这么难使啊~~~~~~~~

使用特权

评论回复
地板
lixiaoxu2meng| | 2011-9-2 11:32 | 只看该作者

使用特权

评论回复
5
liubb1981| | 2012-12-14 17:02 | 只看该作者
谢谢,我看了很受用

使用特权

评论回复
6
p262664916| | 2015-4-29 00:03 | 只看该作者
不错,受用了

使用特权

评论回复
7
lvyunhua| | 2015-4-30 10:20 | 只看该作者
感谢分享!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

0

主题

41

帖子

1

粉丝