🛠 Interruption EXTI + anti-rebond par timer
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
- Carte GD32F103C8T6 / CBT6 compatible Blue Pill, quartz HXTAL 8 MHz soudé (le SystemInit du pack SPL configure le PLL pour 108 MHz depuis 8 MHz)
- Bouton sur PA0 (appui = HIGH, pull-down), LED sur PC13 (active-LOW) — brochage vérifié de la fiche
- Programmeur SWD (ST-Link) + OpenOCD compatible GD32 (tool-openocd-gd32) ou GD-Link
- SPL V2.2.x (miroir CommunityGD32Cores/gigadevice-firmware-and-docs) + outillage ARM GCC ou Keil (avec l'algorithme de flash GD32, pas STM32 !)
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
- Interruption qui redéclenche sans fin : drapeau EXTI ou TIMER non effacé dans le handler ; tout handler doit clear son flag.
- Handler jamais appelé : horloge RCU_AF oubliée (la sélection de source EXTI ne fonctionne pas), ou NVIC non activé, ou groupe de priorité incohérent.
- Timer à 2× ou 13,5× de la bonne vitesse : le timer est cadencé par l'horloge APB ; si vous supposez 72 MHz (réflexe STM32) sur un GD32 à 108 MHz, le « 20 ms » devient 13 ms. Calculez depuis l'horloge réelle.
- Blocage au démarrage sur HSE : projets portés de ST sans le HSE_STARTUP_TIMEOUT = 0xFFFF exigé par AN001.
- Flash qui échoue avec Keil/IAR : remplacer l'algorithme de programmation flash par celui du GD32 (AN001) ; l'algorithme STM32 échoue ou verrouille.
- Priorités EXTI/timer inversées : si EXTI écrase le timer, ticks_ms peut geler pendant les rebonds ; gardez les handlers courts.
Autres tutoriels pour cette carte
Exemples de code pour cette carte
clignotante.inoArduinobouton-led.inoArduinorespiration.inoArduinopotentiometre.inoArduinopont-uart.inoArduinooled-i2c.inoArduino