Se connecter
Se connecter

ou
Créer un compte

ou
FR
EN

Conception électronique: MIDI in et micro-contrôleur (arduino)

  • 23 réponses
  • 6 participants
  • 1 534 vues
  • 7 followers
Sujet de la discussion Conception électronique: MIDI in et micro-contrôleur (arduino)
Salut à tous.

Ca fait quelques années que je pratique l'électronique en tant qu'amateur: réalisation de quelques mini synthés analogiques et numériques.

Je bloque néanmoins sur la conception d'un MIDI in.

Je m'explique: j'ai déjà réalisé des circuits qui pouvaient se synchroniser sur une horloge MIDI, ou pouvaient recevoir des notes MIDI. (6n138 + résistances + diode + micro-contrôleur.)

Cependant ça ne fonctionnait pas toujours très bien... il y avait des notes pas toujours jouées par exemple.


Côté micro-contrôleur j'utilise la gamme Arduino, comme la Uno qui tourne à 16Mhz ou la Due qui tourne à 84MHz. En ce qui concerne la programmation j'ai tendance à bien charger la mule et je pousse souvent ces cartes dans leurs retranchements.


Au final ma question est très simple.

Dans un synthétiseur numérique, vous semble t'il judicieux de dédier un micro-contrôleur à la réception et au filtrage des données MIDI ?

Les arduinos sont capables de lire les données MIDI grâce aux commandes "serial" (qui tournent ici à 31250 bauds). Cependant j'ai l'impression que cette tâche peut être polluée par les autres interruptions de mes programmes, provoquant des pertes de données MIDI.

Quelle solution est habituellement utilisée par les fabricants? un uc dédié ou pas?

Après je suis complètement autodidacte, je ne saisis pas toutes les subtilités de la programmation ... :lol:


Merci à ceux qui pourront m'éclairer. :bravo:

[ Dernière édition du message le 15/12/2018 à 20:52:08 ]

2
Si je devais le faire, je ferais un driver spécial et non des calls a la lib Serial: une petite machine a états qui lit les bytes bruts, filtre/parse les données, crée et passe uniquement les messages intéressants a l’application au travers d'une FIFO.
Arduino, si je ne m'abuse, est construit sur C++.
Une petite classe MIDI devrait faire l'affaire - d'ailleurs, il n'y a vraiment pas de module MIDI dans la lib d'arduino?

Pour les problèmes que tu décris, probable qu'une classe MIDI qui se brancherait directement sur le registre data de l'UART serait plus efficace.
Je ne sais pas comment fonctionne la lib Serial de l'arduino, mais je serais étonné qu'il n'y ait pas au moins un buffer entre l'UART et ton appli.
Le coup des notes non jouées me fait quand meme bien penser a un buffer overrun: les données arrivent plus rapidement que la vitesse a laquelle ton programme les traite.

Resistance is not futile... it's voltage divided by current

[ Dernière édition du message le 15/12/2018 à 21:05:10 ]

3
Citation de aaB :
Le coup des notes non jouées me fait quand meme bien penser a un buffer overrun: les données arrivent plus rapidement que la vitesse a laquelle ton programme les traite.


Oui ça peut ressembler à ça.
Par exemple j'ai une interruption (ISR sur l'arduino Due) qui tourne en boucle à 64Khz. L'audio y est synthétisé en temps réel (oscillateurs, enveloppes, filtre 3 étages, delay... bref les calculs sont gourmands ... :) ).
Les autres fonctions, dont le MIDI sont traitées en second lieu ( "void loop()" sur l'arduino ) et donc moins rapidement.

Extrait de ma "void loop()" pour se synchroniser sur le MIDI:

MidiInput = Serial1.read ();

if ( MidiInput == 240 ) {
MidiPulseCounter = MidiPulseCounter + 1;
if (MidiPulseCounter == 6) {
MidiPulseCounter = 0;
}
}



Citation de aaB :
Si je devais le faire, je ferais un driver spécial et non des calls a la lib Serial: une petite machine a états qui lit les bytes bruts, filtre/parse les données, crée et passe uniquement les messages intéressants a l’application au travers d'une FIFO.


Là tu m'as perdu. :lol:

Il existe des librairies MIDI in pour Arduino qui assument ne pas être toujours réactives.
Pour la carte que j'utilise principalement, la Due qui tourne à 84Mhz, les librairies sont plus rares et les commandes un peu différentes. Je n'ai pas réussi à faire fonctionner le MIDI avec ça.

En tout cas, si je te comprends bien, pour toi la solution devrait être logicielle et non matérielle?
4
Citation de King :

Par exemple j'ai une interruption (ISR sur l'arduino Due) qui tourne en boucle à 64Khz. L'audio y est synthétisé en temps réel (oscillateurs, enveloppes, filtre 3 étages, delay... bref les calculs sont gourmands ...

A te lire, j'ai l'impression que tu as une interruption pour chaque échantillon audio et que tu calcules chaque échantillon dans l'ISR (du DAC?).
Si c'est le cas, c'est le premier problème a régler.
La manière habituelle, c'est d'utiliser un canal DMA entre un buffer (en RAM) et le registre data du DAC, et d'agir sur une interruption générée par le DMA (par exemple quand le buffer est a moitie vide) pour remplir le buffer.
Le DMA se charge de passer les échantillons un a un au DAC (sur interruption du DAC), ce qui te donne plus de temps entre les interruptions pour faire le reste du travail (calculer les échantillons de sortie, scanner les contrôles en façade, traiter les données MIDI, etc.).

Oui, ta solution est a coup sur logicielle, le Cortex-M3 sur lequel est base l'Arduino Due, est largement assez puissant pour faire un synthe (j'imagine VA) qui ne loupe aucune note MIDI et reste synchro.

Resistance is not futile... it's voltage divided by current

5
Citation de aaB :

A te lire, j'ai l'impression que tu as une interruption pour chaque échantillon audio et que tu calcules chaque échantillon dans l'ISR (du DAC?).
Si c'est le cas, c'est le premier problème a régler.

Absolument, c'est ainsi que travaillent mes programmes.

Citation de aaB :
La manière habituelle, c'est d'utiliser un canal DMA entre un buffer (en RAM) et le registre data du DAC, et d'agir sur une interruption générée par le DMA (par exemple quand le buffer est a moitie vide) pour remplir le buffer.

Jamais entendu parler de DMA mais je vois l'idée. Dans ce cas les calculs seraient effectués dans le void loop() ?
Après je me trompe peut-être mais j'ai tendance à penser que les calculs prennent toujours le même temps, que soit dans l'ISR ou ailleurs. Au final le temps libéré devrait être le même non? Ok il sera moins morcelé.


Citation de aaB :

Oui, ta solution est a coup sur logicielle, le Cortex-M3 sur lequel est base l'Arduino Due, est largement assez puissant pour faire un synthe (j'imagine VA) qui ne loupe aucune note MIDI et reste synchro.


Merci, ça c'est de la bonne info. je sais ce qu'il me reste à creuser. :lol:

Un exemple: https://www.youtube.com/watch?v=UO5aa68ijFg

[ Dernière édition du message le 16/12/2018 à 10:58:30 ]

6
Citation de King :

Jamais entendu parler de DMA mais je vois l'idée. Dans ce cas les calculs seraient effectués dans le void loop() ?
Après je me trompe peut-être mais j'ai tendance à penser que les calculs prennent toujours le même temps, que soit dans l'ISR ou ailleurs. Au final le temps libéré devrait être le même non? Ok il sera moins morcelé.

DMA: Direct Memory Access
C'est un mécanisme qui permet d'automatiser le transfert de données entre un périphérique et la RAM (ou entre deux adresses en RAM).
En gros tu le configures en lui donnant l’adresse a laquelle se trouvent tes données en RAM et, sur interruption du périphérique, le DMA va transmettre les données sans intervention de ta part.
Ta responsabilité est seulement de faire en sorte que les données soient disponibles lorsque le DMA en a besoin (et le DMA lui-même peut générer des interruptions pour que tu ne rate aucune deadline).
Pour l'utiliser, la datasheet de ton MCU est ta meilleure amie.
Vu que tu utilises l'arduino, il te faudra aussi vérifier quels canaux DMA sont potentiellement déjà utilisés par le système lui-même.
Peut-être qu'un utilisateur arduino pourrait venir compléter ici (je ne suis pas moi-même arduiniste, mais je programme des MCUs dans mon boulot; mes infos ne peuvent qu’être généralistes et non spécifiques à ta plateforme).

Au niveau du temps, c'est un peu plus complexe que ça:
* oui, le temps nécessaire au calcul est le même
* par contre, il ne faut pas négliger le temps nécessaire aux accès mémoire
* et surtout, ne pas oublier le temps nécessaire aux interruptions: a chaque fois qu'une interruption pète, on doit sauvegarder l’état des registres du processeur, traiter l'interruption, puis recharger les registres pour continuer ce qu'on faisait avant. On appelle ça un context switch.
Rendre le calcul "moins morcelé" comme tu dis, revient a avoir moins de context switchs et sera plus efficace.

Ramener ton calcul audio dans ta main loop permet, entre autres, de garder des ISR le plus simples (donc rapides) possible:
* on calcule les échantillons, le plus vite possible, et on remplit le buffer de sortie
* tant que le buffer est plein, on ne fait rien (ce qui laisse du temps pour le reste du système)
* des que le buffer est vide et on recommence.

Le plus souvent, on utilisera une stratégie de double buffering (je ne connais pas de DMA qui ne soit pas configurable pour fonctionner ainsi):
* on utilise deux buffers de sortie, l'un contrôlé par le DMA, l'autre disponible pour l'application
* l'application remplit son buffer puis attend
* l'ISR du DMA "échange" les buffers (change l'adresse contenue dans un pointeur) et signale a l'application qu'un nouveau buffer de sortie est disponible.

Avec un buffer de taille N et une fréquence d'échantillonnage Fs, on dispose de N / Fs secondes pour calculer un buffer, traiter les autres interruptions, etc. avant la prochaine interruption du DMA.

Resistance is not futile... it's voltage divided by current

7
On peut voir le DMA comme un co-processeur spécialisé dans les copies de données (en mémoire, ou entre mémoire et périphérique).

Et je confirme tout ce que dit aaB. Faire du traitement dans une routine d'interruption n'est pas une bonne idée, il est préférable de garder une telle routine la plus courte possible. Le double buffering est très utilisé dans tout ce qui traite du signal en temps réel (audio, image, radar ...)
Si tu as une FIFO matérielle dans un périphérique, elle a souvent une source d'interruption "presque vide", qui permet de démarrer un remplissage via DMA avant qu'elle soit vraiment vide.
8
Super, merci les gars. :bravo:


D'habitude les datasheet ne me font pas trop peur mais c'est une autre histoire pour les micro-contrôleurs. :)

Bon je vais creuser tout ça... en croisant les doigts pour tomber sur un extrait de code pour la Due.
9
Oui, les Cortex-M ne sont pas ce qui se fait de plus simple comme MCU.
En prenant ton temps, tu devrais quand même t'en sortir, dans mes souvenirs la doc de chez Atmel est en général bien faite.
La dernière foisque j'ai utilisé un SAM4, c'etait il y a 4 pu 5 ans mais je me souviens qu'il y avait pas mal d'exemples de code sur le site d'Atmel.

Resistance is not futile... it's voltage divided by current

10
Merci Aab. :)

C'est vrai qu'Atmel présente bien ses produits, ça devrait m'aider à rentrer dans la doc.

Sous ces éclaircissement je vais pouvoir finaliser le hardware en cours avant d'étudier mieux tout ça.
11
Bon, bon, bon...

Déjà je n'avais pas tilté qu'Atmel s'était fait racheter il y a quelques années. :)

Sinon côté library DMA j'ai bien trouvé quelque chose pour l'Arduino Due.
Sauf que c'est destiné aux DACs de la carte qui sont réputés pour être pollués (mauvais développement/routing ), j'utilise donc des DACS externes qui communiquent en SPI (des MCP-4922). Bref la library n'est pas très utile ici. :??:

Côté SAM3X8E j'ai commencé à voir tout ça, on dirait bien que l'appNote "DMA-Controller-DMAC_ApplicationNote_AT07892.pdf" est mon amie.
Je comprends grosso modo les commandes mais je suis encore à mille lieux de saisir comment organiser tout ça.

Je posterai quelques bouts de codes et détaillerai mon projet dans les jours à venir. Je vous souhaite un joyeux Noël d'ici là.

[ Dernière édition du message le 21/12/2018 à 20:15:48 ]

12
Salut à tous.

J'ai comme l'impression d'être tombé sur le bon topic :-D

Je suis votre discussion car je suis actuellement en réflexion pour me faire un afficheur avec 4 led ou plus pour le tempo et les noires de chaque mesure.

Est-ce que vous sauriez m'aider sachant que j'ai commander des prises midi femelles et que j'ai un arduino (ancien) par lequel je voudrais recevoir le midi pour envoyer un signal sur mes leds ?

D'avance merci.
13
Citation de Obiwan78 :
je suis actuellement en réflexion pour me faire un afficheur avec 4 led ou plus pour le tempo et les noires de chaque mesure.
Est-ce que vous sauriez m'aider sachant que j'ai commander des prises midi femelles et que j'ai un arduino (ancien) par lequel je voudrais recevoir le midi pour envoyer un signal sur mes leds ?


Connais-tu déjà les grandes lignes de la normes MIDI ?

Formateur en techniques sonores ; électronicien ; auteur @ sonelec-musique.com

14
x
Hors sujet :
Citation de Obiwan78 :
Est-ce que vous sauriez m'aider sachant que j'ai commander des prises midi femelles et que j'ai un arduino (ancien) par lequel je voudrais recevoir le midi pour envoyer un signal sur mes leds ?


Tu parles bien d'un système qui est esclave sur une horloge MIDI externe?

Déjà il te faut le bon circuit qui ne se limite pas à des prises:
https://sandsoftwaresound.net/wp-content/uploads/2016/05/schematic_midi_in.jpg
- 1 prise DIN 5 pins
- 2 résistances 220 Ohms
- 1 diode 1N4148
- 1 optocoupleur 6N138

15
Je ne connais pas à proprement parlé la norme midi même si je j’utilise dans mon setup Home Studio avec mes synthés.
La c’est pour avoir un retour les au click (tempo) histoire de bien voir ou j’en suis.
Il y a plein d’appli sur IPad mais aucun ne gère le midi In sauf si vous savez laquelle ?
16
Hello,

Je réponds à la question Dans un synthétiseur numérique, vous semble t'il judicieux de dédier un micro-contrôleur à la réception et au filtrage des données MIDI ?

Dans l'absolu, non. Un buffer de réception bien conçu prend un temps de traitement négligeable.

Ça m’étonnerait que tu aies un tampon d'entrée plein. Pour info tu as à la louche 300 microsecondes entre chaque réception d'octet complet... On peut en faire des choses, en 300 microsecondes. Pour ton histoire de notes qui ne démarrent pas ou ne s'arrêtent pas, je pense que l'erreur vient de là:

if ( MidiInput == 240 ) {

240 = 0xf0 = 0b11110000 = début de message exclusif.

Du coup, je ne vois pas trop ce que fait cet exemple.

Pour gérer les notes le standard midi autorise les messages 1 octet noteon/noteoff + 1 octet note + 1 octet vélocité mais aussi 1 octet noteon/noteoff et autant de couples 1 octet note + 1 octet vélocité. Autrement dit un seul noteon/noteoff et un nombre de notes non prédictible.

De plus les messages temps réel (taille 1 octet unique) peuvent s'intercaler n'importe où dans ton flot d'entrée, causant un bordel monstre si tu ne le gère pas.

J'ai vu quelqu'un te conseiller une machine d'état, c'est une très bonne idée. Saches juste qu'il existe déjà de nombreux drivers de réception midi en C et d'autres langages. Ils sont bien sûrs conçus autour d'une machine d'état et d'une réception bufferisée sous interruption, mais au delà du plaisir de le refaire soi-même, ils ont étés testés sous toutes les coutures. Ça et le fait qu'il y ait une communauté autour sont deux aspects non négligeables.

https://www.midi.org/

A+

Pfeuh



17
Salut à tous.

Par rapport au commentaire de Pfeuh:

Bien vu! cet exemple était ancien et faux. L'idée ici était de lire l'horloge MIDI, la valeur aurait donc dû être 248 qui correspond 11111000.

J'ai remis les mains dans la programmation ces derniers jours, en ce moment je bosse sur un synthé polyphonique, je commence avec trois voix pour déblayer le terrain.
Malgré l'erreur que tu as repéré je n'ai pas trop de soucis avec le traitement du MIDI: note On et Off, etc... je ne sais pas si les choses sont faites dans les règles de l'art mais disons qu'elles fonctionnent.

Le code récemment pondu qui permet de piloter 3 voix (hauteur et trigger):
Spoiler - Cliquer ici pour lire la suite



Voilà , je me sens surtout handicapé face à ces histoires de DMA pour l'audio. Je vais avoir un peu de temps ces jours-ci pour m'y plonger.

A défaut de DMA, j'ai traficoté un système de buffer avec une "array" disponible depuis ma Loop principale et mon ISR. J'imagine que c'est pas vraiment la meilleure formule mais les premiers tests semblaient fonctionner. Je ne sais pas si c'est une bonne idée.

[ Dernière édition du message le 25/12/2018 à 21:51:10 ]

18
Bon, je reviens sur ce sujet. Reprenons depuis le début. :)


Déjà, pour lire les messages MIDI entrant, j'utilise la commande serial. En gros je l'utilise ainsi:


int incoming_MIDI_Byte;

void setup() {
  Serial1.begin(31250);                     // prépare à la communication MIDI sur le port série 1.
}


void loop() {
  incoming_MIDI_Byte = Serial1.read ();       // lit le message sur le port série 1
}

( J'ai fait un effort sur le mise en page ;) )

Voilà c'est tout bête mais je me demande déjà si c'est une bonne idée d'utiliser cette commande serial... car il me semble que c'est une commande Arduino qui n'est pas forcément optimisée... pour vous est-ce à priori quelque chose dont je dois me soucier pour le moment ? (Je suis presque débutant).
Aab tu avais parlé d'une machine à état , de FIFO... ça s'utilise comment dans ce cas là? Je crains que ça dépasse largement le cap de mes compétences.

Ensuite je me demande si une interruption doit être liée à la réception d'un message MIDI. Je sais coder une interruption pour un bouton par exemple mais je ne vois pas trop comment m'y prendre pour la réception d'un message MIDI....

[ Dernière édition du message le 30/01/2019 à 19:59:30 ]

19
Hello,

Sur un arduino, tu n'as pas le choix, le seul driver série (donc capable de transporter des messages midi) est Serial (Serial2, Serial3 et Serial4 aussi si tu as un gros arduino)

Ce driver marche déjà sous interruption. Quand un caractère est reçu, il est mis dans un tampon circulaire. Quand tu appelles la méthode Serial.available(), c'est le nombre de caractères en attente dans ce tampon circulaire qui t'es retourné. Quand tu lis un caractère, ce nombre est décrementé sans que tu ne t'occupes de rien. C'est ça, une gestion des interruptions.

Toi, tu commences ton programme par Serial.read(), ce qui a pour effet... De totalement bloquer le processeur (sauf les interruptions et le DMA jusqu'à ce qu'un caractère arrive... Pas très efficace...

Pour faire plusieurs tâches à la fois, il faut diviser chaque tâche en étapes et en transitions. Tu as ta boucle principale qui appelle toutes les machines d'état les unes après les autres et qui recommence indéfiniment.

Je suis tombé sur cet exemple, il a l'air d'être assez pédagogique.

https://forum.arduino.cc/index.php?topic=470879.0

A vue de nez, tu aurais les états suivants:
etat attente
etat premier octet recu attente du deuxieme
etat deuxieme octet recu attente du troisieme (on traite le message et on revient a l'etat attente)
etat octet clock (on traite l'octet et on revient à l'etat precedent)

C'est le cas le plus simple. Comme dit, il y a déjà d'excellent drivers midi tous faits qui intègrent tout ça et bien plus.

A+

Pfeuh
20
D'accord avec ce que dit pfeuh.

Pour la lib Serial, s'il existe effectivement un call qui te renvoie le nombre de bytes dans le buffer, c'est ce qu'il faut utiliser et n'appeler read() que lorsque le buffer n'est pas vide.


Pour les machines à états, le lien est pas mal, un peu confus peut-être à mon goût.
Ce qu'il faut retenir, c'est que c'est juste une abstraction.
C'est quelque chose que tu peux dessiner sur un papier pour représenter ton système et ainsi construire ta pensée avant de commencer à écrire du code.
En ce sens, l'exemple du lien avec les lumières est bon.

Pour le MIDI, ça sera plus complexe, selon si tu décides d'implémenter toute la spec ou juste la partie qui t'intéresse, mais tu peux tout à fait représenter les actions nécessaires au décodage d'un flux MIDI avec ce genre d'abstraction.
Dans tous les cas, il te faudra construire, sur papier, ta machine à états en te référant à la spec, et ensuite imaginer comment la représenter en C.

Effectivement, la manière naive (pas au sens péjoratif, mais au sens où c'est la correspondance la plus directe entre le dessin et le code) sera un switch():

enum {
    IDLE,
    STATE_1,
    STATE_2,
} state;
for ( ; ; ) {
    switch (state) {
        default:
        case IDLE:
            if (condition) {
                do_something();
                state = STATE_1;
            }
            break;
        case STATE_1:
            /* do something else */    
            if (reset_detected()) {
                state = IDLE;
            } else if (something_happens()) {
                state = STATE_2;
            }
            break;
        /* and so on and so forth */
    }
}


Resistance is not futile... it's voltage divided by current

21
Merci les gars, j'essaye d'assimiler tout ça... Pour la machine à état j'ai encore des doutes pour savoir si on parle bien de la même chose..

Voici une librairie (.cpp) que j'ai pondu il y a quelques semaines, je me demande si ce ne serait pas tout simplement ça une machine à état:

Spoiler - Cliquer ici pour lire la suite

Cette librairie fonctionne ( c'est ma toute première librairie hourra! :lol:), j'utilise des machines à état malgré moi?

En fait je suis super intrigué par ce qu'a écrit Pfeuh:
Citation :
Toi, tu commences ton programme par Serial.read(), ce qui a pour effet... De totalement bloquer le processeur (sauf les interruptions et le DMA jusqu'à ce qu'un caractère arrive... Pas très efficace...

Hum cela veut dire qu'il me faudrait plutôt une commande qui interroge le port pour savoir si il a reçu oui ou non des datas?
Il faudrait donc plutôt déjà utiliser la commande Serial.available() ?

[ Dernière édition du message le 31/01/2019 à 20:09:23 ]

22
Citation de King :

En fait je suis super intrigué par ce qu'a écrit Pfeuh:
Citation :
Toi, tu commences ton programme par Serial.read(), ce qui a pour effet... De totalement bloquer le processeur (sauf les interruptions et le DMA jusqu'à ce qu'un caractère arrive... Pas très efficace...

Hum cela veut dire qu'il me faudrait plutôt une commande qui interroge le port pour savoir si il a reçu oui ou non des datas?
Il faudrait donc plutôt déjà utiliser la commande Serial.available() ?

Exactement.
L'appel à read() est bloquant.
C'est à dire que la fonction ne retournera un byte que s'il y en a un à lire dans le buffer d’entrée, si aucun byte n'arrive, parce que le câble MIDI est débranché par exemple, la fonction ne retournera jamais.

Cela introduit un effet de bord assez ennuyeux: tant que la fonction n'a pas retourne de byte, le programme ne fait rien ne peut plus rien faire.
Bien sur, si une interruption pète, l'ISR correspondante sera exécutée (après un context switch), mais c'est tout.
L'appel à available(), lui, n'est pas bloquant: si le buffer est vide, il retournera 0 et ton programme pourra continuer à travailler à autre chose jusqu'à ce que available() retourne une valeur non nulle.

Spoiler - Cliquer ici pour lire la suite


Pour ta lib, déjà bravo! et oui, c'est le principe.

L'automate fini est juste un moyen de formaliser ta réflexion, de documenter ton code, d'avoir une référence à laquelle comparer ton programme s'il ne fonctionne pas comme prévu, etc.
Sans compter qu'il est toujours préférable de mettre ses idées au clair avant de commencer à coder, et un dessin est plus facile a lire qu'un pavé de texte!

Resistance is not futile... it's voltage divided by current

[ Dernière édition du message le 31/01/2019 à 20:32:45 ]

23
Ok merci Aab j'ai compris des trucs là ! Merci Pfeuh aussi ! :bravo:

Je laisse murir tout ça.... quelques jours.. :8)

24
Sans m'être encore attaqué au DMA j'ai déjà pu utiliser la commande Serial1.available en conditions.

Je me suis déjà fais un petite frayeur, cette commande étant répertorié uniquement pour la Mega sur cette page:
https://www.arduino.cc/en/serial/available
Heureusement la Due la reconnait aussi. :lol:

En pratique c'est le jour et la nuit par rapport aux précédent tests.
A présent la synchro tient comme il faut. C'est un grand bond en avant. :bravo:


Merci encore les gars.

[ Dernière édition du message le 05/02/2019 à 20:01:35 ]