🛠 Interruption EXTI + anti-rebond par timer

GD32F103 (GigaDevice) — chip + Blue-Pill-compatible boards · avance · Schéma : IMG-gd32f103-avance-1-exti-timer-debounce.svg — chronogramme EXTI sur PA0, timer 1 kHz, machine à états anti-rebond sans temporisation bloquante, LED PC13 commandée en SPL

Objectives

Replace blocking debounce with a true event-driven design: a press on PA0 triggers an external interrupt (EXTI0), a general-purpose 16-bit timer running at 1 kHz confirms the press 20 ms later, and the core spends its time in WFI (wait for interrupt). You will use the official SPL (GD32F10x Firmware Library V2.2.x) for: RCU, GPIO, AFIO/exti_source, EXTI, TIMER, and NVIC. This is the foundation of any responsive firmware on Cortex-M3.

Hardware

Complete Code

/* exti_debounce.c — GD32F103C8, SPL V2.2.x. 108 MHz (SystemInit du pack). */
#include "gd32f10x.h"

static volatile uint32_t ticks_ms = 0;
static volatile uint8_t  flag_appui = 0;
static bool led_on = false;

void EXTI0_1_IRQHandler(void)
{
    if (exti_interrupt_flag_get(EXTI_0) == SET) {
        exti_interrupt_flag_clear(EXTI_0);   /* sinon re-entrée infinie */
        flag_appui = 1;                      /* signaler, ne pas traiter */
    }
}

void TIMER2_IRQHandler(void)
{
    if (timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_UP) == SET) {
        timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_UP);
        ticks_ms++;
        if (flag_appui) {
            flag_appui = 0;
            /* confirmation 20 ms apres l'event : le bouton est encore appuye */
            if (ticks_ms >= 20 && gpio_input_bit_get(GPIOA, GPIO_PIN_0) == SET) {
                led_on = !led_on;
                gpio_bit_write(GPIOC, GPIO_PIN_13, led_on ? RESET : SET);
                ticks_ms = 0;
            }
        }
    }
}

int main(void)
{
    timer_parameter_struct tp;

    rcu_periph_clock_enable(RCU_GPIOA);
    rcu_periph_clock_enable(RCU_GPIOC);
    rcu_periph_clock_enable(RCU_AF);        /* AFIO : selection source EXTI */
    rcu_periph_clock_enable(RCU_TIMER2);

    gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13);
    gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_0);

    /* TIMER2 : 108 MHz / (107+1) / (999+1) = 1 kHz */
    timer_deinit(TIMER2);
    tp.prescaler         = 107;
    tp.alignedmode       = TIMER_COUNTER_EDGE;
    tp.counterdirection  = TIMER_COUNTER_UP;
    tp.period            = 999;
    tp.clockdivision     = TIMER_CKDIV_DIV1;
    tp.repetitioncounter = 0;
    timer_init(TIMER2, &tp);
    timer_interrupt_enable(TIMER2, TIMER_INT_UP);
    timer_enable(TIMER2);

    /* EXTI0 sur PA0, fronts montants et descendants */
    gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_0);
    exti_init(EXTI_0, EXTI_INTERRUPT, EXTI_TRIG_BOTH);
    exti_interrupt_flag_clear(EXTI_0);

    nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
    nvic_irq_enable(TIMER2_IRQn, 1, 0);
    nvic_irq_enable(EXTI0_1_IRQn, 1, 1);

    while (1) {
        __WFI;   /* le coeur dort jusqu'a la prochaine interruption */
    }
}

Expected Verification

Each distinct press toggles the LED, without double-triggering, and the program remains responsive (no delay loops). In the debugger, you will observe that the ticks_ms counter increments by 1 per real millisecond if the 108 MHz PLL is active. Power consumption is lower compared to an active loop thanks to WFI (wake-up in a few microseconds, on the order of 4.5 µs for sleep mode according to the datasheet).

Common Errors

Other tutorials for this board

Code examples for this board

← Back to the board 🛠 All tutorials