打印
[牛人杂谈]

M051 GPIO 中断和唤醒功能

[复制链接]
3263|9
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
通过Px_IEN 和 Px_IMD寄存器,每个GPIO引脚都可以设置为中断源。有5种类型的中断条件可以
选择:低电平触发、高电平触发、下降沿触发、上升沿触发、上升和下降沿都触发。对于边沿触
发,用户可以使能输入信号去抖动功能来避免由干扰导致的不期望的中断发生。 de-bounce 时钟源
和采样周期可以由DBNCECON 寄存器设定。 .
当芯片进入空闲/睡眠模式时, GPIO也能唤醒芯片。如果使用GPIO唤醒芯片,唤醒触发条件和中断
触发条件一致,但是有2件事情需要注意(只有M05xxBN):
1. 进入空闲/睡眠模式之前要确定 I/O 的状态
当时有GPIO反转来唤醒系统时,在进入空闲/睡眠模式之前,用户必须确认I/O口的状态。
如果设定的是高电平/上升降沿唤醒,进入空闲/睡眠模式之前必须确认I/O为低电平;如果设定的是
低电平/下降沿唤醒,进入空闲/睡眠模式之前必须确认I/O为高电平。
2. 进入空闲/睡眠模式之前要关闭I/O去抖动功能
如果指定的I/O唤醒引脚使能了输入信号去抖动功能,当系统由GPIO唤醒时,系统将发生两次GPIO
中断事件。一个中断事件由唤醒导致,另一个由 I/O 输入去抖动功能导致。使能空闲/睡眠功能之
前,用户应该关闭去抖动功能,以避免两次中断事件。



沙发
捉虫天师|  楼主 | 2016-7-4 21:30 | 只看该作者
/*---------------------------------------------------------------------------------------------------------*/
/*  GPIO Interrupt Type Constant Definitions                                                               */
/*---------------------------------------------------------------------------------------------------------*/
#define GPIO_INT_RISING         0x00010000UL /*!< Interrupt enable by Input Rising Edge */
#define GPIO_INT_FALLING        0x00000001UL /*!< Interrupt enable by Input Falling Edge */
#define GPIO_INT_BOTH_EDGE      0x00010001UL /*!< Interrupt enable by both Rising Edge and Falling Edge */
#define GPIO_INT_HIGH           0x01010000UL /*!< Interrupt enable by Level-High */
#define GPIO_INT_LOW            0x01000001UL /*!< Interrupt enable by Level-Level */

一共5个中断类型。
void GPIO_EnableInt(GPIO_T *port, uint32_t u32Pin, uint32_t u32IntAttribs);
void GPIO_DisableInt(GPIO_T *port, uint32_t u32Pin);
两个函数用于开启或关闭,开启时候有开启的类型,就是上面五种,
我们看例子。

    /* Configure P3.2 as EINT0 pin and enable interrupt by falling edge trigger */
    GPIO_SetMode(P3, BIT2, GPIO_PMD_INPUT);
    GPIO_EnableEINT0(P3, 2, GPIO_INT_FALLING);
    NVIC_EnableIRQ(EINT0_IRQn);

    /* Configure P3.3 as EINT1 pin and enable interrupt by rising and falling edge trigger */
    GPIO_SetMode(P3, BIT3, GPIO_PMD_INPUT);
    GPIO_EnableEINT1(P3, 3, GPIO_INT_BOTH_EDGE);
    NVIC_EnableIRQ(EINT1_IRQn);

我们看到想用中断,要先配置为输入模式。
之后使能中断,然后通过NVIC中断嵌套控制器,使能中断。
为何前面写的使能中断的名称不一样的,其实内涵是一样的,
#define GPIO_EnableEINT1    GPIO_EnableInt
看到没,都是通过宏定义到了一个函数里。
那么NVIC的控制使能呢?
    NVIC_EnableIRQ(EINT0_IRQn);
   NVIC_EnableIRQ(EINT1_IRQn);
关于这两个函数,可以在
core_cm0.h找到,如下所示
/** \brief  Enable External Interrupt

    The function enables a device-specific interrupt in the NVIC interrupt controller.

    \param [in]      IRQn  External interrupt number. Value cannot be negative.
*/
__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn)
{
  NVIC->ISER[0] = (1 << ((uint32_t)(IRQn) & 0x1F));
}

——————————————————
由此可知,这个IRQn_Type IRQn参数,其实就是个中断类型。
我们看手册可知

大家知道那个名字怎么来的了吧。
定义在文件M051Series.h
typedef enum IRQn
{
    /******  Cortex-M0 Processor Exceptions Numbers ***************************************************/
    NonMaskableInt_IRQn       = -14,      /*!< 2 Non Maskable Interrupt                             */
    HardFault_IRQn            = -13,      /*!< 3 Cortex-M0 Hard Fault Interrupt                     */
    SVCall_IRQn               = -5,       /*!< 11 Cortex-M0 SV Call Interrupt                       */
    PendSV_IRQn               = -2,       /*!< 14 Cortex-M0 Pend SV Interrupt                       */
    SysTick_IRQn              = -1,       /*!< 15 Cortex-M0 System Tick Interrupt                   */

    /******  ARMIKMCU Swift specific Interrupt Numbers ************************************************/
    BOD_IRQn                  = 0,        /*!< Brown-Out Low Voltage Detected Interrupt             */
    WDT_IRQn                  = 1,        /*!< Watch Dog Timer Interrupt                            */
    EINT0_IRQn                = 2,        /*!< EINT0 Interrupt                                      */
    EINT1_IRQn                = 3,        /*!< EINT1 Interrupt                                      */
    GPIO_P0P1_IRQn            = 4,        /*!< GPIO_P0P1 Interrupt                                  */
    GPIO_P2P3P4_IRQn          = 5,        /*!< GPIO_P2P3P4 Interrupt                                */
    PWMA_IRQn                 = 6,        /*!< PWMA Interrupt                                       */
    PWMB_IRQn                 = 7,        /*!< PWMB Interrupt                                       */
    TMR0_IRQn                 = 8,        /*!< TIMER0 Interrupt                                     */
    TMR1_IRQn                 = 9,        /*!< TIMER1 Interrupt                                     */
    TMR2_IRQn                 = 10,       /*!< TIMER2 Interrupt                                     */
    TMR3_IRQn                 = 11,       /*!< TIMER3 Interrupt                                     */
    UART0_IRQn                = 12,       /*!< UART0 Interrupt                                      */
    UART1_IRQn                = 13,       /*!< UART1 Interrupt                                      */
    SPI0_IRQn                 = 14,       /*!< SPI0 Interrupt                                       */
    SPI1_IRQn                 = 15,       /*!< SPI1 Interrupt                                       */
    I2C0_IRQn                 = 18,       /*!< I2C0 Interrupt                                       */
    I2C1_IRQn                 = 19,       /*!< I2C1 Interrupt                                       */
    ACMP01_IRQn               = 25,       /*!< ACMP0/1 Interrupt                                    */
    ACMP23_IRQn               = 26,       /*!< ACMP2/3 Interrupt                                    */
    PWRWU_IRQn                = 28,       /*!< Power Down Wake Up Interrupt                         */
    ADC_IRQn                  = 29        /*!< ADC Interrupt                                        */

} IRQn_Type;



使用特权

评论回复
板凳
捉虫天师|  楼主 | 2016-7-4 21:35 | 只看该作者
/* Enable interrupt de-bounce function and select de-bounce sampling cycle time is 1024 * 10 KHz clock */
    GPIO_SET_DEBOUNCE_TIME(GPIO_DBCLKSRC_LIRC, GPIO_DBCLKSEL_1024);
    GPIO_ENABLE_DEBOUNCE(P3, BIT2 | BIT3);
最后呢,还可以防止中断时候抖动,就启动这个函数。
我们看到,第一个是设置抖动时间,我们可以看到设置的是计时用的时钟源和抖动的时间限制。
最后就是使能需要防止抖动的GPIO端口了。

使用特权

评论回复
地板
捉虫天师|  楼主 | 2016-7-4 21:36 | 只看该作者
/*---------------------------------------------------------------------------------------------------------*/
/*  DBNCECON Constant Definitions                                                                          */
/*---------------------------------------------------------------------------------------------------------*/
#define GPIO_INT_CLK_ON         0x00000020UL /*!< DBNCECON setting for all IO pins edge detection circuit is always active after reset */
#define GPIO_INT_CLK_OFF        0x00000000UL /*!< DBNCECON setting for edge detection circuit is active only if IO pin corresponding GPIOx_IEN bit is set to 1 */

#define GPIO_DBCLKSRC_LIRC      0x00000010UL /*!< DBNCECON setting for de-bounce counter clock source is the internal 10 kHz */
#define GPIO_DBCLKSRC_HCLK      0x00000000UL /*!< DBNCECON setting for de-bounce counter clock source is the HCLK */

#define GPIO_DBCLKSEL_1         0x00000000UL /*!< DBNCECON setting for sampling cycle = 1 clocks */
#define GPIO_DBCLKSEL_2         0x00000001UL /*!< DBNCECON setting for sampling cycle = 2 clocks */
#define GPIO_DBCLKSEL_4         0x00000002UL /*!< DBNCECON setting for sampling cycle = 4 clocks */
#define GPIO_DBCLKSEL_8         0x00000003UL /*!< DBNCECON setting for sampling cycle = 8 clocks */
#define GPIO_DBCLKSEL_16        0x00000004UL /*!< DBNCECON setting for sampling cycle = 16 clocks */
#define GPIO_DBCLKSEL_32        0x00000005UL /*!< DBNCECON setting for sampling cycle = 32 clocks */
#define GPIO_DBCLKSEL_64        0x00000006UL /*!< DBNCECON setting for sampling cycle = 64 clocks */
#define GPIO_DBCLKSEL_128       0x00000007UL /*!< DBNCECON setting for sampling cycle = 128 clocks */
#define GPIO_DBCLKSEL_256       0x00000008UL /*!< DBNCECON setting for sampling cycle = 256 clocks */
#define GPIO_DBCLKSEL_512       0x00000009UL /*!< DBNCECON setting for sampling cycle = 512 clocks */
#define GPIO_DBCLKSEL_1024      0x0000000AUL /*!< DBNCECON setting for sampling cycle = 1024 clocks */
#define GPIO_DBCLKSEL_2048      0x0000000BUL /*!< DBNCECON setting for sampling cycle = 2048 clocks */
#define GPIO_DBCLKSEL_4096      0x0000000CUL /*!< DBNCECON setting for sampling cycle = 4096 clocks */
#define GPIO_DBCLKSEL_8192      0x0000000DUL /*!< DBNCECON setting for sampling cycle = 8192 clocks */
#define GPIO_DBCLKSEL_16384     0x0000000EUL /*!< DBNCECON setting for sampling cycle = 16384 clocks */
#define GPIO_DBCLKSEL_32768     0x0000000FUL /*!< DBNCECON setting for sampling cycle = 32768 clocks */


使用特权

评论回复
5
捉虫天师|  楼主 | 2016-7-4 22:06 | 只看该作者
那么怎么响应中断呢,其实通过NVIC,已经告诉系统的中断程序跳转到指定的入口了,
/**
* @brief       External INT0 IRQ
*
* @param       None
*
* @return      None
*
* @Details     The External INT0(P3.2) default IRQ, declared in startup_M051Series.s.
*/
void EINT0_IRQHandler(void)
{
    /* For P3.2, clear the INT flag */
    GPIO_CLR_INT_FLAG(P3, BIT2);

    printf("P3.2 EINT0 occurred.\n");
}

/**
* @brief       External INT1 IRQ
*
* @param       None
*
* @return      None
*
* @details     The External INT1(P3.3) default IRQ, declared in startup_M051Series.s.
*/
void EINT1_IRQHandler(void)
{
    /* For P3.3, clear the INT flag */
    GPIO_CLR_INT_FLAG(P3, BIT3);

    printf("P3.3 EINT1 occurred.\n");
}


这个函数名绝非随便命名的,都是在系统里定义好的,

使用特权

评论回复
6
捉虫天师|  楼主 | 2016-7-4 22:07 | 只看该作者
我们可以看到,第一件事就是清理中断标志。然后做想做的事情。

使用特权

评论回复
7
捉虫天师|  楼主 | 2016-7-4 22:11 | 只看该作者
我们就来看看普通的IO中断怎么做的
   /* Configure P1.3 as Input mode and enable interrupt by rising edge trigger */
    GPIO_SetMode(P1, BIT3, GPIO_PMD_INPUT);
    GPIO_EnableInt(P1, 3, GPIO_INT_RISING);
    NVIC_EnableIRQ(GPIO_P0P1_IRQn);

    /*  Configure P4.5 as Quasi-bidirection mode and enable interrupt by falling edge trigger */
    GPIO_SetMode(P4, BIT5, GPIO_PMD_QUASI);
    GPIO_EnableInt(P4, 5, GPIO_INT_FALLING);
    NVIC_EnableIRQ(GPIO_P2P3P4_IRQn);
这个是P1.3he P4.5
我们看到,第一步设置为输入模式,只会设置中断类型,选择什么类型触发中断,只会就是通过NVIC使能对应的中断名。
我们看到这里P1,用的名字是P0P1_IRQn
而P4.5用的是P2P3P4_IRQn
我们可以看到,他们可能是共用的入口吧。

20 0x50 4 GP01_INT GPIO P0[7:0] / P1[7:0] 外部信号中断 Yes

21 0x54 5 GP234_INT GPIO P2[7:0]/P3[7:0]/P4[7:0] 外部信号中断 ,除 P32 和 P33 Yes

果然是这样的。

使用特权

评论回复
8
捉虫天师|  楼主 | 2016-7-4 22:13 | 只看该作者
响应中断的方式呢,怎么识别具体是哪个端口呢?
当然在中断函数里面检查了
/**
* @brief       Port0/Port1 IRQ
*
* @param       None
*
* @return      None
*
* @details     The Port0/Port1 default IRQ, declared in startup_M051Series.s.
*/
void GPIOP0P1_IRQHandler(void)
{
    /* To check if P1.3 interrupt occurred */
    if(GPIO_GET_INT_FLAG(P1, BIT3))
    {
        GPIO_CLR_INT_FLAG(P1, BIT3);
        printf("P1.3 INT occurred.\n");
    }
    else
    {
        /* Un-expected interrupt. Just clear all PORT0, PORT1 interrupts */
        P0->ISRC = P0->ISRC;
        P1->ISRC = P1->ISRC;
        printf("Un-expected interrupts.\n");
    }
}

/**
* @brief       Port2/Port3/Port4 IRQ
*
* @param       None
*
* @return      None
*
* @details     The Port2/Port3/Port4 default IRQ, declared in startup_M051Series.s.
*/
void GPIOP2P3P4_IRQHandler(void)
{
    /* To check if P4.5 interrupt occurred */
    if(GPIO_GET_INT_FLAG(P4, BIT5))
    {
        GPIO_CLR_INT_FLAG(P4, BIT5);
        printf("P4.5 INT occurred.\n");
    }
    else
    {
        /* Un-expected interrupt. Just clear all PORT2, PORT3 and PORT4 interrupts */
        P2->ISRC = P2->ISRC;
        P3->ISRC = P3->ISRC;
        P4->ISRC = P4->ISRC;
        printf("Un-expected interrupts.\n");
    }
}


使用特权

评论回复
9
捉虫天师|  楼主 | 2016-7-4 22:22 | 只看该作者
再来说说掉电。
/**************************************************************************//**
* [url=home.php?mod=space&uid=288409]@file[/url]     main.c
* [url=home.php?mod=space&uid=895143]@version[/url]  V3.00
* $Revision: 3 $
* $Date: 14/01/28 11:44a $
* @brief    M051 Series GPIO Driver Sample Code
*
* @note
* Copyright (C) 2013 Nuvoton Technology Corp. All rights reserved.
******************************************************************************/
#include <stdio.h>
#include "M051Series.h"


#define PLL_CLOCK           50000000


/*---------------------------------------------------------------------------------------------------------*/
/*  Function for System Entry to Power Down Mode                                                           */
/*---------------------------------------------------------------------------------------------------------*/
void PowerDownFunction(void)
{
    /* To check if all the debug messages are finished */
    UART_WAIT_TX_EMPTY(UART0);

    SCB->SCR = 4;

    CLK->PWRCON = (CLK->PWRCON & ~(CLK_PWRCON_PWR_DOWN_EN_Msk | CLK_PWRCON_PD_WAIT_CPU_Msk)) |
                  CLK_PWRCON_PD_WAIT_CPU_Msk | CLK_PWRCON_PD_WU_INT_EN_Msk;
    CLK->PWRCON |= CLK_PWRCON_PWR_DOWN_EN_Msk;

    __WFI();
}

/**
* @brief       Port0/Port1 IRQ
*
* @param       None
*
* @return      None
*
* @details     The Port0/Port1 default IRQ, declared in startup_M051Series.s.
*/
void GPIOP0P1_IRQHandler(void)
{
    /* To check if P1.3 interrupt occurred */
    if(GPIO_GET_INT_FLAG(P1, BIT3))
    {
        GPIO_CLR_INT_FLAG(P1, BIT3);
        printf("P1.3 INT occurred.\n");
    }
    else
    {
        /* Un-expected interrupt. Just clear all PORT0, PORT1 interrupts */
        P0->ISRC = P0->ISRC;
        P1->ISRC = P1->ISRC;
        printf("Un-expected interrupts.\n");
    }
}

void SYS_Init(void)
{
    /*---------------------------------------------------------------------------------------------------------*/
    /* Init System Clock                                                                                       */
    /*---------------------------------------------------------------------------------------------------------*/
    /* Enable Internal RC 22.1184MHz clock */
    CLK_EnableXtalRC(CLK_PWRCON_OSC22M_EN_Msk);

    /* Waiting for Internal RC clock ready */
    CLK_WaitClockReady(CLK_CLKSTATUS_OSC22M_STB_Msk);

    /* Switch HCLK clock source to Internal RC and HCLK source divide 1 */
    CLK_SetHCLK(CLK_CLKSEL0_HCLK_S_HIRC, CLK_CLKDIV_HCLK(1));

    /* Enable external XTAL 12MHz clock */
    CLK_EnableXtalRC(CLK_PWRCON_XTL12M_EN_Msk);

    /* Waiting for external XTAL clock ready */
    CLK_WaitClockReady(CLK_CLKSTATUS_XTL12M_STB_Msk);

    /* Set core clock as PLL_CLOCK from PLL */
    CLK_SetCoreClock(PLL_CLOCK);

    /* Enable UART module clock */
    CLK_EnableModuleClock(UART0_MODULE);

    /* Select UART module clock source */
    CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UART_S_PLL, CLK_CLKDIV_UART(1));

    /*---------------------------------------------------------------------------------------------------------*/
    /* Init I/O Multi-function                                                                                 */
    /*---------------------------------------------------------------------------------------------------------*/

    /* Set P3 multi-function pins for UART0 RXD and TXD */
    SYS->P3_MFP &= ~(SYS_MFP_P30_Msk | SYS_MFP_P31_Msk);
    SYS->P3_MFP |= (SYS_MFP_P30_RXD0 | SYS_MFP_P31_TXD0);

}

void UART0_Init(void)
{
    /*---------------------------------------------------------------------------------------------------------*/
    /* Init UART                                                                                               */
    /*---------------------------------------------------------------------------------------------------------*/
    /* Reset UART */
    SYS_ResetModule(UART0_RST);

    /* Configure UART0 and set UART0 Baudrate */
    UART_Open(UART0, 115200);
}

/*---------------------------------------------------------------------------------------------------------*/
/* MAIN function                                                                                           */
/*---------------------------------------------------------------------------------------------------------*/
int main(void)
{
    /* Unlock protected registers */
    SYS_UnlockReg();

    /* Init System, peripheral clock and multi-function I/O */
    SYS_Init();

    /* Init UART0 for printf */
    UART0_Init();

    printf("\n\nCPU [url=home.php?mod=space&uid=72445]@[/url] %d Hz\n", SystemCoreClock);
    printf("+-------------------------------------------------------+\n");
    printf("|    GPIO Power-Down and Wake-up by P1.3 Sample Code    |\n");
    printf("+-------------------------------------------------------+\n\n");

    /* Configure P1.3 as Input mode and enable interrupt by rising edge trigger */
    GPIO_SetMode(P1, BIT3, GPIO_PMD_INPUT);
    GPIO_EnableInt(P1, 3, GPIO_INT_RISING);
    NVIC_EnableIRQ(GPIO_P0P1_IRQn);

    /* Waiting for P1.3 rising-edge interrupt event */
    while(1)
    {
        printf("Enter to Power-Down ......\n");
        PowerDownFunction();
        UART_WAIT_TX_EMPTY(UART0);
        printf("System waken-up done.\n\n");
    }

}

/*** (C) COPYRIGHT 2013 Nuvoton Technology Corp. ***/


使用特权

评论回复
10
捉虫天师|  楼主 | 2016-7-4 22:24 | 只看该作者
/*---------------------------------------------------------------------------------------------------------*/
/*  Function for System Entry to Power Down Mode                                                           */
/*---------------------------------------------------------------------------------------------------------*/
void PowerDownFunction(void)
{
    /* To check if all the debug messages are finished */
    UART_WAIT_TX_EMPTY(UART0);

    SCB->SCR = 4;

    CLK->PWRCON = (CLK->PWRCON & ~(CLK_PWRCON_PWR_DOWN_EN_Msk | CLK_PWRCON_PD_WAIT_CPU_Msk)) |
                  CLK_PWRCON_PD_WAIT_CPU_Msk | CLK_PWRCON_PD_WU_INT_EN_Msk;
    CLK->PWRCON |= CLK_PWRCON_PWR_DOWN_EN_Msk;

    __WFI();
}
主要是这个函数实现了掉电,就是断电吧,可以唤醒的。

使用特权

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

本版积分规则

193

主题

3064

帖子

7

粉丝