« I2L 2025 Groupe2 » : différence entre les versions

De wiki-se.plil.fr
Aller à la navigation Aller à la recherche
Ligne 87 : Ligne 87 :


= Travaux (étudiants) =
= Travaux (étudiants) =
Le programme développé constitue un '''accordeur de guitare numérique complet''', intégrant acquisition analogique, traitement du signal, interprétation musicale et communication USB.
Le programme développé constitue un accordeur de guitare.


L’une des fonctionnalités principales est l’'''acquisition du signal sonore''' via un microphone analogique relié à l’ADC du microcontrôleur. Le signal est échantillonné à une fréquence fixe respectant le '''théorème de Shannon-Nyquist''', qui stipule que pour analyser correctement un signal de fréquence maximale fmax​, il faut une fréquence d’échantillonnage strictement supérieure à 2×fmax​. Dans ce projet, la fréquence d’échantillonnage a été choisie de manière à couvrir toute la tessiture de la guitare standard.
Branché sur un ordinateur ou sur batterie, le microcontrôleur s’allume. On peut apercevoir une LED qui clignote sur toute la durée de fonctionnement du microcontrôleur.


Une fois le signal acquis, l’algorithme central repose sur l’'''autocorrélation'''. Cette méthode consiste à comparer un signal avec lui-même décalé dans le temps afin de mesurer sa similarité. Pour un signal périodique, l’autocorrélation présente des maxima lorsque le décalage correspond à la période fondamentale du signal. Cette propriété permet de déterminer la '''fréquence fondamentale''', c’est-à-dire la fréquence la plus basse du signal, perçue comme la hauteur de la note jouée.
L’écran s’initialise ensuite et affiche « Initializing screen ». Lorsqu’il a fini de s’initialiser, il affiche « Not capturing ».


Contrairement à une transformée de Fourier, qui met en évidence les composantes fréquentielles mais peut favoriser les harmoniques, l’autocorrélation est particulièrement adaptée aux instruments à cordes, dont le spectre contient de nombreuses harmoniques parfois plus énergétiques que la fondamentale.
Lorsque l’on appuie sur le bouton, une deuxième LED s’allume jusqu’à la fin du traitement. L’écran affiche « Capturing… » et le microphone capte le son pendant quelques dixièmes de seconde.


La fréquence fondamentale est ensuite convertie en '''note musicale'''. Cette conversion s’appuie sur le système tempéré égal, dans lequel l’octave est divisée en douze demi-tons de même rapport fréquentiel. La relation logarithmique entre fréquence et hauteur justifie l’utilisation du logarithme base 2. La référence La4 = 440 Hz permet de positionner la note détectée dans l’échelle musicale normalisée.
Ensuite, l’écran affiche le volume, la fréquence et la note sous le format suivant :<code>"V:%3d F:%4uHz N:%s"</code>


Le programme assure également un '''retour utilisateur visuel''' grâce à un écran LCD et des LEDs. L’écran affiche le volume, la fréquence mesurée et la note correspondante, tandis que les LEDs indiquent l’état du système (prêt, capture en cours, transmission USB). Cette interface simple permet de valider rapidement le fonctionnement sans dépendre systématiquement de l’ordinateur.
exemple : <code>V:124 F:294Hz N:D4</code>


Enfin, la communication USB, basée sur la bibliothèque '''LUFA''', permet d’envoyer la note détectée vers un ordinateur. Le choix d’un périphérique USB ''Vendor Specific'' offre une grande flexibilité et évite les contraintes imposées par les classes USB standard. Le script Python complète cette architecture en fournissant un moyen direct et multiplateforme de réception des données.
Le système envoie la note, par exemple <code>D4</code>, via le descripteur USB au PC branché, si présent.


L’ensemble du projet illustre ainsi une approche complète de conception embarquée, combinant électronique, traitement du signal, algorithmique, protocoles de communication et interaction utilisateur.
Après deux secondes, l’écran affiche de nouveau « Not capturing ».
 
 
Limitations:
 
Dû aux limites matérielles du microcontrôleur, spécifiquement au niveau de la RAM & du microphone, les résultats sont souvent brouillé à cause du bruit ambiant et du temps de récupération du son trop court pour avoir des résultats cohérents à tout moment.
 
Notre implémentation fonctionne correctement sur notre microcontrôleur avec de la pratique pour un sous-ensemble de notes principalement de la 3e octave.
 
Il faut, comme démontré dans la vidéo, appuyer sur le moment juste avant de jouer la note pour avoir le plus de chance que l'analyse de passe correctement.
 
 
Tests:
 
Les tests sont effectués à partir de vidéo de note de guitare filmée sur un iPhone 13. La guitare est une Yamaha CG192C de 2012.
 
Utiliser des vidéos de notes permet de faciliter les tests.


= Extraits significatifs de code (étudiants) =
= Extraits significatifs de code (étudiants) =


== 1. Boucle principale et architecture logicielle ==
== 1. Boucle principale et architecture logicielle ==
📁 <code>guitar_tuner/main.c</code>
📁 <code>guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/main.c</code>


La boucle principale de l'application repose sur une logique simple qui gère l’ensemble des interactions entre l’utilisateur, le matériel et l'algorithme de traitement du signal.  
La boucle principale de l'application repose sur une logique simple qui gère l’ensemble des interactions entre l’utilisateur, le matériel et l'algorithme de traitement du signal.  
Ligne 112 : Ligne 128 :
Lorsque l’utilisateur appuie sur le bouton, le microcontrôleur capture un bloc d’échantillons audio, analyse le signal pour en extraire la fréquence fondamentale, convertit cette fréquence en note musicale, puis affiche le résultat sur l’écran LCD.
Lorsque l’utilisateur appuie sur le bouton, le microcontrôleur capture un bloc d’échantillons audio, analyse le signal pour en extraire la fréquence fondamentale, convertit cette fréquence en note musicale, puis affiche le résultat sur l’écran LCD.


La fonction suivante illustre cette logique <syntaxhighlight lang="c" line="1">
La fonction suivante illustre cette logique: <syntaxhighlight lang="c" line="1">
void loop_guitar_tuner(char *note_buf)
void loop_guitar_tuner(char *note_buf)
{
{
Ligne 140 : Ligne 156 :


== 2. Acquisition du signal audio et contraintes matérielles ==
== 2. Acquisition du signal audio et contraintes matérielles ==
📁 <code>guitar_tuner/adc/adc.c</code>
📁 <code>guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/adc/adc.c</code>


📁 <code>guitar_tuner/sound_analysis.h</code>  
📁 <code>guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/sound_analysis.h</code>  


<code>📁 guitar_tuner/sound_analysis.c</code>
<code>📁 guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/sound_analysis.c</code>


Le signal audio provient d’un microphone électret amplifié, dont la sortie est centrée autour de la tension médiane de l’ADC (environ 2,5 V). Le convertisseur analogique-numérique du microcontrôleur fournit une valeur sur 10 bits, comprise entre 0 et 1023.
Le signal audio provient d’un microphone électret amplifié, dont la sortie est centrée autour de la tension médiane de l’ADC (environ 2,5 V). Le convertisseur du microcontrôleur fournit une valeur sur 10 bits, comprise entre 0 et 1023.


L’acquisition est réalisée à une fréquence d’échantillonnage fixée à 4096 Hz, valeur suffisante pour couvrir le spectre utile de la guitare (jusqu’à environ 1,3 kHz pour la corde Mi aiguë). Cette fréquence permet de respecter le théorème de Nyquist tout en limitant la charge de calcul et l’occupation mémoire.
L’acquisition est réalisée à une fréquence d’échantillonnage fixée à 4096 Hz, valeur suffisante pour couvrir le spectre utile de la guitare (jusqu’à environ 1,3 kHz pour la corde Mi aiguë).


Définition des hyper paramètres:<syntaxhighlight lang="c" line="1">
Définition des hyper paramètres:<syntaxhighlight lang="c" line="1">
Ligne 168 : Ligne 184 :
#endif
#endif


</syntaxhighlight>La fonction permettant de récupérer des samples audios:
</syntaxhighlight>La fonction permettant de récupérer des samples audios: Le centrage immédiat du signal autour de zéro est une étape essentielle. Sans cette opération, la composante continue dominerait le calcul de corrélation et fausserait la détection de la période fondamentale.<syntaxhighlight lang="c" line="1">
 
Le centrage immédiat du signal autour de zéro est une étape essentielle. Sans cette opération, la composante continue dominerait le calcul de corrélation et fausserait la détection de la période fondamentale.<syntaxhighlight lang="c" line="1">
void collect_audio_samples(void)
void collect_audio_samples(void)
{
{
Ligne 192 : Ligne 206 :


== 3. Analyse du signal sonore : choix de l’autocorrélation ==
== 3. Analyse du signal sonore : choix de l’autocorrélation ==
📁 <code>guitar_tuner/sound_analysis.c</code>
📁 <code>guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/sound_analysis.c</code>


=== 3.1 Justification de l’algorithme ===
=== 3.1 Justification de l’algorithme ===
Ligne 199 : Ligne 213 :
Lors de notre implémentation, nous avons eu plusieurs soucis notamment lié au manque de RAM.  
Lors de notre implémentation, nous avons eu plusieurs soucis notamment lié au manque de RAM.  


L’autocorrélation a été retenue car elle permet d’extraire directement la '''période fondamentale''' d’un signal quasi périodique, même en présence d’harmoniques. Le principe consiste à comparer le signal avec une version retardée de lui-même et à mesurer la similarité pour différents décalages temporels. Le décalage maximisant cette similarité correspond à la période du signal.<syntaxhighlight lang="c" line="1">
L’autocorrélation a donc été utilisée car elle permet d’extraire directement la '''période fondamentale''' d’un signal quasi périodique, même en présence d’harmoniques. Le principe consiste à comparer le signal avec une version retardée de lui-même et à mesurer la similarité pour différents décalages temporels. Le décalage maximisant cette similarité correspond à la période du signal.<syntaxhighlight lang="c" line="1">
float detect_frequency(void)
float detect_frequency(void)
{
{
Ligne 243 : Ligne 257 :


== 4. Conversion fréquence → note musicale ==
== 4. Conversion fréquence → note musicale ==
📁 <code>guitar_tuner/sound_analysis.c</code>
📁 <code>guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/sound_analysis.c</code>


La conversion d’une fréquence en note musicale repose sur le système tempéré occidental, dans lequel l’octave est divisée en 12 demi-tons logarithmiquement espacés. La référence universelle est le '''A4 (LA) à 440 Hz'''.
La conversion d’une fréquence en note musicale repose sur le système tempéré occidental, dans lequel l’octave est divisée en 12 demi-tons logarithmiquement espacés. La référence universelle est le '''A4 (LA) à 440 Hz'''.
Ligne 273 : Ligne 287 :


== 5. LUFA et architecture USB bas niveau ==
== 5. LUFA et architecture USB bas niveau ==
📁 <code>guitar_tuner/descriptors.c</code>
📁 <code>guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/descriptors.c</code>


LUFA permet d’implémenter un périphérique USB directement sur le microcontrôleur AVR. Le cœur de ce mécanisme repose sur les '''descripteurs USB''', structures de données décrivant précisément l’appareil au système hôte.<syntaxhighlight lang="c" line="1">
LUFA permet d’implémenter un périphérique USB directement sur le microcontrôleur AVR. Le cœur de ce mécanisme repose sur les '''descripteurs USB''', structures de données décrivant précisément l’appareil au système hôte.<syntaxhighlight lang="c" line="1">
Ligne 288 : Ligne 302 :
     .ProductID = 0x0001,
     .ProductID = 0x0001,
};
};
</syntaxhighlight>Ce descripteur indique au PC qu’il s’agit d’un périphérique USB personnalisé (Vendor Specific). Lors de l’énumération, le système d’exploitation lit ces informations pour charger un pilote générique et autoriser la communication.
</syntaxhighlight>Ce descripteur indique au PC qu’il s’agit d’un périphérique USB personnalisé (Vendor Specific). Lors de l’énumération, le système d’exploitation lit ces informations pour charger un pilote générique et autoriser la communication. Le descripteur de configuration définit ensuite les endpoints utilisés pour l’échange de données.<syntaxhighlight lang="c" line="1">
 
Le descripteur de configuration définit ensuite les endpoints utilisés pour l’échange de données.<syntaxhighlight lang="c" line="1">
.InEndpoint =
.InEndpoint =
{
{
Ligne 299 : Ligne 311 :
     .EndpointSize = IN_EPSIZE,
     .EndpointSize = IN_EPSIZE,
}
}
</syntaxhighlight>L’endpoint de type '''Bulk IN''' est parfaitement adapté à l’envoi ponctuel de chaînes de caractères (notes musicales) sans contrainte temps réel stricte.
</syntaxhighlight>
 
== 6. Boucle principale LUFA ==
== 6. Boucle principale LUFA ==
📁 <code>guitar_tuner/Minimal.c</code><syntaxhighlight lang="c" line="1">
📁 <code>guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/Minimal.c</code><syntaxhighlight lang="c" line="1">
int main(void)
int main(void)
{
{
Ligne 327 : Ligne 338 :


== 7.  Communication USB côté hôte : réception des notes via LUFA ==
== 7.  Communication USB côté hôte : réception des notes via LUFA ==
📁 <code>guitar_tuner/receive.py</code>
📁 <code>guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/receive.py</code>


Le projet ne se limite pas au microcontrôleur : une partie essentielle est la communication avec l’ordinateur. Le microcontrôleur, grâce à '''LUFA''', se présente comme un périphérique USB de type ''Vendor Specific''. Le script Python <code>receive.py</code> agit comme client USB côté hôte et permet de lire directement les données envoyées par l’AVR sans pilote spécifique.
Le projet ne se limite pas au microcontrôleur : une partie essentielle est la communication avec l’ordinateur. Le microcontrôleur, grâce à '''LUFA''', se présente comme un périphérique USB de type ''Vendor Specific''. Le script Python <code>receive.py</code> agit comme client USB côté hôte et permet de lire directement les données envoyées par l’AVR sans pilote spécifique.

Version du 2 janvier 2026 à 11:23

Proposition de système (étudiants)

Guitar Tuner est un projet dont l’objectif, comme son nom l’indique, est de faciliter l’accordage des cordes d’une guitare.

Son principe est simple : lorsqu’une corde est jouée, le microcontrôleur capte le son produit à l’aide d’un microphone, analyse sa fréquence, puis affiche sur l’écran la note correspondante.


Une fréquence étant une donnée continue, on affiche par défaut la note la plus proche de la fréquence mesurée.

Comme piste d’amélioration, on pourrait aller plus loin en proposant un affichage plus intuitif :

  • afficher la note au centre de l’écran lorsque la fréquence est correcte (ou très proche de la note visée) ;
  • déplacer la note légèrement vers la gauche si la fréquence est inférieure à la note attendue ;
  • déplacer la note vers la droite si la fréquence est supérieure.

Ainsi, plus la note affichée se rapproche du centre de l’écran, plus l’accord est précis.

Ce principe permettrait également d’accorder plusieurs cordes, et ce, dans différents accordages différent (standart, ré...).

Le circuit se compose des éléments suivants :

  • Un écran LCD pour afficher la note détectée.
  • Un microphone pour capter la fréquence produite par la corde.
  • Une interface USB assurant la communication avec un ordinateur.
  • Une LED d’alimentation indiquant que le microcontrôleur est sous tension.
  • Un bouton permettant d’activer l’enregistrement.
  • Une LED d'action indiquant que la fonction d’enregistrement est activée.

Contre-proposition (intervenant)

OK pour la proposition. Comme périphérique USB vous implanterez un périphérique spécifique à votre projet (classe USB "vendeur") permettant à un ordinateur de récupérer les fréquences detectées en temps réel et de les afficher.

Votre objet doit pouvoir être autonome et marcher sur batterie.

Proposition définitive (étudiants)

Répartition du travail (étudiants)

Jules:

  • Mise en place de la bibliothèque permettant le fonctionnement de l’écran LCD
  • Analyse du signal sonore
  • Conversion des fréquences en notes musicales
  • Ajustement fin des paramètres (fine-tuning) et tests du projet fina


Clément:

  • Initialisation du projet, de l’environnement de travail collaboratif et du code source
  • Création et mise en place de la bibliothèque permettant l’utilisation du microphone
  • Développement de la boucle principale du programme et des différentes fonctionnalités.
  • Tests du microcontrôleur en fonctionnement sur batterie


Jules & Clément:

  • Installation et configuration de la bibliothèque LUFA pour la communication USB
  • Développement du script Python permettant la réception et l’affichage des notes sur ordinateur

Carte

Schéma initial (étudiants)

Schéma de la carte

Carte routée (intervenant)

Vous utiliserez la carte avec batterie.

Composants (intervenant)

Il manque au 10 octobre 2025, un connecteur femelle 16 broches.

Carte réalisée (intervenant)

Photo de la carte

La carte est entiérement soudée à l'exception d'un connecteur 16 broches pour l'écran.

Au 15 octobre 2025 la carte est entiérement soudée.

Travaux (étudiants)

Le programme développé constitue un accordeur de guitare.

Branché sur un ordinateur ou sur batterie, le microcontrôleur s’allume. On peut apercevoir une LED qui clignote sur toute la durée de fonctionnement du microcontrôleur.

L’écran s’initialise ensuite et affiche « Initializing screen ». Lorsqu’il a fini de s’initialiser, il affiche « Not capturing ».

Lorsque l’on appuie sur le bouton, une deuxième LED s’allume jusqu’à la fin du traitement. L’écran affiche « Capturing… » et le microphone capte le son pendant quelques dixièmes de seconde.

Ensuite, l’écran affiche le volume, la fréquence et la note sous le format suivant :"V:%3d F:%4uHz N:%s"

exemple : V:124 F:294Hz N:D4

Le système envoie la note, par exemple D4, via le descripteur USB au PC branché, si présent.

Après deux secondes, l’écran affiche de nouveau « Not capturing ».


Limitations:

Dû aux limites matérielles du microcontrôleur, spécifiquement au niveau de la RAM & du microphone, les résultats sont souvent brouillé à cause du bruit ambiant et du temps de récupération du son trop court pour avoir des résultats cohérents à tout moment.

Notre implémentation fonctionne correctement sur notre microcontrôleur avec de la pratique pour un sous-ensemble de notes principalement de la 3e octave.

Il faut, comme démontré dans la vidéo, appuyer sur le moment juste avant de jouer la note pour avoir le plus de chance que l'analyse de passe correctement.


Tests:

Les tests sont effectués à partir de vidéo de note de guitare filmée sur un iPhone 13. La guitare est une Yamaha CG192C de 2012.

Utiliser des vidéos de notes permet de faciliter les tests.

Extraits significatifs de code (étudiants)

1. Boucle principale et architecture logicielle

📁 guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/main.c

La boucle principale de l'application repose sur une logique simple qui gère l’ensemble des interactions entre l’utilisateur, le matériel et l'algorithme de traitement du signal.

Lorsque l’utilisateur appuie sur le bouton, le microcontrôleur capture un bloc d’échantillons audio, analyse le signal pour en extraire la fréquence fondamentale, convertit cette fréquence en note musicale, puis affiche le résultat sur l’écran LCD.

La fonction suivante illustre cette logique:

void loop_guitar_tuner(char *note_buf)
{
    if (!(PINC & (1 << BTN2)))   // Bouton appuyé (actif à l'état bas)
    {
        display_message("Capturing");

        collect_audio_samples();

        int16_t volume = get_max_sample();
        float frequency = detect_frequency();

        frequency_to_note(frequency, note_buf);

        char buffer[32];
        snprintf(buffer, sizeof(buffer),
                 "V:%d F:%uHz %s",
                 volume, (uint16_t)frequency, note_buf);

        display_message(buffer);
        _delay_ms(800);

        display_message("Not capturing");
    }
}

2. Acquisition du signal audio et contraintes matérielles

📁 guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/adc/adc.c

📁 guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/sound_analysis.h

📁 guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/sound_analysis.c

Le signal audio provient d’un microphone électret amplifié, dont la sortie est centrée autour de la tension médiane de l’ADC (environ 2,5 V). Le convertisseur du microcontrôleur fournit une valeur sur 10 bits, comprise entre 0 et 1023.

L’acquisition est réalisée à une fréquence d’échantillonnage fixée à 4096 Hz, valeur suffisante pour couvrir le spectre utile de la guitare (jusqu’à environ 1,3 kHz pour la corde Mi aiguë).

Définition des hyper paramètres:

#ifndef SOUND_ANALYSIS_H
#define SOUND_ANALYSIS_H

#include <stdint.h>

#define SAMPLE_COUNT 1024
#define DESIRED_FS   4096

#define GTR_LOW   82     // Hz d2
#define GTR_HIGH  659   // Hz e5

void collect_audio_samples(void);
float detect_frequency(void);
int16_t get_max_sample(void);

#endif

La fonction permettant de récupérer des samples audios: Le centrage immédiat du signal autour de zéro est une étape essentielle. Sans cette opération, la composante continue dominerait le calcul de corrélation et fausserait la détection de la période fondamentale.

void collect_audio_samples(void)
{
    const uint16_t prescaler = 128;
    const float adc_clk = (float)F_CPU / prescaler;
    const float conv_us = 13.0f * 1e6f / adc_clk;
    const float period_us = 1e6f / DESIRED_FS;
    uint16_t delay_us = (uint16_t)(period_us - conv_us - 2);

    if (delay_us < 1)
        delay_us = 1;

    for (int i = 0; i < SAMPLE_COUNT; i++) {
        ADCSRA |= (1 << ADSC);
        while (ADCSRA & (1 << ADSC));
        samples[i] = (int16_t)ADC - 512; // centrage autour de zéro
        _delay_us(delay_us);
    }
}

3. Analyse du signal sonore : choix de l’autocorrélation

📁 guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/sound_analysis.c

3.1 Justification de l’algorithme

Dans le cadre de ce projet, plusieurs méthodes de détection de fréquence ont été envisagées, notamment la Transformée de Fourier Rapide (FFT). Toutefois, l’utilisation d’une FFT sur le microcontrôleur présente plusieurs inconvénients majeurs : consommation mémoire élevée, nécessité de calculs complexes en nombres flottants, et sensibilité accrue aux harmoniques de la guitare.

Lors de notre implémentation, nous avons eu plusieurs soucis notamment lié au manque de RAM.

L’autocorrélation a donc été utilisée car elle permet d’extraire directement la période fondamentale d’un signal quasi périodique, même en présence d’harmoniques. Le principe consiste à comparer le signal avec une version retardée de lui-même et à mesurer la similarité pour différents décalages temporels. Le décalage maximisant cette similarité correspond à la période du signal.

float detect_frequency(void)
{
    // Suppression de la composante continue
    int32_t mean = 0;
    for (int i = 0; i < SAMPLE_COUNT; i++)
        mean += samples[i];
    mean /= SAMPLE_COUNT;

    for (int i = 0; i < SAMPLE_COUNT; i++)
        samples[i] -= (int16_t)mean;

    // Bornes des retards (lags)
    uint16_t min_lag = DESIRED_FS / GTR_HIGH;
    uint16_t max_lag = DESIRED_FS / GTR_LOW;

    if (max_lag >= SAMPLE_COUNT)
        max_lag = SAMPLE_COUNT - 1;

    int32_t best_corr = 0;
    uint16_t best_lag = 0;

    for (uint16_t lag = min_lag; lag <= max_lag; lag++) {
        int32_t corr = 0;

        for (uint16_t i = 0; i < SAMPLE_COUNT - lag; i++) {
            corr += (int32_t)samples[i] * samples[i + lag];
        }

        if (corr > best_corr) {
            best_corr = corr;
            best_lag = lag;
        }
    }

    if (best_lag == 0)
        return 0.0f;

    return (float)DESIRED_FS / (float)best_lag;
}

Les bornes min_lag et max_lag sont calculées à partir des fréquences extrêmes de la guitare. Cette restriction est cruciale : elle évite que l’algorithme ne sélectionne des maxima correspondant à des harmoniques ou à du bruit basse fréquence. Le choix de ces bornes améliore significativement la stabilité de la détection et s'assure que les notes affichées sont des notes jouable à la guitare.


4. Conversion fréquence → note musicale

📁 guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/sound_analysis.c

La conversion d’une fréquence en note musicale repose sur le système tempéré occidental, dans lequel l’octave est divisée en 12 demi-tons logarithmiquement espacés. La référence universelle est le A4 (LA) à 440 Hz.

Il est important de noter que le tableau des notes commence par C (Do) et non par A (La). Cette convention n’est pas arbitraire : elle découle de l’organisation historique des octaves dans la notation scientifique, où chaque octave commence à Do. Ainsi, C4 correspond au Do central du piano, tandis que A4 (440 Hz) se situe au milieu de cette octave.

En notation française, C correspond à Do, D à Ré, E à Mi, etc. L’ordre C–D–E–F–G–A–B est donc la traduction directe de Do–Ré–Mi–Fa–Sol–La–Si.

Dans le cadre de guitar_tuner, nous avons décidé d'utiliser la notation anglo-saxonne, car la plupart des partitions sur internet et la plupart des outils d'accordement utilise cette notation.

void frequency_to_note(float freq, char *note)
{
    if (freq <= 0.0f) {
        strcpy(note, "---");
        return;
    }

    float semitones = 12.0f * log2(freq / 440.0f);
    int midi = (int)(69 + semitones + 0.5f);

    const char *note_names[] = {
        "C","C#","D","D#","E","F","F#","G","G#","A","A#","B"
    };

    int octave = (midi / 12) - 1;
    sprintf(note, "%s%d", note_names[midi % 12], octave);
}

Cette méthode prend en entrée la fréquence & insert dans le deuxième paramètre la note & l'octave correspondant.

5. LUFA et architecture USB bas niveau

📁 guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/descriptors.c

LUFA permet d’implémenter un périphérique USB directement sur le microcontrôleur AVR. Le cœur de ce mécanisme repose sur les descripteurs USB, structures de données décrivant précisément l’appareil au système hôte.

const USB_Descriptor_Device_t PROGMEM DeviceDescriptor =
{
    .Header = {.Size = sizeof(USB_Descriptor_Device_t),
               .Type = DTYPE_Device},

    .USBSpecification = VERSION_BCD(1,1,0),
    .Class            = USB_CSCP_NoDeviceClass,
    .Endpoint0Size    = FIXED_CONTROL_ENDPOINT_SIZE,

    .VendorID  = 0x4242,
    .ProductID = 0x0001,
};

Ce descripteur indique au PC qu’il s’agit d’un périphérique USB personnalisé (Vendor Specific). Lors de l’énumération, le système d’exploitation lit ces informations pour charger un pilote générique et autoriser la communication. Le descripteur de configuration définit ensuite les endpoints utilisés pour l’échange de données.

.InEndpoint =
{
    .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t),
               .Type = DTYPE_Endpoint},
    .EndpointAddress = IN_EPADDR,
    .Attributes = EP_TYPE_BULK,
    .EndpointSize = IN_EPSIZE,
}

6. Boucle principale LUFA

📁 guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/Minimal.c

int main(void)
{
    SetupHardware();
    GlobalInterruptEnable();

    init_guitar_tuner();
    char note[6] = {0};

    for (;;)
    {
        USB_USBTask();
        loop_guitar_tuner(note);

        if (note[0] != '\0')
        {
            USB_Manage_In(note);
            note[0] = '\0';
        }
    }
}

Cette boucle illustre l’intégration entre l’application et la pile USB. La fonction USB_USBTask() permet à LUFA de gérer les événements USB (requêtes, transferts, états). Le reste du temps processeur est dédié à l’application métier. Cette cohabitation maîtrisée garantit que la communication USB reste fonctionnelle sans perturber l’analyse du signal.

7. Communication USB côté hôte : réception des notes via LUFA

📁 guitar-tuner/lufa-LUFA-210130-NSI/I2L/Minimal/receive.py

Le projet ne se limite pas au microcontrôleur : une partie essentielle est la communication avec l’ordinateur. Le microcontrôleur, grâce à LUFA, se présente comme un périphérique USB de type Vendor Specific. Le script Python receive.py agit comme client USB côté hôte et permet de lire directement les données envoyées par l’AVR sans pilote spécifique.

import usb.core
import usb.util
import time

ID_VENDOR = 0x4242
ID_PRODUCT = 0x0001

ENDPOINT_IN = 0x81

def read_guitar_note(device):
    try:
        data = device.read(ENDPOINT_IN, 6, 50)

        # Convert raw bytes to string, stop at first null byte
        note_str = bytes(data).split(b'\x00')[0].decode('ascii', errors='ignore')
        return note_str
    
    except usb.core.USBError:
        return None

if __name__ == "__main__":
    dev = usb.core.find(idVendor=ID_VENDOR, idProduct=ID_PRODUCT)
    assert dev is not None

    print("Ecoute en cours...")

    try:
        if dev.is_kernel_driver_active(0):
            dev.detach_kernel_driver(0)
    except (NotImplementedError, usb.core.USBError):
        pass

    while True:
        if dev is None:
            break
        
        note = read_guitar_note(dev)
        if note:
            print(f"Note: {note}")

Les identifiants Vendor ID et Product ID correspondent exactement à ceux définis dans le descripteur USB LUFA du microcontrôleur. Cette correspondance permet au script de retrouver le bon périphérique parmi tous ceux connectés au PC.

Une fois le périphérique détecté, le script s’attache explicitement à l’interface USB. Sur certains systèmes, le noyau peut déjà avoir revendiqué l’interface ; il est alors nécessaire de le détacher afin d’autoriser une communication directe via le paquet PyUSB.

Le format texte permet d’éviter toute complexité de décodage et de faciliter le débogage.

Rendus (étudiants)

Projet KiCAD : Fichier:I2L-2025-Carte-G2-final.zip

Programmes :