🛠 Interruption EXTI + anti-rebond par timer

GD32F103 (GigaDevice) — puce + cartes compatibles Blue Pill · 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

Objectifs

Remplacer l'anti-rebond bloquant par une vraie conception événementielle : l'appui sur PA0 déclenche une interruption externe (EXTI0), un timer général 16 bits tournant à 1 kHz confirme l'appui 20 ms plus tard, et le cœur passe son temps en WFI (wait for interrupt). Vous manipulerez en SPL officielle (GD32F10x Firmware Library V2.2.x) : RCU, GPIO, AFIO/exti_source, EXTI, TIMER, NVIC. C'est la base de tout firmware réactif sur Cortex-M3.

Matériel

Code complet

/* 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 après 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'à la prochaine interruption */
    }
}

Vérification attendue

Chaque appui franc bascule la LED, sans double-déclenchement, et le programme reste réactif (aucune boucle delay). En débogueur, on constate que le compteur ticks_ms avance de 1 par milliseconde réel si le PLL 108 MHz est bien actif. Consommation en baisse par rapport à la boucle active grâce au WFI (réveil en quelques microsecondes, ordre de grandeur 4,5 µs pour le mode sleep d'après la datasheet).

Erreurs fréquentes

Autres tutoriels pour cette carte

Exemples de code pour cette carte

← Retour à la fiche 🛠 Tous les tutoriels