ANALYSEUR de TRAME DCC

















MONTAGE à BASE de l'ANALYSEUR de TRAME




PRÉAMBULE
Dans des pages précédentes nous avons vu le contenu des différentes trames DCC pour :
• La commande des décodeurs de locomotive,
• La commande des décodeurs d'accessoire,
• La programmation des Cv des décodeurs d'accessoire ou de locomotive,
• ...

Nous avons également abordé le fonctionnement des microcontrôleurs ainsi que le rôle des timers/compteurs, registres et interruptions.
Grâce à ces connaissances nous pouvons réaliser un analyseur de trame DCC.

L'analyseur de trame DCC va nous permettre de visualiser sur le moniteur série de l'arduino les trames DCC injectées dans les rails par la centrale DCC.

Cela permettra : 
• De bien ou mieux comprendre le contenu des trames DCC générées par la centrale en visualisant, les 1 ou les 0 circulant sur la voie,
• Pour ceux qui développe leur propre centrale ou décodeur, de détecter et de corriger les erreurs ainsi décelées.

Ces trames seront organisées d'une manière aérée facilitant l'observation et le changement des différents bytes en temps réel.

trame idle
Trame Idle

Trame Locomotive : Adresse 16, Vitesse 18 

Ce programme fonctionne sur un Atméga 328 qui équipe les Arduino Uno.

Le programme développé se veut le plus simple possible pour permettre de le modifier et le personnaliser.
Je détaillerai le programme pour une meilleure appropriation, compréhension et modifications ultérieures.

Si vous comptez l'utiliser dans vos sites web merci de faire référence à son développeur : Aéronef du site ferroviaire.
ADAPTER les SIGNAUX DCC
Dans un premier temps il faut adapter les signaux qui circulent sur le réseau et les envoyer vers l'Arduino.

En effet les signaux DCC ont une amplitude qui est de +18 volts à -18 volts.

Cette amplitude de tension est trop importante pour l'Arduino qui ne supporte en entrée qu'une tension maximale de : + 5 volts.

signal dcc
Signal DCC

Cette adaptation doit remplir deux conditions essentielles :
• Diminuer la tension de +/- 18 volts à une tension de +5 volts maximum,
• Conserver la forme des créneaux représentant les bytes 0 et 1 du signal DCC sans les altérer pour permettre leurs détections.

Cette tâche est réalisée par un composant dénommé OPTOCOUPLEUR.

Optocoupleur

L'optocoupleur permet également d'isoler électriquement la partie puissance, le RÉSEAU, de la partie commande, l'ARDUINO, puisque la transmission entre les deux est réalisée par l'intermédiaire de la lumière.

Cela présente l'avantage en cas de problème sur la partie puissance de ne pas impacter la partie commande ce qui constitue une protection pour l'Arduino.

BRANCHEMENT de l'OPTOCOUPLEUR
Il existe de nombreux types d'optocoupleur qui présente des caractéristiques différentes.

Je vous présente les montages que j'ai testés et qui fonctionnent :
• Optocoupleur H11L1M,
• Optocoupleur 4N37.

Optocoupleur H11L1M

branchement optocoupleur h11l1m
Avec optocoupleur H11L1M

Les composants :
Optocoupleur : H11L1M
R1 = 10KΩ,
R2 = 100 Ω,
R3 = 10 KΩ,

D1 et D2 = 1N4148.


Optocoupleur 4N37

branchement optocoupleur 4n37
Avec optocoupleur 4N37

Les composants :
Optocoupleur : 4N37
R1 = 2,2KΩ,
R2 = 1,5 KΩ,

D1 = 1N4148.

Le LOGICIEL
Préambule
Une fois l'adaptation des signaux réalisée il faut développer le programme qui va permettre de reconstituer la trame DCC.

La fréquence d'une trame DCC est d'environ 10 Khz. Les signaux changent d'état (Haut, Bas) en permanence, il n'est donc pas possible de surveiller de manière logicielle ce changement d'état en permanence car cela monopoliserais trop le microcontrôleur pour cette seule tâche.

Pour se décharger de cette tâche de surveillance nous allons utiliser une interruption qui s'en chargera.
Le signal DCC provenant de la voie nous utiliserons une interruption externe
PCINT2.
L'interruption externe
PCINT2 s'applique pour un port complet (PORT D).

En complément de cela les interruptions externes
INT0 et INT1 possèdent chacune leur propre vecteur d'interruption. C'est à dire que l'on peut les utiliser et les configurer séparément.

Nous allons donc utiliser le vecteur d'interruption
INT0 pour surveiller le changement d'état de la broche 2 (PD2) de l'arduino.

Pourquoi utiliser l'INT0 broche 2 de l'arduino :
• Chaque interruption ou vecteur d'interruption possède un niveau de priorité au sein du microcontrôleur,
• Le vecteur d'interruption
INT0 possède le niveau de priorité le plus haut après le RESET, c'est pourquoi on l'utilise.

niveau priorite interruptions arduino
Niveau de priorité des interruptions

L'interruption utilisée
Le vecteur d'interruption utilisée sera INT0 branché sur la broche 2 de l'arduino (PORTD 2).
L'interruption INT0 peut se déclencher suivant 4 conditions :
• Interruption sur détection d'un niveau bas (Low level), 
  • Tant que le niveau sur la broche correspondante est bas il y aura exécution de la routine d'interruption, 
• Interruption sur changement d'état (Any logical change), 
  • L'interruption se déclenche à chaque fois que la broche change d'état : HIGH à LOW ou LOW à HIGH
• Interruption sur front montant (Falling), 
  • L'interruption se déclenche à chaque fois que la broche passe de LOW à HIGH
• Interruption sur front descendant (Rising). 
  • L'interruption se déclenche à chaque fois que la broche passe de HIGH à LOW.

Choix du déclenchement
Il faut maintenant décider sur quelles conditions notre interruption se déclenchera car cela conditionne le fonctionnement du programme donc son écriture.

Le signal DCC après passage dans l'optocoupleur passe en permanence de l'état Haut (5v) à l'état Bas (0v) :
• On peut déclencher l'interruption sur un passage de LOW à HIGH (Falling), ou de HIGH à LOW (Rising). On mesure ensuite le temps écoulé entre deux interruptions pour savoir si l'on à a faire à un byte 1 ou un byte 0.
   • L'inconvénient de cette méthode est que notre analyseur possèdera un sens de branchement sur la voie pour capter les signaux DCC.

                               
                                                                                                                              
Byte 0
                                                
Byte 1

Pour ne pas avoir cette limitation et pouvoir brancher notre analyseur de manière indifférente sur la voie nous utiliserons l'interruption sur changement d'état (Any logical change) qui déclenche une interruption à chaque fois que la broche passe de
HIGH à LOW ou LOW à HIGH.
   • Il faudra dans ce cas tenir compte que nous détecterons des durées correspondant à un demi-byte 1 ou 0.

                                   
                                                                                                                         
Demi-byte 0
                                      
Demi-byte 1
Déclaration des variables
Comme dans tout programme on commence par déclarer les variables que l'on va utiliser.
Pour minimiser l'impact en mémoire on déclarera les variables en fonction de leur contenu futur.

De plus comme le contenu des variables va changer pendant le traitement de l'interruption, on les déclarera comme volatile :

• Volatile force le compilateur à aller chercher la variable en mémoire à chaque fois qu’elle est utilisée. De cette manière la variable en mémoire est toujours à jour,
• En l’absence de volatile, le compilateur pourrait produire un code où une variable partagée est copiée dans un registre et la variable en mémoire n’est jamais mise à jour dans une partie du programme,
• Des modifications faites dans le programme principal ne serait pas vues par la routine d’interruption.

volatile byte demi_bitun = 0;  //Variable de comptage d'un demi_byte à 1 
volatile byte demi_bitzero = 0;  //Variable de comptage d'un demi_byte à 0 
 
volatile byte preambule;  //Variable de comptage du Préambule 
 
volatile boolean validpreambule = 1;  //Valide la construction du préambule 
 
volatile int tramedcc = 0;  //Valide la construction de la trame DCC 
 
volatile byte tramedcccomplete [55] = {0};  //Tableau d'enregistrement de la Trame DCC 
 
volatile int i;  //Variable de comptage du tableau
Déclaration des variables

Le Setup
Dans le Setup on trouve les instructions qui ne seront exécutées qu'une seule fois.

Pour obtenir une vitesse maximale on utilisera des instructions de bas niveau chaque fois que possible.

void setup() 

Serial.begin (1000000);
//Vitesse transmission du moniteur série 
 
TCNT0 = 0;//Mise à 0 du timer 0 
 
TCCR0B = 0b00000011;//Défini le rapport de division du Timer 0 par 64 
 
EICRA = 0b00000001;//CHANGE INT0 

EIMSK |= (1<<0);//Autorise INT0 
 
}
void setup() 

Serial.begin (1000000);
//Vitesse transmission du moniteur série 
 
TCNT0 = 0;//Mise à 0 du timer 0 
 
TCCR0B = 0b00000011;//Défini le rapport de division du Timer 0 par 64 
 
pinMode(interruptPin, INPUT_PULLUP);//Déclaration en entrée de la broche 2 

attachInterrupt (digitalPinToInterrupt(interruptPin), dureeimpulsion, CHANGE); 
//Déclaration et appel de l'interruption 
}
                                              
Setup avec instruction bas niveau
                                                                                                           
Setup avec instruction haut niveau

Serial.begin (1000000);//Vitesse transmission du moniteur série
Permet de régler la vitesse de transmission pour visualiser les trames DCC,
• La vitesse indiquée dans le programme doit être identique avec celle dans le moniteur série.

TCNT0 = 0;//Mise à 0 du timer 0 
• Utilisation du timer 0 (8 bits) pour mesurer la durée des bytes des trames DCC,
• Le timer 0 est mis à 0.


TCCR0B = 0b00000011;//Défini le rapport de division du Timer 0 par 64
• Le timer 0 étant un compteur 8 bits il ne peut compter que jusqu'à 256,
• On va ralentir le comptage du timer 0 en divisant la fréquence d'horloge (16Mhz) par 64 par utilisation du diviseur du timer 0 en agissant sur le registre
TCCR0B de l'Atméga 328,
• Le timer 0 s'incrémentera tous les (1/(16000000/64) = 4µs (4 microsecondes),
• L'utilisation du diviseur du timer 0 et non pas le prescaler permet de ne pas ralentir la liaison série pour visualiser les trames DCC sur le moniteur série.


EICRA = 0b00000001;//CHANGE INT0
• On agit ici sur le registre EICRA de l'Atméga 328 pour indiquer que l'on désire une interruption pour chaque changement d'état du signal (Any logical change),
• Le vecteur d'interruption sera : ISR (INT0_vect)

EIMSK |= (1<<0);//Autorise INT0
• On agit ici sur le registre EIMSK de l'Atméga 328 pour indiquer que l'on autorise l'interruption pour INT0,

pinMode(interruptPin, INPUT_PULLUP);//Déclaration en entrée de la broche 2 
attachInterrupt (digitalPinToInterrupt(interruptPin), dureeimpulsion, CHANGE); //Déclaration et appel de l'interruption
En instruction de haut niveau :
  • On déclare la
broche 2 en entrée,
  • On déclare l'interruption :
      • Broche d'entrée : digitalPinToInterrupt(interruptPin),
      • Nom de la fonction appelée : dureeimpulsion,
      • Condition du déclenchement : CHANGE.

Le Loop
Le Loop restera vide puisqu'on ne fera appel qu'à l'interruption ou des fonctions.

L'interruption
Nous allons voir ici l'interruption appelée lorsqu'un changement d'état se produit sur la broche 2.

ISR (INT0_vect) 
  //void dureeimpulsion() //Déclenché par CHANGE

if (TCNT0 < 19) 
//Si TIMER0 : durée < 76µs alors c'est un bit 1 

TCNT0 = 0; 
//Raz compteur interruption 
 
demi_bitun = demi_bitun + 1 ; //Incrémente demi_bitun
demi_bitzero = 0; //Raz pour éviter un demi_zero suivi d'un demi_un 
 
if (demi_bitun = = 2) //Valide si deux demi_un se suivent 
{demi_bitun = 0; 
 
if (validpreambule = = 1) 
//Si à 1 permet la construction du préambule 
{bitun();} 
 
if (tramedcc = = 1) //Si à 1 permet la construction de la trame DCC 
{debuttramedccun();} 
 
  }   
//Fin du Valide si deux demi_un se suivent 
  }  //Fin du if (TCNT0 < 19)  //Si durée < 76µs alors c'est un bit 1 
     //(16000000/64) = 4µs * 19 = 76µs 

 
 
else //Si timer TC0 > 19 alors c'est un bit 0 

TCNT0 = 0; 
//Raz compteur interruption 
 
demi_bitzero = demi_bitzero + 1 ; //Incrémente demi_bitzero
demi_bitun = 0; //Raz pour éviter un demi_un suivi d'un demi_zero 
 

if (demi_bitzero = = 2) //Valide si deux demi_zero se suivent 
{demi_bitzero = 0; 
 
if (validpreambule = = 1) //Si à 1 permet la construction du préambule 
{bitzero();} 
 
if (tramedcc = = 1) 
//Si à 1 permet la construction de la trame DCC 
{debuttramedcczero();} 
 
  }  
//Fin du Valide si deux demi_bitzéro se suivent 
  }  //Fin du if (TCNT0 < 19)  //Si durée > 76µs alors c'est un bit 0 
     //(16000000/64) = 4µs * 19 = 76µs
 
 
 }  //Fin du ISR (INT0_vect) ou void dureeimpulsion()  //Déclenché par CHANGE

Traitement de l'interruption

ISR (INT0_vect) 
• A chaque fois qu'une interruption est déclenchée par le changement d'état de la broche 2, le microcontrôleur interrompt le programme principal et saute au vecteur d'interruption correspondant et traite les instructions de l'interruption.

En instruction de haut niveau l'instruction :
• ISR (INT0_vect) est remplacée par void dureeimpulsion() //Déclenchement par CHANGE

Généralités
Le début de l'interruption commence par un test pour savoir si l'on a à faire à un byte à 1 ou à 0.
A cet effet on utilise le test : 

if (TCNT0 < 19)
  {Instructions}

  else
  {Instructions}


Le traitement de l'information est similaire que l'on est à faire à un byte 1 ou 0.
Détails du programme
if (TCNT0 < 19)
• Ici s'effectue la mesure du temps entre deux changement d'état de la broche 2, (Rappel : le timer 0 est incrémenté tous les 4 µs),
• On teste si l'on a à faire à un
demi_byte 1 ou un demi_byte 0,
   • Un
byte à 1 mesure entre 110 et 122 µs. Un demi_byte à 1 mesure donc entre 55 et 61 µs,
   • Un
byte à 0 mesure entre 200 µs et 19800 µs. Un demi_byte à 0 mesure donc entre 100 et 9900 µs,
• Lorsque l'interruption est appelée on mesure la durée entre les deux appels :
   • 4µs * 19 = 76µs
   • Si le résultat est inférieur à 76 µs (en incluant une marge) correspondant à une incrémentation de 19 du timer 0, alors c'est un byte 1, et on exécute les instructions correspondant à if
(TCNT0 < 19),
   • Si le résultat est supérieur à 76 µs (en incluant une marge) correspondant à une incrémentation supérieur à 19 du timer 0, alors c'est un byte 0, et on exécute les instructions correspondant à else.

TCNT0 = 0; //Raz compteur interruption
• On remet le timer 0 à 0 pour une nouvelle mesure.

demi_bitun = demi_bitun + 1 ; //Incrémente demi_bitun
demi_bitzero = 0; //Raz pour éviter un demi_zero suivi d'un demi_un
if (demi_bitun = = 2) //Valide si deux demi_un se suivent
{demi_bitun = 0;
• Lors d'un début de détection de la trame DCC ou d'un changement de trame DCC on ne sait pas si c'est un byte à 1 ou 0 qui va arriver le premier,
• De plus il est possible que l'on se trouve à cheval sur deux bytes différents 1 ou 0,
• Si l'on se trouvait à cheval sur un demi_byte à 1 et un demi_byte à 0 on ne pourrait jamais décoder la trame,
• Ces deux instructions sont là pour s'assurer que l'on a bien deux demi_byte 1 qui se suivent (ou deux demi_byte à 0 dans le traitement des informations après le else),
   • Si un demi_byte à 1 arrive on incrémente la variable
demi_bitun (demi_bitun = demi_bitun + 1 ; //Incrémente demi_bitun),
   • Si le demi_byte précédent était un byte 0 on met la variable
demi_bitzero à 0 (demi_bitzero = 0; //Raz pour éviter un demi_zero suivi d'un demi_un),
   
 On agit de même mais en sens inverse si un byte_zero (else) avait été détecté,


Ensuite lorsque l'on effectue le test (if (demi_bitun == 2) //Valide si deux demi_un se suivent) on peut savoir dans quelle configuration on se trouve :
   • Si le test est négatif il s'agissait d'un demi_byte 0 suivi d'un demi_byte 1,
      • Rien n'est validé et on attend une autre interruption,
   • Si le test est positif, on a bien deux demi_byte à 1 qui se suivent, et on peut valider l'arrivée d'un byte 1,
      • On peut exécuter les instructions suivantes :
         • On commence par remettre à zéro la variable demi_byte 1 pour pouvoir recommencer notre test : {demi_bitun = 0;

if (validpreambule = = 1) //Si à 1 permet la construction du préambule 
{bitun();}  
• La deuxième instruction exécutée lorsque le test est positif est if (validpreambule = = 1) //Si à 1 permet la construction du préambule) :
• La variable validpreambule est mis à 1 dans le setup,
• On teste la variable validpreambule qui permet la construction du préambule :
   • Tant que la variable validpreambule est à 1 le préambule n'est pas validé et aucun des bytes composant la trame DCC n'est acceptés. 
   • On appelle ensuite la fonction (bitun();) qui permet de construire le préambule,
• Lorsque la variable validpreambule est mise à 0, le préambule est construit et validé, le programme peut passer à la suite de la trame DCC.

if (tramedcc = = 1) //Si à 1 permet la construction de la trame DCC 
{debuttramedccun();} 
• L'instruction suivante exécutée lorsque le test est positif est if (tramedcc = = 1) //Si à 1 permet la construction de la trame DCC ) :
• La variable tramedcc est mis à 0 dans le setup,
• On teste la variable tramedcc qui permet la construction de la trame DCC :
   • Tant que la variable tramedcc est à 1 la trame DCC n'est pas validé et on continu d'accepter les bytes suivants, 
   • On appelle ensuite la fonction (debuttramedccun();) qui permet de construire la trame DCC,
• Lorsque la variable tramedcc est mise à 0, la trame DCC est construite et validée, le programme peut passer à l'exploitation des informations enregistrées.

Traitement du else
• Le processus est le même mais pour un byte_0 
• Seule la partie suivante est différente mais le principe reste le même :

demi_bitzero = demi_bitzero + 1 ; //Incrémente demi_bitzero
demi_bitun = 0; //Raz pour éviter un demi_un suivi d'un demi_zero 
 

if (demi_bitzero = = 2) //Valide si deux demi_zero se suivent 
{demi_bitzero = 0; 

Fonction bitun()

void bitun() 

if (validpreambule == 1)  
//Si à 1 permet la construction du préambule 

preambule = preambule + 1 ;  
//Augmente le compteur du preambule 
} //Fin du If
//Fin du Void bitun()
Fonction bitun()

Lorsqu'un byte à 1 est reçu (c'est à dire après la validation de deux demi_byte à 1 par :
demi_bitun = demi_bitun + 1 ; //Incrémente demi_bitun
demi_bitzero = 0; //Raz pour éviter un demi_zero suivi d'un demi_un
if (demi_bitun = = 2) //Valide si deux demi_un se suivent
{demi_bitun = 0;

La fonction bitun() est appelée et la construction du préambule commence :
• Tant que la variable
validpreambule est à 1 les instructions sont exécutées :
  • La variable
préambule est incrémentée de 1.
Fonction bitzero()

void bitzero() 

if (preambule < 12 && validpreambule = = 1)
//Si validpreambule = 1 permet la construction du préambule

preambule = 0;  
//Remet la variable préambule à zéro : il faut au moins 12 bit à un consécutifs 

} //Fin du If
 
if (preambule > 11 && validpreambule = = 1)  //Le préambule est validé 
{  
tramedcc = 1;  
//L'enregistrement de la trame DCC est possible 
validpreambule = 0;  //Préambule contient le nombre de 1 
} //Fin du If
 
}  //Fin du Void bitzero()

Fonction bitzero()

Lorsqu'un byte à 0 est reçu (c'est à dire après la validation de deux demi_byte à 0 par :
demi_bitzero = demi_bitzero + 1 ; //Incrémente demi_bitzero
demi_bitun = 0; //Raz pour éviter un demi_un suivi d'un demi_zero 
if (demi_bitzero = = 2) //Valide si deux demi_zero se suivent 
{demi_bitzero = 0; 

La fonction bitzero() est appelé :
• Lorsqu'un zéro est reçu, un premier test vérifie si le préambule peut être validé : if (preambule < 12 && validpreambule = = 1)
  • La variable validpreambule est à 1 cela permet la construction du préambule,
  • Pour valider le préambule il faut 16 bytes à 1 consécutifs (Ici le préambule est validé à partir de 12 bytes à 1 consécutifs pour garantir une compatibilité avec d'ancienne centrale DCC),
  • Si le test est positif cela signifie qu'un byte 0 c'est intercalé pendant la construction du préambule avant d'avoir reçu 12 bytes à 1 consécutifs, 
  • Le préambule n'est pas validé, la variable preambule est remise à 0 et le processus recommence.

                                                                                                                         ---------------------------------------
                                                                                                                         ---------------------------------------
• Lorsqu'un zéro est reçu, le deuxième test vérifie si le préambule peut être validé : if (preambule > 11 && validpreambule = = 1)
  • La variable validpreambule est à 1, cela permet la construction du préambule,
  • Pour valider le préambule il faut 16 bytes à 1 consécutifs (Ici le préambule est validé à partir de 12 bytes à 1 consécutifs pour garantir une compatibilité avec d'ancienne centrale DCC),
  • Si le test est positif cela signifie qu'au moins 12 bytes à 1 consécutifs ont été reçus,
  • Le préambule est validé :
     • La variable preambule contient le nombre de byte à 1 composant le préambule,
         • La validation du préambule signifie que l'on a reçu un byte à 0,
         • Implicitement cela signifie que ce byte à 0 reçu est le byte séparant le préambule du premier octet de la trame DCC,
     • La variable tramedcc est mise à 1 et permet l'enregistrement de la trame DCC,
     • La variable validpreambule est mise à 0 pour permettre la construction de la trame DCC.

Enregistrement trame DCC
Une trame DCC permettant la programmation de 510 décodeurs d'accessoire contient 6 octets plus les bytes 0 de séparation de chaque octet et le byte à 1 de fin de trame.
Notre analyseur pourra décoder au maximum 6 octets, si on veut plus il est possible de le faire.

La trame DCC est enregistrée dans un tableau de
55 cases :
6 octets * 8 bytes = 48,
6 bytes à 0 de séparation des octets,
1 byte à 1 de fin de trame.

Fonction debuttramedccun

void debuttramedccun()  //Construction de la trame DCC bit 1 
{if (i<55) 
{tramedcccomplete[i] = 1;  
//Enregistrement des bits dans le tableau de la trame DCC 
i = i + 1; 

else 

nbreoctet();  
//Envoie le Void nbreoctet lorsque le tableau est complet 

 
}  //Fin du void octetun()
Fonction debuttramedccun

Le préambule est construit et validé.
Tout byte à 1 arrivant est maintenant dirigé vers cette fonction puisque :
• La variable
tramedcc est mise à 1,
• La variable 
validpreambule est mise à 0.

Tant que le nombre de byte enregistrés n'atteint pas 54 :
• Le tableau enregistre le byte à 1,
• La variable
i du tableau est incrémentée,
• Le programme attend un autre byte à 1.

Lorsque le tableau est remplit l'arrivée d'un autre byte à 1 de fin de trame permet d'appeler la fonction
nbreoctet() pour le traitement de la trame DCC.

                                                                                                                         ---------------------------------------
                                                                                                                         ---------------------------------------
Fonction debuttramedcczero

void debuttramedcczero()  //Construction de la trame DCC bit 0 
{if (i<55) 
{tramedcccomplete[i] = 0;  
//Enregistrement des bits dans le tableau de la trame DCC 
i = i + 1; 
}
 

}  //Fin du void octetzero1()

Fonction debuttramedcczero

Le préambule est construit et validé.
Tout byte à 0 arrivant est maintenant dirigé vers cette fonction puisque :
• La variable
tramedcc est mise à 1,
• La variable 
validpreambule est mise à 0.

Tant que le nombre de byte enregistrés n'atteint pas 54 :
• Le tableau enregistre le byte à 0,
• La variable
i du tableau est incrémentée,
• Le programme attend un autre byte à 0.

Décodage de la trame DCC
La trame DCC est complète et les informations sont stockées dans notre tableau.

Il faut maintenant déterminer de combien d'octet valide notre trame DCC est composée.


Pour indiquer qu'une trame DCC est complète, le protocole DCC indique qu'il faut qu'un octet soit suivi par un byte à 1.

On va donc contrôler lequel des bytes
27, 36, 45, 54 est à 1. Nous serons ainsi de combien d'octet est composée la trame DCC.

Fonction nbreoctet

void nbreoctet()  //Détermine le nombre d'octet valide 


EIMSK &=~ (1<<0);  //Arrêt de l'Interruptions INT0 
//detachInterrupt (digitalPinToInterrupt(interruptPin), dureeimpulsion, CHANGE); 

 
if (tramedcccomplete[27] == 1) //Teste s'il s'agit d'une trame 3 octets
{affichagetrame3octets();} 
 
else if (tramedcccomplete[36] == 1) //Teste s'il s'agit d'une trame 4 octets
{affichagetrame4octets();} 
 
else if (tramedcccomplete[45] == 1) //Teste s'il s'agit d'une trame 5 octets
{affichagetrame5octets();} 
 
else if (tramedcccomplete[54] == 1) //Teste s'il s'agit d'une trame 6 octets
{affichagetrame6octets();} 

 
//Retour à l'état initiale des variables 
tramedcc = 0;  //Construction trame DCC impossible 
 
preambule = 0;  //Variable préambule à 0 
 
i = 0;  //Compteur variable tableau à 0 
 
validpreambule = 1;  //Construction Préambule possible 
 

EIMSK |= (1<<0);  //Reprise de l'Interruptions INT0 
//attachInterrupt (digitalPinToInterrupt(interruptPin), dureeimpulsion, FALLING); 

 
}  //Fin de void nbreoctet()

Fonction nbreoctet

Notre fonction commence par interrompre les interruptions pour permettre le traitement de la trame par l'instruction :
C'est le registre EIMSK qui permet l'activation ou la désactivation de l'interruption INT0 :
En bas niveau :
• EIMSK &=~ (1<<0);  //Arrêt de l'Interruptions INT0 

En haut niveau :
detachInterrupt (digitalPinToInterrupt(interruptPin), dureeimpulsion, CHANGE); 
                                                                                                                         ---------------------------------------
                                                                                                                         ---------------------------------------
if (tramedcccomplete[27] == 1) //Teste s'il s'agit d'une trame 3 octets
{affichagetrame3octets();} 
 
else if (tramedcccomplete[36] == 1) //Teste s'il s'agit d'une trame 4 octets
{affichagetrame4octets();} 
 
else if (tramedcccomplete[45] == 1) //Teste s'il s'agit d'une trame 5 octets
{affichagetrame5octets();} 
 
else if (tramedcccomplete[54] == 1) //Teste s'il s'agit d'une trame 6 octets
{affichagetrame6octets();}

On teste ici le byte qui permet d'identifier le nombre d'octet de la trame DCC et on appelle la fonction correspondante.

                                                                                                                         ---------------------------------------
                                                                                                                         ---------------------------------------
//Retour à l'état initiale des variables 
tramedcc = 0;  //Construction trame DCC impossible 
 
preambule = 0;  //Variable préambule à 0 
 
i = 0;  //Compteur variable tableau à 0 
 
validpreambule = 1;  //Construction Préambule possible 
 
Après traitement et affichage de la trame DCC sur le moniteur série on remet les variables dans un état qui permet de recommencer le processus de décodage de la trame DCC. 

                                                                                                                         ---------------------------------------
                                                                                                                         ---------------------------------------
Ensuite on autorise à nouveau les interruptions :
En bas niveau :
• EIMSK |= (1<<0);  //Reprise de l'Interruption INT0 

En haut niveau :
attachInterrupt (digitalPinToInterrupt(interruptPin), dureeimpulsion, CHANGE); 

Affichage de la trame DCC
Fonction affichagetrame3octets()

void affichagetrame3octets()  //Affichage de la trame 3 octets 

Serial.print (preambule);  //Affichage du contenu de la variable Préambule 
Serial.print (" ");  //Intervalle 
 
Serial.print (tramedcccomplete[0]); //Affichage premier zéro de séparation
Serial.print (" "); 
 
//Affichage octet 1 
for (volatile byte j=1;j<=8;j++)
{Serial.print (tramedcccomplete[j]);} 
Serial.print (" "); 
 
Serial.print (tramedcccomplete[9]); 
Serial.print (" "); 
 
//Affichage octet 2 
for (volatile byte j=10;j<=17;j++)
{Serial.print (tramedcccomplete[j]);} 
Serial.print (" "); 
 
Serial.print (tramedcccomplete[18]); 
Serial.print (" "); 
 
//Affichage octet 3 
for (volatile byte j=19;j<=26;j++)
{Serial.print (tramedcccomplete[j]);} 
Serial.print (" "); 
 
Serial.println (tramedcccomplete[27]); 
 
}

Fonction affichagetrame3octets()

Serial.print (preambule);  //Affichage du contenu de la variable Préambule 
Serial.print (" ");  //Intervalle 
 
• On affiche sur le moniteur série le contenu de la variable préambule qui contient le nombre de byte à 1 du préambule,
• On affiche un intervalle pour plus de clarté dans l'affichage de la trame DCC.


                                                                                                                         ---------------------------------------
                                                                                                                         ---------------------------------------
Serial.print ("0"); //Affichage premier zéro de séparation
Serial.print (" "); 
 
• On affiche sur le moniteur série le premier zéro de séparation suivant le préambule,
• On affiche un intervalle pour plus de clarté dans l'affichage de la trame DCC.

                                                                                                                         ---------------------------------------
                                                                                                                         ---------------------------------------
//Affichage octet 1 
for (volatile byte j=1;j<=8;j++)
{Serial.print (tramedcccomplete[j]);} 
Serial.print (" "); 

• On affiche sur le moniteur série le premier octet en prenant les valeurs dans le tableau par l'intermédiaire d'une boucle FOR,
• On affiche un intervalle pour plus de clarté dans l'affichage de la trame DCC.
                                                                                                                         ---------------------------------------
                                                                                                                         --------------------------------------- 
Serial.print (tramedcccomplete[9]); 
Serial.print (" "); 
 
• On affiche sur le moniteur série le deuxième zéro de séparation suivant le premier octet,
• On affiche un intervalle pour plus de clarté dans l'affichage de la trame DCC.
                                                                                                                         ---------------------------------------
                                                                                                                         ---------------------------------------
//Affichage octet 2 
for (volatile byte j=10;j<=17;j++)
{Serial.print (tramedcccomplete[j]);} 
Serial.print (" "); 
 
• On affiche sur le moniteur série le deuxième octet en prenant les valeurs dans le tableau par l'intermédiaire d'une boucle FOR,
• On affiche un intervalle pour plus de clarté dans l'affichage de la trame DCC.
                                                                                                                         ---------------------------------------
                                                                                                                         ---------------------------------------
Serial.print (tramedcccomplete[18]); 
Serial.print (" "); 

• On affiche sur le moniteur série le troisième zéro de séparation suivant le deuxième octet,
• On affiche un intervalle pour plus de clarté dans l'affichage de la trame DCC.
                                                                                                                         ---------------------------------------
                                                                                                                         ---------------------------------------

//Affichage octet 3 
for (volatile byte j=19;j<=26;j++)
{Serial.print (tramedcccomplete[j]);} 
Serial.print (" "); 

• On affiche sur le moniteur série le troisième octet en prenant les valeurs dans le tableau par l'intermédiaire d'une boucle FOR,
• On affiche un intervalle pour plus de clarté dans l'affichage de la trame DCC.
                                                                                                                         ---------------------------------------
                                                                                                                         ---------------------------------------
Serial.println (tramedcccomplete[27]);

• On affiche sur le moniteur série le 1 indiquant la fin de la trame 3 octets,
• Dans le même temps on va à la ligne pour l'affichage d'une autre trame DCC.
                                                                                                                         ---------------------------------------
                                                                                                                         ---------------------------------------

Je n'ai traité ici que la trame 3 octets mais pour les autres trames le principe est le même.

TÉLÉCHARGEMENT du PROGRAMME
Téléchargement du programme : Analyseur de trame DCC

Nota : En cas de problème contactez-moi, je vous fournirai le logiciel en format INO directement.

MONTAGE à BASE de l'ANALYSEUR de TRAME