# 08.02_EXTI-上升沿触发

## 测试步骤

使用P0.15作为外部中断引脚，上升沿&下降沿均触发中断。 外部输入高低电平（默认低电平），记录中断发生次数。

## 实测数据

t | P0.15Val | count
--- | --- | ---
0 | 0 | 0
1 | 1 | 1
2 | 0 | 1
3 | 1 | 2
4 | 0 | 2
5 | 1 | 3
6 | 0 | 3
7 | 1 | 4
8 | 0 | 4
9 | 1 | 5
10 | 0 | 5
11 | 1 | 6
12 | 0 | 6
13 | 1 | 7
14 | 0 | 7
15 | 1 | 8
16 | 0 | 8
17 | 1 | 9
18 | 0 | 9
19 | 1 | 10

测试结果：通过

## 参考代码

08.02_EXTI_Toggle.c

    /**
     * @brief EXTI-上升沿触发
     * @details
     * 使用P0.15作为外部中断引脚，上升沿&下降沿均触发中断。
     * 外部输入高低电平（默认低电平），记录中断发生次数。
     */
    #include "lks32mc09x_exti.h"
    #include "lks32mc09x_gpio.h"
    #include "lks32mc09x_sys.h"
    volatile uint32_t count = 0;
    // EXTI中断处理函数
    void GPIO_IRQHandler(void)
    {
        // 检查P0.15引脚的中断标志
        if (EXTI_GetIRQFlag(EXTI0, EXTI_IF_P0_15))
        {
            // 清除中断标志
            EXTI_ClearIRQFlag(EXTI0, EXTI_IF_P0_15);
            count++;
        }
    }
    /**
     * @brief 主函数
     */
    int main(void)
    {
        GPIO_InitTypeDef GPIO_InitStruct;
        GPIO_StructInit(&GPIO_InitStruct);

        // 使能GPIO0时钟
        SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);

        // 初始化P0.15为输入，并配置为外部中断引脚
        GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
        GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
        GPIO_Init(GPIO0, &GPIO_InitStruct);

        // 配置P0.15的上升沿、下降沿均触发外部中断
        EXTI_TriggerConfig(EXTI0, EXTI_CR_P0_15, EXTI_Trigger_Posedge);
        EXTI_EnableIRQFlag(EXTI0, EXTI_IF_P0_15);

        // 使能EXTI0中断
        NVIC_EnableIRQ(GPIO_IRQn);
        __enable_irq();
        while (1)
        {
            // P0.7不停翻转电平，提供中断信号
            GPIO_ToggleBits(GPIO0, GPIO_Pin_7);
            for (volatile uint32_t i = 0; i < 100; i++)
                ; // 延时
        }
    }

## 使用到的库函数

库函数部分代码

    #define REG_RESET(reg,mask) reg &= ~(mask)
    #define REG_SET(reg,mask) reg |= (mask)
    #define REG_WRITE(reg,mask) reg = (mask)
    #define SYS0 ((SYS_TypeDef *)(SYS_BASE))
    #define SYS_MODULE_GPIO BIT11
    /**
     * @brief GPIO功能配置结构体句柄
     */
    typedef struct GPIO_InitTypeDef {
        uint32_t GPIO_Pin;
        GPIO_Mode_TypeDef GPIO_Mode;
        GPIO_PuPd_TypeDef GPIO_PuPd;
        uint32_t GPIO_PODEna;
        uint32_t GPIO_PFLT;
    };
    /**
     * EXTI触发信号定义
     */
    typedef enum EXTI_Mode_TypeDef {
        EXTI_Trigger_None = 0,
        EXTI_Trigger_Negedge = 1,
        EXTI_Trigger_Posedge = 2,
        EXTI_Trigger_Edge = 3,
    }} {enum_name};
    /**
     * @brief 使能外部中断标志
     *
     * @param EXTIx EXTI模块指针
     * @param EXTI_IF_x 外部中断源
     */
    void EXTI_EnableIRQFlag(EXTI_TypeDef *EXTIx, uint32_t EXTI_IF_x)
    {
        exti_ie_flg |= EXTI_IF_x;
        if (exti_ie_flg & 0x7f)
        {
            EXTIx->IE |= BIT0;
        }
        else
        {
            EXTIx->IE &= ~BIT0;
        }
        if (exti_ie_flg & 0x80)
        {
            EXTIx->IE |= BIT1;
        }
        else
        {
            EXTIx->IE &= ~BIT1;
        }
        if (exti_ie_flg & 0xff00)
        {
            EXTIx->IE |= BIT2;
        }
        else
        {
            EXTIx->IE &= ~BIT2;
        }
    }
    /**
     * @brief 初始化GPIO
     * @param GPIOx GPIO模块指针
     * @param GPIO_InitStruct 指向包含初始化参数的GPIO_InitTypeDef结构体
     */
    void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
    {
        // 使能GPIO时钟
        SYS_ModuleClockCmd(SYS_MODULE_GPIO, ENABLE);

        // 配置引脚模式
        if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IN)
        {
            GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin;  // 打开输入使能
            GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
            GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
        }
        else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_OUT)
        {
            GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
            GPIOx->POE |= GPIO_InitStruct->GPIO_Pin;  // 使能输出
        }
        else if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IO)
        {
            GPIOx->PIE |= GPIO_InitStruct->GPIO_Pin; // 使能输入
            GPIOx->POE |= GPIO_InitStruct->GPIO_Pin; // 使能输出
        }
        else // GPIO_Mode_ANA
        {
            GPIOx->PIE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输入使能
            GPIOx->POE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭输出使能
            GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉使能
        }

        if (GPIO_InitStruct->GPIO_PuPd == GPIO_PuPd_UP)
        {
            GPIOx->PUE |= GPIO_InitStruct->GPIO_Pin; // 使能上拉
        }
        else
        {
            GPIOx->PUE &= ~GPIO_InitStruct->GPIO_Pin; // 关闭上拉
        }
        // 配置开漏使能
        if (GPIO_InitStruct->GPIO_PODEna)
        {
            GPIOx->PODE |= GPIO_InitStruct->GPIO_Pin;
        }
        else
        {
            GPIOx->PODE &= ~GPIO_InitStruct->GPIO_Pin;
        }

        // 配置滤波使能
        if (GPIO_InitStruct->GPIO_PFLT)
        {
            GPIOx->PFLT |= GPIO_InitStruct->GPIO_Pin;
        }
        else
        {
            GPIOx->PFLT &= ~GPIO_InitStruct->GPIO_Pin;
        }
    }
    /**
     * @brief 初始化GPIO结构体为默认值
     * @param GPIO_InitStruct 指向要初始化的GPIO_InitTypeDef结构体
     */
    void GPIO_StructInit(GPIO_InitTypeDef *GPIO_InitStruct)
    {
        for (int i = 0; i < sizeof(GPIO_InitTypeDef); i++)
        {
            ((uint8_t *)GPIO_InitStruct)[i] = 0;
        }
    }
    /**
     * @brief 数字模块时钟使能
     * @param nModule 模块编号
     * @param state 使能或禁用状态
     */
    void SYS_ModuleClockCmd(uint32_t nModule, uint8_t state)
    {
        REG_WRITE(SYS0->PROTECT, 0x7a83);
        if (state)
        {
            REG_SET(SYS0->CLK_FEN, nModule);
        }
        else
        {
            REG_RESET(SYS0->CLK_FEN, nModule);
        }
        REG_WRITE(SYS0->PROTECT, 0);
    }
    /**
     * @brief 清除GPIO引脚的中断标志
     *
     * @param GPIOx GPIO模块指针
     * @param GPIO_PinSource GPIO引脚源
     */
    void EXTI_ClearIRQFlag(EXTI_TypeDef *EXTIx, uint32_t EXTI_IF_x)
    {
        // 清除对应GPIO引脚的中断标志
        EXTIx->IF = EXTI_IF_x;
    }
    /**
     * @brief 翻转指定的GPIO引脚位
     * @param GPIOx GPIO模块指针
     * @param GPIO_Pin 要翻转的引脚
     */
    void GPIO_ToggleBits(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
    {
        GPIOx->PDO = GPIOx->PDO ^ GPIO_Pin;
    }
    /**
     * @brief 获取GPIO引脚的中断标志状态
     *
     * @param GPIOx GPIO模块指针
     * @param GPIO_PinSource GPIO引脚源
     * @return 中断标志状态
     * @retval 0 无中断
     * @retval 1 有中断
     */
    uint8_t EXTI_GetIRQFlag(EXTI_TypeDef *EXTIx, uint32_t EXTI_IF_x)
    {
        // 获取对应GPIO引脚的中断标志状态
        return EXTIx->IF & EXTI_IF_x;
    }
    /**
     * @brief 配置GPIO引脚的中断触发模式
     *
     * @param GPIOx GPIO模块指针
     * @param GPIO_PinSource GPIO引脚源
     * @param EXTI_Trigger 触发模式
     */
    void EXTI_TriggerConfig(EXTI_TypeDef *EXTIx, uint8_t EXTI_CR_x, EXTI_Mode_TypeDef EXTI_Trigger)
    {
        // 根据GPIO引脚源和触发模式配置EXTI触发寄存器
        if (EXTI_CR_x < 8) // cr0
        {
            EXTIx->CR0 &= ~(0x03 << (EXTI_CR_x * 2));
            EXTIx->CR0 |= (EXTI_Trigger << (EXTI_CR_x * 2));
        }
        else // cr1
        {
            EXTIx->CR1 &= ~(0x03 << ((EXTI_CR_x - 8) * 2));
            EXTIx->CR1 |= (EXTI_Trigger << ((EXTI_CR_x - 8) * 2));
        }
    }