3. 外部中断控制LED

外部中断控制LED 学习卡片

任务目标

使用沁恒CH32V307的PA0引脚作为外部中断输入,连接按键。当按键按下时触发EXTI0外部中断,在中断服务函数中翻转PB0引脚连接的LED状态。通过本案例掌握RISC-V内核下NVIC中断控制器配置、EXTI外部中断初始化及中断服务函数编写方法。

硬件连接

  • PA0 → 按键(按下接地,默认上拉)
  • PB0 → LED(阳极接PB0,阴极经限流电阻接地)

核心代码


#include "debug.h"

/* 全局标志,用于主循环判断 */
volatile uint8_t led_state = 0;

/* GPIO初始化:PA0输入上拉,PB0推挽输出 */
void GPIO_Init_Exti(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);

    /* PA0 上拉输入 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* PB0 推挽输出 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_WriteBit(GPIOB, GPIO_Pin_0, Bit_SET);  /* 初始LED灭 */
}

/* EXTI0中断服务函数 */
void EXTI0_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line0) != RESET)
    {
        /* 翻转LED状态 */
        led_state ^= 1;
        if(led_state)
            GPIO_WriteBit(GPIOB, GPIO_Pin_0, Bit_RESET);  /* LED亮 */
        else
            GPIO_WriteBit(GPIOB, GPIO_Pin_0, Bit_SET);    /* LED灭 */

        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

/* EXTI0初始化:PA0下降沿触发 */
void EXTI0_Init(void)
{
    EXTI_InitTypeDef EXTI_InitStructure = {0};
    NVIC_InitTypeDef NVIC_InitStructure = {0};

    /* 将PA0连接到EXTI0 */
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);

    EXTI_InitStructure.EXTI_Line = EXTI_Line0;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;  /* 按键按下时下降沿 */
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
    NVIC_InitStructure.NVIC_IRQPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

int main(void)
{
    Delay_Init();
    USART_Printf_Init(115200);
    printf("SystemClk:%d\r\n", SystemCoreClock);

    GPIO_Init_Exti();
    EXTI0_Init();

    printf("EXTI0 Test: Press PA0 button to toggle LED.\r\n");

    while(1)
    {
        Delay_Ms(100);  /* 主循环空转,等待中断 */
    }
}

编译与下载

  1. 使用MounRiver Studio新建CH32V307工程,将上述代码替换main.c内容。
  2. 编译工程,通过WCH-Link下载到开发板。
  3. 开发板供电后,打开串口调试助手(115200-8-N-1),应看到系统时钟打印及提示信息。

验证方法

  • 按下PA0按键,每次按下LED状态应翻转一次(亮→灭→亮)。
  • 串口无额外中断打印(本案例中断中未加打印,避免影响实时性)。
  • 若LED不翻转,检查PA0是否上拉、按键是否接GND,以及PB0限流电阻是否合适。

关键说明

  • CH32V307的EXTI0中断服务函数名为EXTI0_IRQHandler,由启动文件中的中断向量表决定,不可改名。
  • GPIO_EXTILineConfig用于将指定GPIO引脚映射到EXTI线,PA0对应EXTI_Line0。
  • NVIC配置中优先级分组默认在system_ch32v30x.c中设为2位抢占+2位子优先级,此处抢占优先级1、子优先级1为合法组合。
  • 中断服务函数中必须清除中断挂起位,否则会反复进入中断。