# 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
```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++)
            ; // 延时
    }
}

```

## 使用到的库函数  
库函数部分代码
```c
#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));
    }
}
```
