SE3Groupe2025-16

De projets-se.plil.fr
Aller à la navigation Aller à la recherche

Programmation des systèmes embarqués

Dans le cadre de l'unité de Programmation des Systèmes Embarqués, nous avons réalisé la conception et le développement d'un programmateur d'Arduino. L'objectif principal de ce projet était de comprendre les mécanismes de bas niveau permettant le transfert de code compilé (binaire) vers un microcontrôleur (principalement les ATMega). Ce dispositif agit comme une interface de communication entre un environnement de développement (PC) et la puce cible.

Carte électronique

Carte réalisée en utilisant le logiciel KiCAD : Fichier:2025-pse-b16-prog.zip.

Schéma électronique de la carte :

Mon schéma électronique

Résultat du routage :

Mon routage

Configuration matérielle

Pins utilisées sur l'ATmega16U2

Pin Fonction Direction Rôle
PB1 SCK Sortie Horloge SPI
PB2 MOSI Sortie Master Out Slave In
PB3 MISO Entrée Master In Slave Out
PB4 LED1 Sortie LED de programmation active
PB5 LED2 Sortie LED témoin
PB6 TARGET_RESET Sortie Reset de la cible

Configuration USB détaillée

Pourquoi une configuration vendor-specific ?

La classe USB standard "AVRISP" n'existe pas officiellement. Les programmateurs AVR d'Atmel utilisent soit :

  • Une classe CDC (qui apparaît comme un port série)
  • Un protocole propriétaire sur des endpoints interrupt

J'ai choisi une approche vendor-specific (classe 0xFF) car :

  1. Pas besoin de driver spécifique sous Linux (libusb suffit)
  2. Contrôle total du protocole
  3. Pas de conflit avec d'autres périphériques série
  4. Temps de latence prévisible avec endpoints interrupt

Descripteur de périphérique

const USB_Descriptor_Device_t PROGMEM DeviceDescriptor = {
    .Header = {.Size = sizeof(USB_Descriptor_Device_t), .Type = DTYPE_Device},
    
    .USBSpecification = VERSION_BCD(1, 1, 0),  // USB 1.1
    .Class = USB_CSCP_NoDeviceClass,           // 0x00 = classe définie par interface
    .SubClass = USB_CSCP_NoDeviceSubclass,     // 0x00
    .Protocol = USB_CSCP_NoDeviceProtocol,     // 0x00
    
    .Endpoint0Size = FIXED_CONTROL_ENDPOINT_SIZE,  // 8 octets
    
    .VendorID = VENDOR_ID,     // 0x16C0 (VID Openmoko, domaine "hobby")
    .ProductID = PRODUCT_ID,   // 0x05DC (dans la plage réservée aux projets perso)
    .ReleaseNumber = RELEASE_NUMBER,  // 1.0.0
    
    .ManufacturerStrIndex = 0x01,  // "TP AVR"
    .ProductStrIndex = 0x02,       // "USB AVR ISP Programmer"
    .SerialNumStrIndex = NO_DESCRIPTOR,  // Pas de numéro de série
    
    .NumberOfConfigurations = FIXED_NUM_CONFIGURATIONS  // 1 seule configuration
};
  • USB_CSCP_NoDeviceClass : la classe est définie au niveau de l'interface, pas du périphérique
  • VERSION_BCD(1, 1, 0) : USB 1.1 (Full-Speed, 12 Mbit/s) - suffisant pour notre besoin
  • Endpoint0Size = 8 : endpoint de contrôle standard pour USB 1.1

Descripteur de configuration

const USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = {
    .Config = {
        .TotalConfigurationSize = sizeof(USB_Descriptor_Configuration_t),
        .TotalInterfaces = 1,                    // Une seule interface
        .ConfigurationNumber = 1,
        .ConfigAttributes = USB_CONFIG_ATTR_RESERVED,  // Pas d'attribut particulier
        .MaxPowerConsumption = USB_CONFIG_POWER_MA(100) // 100 mA maximum
    },
    
    .Generic_Interface = {
        .TotalEndpoints = 2,      // 2 endpoints (IN + OUT)
        .Class = 0xFF,            // Vendor-specific !
        .SubClass = 0x00,
        .Protocol = 0x00
    },
    
    .DataOutEndpoint = {
        .EndpointAddress = GENERIC_OUT_EPADDR,  // 0x02 (OUT endpoint 2)
        .Attributes = (EP_TYPE_INTERRUPT | ...),  // Type Interrupt
        .EndpointSize = GENERIC_EPSIZE,            // 64 octets
        .PollingIntervalMS = 5                    // Interrogation toutes les 5 ms
    },
    
    .DataInEndpoint = {
        .EndpointAddress = GENERIC_IN_EPADDR,   // 0x81 (IN endpoint 1)
        .Attributes = (EP_TYPE_INTERRUPT | ...),
        .EndpointSize = GENERIC_EPSIZE,         // 64 octets
        .PollingIntervalMS = 5
    }
};

Pourquoi le type Interrupt ?

  • Les endpoints Interrupt garantissent une latence maximale (5 ms ici)
  • Les endpoints Bulk auraient été possibles mais avec une latence moins prévisible
  • Le débit n'est pas critique (programmation ISP lente), la fiabilité prime

Les strings USB

// La chaîne langue 0x00 obligatoire
const USB_Descriptor_String_t PROGMEM LanguageString =
    USB_STRING_DESCRIPTOR_ARRAY(LANGUAGE_ID_ENG);  // Anglais (0x0409)

// Fabricant
const USB_Descriptor_String_t PROGMEM ManufacturerString =
    USB_STRING_DESCRIPTOR(L"TP AVR");

// Produit
const USB_Descriptor_String_t PROGMEM ProductString =
    USB_STRING_DESCRIPTOR(L"USB AVR ISP Programmer");

Configuration LUFA (LUFAConfig.h)

#define USB_DEVICE_ONLY           // Mode périphérique uniquement (pas hôte)
#define USE_FLASH_DESCRIPTORS     // Descripteurs stockés en flash (PROGMEM)
#define FIXED_CONTROL_ENDPOINT_SIZE 8   // Taille fixe de l'endpoint 0
#define FIXED_NUM_CONFIGURATIONS 1      // Une seule configuration

Explication de USE_FLASH_DESCRIPTORS :

  • Les descripteurs sont marqués PROGMEM pour être stockés dans la flash
  • Sans cette macro, LUFA s'attendrait à ce que les pointeurs pointent vers la RAM
  • Cela économise la RAM précieuse de l'ATmega16U2 (512 octets seulement !)

Gestion des descripteurs dans main.c

void EVENT_USB_Device_ConfigurationChanged(void)
{
    // Configuration des endpoints après que l'hôte ait choisi la configuration
    Endpoint_ConfigureEndpoint(GENERIC_OUT_EPADDR, EP_TYPE_INTERRUPT, GENERIC_EPSIZE, 1);
    Endpoint_ConfigureEndpoint(GENERIC_IN_EPADDR,  EP_TYPE_INTERRUPT, GENERIC_EPSIZE, 1);
}

Cette fonction est appelée automatiquement par LUFA quand l'hôte USB envoie une requête SET_CONFIGURATION. C'est à ce moment que les endpoints deviennent actifs.

Protocole de communication

Structure d'une commande

Toutes les commandes suivent le même format de base sur 64 octets :

Offset Taille Description
0 1 Code commande
1 1 Argument 1 (adresse haute ou donnée)
2 1 Argument 2 (adresse basse ou donnée)
3 1 Longueur des données (facultatif)
4-63 60 Charge utile (données)

Détail des commandes

CMD_PING (0x01)
Test simple de communication. Le programmateur répond avec "OK" dans les octets 1-2.
PC → programmateur: [0x01, 0x00, 0x00, 0x00, ...]
← Programmateur: [0x00, 'O', 'K', 0x00, ...] // STATUS_OK
CMD_ENTER_PROGMODE (0x10)
Active le mode ISP sur la cible :
  1. Configure le SPI en maître (~125 kHz)
  2. Met le RESET cible à 0
  3. Envoie la commande de programmation AVR (0xAC 0x53 0x00 0x00)
  4. Vérifie la réponse (doit être 0x53)
// Dans main.c
static bool ISP_EnterProgramming(void)
{
    ISP_StartSPI();                    // Active SPI
    if (!ISP_EnableProgramming())      // Tente la synchronisation
        return false;
    ISPActive = true;
    LED1_On();                         // Allume la LED de programmation
    return true;
}
CMD_LOAD_FLASH (0x20)
Charge des données dans le buffer interne du programmateur. Les données sont ensuite écrites par CMD_WRITE_PAGE.
Particularité importante : Les données sont chargées par mots de 16 bits (low byte puis high byte) car la flash AVR est organisée en mots.
// Exemple : chargement de 4 octets (2 mots) à l'adresse word 0x1000
// Commande: [0x20, 0x10, 0x00, 0x04, low0, high0, low1, high1, ...]
CMD_WRITE_PAGE (0x21)
Déclenche l'écriture d'une page flash complète (128 octets pour l'ATmega328P) à partir du buffer interne.
La commande 0x4C envoyée à la cible déclenche l'écriture.
// Dans main.c
ISP_Transaction(0x4C, (uint8_t)(page_word_addr >> 8), 
                (uint8_t)page_word_addr, 0x00);
_delay_ms(15);  // Attente fin écriture page
CMD_READ_FLASH (0x22)
Lit une zone de la flash cible.
// La lecture utilise deux commandes différentes selon le poids faible de l'adresse
const uint8_t command = (i & 1) ? 0x28 : 0x20;
// 0x20 = lecture octet bas, 0x28 = lecture octet haut

Structure de la réponse

Le programmateur répond toujours par un paquet de 64 octets :

Offset Description
0 Code statut (0x00 = OK, ≠0 = erreur)
1-3 Données spécifiques à la commande
4-63 Données de lecture (pour CMD_READ_FLASH)

Statuts d'erreur

Code Constante Description
0x00 STATUS_OK Succès
0x01 STATUS_ERROR Erreur générique
0x02 STATUS_BAD_COMMAND Commande inconnue
0x03 STATUS_BAD_LENGTH Longueur de données invalide
0x04 STATUS_ISP_FAILED Échec de la communication ISP

Installation et compilation

Arborescence du projet

Programmateur/
├── Hardware/                           # Conception PCB (KiCad)
│   ├── datasheet/                      # Documentation technique
│   └── programateur-V-PCB/             # Fichiers du circuit imprimé
└── Software/
|   ├── programmateur_final/            # ⭐ FIRMWARE FINAL ⭐
|   │   ├── Makefile                    # Compilation firmware
|   │   ├── main.c                      # Programme principal
|   │   ├── Descriptors.c/h             # Descripteurs USB
|   │   ├── LUFAConfig.h                # Configuration LUFA
|   │   └── host/                       # Programme PC
|   │       ├── Makefile
|   │       └── host.c                  # Code hôte (libusb)
|   ├── test_leds+boutons/              # Firmware de test
|   ├── led/                            # Test LED simple
|   └── bouton/                         # Test bouton simple
|___test_leds+boutons                   # Test des boutons et leds (utilisant la LUFA)

Prérequis

# Compilation AVR
sudo apt install avr-gcc avr-libc make dfu-programmer

# Compilation hôte PC
sudo apt install build-essential libusb-1.0-0-dev

Compilation du firmware (ATmega16U2)

cd Software/programmateur_final
make clean
make

Flash du firmware

Avec dfu-programmer :

Mettre l'ATmega16U2 en mode DFU :

  1. Court-circuiter les pins HWB et GND
  2. Appuyer sur RESET puis relâcher
  3. Relâcher HWB
sudo dfu-programmer atmega16u2 erase
sudo dfu-programmer atmega16u2 flash programmateur_final.hex
sudo dfu-programmer atmega16u2 start

Compilation du programme hôte

cd Software/programmateur_final/host
make

Utilisation

Programmation d'une cible

cd Software/programmateur_final/host
./avr_upload chemin/vers/firmware.hex

Premier système embarqué

Pour notre premier système embarqué notre projet consiste en la conception et la réalisation complète d'un clavier mécanique ergonomique séparé en deux moitiés, développé en collaboration avec le groupe B4[1], notre binôme est chargé de la partie gauche du clavier. Chaque moitié embarque un microcontrôleur, une matrice de 30 touches (5×6), et un rétroéclairage RGB ainsi qu'une gestion autonome de l'alimentation.

Matériel

La documentation complète de tout le matériel utilisé est disponible sur le dépôt GIT du projet [2].

Chargeur Li-ion MAX1811

Le MAX1811 est un chargeur de batterie Li-ion monocellulaire pouvant être alimenté directement depuis un port USB ou une alimentation externe jusqu'à 6,5 V. Il intègre un FET interne permettant de délivrer jusqu'à 500 mA de courant de charge. La précision de régulation de tension de charge est de 0,5 %, ce qui permet une utilisation maximale de la capacité de la batterie. La tension de fin de charge est sélectionnable entre 4,1 V et 4,2 V via la broche SELV, et le courant de charge entre 100 mA et 500 mA via SELI. Une sortie drain ouvert (/CHG) indique l'état de charge, et une fonction de précharge permet de récupérer une batterie profondément déchargée avant d'appliquer le courant nominal.

Connecteur Jack 3 pôles

connecteur audio stéréo TRS (Tip-Ring-Sleeve) au format 3,5 mm, monté en surface (SMT). Il est utilisé ici pour la communication UART entre les deux moitiés du clavier.

Connecteur Jack 4 pôles (TRRS)

variante à 4 contacts (Tip-Ring-Ring-Sleeve) du connecteur jack 3,5 mm. Le contact supplémentaire permet de faire passer un signal numérique additionnel (ex. alimentation ou second canal UART) sur le même câble.

Régulateur à découpage DC/DC — MCP1640

Le MCP1640 est un convertisseur élévateur (boost) synchrone à fréquence fixe, compact et haute efficacité. Il est conçu pour alimenter des systèmes fonctionnant sur batterie monocellulaire ou multicellulaire (alcaline, NiMH, Li-ion ou Li-Po). Sa tension de démarrage est de 0,65 V, et il dispose d'une déconnexion vraie de la sortie à l'arrêt. La fréquence de découpage est de 500 kHz. La tension de sortie est réglable par un pont diviseur externe. Dans ce projet, il élève la tension batterie (3,7 V) à 5 V pour alimenter le MCU, les drivers et les LEDs.

Hot-Swap Kailh (CPG135001S30)

Les sockets hot-swap Kailh CPG135001S30 sont des connecteurs CMS conçus pour les claviers mécaniques à switches low-profile Choc V1. Leur conception compacte est optimisée pour minimiser l'encombrement sous le switch, et leur durée de vie est de 100 cycles de remplacement. Seules les sockets sont soudées sur le PCB : les switches peuvent être insérés et retirés à volonté sans dessoudage, ce qui facilite la réparation et la personnalisation du clavier.

LED RGB

LED RGB CMS au format 1411 (1,4 × 1,1 mm) à anode commune, avec trois cathodes indépendantes (rouge, vert, bleu). Chaque couleur est pilotée individuellement par PWM via les drivers TLC5947. Les luminosités typiques sont 270 mcd (rouge), 950 mcd (vert) et 230 mcd (bleu).

Inductance SRN4018

Le SRN4018 est une inductance de puissance semi-blindée à faible profil (1,8 mm max), couvrant une plage d'inductance de 0,82 à 220 µH et pouvant supporter jusqu'à 4 A de courant nominal. La variante utilisée (SRN4018-4R7M) présente une inductance de 4,7 µH, un courant nominal de 1,9 A, un courant de saturation de 2 A et une résistance DC de 84 mΩ, dans un boîtier SMD 4 × 4 × 1,8 mm. Elle est utilisée comme élément de stockage d'énergie dans le convertisseur boost MCP1640.

Driver de LED TLC5947DAP

Le TLC5947 est un driver 24 canaux à courant constant en mode sink. Chaque canal dispose d'un contrôle de luminosité PWM sur 4096 niveaux (12 bits). Le courant maximal sur tous les canaux est fixé par une résistance externe unique. Les données de niveaux de gris sont transmises via une interface série, et le contrôle PWM est répété automatiquement une fois la trame chargée. La tension d'alimentation est de 3 à 5,5 V, la tension d'anode des LEDs peut atteindre 30 V, et les drivers sont chaînables. Dans ce projet, deux TLC5947 sont montés en cascade pour piloter 48 canaux (16 LEDs RGB × 3 couleurs).

ATmega32U4

L'ATmega32U4 intègre un contrôleur USB 2.0 full-speed (12 Mb/s) avec un buffer DPRAM de 832 octets et jusqu'à 6 endpoints configurables, ce qui en fait un choix idéal pour l'émulation de périphériques HID et de ports série. Il embarque 32 Ko de mémoire Flash, 2,5 Ko de SRAM et 1 Ko d'EEPROM, avec une horloge pouvant atteindre 16 MHz. Grâce à son USB natif, il n'a pas besoin de puce USB-to-Serial additionnelle et peut émuler directement un clavier, une souris ou une manette HID. Il est cadencé ici à 16 MHz par un quartz externe et programmable en ISP ou par bootloader DFU.

Archive GIT

Notre archive GIT pour le projet KiCAD et pour les programmes : [3].


Programmation

Le développement logiciel du système embarqué a été réalisé en langage C pour le microcontrôleur ATmega32U4. Le programme assure la gestion des entrées utilisateur, la communication entre les différents périphériques ainsi que le pilotage du système de rétroéclairage à LEDs.

L'organisation du logiciel repose sur plusieurs modules spécialisés permettant de séparer les différentes fonctions du système :

initialisation du microcontrôleur et des périphériques ;

gestion des entrées utilisateur ;

contrôle des drivers de LEDs TLC5947 ;

communication entre les deux demi-claviers ;

gestion de l'alimentation et des indicateurs lumineux.

Le développement a été effectué de manière progressive en validant chaque sous-ensemble matériel indépendamment avant son intégration dans le système complet.

Une attention particulière a été portée à la mise au point du pilotage des LEDs RGB. Pour cela, un programme de test dédié a été développé afin de vérifier le fonctionnement des drivers TLC5947, le transfert des données PWM et la commande individuelle des différentes sorties.

Le logiciel utilise des structures de données permettant de stocker l'état des différents périphériques ainsi que les valeurs de luminosité des LEDs. Les informations sont ensuite transmises aux composants matériels par l'intermédiaire des interfaces de communication du microcontrôleur.

Cette approche modulaire facilite les opérations de maintenance, de débogage et l'ajout de nouvelles fonctionnalités tout en conservant une architecture logicielle claire et évolutive.

Test TLC5947 — Pilotage de LEDs avec AVR

Présentation

Cette partie permet de tester le pilotage de LEDs à l'aide d'un ou plusieurs drivers TLC5947 connectés à un microcontrôleur AVR.

Le programme initialise les sorties du TLC5947, prépare un buffer PWM pour 2 drivers chaînés puis active plusieurs canaux avec une valeur maximale de 4095.

for (int i = 0; i < (DLED_CHANNELS * NB_DRIVERS); i++)
    pwm[i] = 0;

pwm[0]  = 4095;
pwm[7]  = 4095;
pwm[11] = 4095;

set_LED_Drivers(pwm, NB_DRIVERS);

Configuration matérielle

#define DLED_CHANNELS 24
#define NB_DRIVERS    2

Soit :

  • 24 canaux × 2 drivers = 48 sorties PWM

Broches utilisées

Signal TLC5947 Broche AVR
DATA (SIN) PB5
CLOCK (SCLK) PD0
LATCH PD4
BLANK PD6

Fonctionnement

Le TLC5947 pilote chaque sortie avec une résolution PWM de 12 bits :

PWM État
0 LED éteinte
4095 Luminosité maximale

Pour 2 drivers, le microcontrôleur transmet :

2 × 24 × 12 = 576 bits

à chaque mise à jour.


Correction logicielle

Une anomalie a été identifiée dans le pilote : la broche BLANK était définie mais non configurée dans init_LED_Drivers().

La correction consiste à configurer BLANK comme sortie et à la maintenir à l'état bas :

DDR_ELSE |= (1 << PIN_DLED_CLOCK)
         |  (1 << PIN_DLED_LATCH)
         |  (1 << PIN_DLED_BLANK);

PORT_ELSE &= ~(1 << PIN_DLED_BLANK);

Cette modification améliore la stabilité de l'affichage et évite les extinctions parasites des sorties.


Défauts matériels identifiés

Deux problèmes matériels ont été trouvés sur le second TLC5947.

Court-circuit BLANK / SCLK

Un court-circuit existait entre :

  • BLANK
  • SCLK

La piste BLANK était endommagée.

Correction réalisée

  • dégagement du vernis ;
  • mise à nu du cuivre ;
  • soudure d'un fil de reprise.

Court-circuit VCC / RREF

Un second court-circuit reliait :

  • VCC
  • RREF

Après suppression du défaut, le fonctionnement du courant de référence est redevenu normal.


Reprise des soudures du TLC5947

Les soudures du TLC5947 ont également été reprises.

Cette intervention a amélioré :

  • la qualité des contacts électriques ;
  • la stabilité de la communication série ;
  • le contrôle des LEDs ;
  • la fiabilité générale du système.

Les commandes PWM sont désormais appliquées de manière plus régulière et prévisible.


Cas de la LED 0

Après vérification du code, du câblage et du mapping des sorties, il a été constaté que la LED reliée au canal 0 est endommagée ou grillée.

Le canal reçoit correctement la commande :

pwm[0] = 4095;

mais la LED ne s'allume pas.

Le problème n'est donc pas lié au logiciel ni au TLC5947, mais à la LED elle-même.


Analyse des LEDs rouges dégradées

Les caractéristiques du TLC5947 montrent qu'il fonctionne comme un générateur de courant constant dont le courant est fixé par la résistance RREF.

D'après la documentation fournie :

  • un courant de sortie de 30 mA correspond à une résistance d'environ 2,46 kΩ ;
  • une diminution de RREF augmente le courant dans les LEDs ;
  • un défaut sur la broche RREF peut entraîner un courant excessif.

Or, un court-circuit entre VCC et RREF a été observé sur le second driver.

Cette anomalie a probablement provoqué un courant anormalement élevé dans certaines sorties avant la réparation.

Conséquences possibles

  • échauffement excessif des LEDs rouges ;
  • vieillissement prématuré ;
  • baisse de luminosité ;
  • destruction partielle ou totale de certaines LEDs.

Les LEDs rouges sont généralement plus sensibles aux surintensités car leur tension directe est plus faible (environ 2 V) que celle des LEDs vertes ou bleues.

Le graphique de tension directe montre également qu'une LED rouge atteint son courant nominal pour une tension beaucoup plus faible que les autres couleurs. En cas de défaut de régulation du courant, elle est donc souvent la première à subir une surintensité et à se dégrader.


Cause probable de la faible luminosité observée

La combinaison des facteurs suivants est probablement responsable :

  • court-circuit entre VCC et RREF ;
  • piste BLANK endommagée ;
  • soudures imparfaites sur le TLC5947 ;
  • fonctionnement prolongé dans cet état avant diagnostic.

Ces défauts ont pu provoquer des courants incorrects dans certaines sorties, entraînant :

  • une diminution permanente de la luminosité de plusieurs LEDs rouges ;
  • la destruction de la LED connectée au canal 0 ;
  • un comportement irrégulier du second driver avant réparation.

Vidéos

Test des LEDs : [4]

Test des touches du clavier :

Conclusion

Les principaux problèmes rencontrés étaient principalement matériels :

  • court-circuit entre BLANK et SCLK ;
  • court-circuit entre VCC et RREF ;
  • piste BLANK endommagée ;
  • soudures du TLC5947 à reprendre ;
  • LED du canal 0 défectueuse.

Test Clavier Matriciel HID — ATmega32U4 (LUFA)

Ce projet transforme un clavier matriciel 5×6 en un périphérique USB HID standard. Chaque touche pressée envoie le caractère AZERTY correspondant via USB, comme un clavier ordinaire, sans installation de driver.

Fonctionnalités

  • Matrice 5 lignes × 6 colonnes (30 touches)
  • Disposition AZERTY FR
  • Gestion des modificateurs : Shift, Ctrl, AltGr
  • Shift interne automatique pour les chiffres AZERTY (0 à 9, %, /)
  • Jusqu'à 6 touches simultanées (standard HID)
  • Interface USB HID sans driver (compatible Windows, Linux, macOS)

Note : Le retour visuel par LED est déclaré dans le code mais inactif, car le Makefile utilise BOARD = NONE. Les appels LEDs_SetAllLEDs() compilent correctement mais ne pilotent aucune broche physique. Le clavier fonctionne normalement.


Architecture matérielle

Brochage

Lignes (sorties — actives à l'état bas)

Ligne Broche AVR Registre
L0 PB4 PORTB
L1 PC7 PORTC
L2 PD1 PORTD
L3 PB6 PORTB
L4 PB7 PORTB

PB6 et PB7 sont partagées avec l'interface JTAG (TDI/TDO). Elles ne fonctionnent pas en E/S classiques tant que le JTAG n'est pas désactivé. Voir section "Problèmes rencontrés".

Colonnes (entrées avec pull-up interne)

Colonne Broche AVR Registre de lecture
C0 PF0 PINF
C1 PF1 PINF
C2 PF4 PINF
C3 PF5 PINF
C4 PF6 PINF
C5 PF7 PINF

Configuration dans le code

<void Configuration_Clavier(void) {
    DDRB  |= (1<<PB4)|(1<<PB6)|(1<<PB7);  PORTB |= (1<<PB4)|(1<<PB6)|(1<<PB7);
    DDRC  |= (1<<PC7);                      PORTC |= (1<<PC7);
    DDRD  |= (1<<PD1);                      PORTD |= (1<<PD1);
    DDRF  &= ~((1<<PF0)|(1<<PF1)|(1<<PF4)|(1<<PF5)|(1<<PF6)|(1<<PF7));
    PORTF |=   (1<<PF0)|(1<<PF1)|(1<<PF4)|(1<<PF5)|(1<<PF6)|(1<<PF7);
}

Structure couple_t

La structure couple_t associe un registre AVR à une broche, pour rendre le balayage générique et indépendant du brochage physique :

<typedef struct {
    volatile uint8_t *port;  // Adresse du registre (ex: &PORTB)
    int pin;                 // Numéro de broche   (ex: PB4)
} couple_t;
  • volatile : empêche le compilateur d'optimiser les accès aux registres matériels.
  • uint8_t* : on stocke l'adresse pour écrire directement dans le registre.

Exemple d'utilisation :

<// Activer la ligne 0 (mise à l'état bas)
*(lignes[0].port) &= ~(1 << lignes[0].pin);

// Lire la colonne 0
if (!(PINF & (1 << colonnes[0].pin))) { /* touche enfoncée */ }

Cette approche évite les #ifdef et facilite l'adaptation à un autre brochage.


Fonctionnement du balayage

Le scan se fait ligne par ligne, en deux passes successives :

  1. Première passe — détection des modificateurs et du Shift interne AZERTY.
  2. Seconde passe — construction du rapport HID avec les keycodes.

Entre chaque activation de ligne, un délai de 50 µs stabilise les signaux avant la lecture :

<*(lignes[ligne].port) &= ~(1 << lignes[ligne].pin);
_delay_us(50);
// lecture des colonnes...
Lignes_DesactiverToutes();

La période de scan est de 20 ms (50 Hz), définie par PERIODE_SCAN.


Gestion du Shift interne (AZERTY)

En AZERTY, les chiffres 0 à 9, % et / nécessitent la touche Shift pour être produits. Ce Shift est géré logiciellement : chaque touche de ce type porte un flag modifier_int = MOD_LEFT_SHIFT.

La règle appliquée est :

  • Si toutes les touches enfoncées portent ce flag → Shift est activé dans le rapport.
  • Si au moins une touche ne le porte pas → Shift n'est pas activé, pour éviter les conflits.
bool appliquer_shift_int = a_shift_interne && !a_sans_shift_int;

Structure du rapport HID

Le rapport suit le format HID Boot Keyboard standard sur 8 octets :

<typedef struct {
    uint8_t Modifiers;  // Masque : Ctrl, Shift, Alt, AltGr...
    uint8_t Reserved;   // Toujours 0
    uint8_t KeyCode[6]; // Jusqu'à 6 touches simultanées
} KeyboardReport_t;

Le rapport n'est envoyé que si son contenu a changé par rapport au cycle précédent :

<if (!Rapport_Identique(&rapport, &ancien)) {
    Keyboard_SendReport(&rapport);
    Rapport_Copier(&ancien, &rapport);
}

Descripteurs USB

Le périphérique est déclaré en classe HID Boot Keyboard :

<.Class    = HID_CSCP_HIDClass,
.SubClass = HID_CSCP_BootSubclass,
.Protocol = HID_CSCP_KeyboardBootProtocol,

L'endpoint HID est de type Interrupt IN, taille 8 octets, polling toutes les 10 ms :

<#define KEYBOARD_EPADDR  (ENDPOINT_DIR_IN | 1)
#define KEYBOARD_EPSIZE  8

Problèmes rencontrés et solutions

1. Conflit JTAG sur PB6 et PB7

Problème : PB6 et PB7 sont utilisées par défaut pour l'interface JTAG (TDI/TDO). Tant que le JTAG est actif, ces broches ne fonctionnent pas en E/S normales, ce qui rendait les lignes L3 et L4 inopérantes.

Solution : Désactiver le JTAG en écrivant deux fois consécutivement le bit JTD dans MCUCR (double écriture obligatoire selon la documentation AVR) :

<#ifdef JTD
uint8_t mcucr = MCUCR | (1 << JTD);
MCUCR = mcucr;
MCUCR = mcucr;
#endif

Cette séquence est présente dans SetupHardware().

2. Mauvaises soudures sur plusieurs touches

Problème : Lors du test de la matrice, plusieurs touches ne répondaient pas ou produisaient des détections aléatoires malgré un câblage correct en apparence.

Diagnostic : Après inspection visuelle et test à la continuité au multimètre, des soudures froides ont été identifiées sur plusieurs contacts de touches : joints ternes et granuleux, contact intermittent sous pression mécanique, certaines touches nécessitant un appui très fort pour être détectées.

Correction : Reprise des soudures défectueuses :

  • dégagement de l'excédent d'étain oxydé ;
  • refusion au fer à souder avec apport de flux ;
  • vérification de la continuité après chaque reprise.

Après intervention, toutes les touches concernées ont retrouvé un fonctionnement stable et fiable.


Structure du code

Fichier Rôle
Button_Test.c Logique principale : balayage matrice, construction rapport HID, boucle principale
Button_Test.h Définitions, structures, prototypes
Descriptors.c Descripteurs USB (périphérique, configuration, rapport HID)
Descriptors.h Déclarations des descripteurs

Compilation et flash

Ce projet utilise LUFA (Lightweight USB Framework for AVR).

Prérequis :

  • AVR-GCC + avr-libc
  • LUFA placé dans ../LUFA/ par rapport au projet
  • dfu-programmer pour le flash via DFU

Commandes :

<make clean
make
make dfu        # flash via DFU (bootloader)


Description technique du système embarqué

Cette carte électronique constitue l’une des deux moitiés d’un clavier mécanique ergonomique scindé. Elle assure l’acquisition locale des touches, la gestion du rétroéclairage RGB, l’alimentation autonome sur batterie et la communication avec l’autre moitié du clavier. L’ensemble est piloté par un microcontrôleur ATmega32U4 permettant l’émulation d’un périphérique USB HID standard.


Microcontrôleur principal

ATmega32U4

Le contrôle du système est assuré par un microcontrôleur ATmega32U4, choisi pour son contrôleur USB Full-Speed intégré permettant une connexion directe à l’ordinateur sans convertisseur USB-série externe.

Fonctions assurées :

  • Balayage de la matrice de touches.
  • Gestion des communications UART.
  • Contrôle du rétroéclairage RGB.
  • Génération des rapports USB HID.
  • Gestion des états d’alimentation.

Caractéristiques principales :

  • Architecture AVR 8 bits.
  • Fréquence d’horloge : 16 MHz (quartz externe).
  • Mémoire Flash : 32 Ko.
  • SRAM : 2,5 Ko.
  • EEPROM : 1 Ko.
  • Contrôleur USB 2.0 Full-Speed (12 Mb/s).
  • Jusqu’à 6 endpoints USB configurables.
  • Programmation par interface ISP ou via bootloader DFU.

L’utilisation de l’USB natif permet au clavier d’être reconnu automatiquement comme périphérique HID standard par le système d’exploitation.

Firmware embarqué

Le firmware constitue la couche logicielle principale du clavier. Exécuté sur l'ATmega32U4, il assure l'acquisition des états des touches, la communication avec l'autre moitié du clavier, la génération des rapports USB HID et la gestion du rétroéclairage RGB. Le logiciel est développé en langage C et repose sur une architecture modulaire facilitant les phases de développement, de test et de maintenance. Les différents modules logiciels coopèrent afin de fournir à l'utilisateur un périphérique clavier USB standard reconnu nativement par les systèmes d'exploitation.

Les principales fonctions assurées par le firmware sont :

  • Balayage de la matrice de touches.
  • Gestion de la communication UART entre les deux demi-claviers.
  • Génération des rapports USB HID.
  • Gestion du rétroéclairage RGB.
  • Initialisation et supervision des périphériques du système.

Interface utilisateur

Matrice de touches

Le clavier est organisé sous la forme d’une matrice 5 × 6, soit un total de 30 touches mécaniques.

Le balayage de la matrice est réalisé périodiquement par le microcontrôleur afin de détecter :

  • les appuis de touches ;
  • les relâchements ;
  • les combinaisons simultanées.

Cette architecture réduit le nombre d’entrées/sorties nécessaires par rapport à un câblage individuel de chaque touche.

Acquisition des touches

La matrice de touches est balayée périodiquement par le firmware afin de détecter les appuis et relâchements. Le principe consiste à activer successivement chaque ligne puis à lire l'état des colonnes correspondantes.

Les informations collectées sont stockées dans une structure représentant l'état courant du clavier. Cette méthode permet de réduire significativement le nombre d'entrées/sorties nécessaires tout en conservant un temps de réponse faible.

Les événements détectés sont ensuite convertis en codes HID avant leur transmission à l'ordinateur.

Sockets Hot-Swap Kailh CPG135001S30

Les switches mécaniques sont montés sur des sockets Kailh Hot-Swap CPG135001S30.

Caractéristiques :

  • Compatible avec les switches Kailh Choc V1.
  • Montage CMS (Surface Mount Technology).
  • Remplacement des switches sans dessoudage.
  • Durée de vie d’environ 100 cycles d’insertion/extraction.

Cette solution facilite la maintenance et la personnalisation du clavier.

Communication entre les demi-claviers

Les deux moitiés du clavier échangent leurs états via une liaison UART.

Chaque demi-clavier transmet périodiquement l'état de sa matrice sous la forme d'une trame contenant les informations nécessaires à la reconstruction de l'état global du clavier. Les données reçues sont vérifiées puis fusionnées avec les événements locaux avant la génération du rapport USB HID.

Cette architecture permet de répartir l'acquisition des touches sur deux cartes tout en conservant une unique interface USB vers l'ordinateur.

Gestion de l’alimentation

Le système peut être alimenté soit par le port USB, soit par une batterie Li-ion rechargeable.

Chargeur Li-ion MAX1811

La recharge de la batterie est assurée par le circuit intégré MAX1811.

Fonctions principales :

  • Recharge d’une cellule Li-ion unique.
  • Alimentation directe depuis un port USB.
  • Gestion automatique du cycle de charge.

Caractéristiques :

  • Tension d’alimentation maximale : 6,5 V.
  • Courant de charge configurable :
    • 100 mA ;
    • 500 mA.
  • Tension de fin de charge sélectionnable :
    • 4,1 V ;
    • 4,2 V.
  • Précision de régulation : ±0,5 %.
  • Sortie d’état de charge : /CHG.
  • Fonction de précharge pour batterie profondément déchargée.

Cette précision permet d’optimiser l’utilisation de la capacité disponible de la batterie tout en garantissant sa sécurité de fonctionnement.

Convertisseur boost MCP1640

Lorsque le clavier fonctionne sur batterie, la tension est convertie par un MCP1640 afin d’obtenir une alimentation stable de 5 V.

Caractéristiques :

  • Convertisseur DC/DC élévateur synchrone.
  • Fréquence de découpage : 500 kHz.
  • Démarrage à partir de 0,65 V.
  • Déconnexion réelle de la sortie à l’arrêt.
  • Tension de sortie réglable.

Configuration utilisée :

  • Entrée : batterie Li-ion 3,7 V.
  • Sortie : 5 V.

Cette tension alimente :

  • le microcontrôleur ;
  • les drivers de LED ;
  • l’ensemble du système de rétroéclairage.

Inductance SRN4018-4R7M

Le convertisseur MCP1640 utilise une inductance SRN4018-4R7M comme élément de stockage d’énergie.

Caractéristiques :

  • Inductance : 4,7 µH.
  • Courant nominal : 1,9 A.
  • Courant de saturation : 2 A.
  • Résistance série : 84 mΩ.
  • Boîtier CMS : 4 × 4 × 1,8 mm.

Son faible encombrement est particulièrement adapté à l’intégration dans un clavier compact.


Système de rétroéclairage RGB

Chaque touche dispose d’un rétroéclairage RGB piloté individuellement.

LEDs RGB

Le rétroéclairage est réalisé à l’aide de LEDs RGB CMS au format 1411.

Caractéristiques :

  • Dimensions : 1,4 mm × 1,1 mm.
  • Anode commune.
  • Trois cathodes indépendantes :
    • Rouge ;
    • Vert ;
    • Bleu.

Luminosités typiques :

  • Rouge : 270 mcd.
  • Vert : 950 mcd.
  • Bleu : 230 mcd.

Chaque couleur est pilotée indépendamment afin de produire un large éventail de teintes et d’effets lumineux.

Drivers TLC5947DAP

Le pilotage des LEDs est assuré par deux circuits TLC5947DAP montés en cascade.

Fonctions principales :

  • Génération des signaux PWM.
  • Régulation du courant de chaque canal.
  • Contrôle individuel de l’intensité lumineuse.

Caractéristiques :

  • 24 sorties à courant constant par circuit.
  • Résolution PWM : 12 bits.
  • 4096 niveaux de luminosité par canal.
  • Interface série de configuration.
  • Tension d’alimentation : 3 V à 5,5 V.
  • Tension maximale sur les LEDs : 30 V.

Implémentation retenue :

  • 2 TLC5947 montés en cascade.
  • 48 canaux disponibles.
  • Pilotage de 16 LEDs RGB (48 canaux = 16 × 3 couleurs).

Une fois les données chargées dans les registres internes, la génération PWM est effectuée automatiquement par les drivers.

Gestion logicielle du rétroéclairage

Le rétroéclairage RGB est contrôlé par le firmware à travers les drivers TLC5947. Les intensités lumineuses de chaque canal sont stockées dans un tableau mémoire puis transmises périodiquement aux drivers.

Le logiciel permet de générer différents effets lumineux en modifiant dynamiquement les valeurs PWM envoyées aux TLC5947. Une animation de type arc-en-ciel a notamment été développée afin de valider le fonctionnement du système RGB et de démontrer le pilotage indépendant de chaque LED.


Résumé des composants principaux

Composant Fonction
ATmega32U4 Contrôle principal et interface USB HID
MAX1811 Recharge de la batterie Li-ion
MCP1640 Conversion de la tension batterie vers 5 V
SRN4018-4R7M Inductance du convertisseur boost
TLC5947DAP (×2) Pilotage PWM des LEDs RGB
LED RGB 1411 Rétroéclairage individuel des touches
Kailh CPG135001S30 Sockets Hot-Swap pour switches mécaniques
Jack TRS 3,5 mm Communication UART entre les deux cartes
Jack TRRS 3,5 mm Extension de communication ou alimentation

L’ensemble de ces composants permet la réalisation d’un clavier mécanique ergonomique autonome, rechargeable, doté d’un rétroéclairage RGB programmable et d’une communication USB HID native compatible avec les principaux systèmes d’exploitation.

Carte électronique

La carte a été conçue sous KiCad. Le schéma est découpé en deux feuilles hiérarchiques :

  • matrice.kicad_sch [5] : matrice de touches 5×6 avec switches Kailh Hot-Swap
  • led.kicad_sch [6] : chaîne de drivers TLC5947 et connexions LEDs RGB

Les fichiers de production (Gerber + netlist IPC) sont disponibles dans [7].La disposition physique des touches a été conçue avec un outil de layout de clavier (fichiers JSON [8]), avec plusieurs itérations.

Travail effectué

Tâches effectuées

1- Le 17 février 2026 :

- Choix du projet

2- Le 3 mars 2026 :

- Réalisation initiale du schéma électronique de la carte

- Ajout des composants suivants : chargeur batterie, connecteur USB, convertisseur DC-DC

3- Le 10 mars 2026 :

- Correction du schéma électronique de la carte

- Choix des composants adéquats; notamment les LEDs, selon la disponibilité et la correspondance au projet

4- Le 17 mars 2026 :

-Consultation des empreintes des composants

-Connection de TLC5947 au µC

5- Le 24 mars 2026 :

- Correction du schéma électronique

- Ajout des capacités de découplage

- Ajout d'un connecteur ISP

6- Le 31 mars 2026 :

-Effectuation du routage

7- Le 15 mai 2026 :

-Test des boutons

-Test des LEDs

8- Le 12 mai 2026 :

-Récuperation de la carte

9- Le 18 mai 2026 :

-Obtention des composants et premières étapes de la soudure

10- Le 25 mai 2026 :

-Modification des valeurs des résistances

11- Le 26 mai 2026 :

-Fin de la soudure

12- Le 2 juin 2026 :

-Solution du problème de clignotement des LEDs

Difficultés

Au cours du projet, plusieurs difficultés techniques ont dû être résolues :

  • Validation du pilotage des TLC5947 et détection de défauts de soudure sur certains canaux.
  • Diagnostic de courts-circuits affectant certaines lignes de commande du système RGB.
  • Adaptation de la disposition logique des touches suite aux contraintes d'implantation des switches sur le PCB.
  • Mise au point de la communication entre les deux demi-claviers et validation des échanges de données.

Ces étapes ont permis de fiabiliser le système avant son intégration finale.

Bilan

Le projet a permis de mener un développement complet de système embarqué, de la conception PCB jusqu'au firmware USB. Les principales difficultés ont été d'ordre matériel (défauts PCB sur les drivers TLC5947) et ont nécessité des interventions de réparation (reprise de pistes et soudures). Côté logiciel, la mise en œuvre du protocole HID USB via LUFA et la gestion correcte du mapping AZERTY (notamment le Shift interne conditionnel) ont constitué les points techniques les plus complexes.

Annexe

Schématique du clavier

Mon schéma électronique du clavier

Le Routage

La PCB final

La carte final

une photo du clavier

Le programmateur AVR

Des photo de la carte final

2025 PSE-B16-1er photo du programmateur.JPEG
2025 PSE-B16-2er photo du programmateur.JPEG

Démonstration de la programmation d'une Arduino

Démonstration du test des boutons et leds