LOCODUINO

Parlons Arduino => Vos projets => Discussion démarrée par: lebelge2 le avril 24, 2024, 02:26:08 pm

Titre: RailCom: Générateur de CutOut
Posté par: lebelge2 le avril 24, 2024, 02:26:08 pm
Bonjour.
Certaines centrales DCC notamment les DIY ne sont pas compatibles RailCom car ne génèrent pas le CutOut.
Ce petit montage à base, seulement d’un Arduino Uno, Nano ou Micro s’insère entre la centrale et le Booster.
Il génère le CutOut  afin de la rendre compatible RailCom.
Testé sur un Booster L298N et L6203.
Si dessous, le code et le schéma.
Bien à vous.

// RailCom: Programme pour insérer un CutOut dans les trames DCC.  Arduino Uno, Nano, Micro.
// Testé avec Bridge Driver L6203 et L298N
// V.1.0.   24-04-24
// lebelge2@yahoo.fr

volatile uint8_t dccrecState;
volatile uint8_t tempByte;
#define WAIT_PREAMBLE    0
#define WAIT_START_BIT   1
#define WAIT_DATA        2
#define WAIT_END_BIT     3
#define MaxDccSize 6                         // DCC messages can have a length upto this valu
#define PinIn1 3                             // Entrée signalDcc sans CutOut
#define PinOut1 4                            // Sortie signal DCC avec CutOut
#define PinOut2 5                            // Sortie signal DCC inversé avec CutOut

class DccMessage {
  public:
    volatile uint8_t size;
    volatile uint8_t data[MaxDccSize];        // The contents of the last dcc message received
} dccMessage;

struct {
  uint8_t bitCount;                           // Count number of preamble bits / if we have a byte
  volatile uint8_t tempMessage[MaxDccSize];   // Once we have a byte, we store it in the temp message
  volatile uint8_t tempMessageSize;           // Here we keep track of the size, including XOR
} dccrec;                                     // The received DCC message is assembled here

struct {
  uint8_t port;
  uint8_t bit;                                // Bitmask for reading the input Port
  volatile uint8_t *portRegister;
} dccIn;

void setup() {
  Serial.begin(9600);
  pinMode(PinIn1, INPUT);
  pinMode(PinOut1, OUTPUT);
  pinMode(PinOut2, OUTPUT);
  init_timer2();
  attachInterrupt(digitalPinToInterrupt(PinIn1), Dcc_Interrupt, CHANGE );
  dccIn.port = digitalPinToPort(PinIn1);
  dccIn.bit = digitalPinToBitMask(PinIn1);
  dccIn.portRegister = portInputRegister(dccIn.port);
  Serial.println ("SetUp OK");
}

void loop() {

}

void CutOut(void) {
  delayMicroseconds(8);
  asm("sbi 0x0B, 4");
  asm("sbi 0x0B, 5");
  delayMicroseconds(425);
  asm("cbi 0x0B, 4");
  asm("cbi 0x0B, 5");
}
//----------------------------------------------------------------------------------------------------------
void init_timer2(void) {
#define T2_PRESCALER   8
#define T2_PRESCALER_BITS   ((0<<CS02)|(1<<CS01)|(0<<CS00))
#define T77US (F_CPU * 77L / T2_PRESCALER / 1000000L)
#if (T77US > 254)
#warning T77US too big, use either larger prescaler or slower processor
#endif
#if (T77US < 32)
#warning T77US too small, use either smaller prescaler or faster processor
#endif
  noInterrupts();              // disable all interrupts
  TCCR2A = 0;                  // should be zero for our purpose
  TCCR2B = 0;                  // 0 => timer is stopped
  TCNT2 = 256L - T77US;        // preload the timer
  TIMSK2 |= (1 << TOIE2);      // the timer is used in overflow interrupt mode
  interrupts();                // enable all interrupts
}
//----------------------------------------------------------------------------------------
void Dcc_Interrupt() {
  if (digitalRead(PinIn1) == true) {
    asm("cbi 0x0B, 5");
    asm("sbi 0x0B, 4");
    TCCR2B |= (T2_PRESCALER_BITS);  // Start Timer 2
  }
  else {
    asm("cbi 0x0B, 4");
    asm("sbi 0x0B, 5");
  }
}
//-----------------------------------------------------------------------------------
ISR(TIMER2_OVF_vect) {
  uint8_t DccBitVal;
  DccBitVal = !(*dccIn.portRegister & dccIn.bit);
  TCCR2B = 0;                                    // 0 => timer is stopped
  TCNT2 = 256L - T77US;                          // preload the timer
  dccrec.bitCount++;
  switch (dccrecState) {
    case WAIT_PREAMBLE:
      if (DccBitVal) {                           // a "1" bit is received
        if (dccrec.bitCount >= 10)
          dccrecState = WAIT_START_BIT;
      }
      else
        dccrec.bitCount = 0;                     // not a valid preamble.
      break;
    case  WAIT_START_BIT:
      if ( !DccBitVal ) {                        // a "0" bit is received
        dccrecState = WAIT_DATA;
        dccrec.tempMessageSize = 0;
        uint8_t i;
        for (i = 0; i < MaxDccSize; i++ )
          dccrec.tempMessage[i] = 0;
        dccrec.bitCount = 0;
      }
      break;
    case WAIT_DATA:
      if ( dccrec.bitCount == 8 ) {                 // byte is complete
        if (dccrec.tempMessageSize == MaxDccSize ) { // Packet is too long - abort
          dccrecState = WAIT_PREAMBLE;
          dccrec.bitCount = 0;
        }
        else
          dccrecState = WAIT_END_BIT;              // Wait for next byte or end of packet
      }
      break;
    case WAIT_END_BIT:
      if ( DccBitVal ) {                           // End of packet?
        CutOut();
        dccrecState = WAIT_PREAMBLE;
      }
      else                                         // Get next Byte
        dccrecState = WAIT_DATA;
      dccrec.bitCount = 0;                         // prepare for the next byte
  }
}

Titre: Re : RailCom: Générateur de CutOut
Posté par: trimarco232 le avril 24, 2024, 11:36:06 pm
Bonjour,
encore une réalisation remarquable , bravo !
Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 24, 2024, 11:54:39 pm
Bonjour lebelge,

Je suis très intéressé et en même temps certaines choses m'étonnent.

Je comprends bien le principe adopté pour insérer le cutout à la place de certains bits du préambule. Mais encore faut il qu'il y ait assez de bits ! Si la centrale ne génère que 16 bits de préambule, ou 14 ? Avec une centrale en DIY, on peut générer le nombre de bits que l'on veut, mais une centrale du commerce ?

Par ailleurs, pour activer le transfert, il faut que le booster court-circuite la sortie pour que le décodeur envoie un courant dans la boucle ainsi créée. Or je ne vois pas comment tu réalises cela ici ? Avec un LMD18200 qui dispose d'une entrée BRAKE, c'est très simple. Sur un L603 je crois que l'on peut le faire avec la pin ENABLE1 et  ENABLE1 (mais je ne vois pas cela dans ton montage). Et avec un L298N ?

Merci pour tes réponses.

Christophe
Titre: Re : RailCom: Générateur de CutOut
Posté par: CATPLUS le avril 25, 2024, 07:41:12 am
Bonjour LeBelge

Trés intéressé et voir plus, un autre ami est sur le même projet (en cours de mise au point). Pour le soft (je me garderais bien de faire une remarque, "Christophe est beaucoup plus compétent")
4 questions:
Sur le schéma, la sortie de la centrale a un seul fil via la broche "3" du Nano, la question où brancher le second fil de la centrale?
Les centrales ont 2 bornes de sorties "J & K" merci de nous donner les branchements.
En cas de court-circuit, comment réagi le montage?
Une  centrale réglée pour 4 voir plus ampères, garde-t-on la puissance?

Je soupçonne que vous avez tester votre montage, auriez-vous une photo ou vidéo

Merci pour votre réponse

Marcel
Titre: Re : Re : RailCom: Générateur de CutOut
Posté par: trimarco232 le avril 25, 2024, 09:24:07 am
(...) on peut générer le nombre de bits que l'on veut, mais une centrale du commerce ?
je me permets de répondre , en fonction de ce que j'ai cru comprendre)
avec une centrale du commerce , il faut connaître le preamble pour savoir si c'est compatible (vous noterez le niveau de réflexion) ; en général , c'est compatible , si non c'est sniffer + scheduler , pas la même approche
Citer
on peut le faire avec la pin ENABLE1 et  ENABLE1 (mais je ne vois pas cela dans ton montage). Et avec un L298N ?
(...)
en l'état , c'est prévu pour un IC pont en H classique , cad. 1/0 = 1/0  ,  0/1 = 0/1 et , pour le cutout , 1/1 = 0/0
pour des IC ou des shields avec une entrée brake dédiée , il faut adapter/compléter le soft
Titre: Re : Re : RailCom: Générateur de CutOut
Posté par: trimarco232 le avril 25, 2024, 09:35:51 am
(...) où brancher le second fil de la centrale?
on n'en branche qu'un , en adaptant la tension  , ou mieux :
- en DIY , on se prend à la sortie DCC de l'arduino
- avec une centrale du commerce , on va chercher ce signal directement sur la carte
Citer
En cas de court-circuit, comment réagi le montage?
Une  centrale réglée pour 4 voir plus ampères, garde-t-on la puissance?
ces questions ne concernent pas le montage en lui-même , mais le (les) pont en H se trouvant en aval
c'est d'abord orienté DIY , mais on peut peut-être insérer ceci dans une centrale du commerce , cad. entre la génération du signal DCC et la partie puissance , mais c'est à étudier au cas par cas , ça risque d'être lourd , voire impossible
Citer
Je soupçonne que vous avez tester votre montage, auriez-vous une photo ou vidéo
why not , mais amha , les dessin et chrono-diagrammes sont assez parlants


Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 25, 2024, 09:53:23 am
Bonjour Marc,

Merci d’apporter des éclairages. Je ne suis pas aussi sûr que toi que les centrales du commerce génèrent (beaucoup) plus de bits de préambule que nécessaire soit 12, 14 ou 16.

Sait-on par exemple combien de bit de préambules sont générés dans DCC-Ex, soft qui est à la base de la Box ?

Oui pour obtenir le signal de DIR soit les 1 (HIGH) soit les 0 (LOW), baiser en tension l’un des conducteurs du signal DCC, sans doute une bonne idée. Comment procèderais-tu pour « tenir » dans un 5V max ?

Il est plus facile à implanter directement en sortie du µcontroleur. Ce qui est désigné sur le schéma comme « entrée DCC » serait en fait la sortie DIR. C’est elle qui génère soit les 1 (HIGH) soit les 0 (LOW).

Pour le court-circuit, tu penses que mettre EN1 et EN2 en cc suffit ! cad. 1/0 = 1/0 , 0/1 = 0/1 et , pour le cutout , 1/1 = 0/0 Tu as certainement raison, je ne suis pas ici dans ma zone de confort pour ce qui est de mes compétences.

Merci de nous permettre à tous de mieux appréhender ce sujet.

Christophe




Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 25, 2024, 10:10:07 am
Au niveau du soft, j’ai aussi quelques interrogations quant à certains choix.

Par exemple, je suis à peu près certain que cette commande écrite en partie en assembleur est malgré tout moins rapide en exécution qu’une écriture directe sur les ports.

void Dcc_Interrupt() {
  if (digitalRead(PinIn1) == true) {
    asm("cbi 0x0B, 5");
    asm("sbi 0x0B, 4");
    TCCR2B |= (T2_PRESCALER_BITS);  // Start Timer 2
  }
  else {
    asm("cbi 0x0B, 4");
    asm("sbi 0x0B, 5");
  }
}

Et que le digitalRead() aurait pu être très avantageusement remplacé par une lecture du port !!!

Ceci n’est pas très important à ce stade mais pourra faire partie des réflexions d’évolution
Titre: Re : RailCom: Générateur de CutOut
Posté par: trimarco232 le avril 25, 2024, 11:25:15 am
concernant DCC-EX , leur principe actuel est de générer le cutout en aval de la centrale (pour être compatible avec AVR et ESP-32) , donc il doivent forcément générer un nombre suffisant de bits de preambles
si tu veux je vérifie , et je regarde comment insérer le HW dans LaBox
Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 25, 2024, 11:29:59 am
Oui je veux bien que tu regardes si tu peux. Selon moi, ce doit être proche du "minimum syndical" mais probablement pas plus de 16.

En modifiant très légèrement DCC-Ex cela est certainement modifiable... mais attention à l'occasion de mises à jour !

et je regarde comment insérer le HW dans LaBox

Pas comprendre HW ?

Merci

Christophe
Titre: Re : Re : RailCom: Générateur de CutOut
Posté par: trimarco232 le avril 25, 2024, 11:46:41 am
(...)
Par exemple, je suis à peu près certain que cette commande écrite en partie en assembleur est malgré tout moins rapide en exécution qu’une écriture directe sur les ports.
- on ne peut pas aller + vite que l'assembleur , mais on peut écrire l'équivalent en C (me souviens plus comment)
- écrire directement sur le PORT , quelque soit la méthode , a pour inconvénient de modifier toutes les sorties du PORT , voir si c'est gênant ou pas
je crois le + rapide c'est :
PINB = 0b00110000 ; // on bascule les bits 4 et 5 sans toucher aux autres
Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 25, 2024, 11:54:22 am
(...)
Par exemple, je suis à peu près certain que cette commande écrite en partie en assembleur est malgré tout moins rapide en exécution qu’une écriture directe sur les ports.
- on ne peut pas aller + vite que l'assembleur , mais on peut écrire l'équivalent en C (me souviens plus comment)
- écrire directement sur le PORT , quelque soit la méthode , a pour inconvénient de modifier toutes les sorties du PORT , voir si c'est gênant ou pas
je crois le + rapide c'est :
PINB = 0b00110000 ; // on bascule les bits 4 et 5 sans toucher aux autres

Si on peut aller plus vite que l'assembleur quand on travaille avec l'IDE Arduino. C'est le cas ici et le fichier a bien comme extension .ino. Christian explique précisément cela dans son article : https://www.locoduino.org/spip.php?article280

Timing d’une fonction
Dans certains cas, l’IDE d’Arduino ne permet pas d’obtenir ce qu’on veut. Consultez la page du site Arduino qui décrit la fonction delayMicroseconds() et examinez le petit programme fourni en fin de page et repris dans la figure 3.

Selon Arduino, ce programme tout simple ne donnera pas le résultat escompté.
Si vous examinez à l’oscilloscope le signal obtenu sur la sortie (ici broche 8), vous constaterez un signal carré cyclique dont la demi-période fait 53,5 µs au lieu de 50 µs comme le voulait le programmeur et parfois ce signal montre une demi-période de presque 60 µs. Comme il est dit sur la page du site d’Arduino, cette approximation est due à l’exécution de code qui a été rajouté par l’IDE.


Il en est de même pour digitalRead() ou digitalWrite() dont on estime le temps d'exécution entre 16 et 20 fois supérieur à l'écriture ou la lecture directe du port.

Voir également : https://docs.arduino.cc/retired/hacking/software/PortManipulation/

Je crois le + rapide c'est :
PINB = 0b00110000 ; //

Nous sommes d'accord.

Ceux qui commence à s'attaquer à ce type de programmation ne sont pas des perdreaux de l'année, ils savent adapter les ports selon la platelorme. Et rien n'empèche de mettre des #define en début de programme selon les µc.
Titre: Re : RailCom: Générateur de CutOut
Posté par: trimarco232 le avril 25, 2024, 12:14:51 pm
HW , pardon , c'est parce que tu es un pro du SW

l'assembleur , c'est juste en Anglais ce que la machine fait en réel , donc on ne peut pas aller + vite ; bien entendu , un truc mal écrit en assembleur pourra aller moins vite qu'un truc bien écrit en Basic ...

le nombre de bits des preambles DCC-EX , c'est dans "DCCWaveform.h" :
// Number of preamble bits.
const int   PREAMBLE_BITS_MAIN = 16;
const int   PREAMBLE_BITS_PROG = 22;
Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 25, 2024, 12:24:42 pm
le nombre de bits des preambles DCC-EX , c'est dans "DCCWaveform.h" :
// Number of preamble bits.
const int   PREAMBLE_BITS_MAIN = 16;
const int   PREAMBLE_BITS_PROG = 22;

Bon d'origine on est trop court avec 16 bits que l'on fasse du Railcom Chanel 1 simplement ou Chanel 1 et 2 bien sûr. Heureusement c'est modifiable mais attention aux MAJ de DCC-Ex comme je le disais plus haut.

l'assembleur , c'est juste en Anglais ce que la machine fait en réel , donc on ne peut pas aller + vite ; bien entendu , un truc mal écrit en assembleur pourra aller moins vite qu'un truc bien écrit en Basic ...

Bon Christian nous raconte de con**ries ! Je lui en parlerai quand je vais le voir !
Titre: Re : RailCom: Générateur de CutOut
Posté par: lebelge2 le avril 25, 2024, 12:56:47 pm
Bonjour.

- centrale du commerce :
     Il serait plus prudent d’insérer un optocoupleur entre la centrale et le montage.
Pour savoir si le préambule est suffisamment long, il suffit d’ajouter dans le programme : Serial.println(dccrec.bitCount) dans case WAIT_PREAMBLE: et mettre Serial.begin à 250000

- LMD18200 :
     Je ne possède pas encore ce module mais d’après le datasheet, au moment du CutOut, il faut que Dir soit à 0 et Brake à 1, il faudra adapter le soft plus deux sorties supplémentaires dédiées à ce module.
Je viens d’en commander un, adaptation du soft dès réception.

- Assembleur :
     Entre deux instructions il y a 0,125 µ seconde, difficile de faire mieux.

Test et essais fait avec un Arduino Mega pour centrale et Nano pour insérer le CutOut.
Logiciel :  CommandStaion-EX et Base Station.
Ci dessous, montage prototype pour essais.



Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 25, 2024, 01:12:42 pm
Bonjour,

Merci tout d’abord pour ce partage. C’est effectivement un sujet qui en intéresse plus d’un à Locoduino à l’heure actuelle et principalement paracerque la Box qui est une super centrale par ailleurs ne dispose pas de Railcom.

Cette proposition est une réponse intéressante à ce problème. Personnellement je suggère de « redessiner » le PCB de la Box pour y inclure un µc entre les sorties de l’ESP32 et le L6203. Cela me semble la solution la plus « propre »,la moins compliquée et la moins couteuse aussi.

Le LMD18200, au-delà d’être une carte largement éprouvée n’offre ici comme avantage supplémentaire que l’exposition de la broche Brake.

Maintenant, un L6203 que l’on peut mettre en court-circuit le temps du cutout est sans doute plus intéressant qu’un LMD18200 (4A pour le premier contre 3A pour le second).

Je m’étais donné d’autres priorités, mais je pense que je vais tester très rapidement cette proposition.

Merci lebelge de continuer à nous alimenter de belles solutions comme celle-ci !

Christophe
Titre: Re : RailCom: Générateur de CutOut
Posté par: CATPLUS le avril 25, 2024, 01:16:48 pm
Merci pour le partage & ces explications
Je vais suivre avec intérêt vos évolutions.
Titre: Re : Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 25, 2024, 01:24:55 pm
Test et essais fait avec un Arduino Mega pour centrale et Nano pour insérer le CutOut.
Logiciel :  CommandStaion-EX et Base Station.

Au fait, a t'il fallu modifier le nombre de bits des preambles DCC-EX dans "DCCWaveform.h"
// Number of preamble bits.
const int   PREAMBLE_BITS_MAIN = 16;

et mettre une valeur supérieure ? Je pense que oui, donc quelle valeur ? 21 ?
Titre: Re : RailCom: Générateur de CutOut
Posté par: trimarco232 le avril 25, 2024, 01:33:09 pm
ben si , tu peux mettre les 2 instructions en 1 seule , en écrivant dans PORTB ou PINB

pour LaBox version  L6203 , c'est faisable sans modifier le SW du générateur , vu que le L6203 accepte la commande :
L , L = Sink 1, Sink 2
mais il faudra charcuter ...
Titre: Re : RailCom: Générateur de CutOut
Posté par: lebelge2 le avril 25, 2024, 02:04:33 pm
J’ai laissé le Préambule à 16, il n’en reste plus que 11 à la fin… ( Norme ; minimum 10)

Par contre, DCC-EX inverse le signal (voir trame n°1) et le programme ne fonctionne plus.
Heureusement on peut activer une sortie (signal_pin2) qui inverse de nouveau le signal (voir trame 2) donc on retrouve un signal DCC conforme et tout ok.
Trame 3 et 4 avec CutOut et préambule à 11 ou 12

Notes : BaseStation génère un signal correct (pas d’inversion)
Vais voir pour modifier le soft pour accepter aussi un signal inversé.

Et après réflexions....... Ce n’est pas la Centrale qui génère le CutOut, c’est le Booster…
En soit un Booster ce n’est pas compliqué, c’est un ou deux optocoupleurs et un Bridge Driver.
Un microprocesseur surveille les trames et insère un CutOut au moment opportun.

Juste ce que fait ce montage.
Titre: Re : Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 25, 2024, 03:06:02 pm
J’ai laissé le Préambule à 16, il n’en reste plus que 11 à la fin… ( Norme ; minimum 10)

Je croyais que le décodeur devait recevoir un préambule à au moins 14 bits pour répondre canal 1 et 16 bits pour avoir le canal 1 et le canal 2
Titre: Re : RailCom: Générateur de CutOut
Posté par: trimarco232 le avril 25, 2024, 03:49:53 pm
le cutout fait , pour les 2 canaux , selon la norme , 500 us
Édit : précisément 442 us , mais pouvant aller jusqu'à 462 us
cela correspond à 500/116 = 4.3 bits 1 DCC , arrondi à 5
si on a un preamble de 16 bits , il doit en rester 11 , ce qui va bien pour (de mémoire) un minimum de 10
amha ça ne sert à rien d'en avoir + de 16 au départ
Titre: Re : RailCom: Générateur de CutOut
Posté par: CATPLUS le avril 25, 2024, 03:59:35 pm
@Mr LeBelge

Sur la photo que vous présenterz l'affichage du # de la loco est inscrite sur un LCD, pourriez-vous svp nous donner  des explications?
Soft, schéma, Nano, Uno, ESP.....



Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 25, 2024, 04:34:53 pm
le cutout fait , pour les 2 canaux , selon la norme , 500 us
cela correspond à 500/116 = 4.3 bits 1 DCC , arrondi à 5
si on a un preamble de 16 bits , il doit en rester 11 , ce qui va bien pour (de mémoire) un minimum de 10
amha ça ne sert à rien d'en avoir + de 16 au départ

Marc, ce n'est pas exactement de cela que je parlais. On est d'accord sur la durée du cutout à environ 500 µS. Le minimum de 10, c'est la norme "hors Railcom". Ce que je dis, c'est que pour que le décodeur envoie le chanel 1 il faut qu'il ait reçu au minimum 14 bits de préambule et pour renvyer le canal 1 et le canal 2 il faut qu'il ait reçu 16 bits de préambule au moins. A 10 ou 11 bits on sera dans la norme mais le décodeur n'enverra pas ses infos Railcom
Titre: Re : RailCom: Générateur de CutOut
Posté par: lebelge2 le avril 25, 2024, 04:44:48 pm
CATPLUS, c'est le montage de de Paco cañada

https://usuaris.tinet.cat/fmco/home_sp.htm

https://usuaris.tinet.cat/fmco/download/RailComDisplay_manual.pdf
Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 25, 2024, 11:52:21 pm
Bonjour à tous,

J'ai réussi à faire fonctionner le montage avec un LMD18200. C'est tout ce que j'avais sous le coude. Les adresses de locomotives sont bien lue. Je n'ai essayé pour l'instant que avec DCC++ (version originale) qui génère 22 bits de préambule ! J'ai commencé à tester avec DCC-Ex mais je n'avais rien, certainement ce que lebelge soulevait à propos de DCC-Ex, du coup je n'ai pas poursuivis car un peu fatigué.

@lebelge

- LMD18200 :
     Je ne possède pas encore ce module mais d’après le datasheet, au moment du CutOut, il faut que Dir soit à 0 et Brake à 1, il faudra adapter le soft plus deux sorties supplémentaires dédiées à ce module.

J'ai effectivement utilisé une broche supplémentaire, la 6

#define PinIn1 3                              // Entrée signalDcc sans CutOut = Pin DIR du MEGA (pin 12 dans DCC++)
#define PinOut1 4                            // Sortie signal DCC avec CutOut =  Pin DIR du LMD18200
// La sortie 3 du MEGA (PWM) est reliée en direct au PWM du LMD18200
#define PinOut2 5                            // Sortie signal DCC inversé avec CutOut // Je ne l'utilise pas ici
#define PinBrake 6                           // Sortie signal BRAKE = Pin 6 du Nano dans pin BRAKE du LMD18200

et dans la fonction CutOut(), j'active la broche 6 en même temps que la 4

void CutOut(void) {
  delayMicroseconds(8);
  PORTD = PORTD | B01010000;
  PORTD = PORTD &= ~ B00100000;
  //
  delayMicroseconds(425);
  PORTD = PORTD | B00100000;
  PORTD = PORTD &= ~ B01010000;
}

Bon ça mérite encore un peu de travail sur le code mais le principe est bon. C'est une énorme avancée et de belles perspectives pour Railcom sur la Box dans des conditions simples et peu couteuses.

Je ne serai en effet pas étonné que l'on puisse à terme mettre un ATTiny (modèles récents) à moins d'1€.

Merci encore lebelge pour ce partage bénéfique.

PS : Je mesure environ 10% des trames DCC en dehors des spécifications NMRA, et encore de très peu en dehors. Ce qui pour moi est très acceptable et qui est déjà peut-être le cas en sortie directe de DCC++ ce que je pourrai mesurer plus tard.

Christophe

Titre: Re : RailCom: Générateur de CutOut
Posté par: CATPLUS le avril 26, 2024, 06:53:07 am
Bonjour Christophe

Bravo, pourrais-tu stp nous faire un schéma de ton montage.

Marcel
Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 26, 2024, 09:17:26 am
Bonjour Marcel,

Je n'ai pas beaucoup de temps disponible aujourd'hui mais les équivalence de broche sont dans le commentaire en début du code modifié ci-dessous. Normalement tu vas t'y retrouver sans problème.

Le code ci-dessous est à copier sur un Nano par exemple. Sur le MEGA qui sert de centrale, j'ai copié le code original de DCC++ qui a 22 bits de préambule : https://github.com/DccPlusPlus/BaseStation

Je n'ai pas le temps de faire les tests avec moins de bits dans le préambule, à voir par la suite.

Je précise bien, mais ça ce voit dans le cablage, que le UNO est directement derrière le MEGA et avant le LMD. Cette solution ne convient pas derrière un booster. A voir par la suite.


// RailCom: Programme pour insérer un CutOut dans les trames DCC.  Arduino Uno, Nano, Micro.

// V.1.0.   24-04-24
// lebelge2@yahoo.fr

/*
Modifié pour LMD18200 le 26/04/24

Testé à partir de DCC++ version originale téléchargé sur Arduino MEGA : https://github.com/DccPlusPlus/BaseStation
Ce code téléchargé sur un Arduino Nano.

Cablage :

  MEGA 3 ------> LMD18200 PWM
  MEGA 12 -----> NANO 3
  NANO 4 ------> LMD18200 DIR
  NANO 6 ------> LMD18200 BRAKE
  MEGA GND ----> NANO GND
  MEGA GND ----> LMD18200 GND

*/

volatile uint8_t dccrecState;
volatile uint8_t tempByte;
#define WAIT_PREAMBLE    0
#define WAIT_START_BIT   1
#define WAIT_DATA        2
#define WAIT_END_BIT     3
#define MaxDccSize 6                         // DCC messages can have a length upto this valu
#define PinIn1 3                             // Entrée signalDcc sans CutOut
#define PinOut1 4                            // Sortie signal DCC avec CutOut
#define PinOut2 5                            // Sortie signal DCC inversé avec CutOut
#define PinBrake 6                           // Sortie signal brake

class DccMessage {
  public:
    volatile uint8_t size;
    volatile uint8_t data[MaxDccSize];        // The contents of the last dcc message received
} dccMessage;

struct {
  uint8_t bitCount;                           // Count number of preamble bits / if we have a byte
  volatile uint8_t tempMessage[MaxDccSize];   // Once we have a byte, we store it in the temp message
  volatile uint8_t tempMessageSize;           // Here we keep track of the size, including XOR
} dccrec;                                     // The received DCC message is assembled here

struct {
  uint8_t port;
  uint8_t bit;                                // Bitmask for reading the input Port
  volatile uint8_t *portRegister;
} dccIn;

void setup() {
  Serial.begin(115200);

  DDRD = DDRD | B01110000;  // pin 3 -> input, pin 4 à 6 -> output
  init_timer2();
  attachInterrupt(digitalPinToInterrupt(PinIn1), Dcc_Interrupt, CHANGE );
  Serial.println ("SetUp OK");
}

void loop() {}

void CutOut(void) {
  delayMicroseconds(8);
  PORTD = PORTD | B01010000;
  PORTD = PORTD &= ~ B00100000;
  //
  delayMicroseconds(425);
  PORTD = PORTD | B00100000;
  PORTD = PORTD &= ~ B01010000;
}
//----------------------------------------------------------------------------------------------------------
void init_timer2(void) {
#define T2_PRESCALER   8
#define T2_PRESCALER_BITS   ((0<<CS02)|(1<<CS01)|(0<<CS00))
#define T77US (F_CPU * 77L / T2_PRESCALER / 1000000L)
#if (T77US > 254)
#warning T77US too big, use either larger prescaler or slower processor
#endif
#if (T77US < 32)
#warning T77US too small, use either smaller prescaler or faster processor
#endif
  noInterrupts();              // disable all interrupts
  TCCR2A = 0;                  // should be zero for our purpose
  TCCR2B = 0;                  // 0 => timer is stopped
  TCNT2 = 256L - T77US;        // preload the timer
  TIMSK2 |= (1 << TOIE2);      // the timer is used in overflow interrupt mode
  interrupts();                // enable all interrupts
}
//----------------------------------------------------------------------------------------
void Dcc_Interrupt() {
  if ((PIND & 0x04) >> 2) {
    PORTD = PORTD | B00010000;
    TCCR2B |= (T2_PRESCALER_BITS);  // Start Timer 2
  }
  else {
    PORTD = PORTD &= ~ B00010000;
  }
}
//-----------------------------------------------------------------------------------
ISR(TIMER2_OVF_vect) {
  uint8_t DccBitVal = !(PORTD & (1 << 4));
  TCCR2B = 0;                                    // 0 => timer is stopped
  TCNT2 = 256L - T77US;                          // preload the timer
  dccrec.bitCount++;
  switch (dccrecState) {
    case WAIT_PREAMBLE:
      if (DccBitVal) {                           // a "1" bit is received
        if (dccrec.bitCount >= 10)
          dccrecState = WAIT_START_BIT;
      }
      else
        dccrec.bitCount = 0;                     // not a valid preamble.
      break;
    case  WAIT_START_BIT:
      if ( !DccBitVal ) {                        // a "0" bit is received
        dccrecState = WAIT_DATA;
        dccrec.tempMessageSize = 0;
        uint8_t i;
        for (i = 0; i < MaxDccSize; i++ )
          dccrec.tempMessage[i] = 0;
        dccrec.bitCount = 0;
      }
      break;
    case WAIT_DATA:
      if ( dccrec.bitCount == 8 ) {                 // byte is complete
        if (dccrec.tempMessageSize == MaxDccSize ) { // Packet is too long - abort
          dccrecState = WAIT_PREAMBLE;
          dccrec.bitCount = 0;
        }
        else
          dccrecState = WAIT_END_BIT;              // Wait for next byte or end of packet
      }
      break;
    case WAIT_END_BIT:
      if ( DccBitVal ) {                           // End of packet?
        CutOut();
        dccrecState = WAIT_PREAMBLE;
      }
      else                                         // Get next Byte
        dccrecState = WAIT_DATA;
      dccrec.bitCount = 0;                         // prepare for the next byte
  }
}
Titre: Re : Re : RailCom: Générateur de CutOut
Posté par: trimarco232 le avril 26, 2024, 10:26:55 am
Bonjour ,
Bravo Christophe !
(...)
Je ne serai en effet pas étonné que l'on puisse à terme mettre un ATTiny (modèles récents) à moins d'1€.
on peut , ce serait un petit projet intéressant pour notre ami Laurent
(dans l'idéal , le "mégatiny" se verrait téléversé par une broche de l'ESP32 , on n'aurait pas besoin de connecteur ou programmateur spécial)
après , on a 3 approches :
1) faire générer par le mégatinny , à partir de la sortie DCC de l'ESP32 , les signaux pour le cutout , conformément au soft de lebelge2
2) faire générer au mégatiny toute la trame DCC (il le fera mieux que l'ESP32), à partir d'une liaison série de l'ESP32 , avec petit protocole local adhoc ; (on pourrait utiliser la même broche de l'ESP32 , pour le téléversement et la liaison série) ; c'est techniquement le mieux , mais ça nécessiterait de retoucher le soft de DCC-EX(
3) décoder , à la manière d'un sniffer , le DCC issu de l'ESP32 , puis le reproduire , (avec un packet de retard) , pourvu des signaux qui vont bien ; c'est ma préférence

le chois d'un megatiny n'est pas critique , on pourrait aussi envisager , dans le même format à 8 broches cms , un CH32V003 , ou même un STM32 (ce dernier n'ayant toutefois pas ma faveur , car il utilise ("bêtement") , 2 broches au lieu d'1 pour le téléversement

Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 26, 2024, 10:43:06 am
Merci Marc pour les perspectives que tu apportes.

Dans le cas de laBox qui s’adresse à un public sans compétences particulières, je pense qu’il ne faut pas modifier DCC-Ex. Je pense principalement aux MAJ, car on oublie vite que l’on avait changé tel ou tel paramètre, modifié telle ou telle configuration.

Par ailleurs, les trames générées par DCC-Ex sont parfaites !

L’idée de téléverser le soft à partir de l’ESP32 est vraiment intéressante. Pas certain qu’il faille utiliser la même broche pour le téléversement et la liaison série. Il faudra il est vrai ajouter du code dans DCC-Ex pour cela (avec les précautions de MAJ dont je parlais). Je vois bien une commande en synthaxe DCC++, <xx> qui lancerait le processus !!!

Je parle ATTiny ou AVR car c’est ce qui est le plus compatible avec l’environnement Arduino en programmation. Et celui dont je connais le mieux la manipulation des ports  ;D

Il reste à adapter le programme du belge à l’ESP32 et surtout à DCC-Ex, j’avoue ne pas avoir bien compris l’histoire des trames inversées.

Il y a aussi une demande d’adaptation pour placer ce montage, non plus derrière une centrale sans booster, mais derrière un booster.
Titre: Re : RailCom: Générateur de CutOut
Posté par: trimarco232 le avril 26, 2024, 12:09:46 pm
j'ai donné toutes mes hypothèses , mais je suis bien d'accord avec toi : il faut aller au + simple :
- ne pas toucher au code DCC-EX , dès qu'il a assez de bits pour le préamble
- choisir un attiny412 (8 broches , récent) , prévoir le téléversement par l'ESP32 , mais aussi et d'abord par un connecteur externe , sur lequel on pourra brancher un convertisseur USB <-> série , que tout le monde a ; l'un ou l'autre composant supplémentaire nécessaire (résistance , diode) étant aussi à ajouter sur la carte
- utiliser le code de lebelge2 , vu qu'il existe et qu'il fonctionne (le code) ; je n'ai pas non plus compris le problème de l'inversion du signal (l'opposé de l'inverse étant identique à la même chose) ... à défaut , je pourrai proposer la variante que j'ai éditée au point 3) de mon post précédent
Titre: Re : Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 27, 2024, 09:28:47 am
Bonjour Marc, bonjour à tous,

Je te rejoins totalement sur l’ensemble des points que tu soulignes.

- ne pas toucher au code DCC-EX , dès qu'il a assez de bits pour le préamble

Tu as montré où il était possible de modifier le nombre de bits de préambule dans DCC-Ex, il faudra si nécessaire faire une petite entorse  au principe de ne pas modifier le programme original ou trouver une astuce pour contourner.

- choisir un attiny412 (8 broches , récent) , prévoir le téléversement par l'ESP32 , mais aussi et d'abord par un connecteur externe , sur lequel on pourra brancher un convertisseur USB <-> série , que tout le monde a ; l'un ou l'autre composant supplémentaire nécessaire (résistance , diode) étant aussi à ajouter sur la carte

C’est aussi à mon avis la meilleure solution. A tester, je vais essayer de m’y coller dans la semaine ou les 15 jours à venir.

utiliser le code de lebelge2 , vu qu'il existe et qu'il fonctionne (le code) ; je n'ai pas non plus compris le problème de l'inversion du signal (l'opposé de l'inverse étant identique à la même chose)

J’espère que le belge va nous apporter une réponse sur ce point, sinon je vais essayer au cours du WE de trouver le problème (s’il y en a un) et la solution concernant DCC-Ex.

Par ailleurs, je suis très intéressé par ce que tu appelles le point n°3 pour un autre sujet :

3) décoder , à la manière d'un sniffer , le DCC issu de l'ESP32 , puis le reproduire

C’est un sujet sur lequel nous travaillons à quelques-uns. Si tu as déjà des choses réalisées, cela est très intéressant. L’objectif est de capter l’ensemble des trames DCC qui circulent sur le bus et, au travers d’une passerelle, envoyer ces trames sur un bus CAN pour par exemple permettre à un gestionnaire mais aussi tout autres appareils de connaitre « au plus près » l’état du réseau.

Je m’explique avec un exemple concret. La Box (basée sur DCC-Ex) sait retourner une confirmation de réception de commande, soit en CAN (les méthodes CAN revoyant un accusé de réception pour chaque commande) soit en TCP (WiFi, Ethernet) à l’appareil qui a envoyé cette commande. Mais dans ce dernier cas, seul l’appareil à l’origine de la commande reçoit la confirmation. Par exemple smartphone avec une appli Z21. Mais pas le gestionnaire de réseau par exemple. En cherchant à faire une diffusion CAN des commandes du bus DCC, on rend les informations accessibles à tous.

Donc si tu as déjà des solutions, je suis bien évidement très intéressé. Je vais créer un fil à part pour ne pas interférer avec celui-ci.

Bon week-end  tous

Christophe
Titre: Re : RailCom: Générateur de CutOut
Posté par: lebelge2 le avril 27, 2024, 12:46:36 pm
Bonjour.

Au sujet des trames inversées. (2 fils)

La première via l’entrée  In1  commande la partie droite du pont.
La deuxième via l’entrée In2  commande la partie gauche.
En mettant In1 et In2 à l’état haut, les transistors du haut (gauche et droit) sont passant et provoquent un court-circuit nécessaire à la circulation des data RailCom
Méthode utilisée par les L6203, L928N et autres.

Pour le LMD18200, un fil suffit, car les signaux sont inversés dans le composant.
Un autre fil met le pont en court circuit comme dit plus haut.

Evolution du soft , maintenant accepte les deux polarités.
Fonctionne aussi avec un  Attiny85

Bien à vous.
Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 27, 2024, 12:51:41 pm
Merci vraiment une nouvelle fois.


Evolution du soft , maintenant accepte les deux polarités.
Fonctionne aussi avec un  Attiny85


Est-elle disponible cette évolution ???
Titre: Re : RailCom: Générateur de CutOut
Posté par: lebelge2 le avril 27, 2024, 12:54:01 pm
Oui ce soir car je teste.
Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 27, 2024, 12:57:37 pm
Ok. Pour le LMD18200, tu as peut-êtr vu que j'ai effectivement ajouté la broche BRAKE. Ca fonctionne nickel !
Titre: Re : RailCom: Générateur de CutOut
Posté par: lebelge2 le avril 27, 2024, 08:23:51 pm
Bonjour.

Ci joint le soft avec un correctif pour décoder les trames inversées.
Fonctionne aussi avec un Attiny85 en version bêta.

Attention changement des Pins pour compatibilité avec l’Attiny
     2   Entrée Dcc
     3   Sortie vers IN1  (ou Dir     pour LMD18200)
     4   Sortie vers IN2  (ou Brake pour LMD18200)

Prévu pour LMD18200 mais rien testé, j’attends la réception de ce module.
Ligne n° 10:       //#define LMD18200

Bien à vous.

// RailCom: Programme pour insérer un CutOut dans les trames DCC.  Arduino Uno, Nano, Micro.  Attiny85.(Betâ)
//
// V.1.0.   24-04-24  Testé avec Bridge Driver L6203 et L298N
// V.1.1.   27-04-24  Upgrade pour Attiny85 et polarité inverse.
//
// lebelge2@yahoo.fr
//--------------------------------- Sélectionné votre  Hardware Bridge Driver------------------------------------
#define L298N           // L6203
//#define LMD18200
//----------------------------------------------------------------------------------------------
volatile uint8_t dccrecState;
volatile uint8_t tempByte;
bool PolariteDcc = true;
bool Brake;
unsigned long currentTime = 0;
unsigned long previousTime = 0;
unsigned long Temps;
#define WAIT_PREAMBLE    0
#define WAIT_START_BIT   1
#define WAIT_DATA        2
#define WAIT_END_BIT     3
#define MaxDccSize 6                         // DCC messages can have a length upto this value

class DccMessage {
  public:
    volatile uint8_t size;
    volatile uint8_t data[MaxDccSize];        // The contents of the last dcc message received
} dccMessage;

struct {
  uint8_t bitCount;                           // Count number of preamble bits / if we have a byte
  volatile uint8_t tempMessage[MaxDccSize];   // Once we have a byte, we store it in the temp message
  volatile uint8_t tempMessageSize;           // Here we keep track of the size, including XOR
} dccrec;                                     // The received DCC message is assembled here

struct {
  uint8_t port;
  uint8_t bit;                                // Bitmask for reading the input Port
  volatile uint8_t *portRegister;
} dccIn;
//---------------------------------------------- SetUp -------------------------------------------------------
void setup() {
#ifdef LMD18200
  Brake = true;
#endif
#ifdef __AVR_ATmega328P__                    // --- Arduino AVR ---
#define PORTx PORTD                          // PORTD
  Serial.begin(250000);
  init_timer2();                             // Timer 2
#elif ARDUINO_AVR_ATTINYX5                   // --- ATTINY CORE ATTINY85 ---  (Micronucleus DigiSpark)
#define PORTx PORTB                          // PORTB
  init_timer1();                             // Timer 1
#else
#error "Unsupported CPU, you need to add another configuration section for your CPU"
#endif
#define PinIn 2                               // Entrée trames Dcc sans CutOut
#define PinOut1 3                             // Sortie trames DCC avec CutOut           (LMD18200: Dir (sans CutOut))
#define PinOut2 4                             // Sortie trames DCC inversées avec CutOut (LMD18200: Brake (uniquement CutOut))
  pinMode(PinIn, INPUT);
  pinMode(PinOut1, OUTPUT);
  pinMode(PinOut2, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(PinIn), Dcc_Interrupt, CHANGE );
  dccIn.port = digitalPinToPort(PinIn);
  dccIn.bit = digitalPinToBitMask(PinIn);
  dccIn.portRegister = portInputRegister(dccIn.port);
}
//--------------------------------------------- Loop --------------------------------------------------------
void loop() {}
//------------------------------------------- Attiny 85 -----------------------------------------------------------
#ifdef ARDUINO_AVR_ATTINYX5
void init_timer1(void) {
  noInterrupts();              // disable all interrupts
  TCCR1 = 0;                   // B0 à B3: diviseur par 1, 2, 4, 8
  TCNT1 = 102;                 // preload the timer
  TIMSK = 4;                   // the timer is used in overflow interrupt mode
  interrupts();                // enable all interrupts
}
//--------------------------------------------- AVR ---------------------------------------------------------
#elif __AVR_ATmega328P__
void init_timer2(void) {
  noInterrupts();              // disable all interrupts
  TCCR2A = 0;                  // should be zero for our purpose
  TCCR2B = 0;                  // 0 => timer is stopped
  TCNT2 = 102;                 // preload the timer
  TIMSK2 |= 1;                 // the timer is used in overflow interrupt mode
  interrupts();                // enable all interrupts
}
#endif
//----------------------------------------------------------------------------
void CutOut() {
  if (PolariteDcc == false) {        // Si DCC inversé,
    delayMicroseconds(38);           // retarder CutOut d'un demis-bit
    PORTx = PORTx | B00001000;
    PORTx = PORTx &= ~ B00010000;
    delayMicroseconds(20);
  }
  delayMicroseconds(8);
  PORTx = PORTx | B00010000;
  PORTx = PORTx | B00001000;
  delayMicroseconds(430);
  PORTx = PORTx &= ~ B00001000;
  PORTx = PORTx &= ~ B00010000;
}
//------------------------------------------ DCC Interrupt -------------------------------------------
void Dcc_Interrupt() {
#ifdef __AVR_ATmega328P__
  if ((bitRead(PIND, PinIn)) == 1) {                // bitRead en 3µs,    digitalRead en 7µs
    TCCR2B |= 2;                                    // Start Timer 2
#elif ARDUINO_AVR_ATTINYX5
  if ((bitRead(PINB, PinIn)) == 1) {
    TCCR1 |= 4;                                     // Start Timer 1
#endif
    if (Brake == false)
      PORTx = PORTx &= ~ B00010000;                 // Temps d'exécution de ces instructions:
    PORTx = PORTx | B00001000;                      // 0,125µs en ASM,    0,170µs en C,      6µs en C++
  }
  else {
    if (Brake == false)
      PORTx = PORTx | B00010000;
    PORTx = PORTx &= ~ B00001000;
  }
  if (dccrecState == WAIT_START_BIT)  {           // L'intervalle de temps après le préambule est évalué,
    Temps = micros();
    previousTime = Temps - currentTime;
    currentTime = Temps;                          // si l'écart entre le dernier et le premier zéro est de 116 µs,
    if (previousTime > 90)                        // alors le signal est reconnu dans la bonne phase.
      PolariteDcc = true;                         // Polarité correcte
    else
      PolariteDcc = false;                        // Polarité inversée
  }
}
//------------------------------------------- ISR Timers ----------------------------------------------------
#ifdef __AVR_ATmega328P__
ISR(TIMER2_OVF_vect) {
  TCCR2B = 0;                                    // 0 => timer is stopped
  TCNT2 = 102;                                   // preload the timer. Fr. 16Mhz
#elif ARDUINO_AVR_ATTINYX5
ISR(TIMER1_OVF_vect) {
  TCCR1 = 0;                                     // 0 => timer is stopped
  TCNT1 = 112;                                   // preload the timer.  Fr. 16,5Mhz
#endif
  uint8_t DccBitVal;
  DccBitVal = !(*dccIn.portRegister & dccIn.bit);
  dccrec.bitCount++;
  switch (dccrecState) {
    case WAIT_PREAMBLE:
      if (DccBitVal) {                              // a "1" bit is received
        if (dccrec.bitCount >= 10)
          dccrecState = WAIT_START_BIT;
      }
      else
        dccrec.bitCount = 0;                        // not a valid preamble.
      break;
    case  WAIT_START_BIT:
      if ( !DccBitVal ) {                           // a "0" bit is received
        dccrecState = WAIT_DATA;
        dccrec.tempMessageSize = 0;
        uint8_t i;
        for (i = 0; i < MaxDccSize; i++ )
          dccrec.tempMessage[i] = 0;
        dccrec.bitCount = 0;
      }
      break;
    case WAIT_DATA:
      if ( dccrec.bitCount == 8 ) {                  // byte is complete
        if (dccrec.tempMessageSize == MaxDccSize ) { // Packet is too long - abort
          dccrecState = WAIT_PREAMBLE;
          dccrec.bitCount = 0;
        }
        else
          dccrecState = WAIT_END_BIT;                // Wait for next byte or end of packet
      }
      break;
    case WAIT_END_BIT:
      if ( DccBitVal ) {                             // End of packet?
        CutOut();
        dccrecState = WAIT_PREAMBLE;
      }
      else                                           // Get next Byte
        dccrecState = WAIT_DATA;
      dccrec.bitCount = 0;                           // prepare for the next byte
  }
}
Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 27, 2024, 10:35:23 pm
Bonsoir à tous,

@lebelge : Bravo, je viens de tester la version 1.1 sur un MEGA avec DCC-EX et un L298 La détection Railcom fonctionne nickel !

Dans mon cas il a cependant fallu que je mette IN1 du L298 sur la pin 6 du Uno.

Je n'ai pas eu besoin de modifier le nombre de bits du préambule qui est donc resté à 16 (réglage par défaut de DCC-Ex)

Les réglages pour la carte L298 dans DCC-Ex sont les suivants :

#define MY_MOTOR_SHIELD F("L298"),\
  new MotorDriver(7, 6, 5, UNUSED_PIN, A0, 2.99, 2000, UNUSED_PIN), \
  new MotorDriver(2, 4, 3, UNUSED_PIN, A1, 2.99, 2000, UNUSED_PIN)

Le câblage dans mon cas est donc celui-ci.

MEGA 6 -------> NANO 2
MEGA 7 -------> L298 EN1
NANO 3 -------> L298 IN2
NANO 4 -------> L298 IN1

Demain je vais tester le même montage mais en remplaçant le MEGA par un ESP32 pour me mettre dans les conditions les plus proches de laBox.

Je crois que j’ai des ATTiny85 dans mon bazar, je ferai aussi alors le test.

Et enfin, comme j’ai aussi des L6203 et si j’ai le temps je remplacerai le L298.

Voilà un chantier important qui a sans doute trouvé sa solution.

Restera cependant la question des centrales sur lesquelles on ne peut pas s’intercaler entre le µC et le booster !!!

Christophe




Titre: Re : RailCom: Générateur de CutOut
Posté par: rNe le avril 27, 2024, 10:40:49 pm
Bonsoir à tous.

Formidable votre travail !
Sachez que je suis à votre disposition pour redessiner un PCB compatible avec toutes ces évolutions. Si ma proposition vous convient, n'hésitez pas à me fournir les informations nécessaires pour réaliser, dans un premier temps, le schéma de principe.
Et après validation de votre part, dans un deuxième temps, traçage du PCB et GERBER au final.
Je ferai l'impossible pour que l'ensemble tienne dans un format inférieure à 10 x 10 cm...

Cordialement
Titre: Re : RailCom: Générateur de CutOut
Posté par: lebelge2 le avril 28, 2024, 12:18:35 am
BobyAndCo

Avec exactement le même matériel que toi, ça marche chez moi.
Mega2560 ==> Uno ==>  L298N

J’ai mis ta configuration pin :
 new MotorDriver(7, 6, 5, UNUSED_PIN, A0, 2.99, 2000, UNUSED_PIN), \

Pin 5 et 6 fonctionnent.

Attention que dans cette version (V.1.1)
Entrée Dcc      Pin 2  (Sur Uno)
Out1              Pin 3
Out2              Pin 4

Le cablage:
MEGA 5 ou 6 -------> UNO 2
MEGA 7 ------------> L298 EN1
UNO 3 -------------> L298 IN2
UNO 4 -------------> L298 IN1

Décodeur Loco: ESU standard
Il faudra certainement ajuster les tempos dans: void CutOut()
Titre: Re : Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 28, 2024, 07:30:30 am
Attention que dans cette version (V.1.1)
Entrée Dcc      Pin 2  (Sur Uno)
Out1              Pin 3
Out2              Pin 4

Le cablage:
MEGA 5 ou 6 -------> UNO 2
MEGA 7 ------------> L298 EN1
UNO 3 -------------> L298 IN2
UNO 4 -------------> L298 IN1

Oups, je m'ai gouré en effet. J'avais pris une photo et avec l'erreur de parallaxe j'ai décallé les broches. Du coup, j'ai modifié dans mon post au dessus
Titre: Re : Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 28, 2024, 07:49:35 am
J'arrive un peu après la bataille... désolé

Toutefois je vous présente le "petit dernier".

Bonjour Laurent, bonjour à tous.

Tout d’abord bravo à toi aussi Laurent pour cette réalisation. Je trouve que Locoduino est un sacré incubateur de propositions très sympatiques.

Il est important de préciser que ta proposition et celle du belge, bien qu’ayant toutes les deux le même objectif, ont des applications différentes.

Pour le belge, le montage se place entre la partie logicielle, par exemple un MEGA avec DCC-Ex et un booster

Dans ton cas, ton montage se place à la sortie d’une centrale avec booster (cas d’une centrale du commerce par exemple), génère un nouveau signal DCC avec cutout et l’envoie dans un nouveau booster, ici un L6203.

Ce qui fait que dans le premier cas nous n'avons besoin que d'un Nano/Uno voire même un ATTiny85 alors que dans le second, on reconstruit une centrale au moins pour la partie HardWare.

L'un et l'autre montages répondent à des contraintes différentes.

Il est probable pour éviter toute confusion que je séparerai les deux sujets dont les domaines d’application ne sont pas les mêmes.

Christophe
Titre: Re : RailCom: Générateur de CutOut
Posté par: phenixpopol le avril 28, 2024, 12:51:42 pm
Bonjour à tous,
Je suis attentivement ce forum «  Générateur de CutOut «  mais hélas 90 % des post me passent au dessus de la tête. Pourquoi certains contributeurs ne répondraient à la proposition de BobyAnd Co du 25 avril à 01,12,42 : « 
C’est effectivement un sujet qui en intéresse plus d’un à Locoduino à l’heure actuelle et principalement parace que la Box qui est une super centrale par ailleurs ne dispose pas de Railcom.

Cette proposition est une réponse intéressante à ce problème. Personnellement je suggère de « redessiner » le PCB de la Box pour y inclure un µc entre les sorties de l’ESP32 et le L6203. Cela me semble la solution la plus « propre »,la moins compliquée et la moins couteuse aussi. « 

Je sais que dans un post sur la box, cette évolution n’était pas prévue, mais je pense qu’elle permettrait à beaucoup d’évoluer vers la détection rail com.

Merci encore pour toutes les contributions même si eles sont souvent trop savantes pour moi
Titre: Re : RailCom: Générateur de CutOut
Posté par: lebelge2 le avril 28, 2024, 01:33:36 pm
Pour les possesseurs de La Box qui souhaitent la fonction RailCom.
Une légère modification est possible :
Enlever Q1, R1, R2 et couper une piste.
Placer en « volant » un Attiny85 avec le soft.

N’ayant pas La Box, je ne sais pas aider plus.

Aliexpress Attiny85

https://fr.aliexpress.com/item/2043055746.html


Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 28, 2024, 02:48:19 pm
@lebelge : J'ai moins de succès avec l'ATTiny !!!

Toutes les commandes DCC fonctionnent mais je n'ai aucune réponse du décodeur.

Configuration : MEGA avec DCC-Ex / L298 / ATTiny45

Peut-être un problème de câblage ?

MEGA 6 -------> ATTiny PB2
MEGA 7 -------> L298 EN
ATTiny PB3 ---> L298 IN1
ATTiny PB4 ---> L298 IN2

Une idée de solution ???

Christophe
Titre: Re : RailCom: Générateur de CutOut
Posté par: laurentr le avril 28, 2024, 05:17:13 pm
Bonjour

J ai une question qui va aider la recherche d éventuels autres pont H.

D après OPEN DCC il faut insérer un "SECURITY GAP" de 4us  sur le L6203 entre son activation/désactivation ( broche ENABLE) et la commande de mise en "CC" ( via IN 1 et IN2)
Ceci est (fortement) recommandé notamment dans le cas de la présence de plusieurs boosters.

Comment gérer cela au niveau du L298 ( que je ne connais pas) ou d'autres ponts H qui ne reposent pas sur la présence de broche ENABLE? ( et donc intégrer cela au dispositif en cours de mise au point pour LABOX)

Ltr
Titre: Re : RailCom: Générateur de CutOut
Posté par: lebelge2 le avril 28, 2024, 06:11:25 pm
BodyAndCo
Avec Mon Attiny85, ça fonctionne très bien, j’ai ajusté les tempos.
Dans la void CutOut()
Remplacer le delayMicroseconds(38) par 36 et le delayMicroseconds(20)  par 24
On peut ajuster précisément les signaux, si ça ne vas pas, je ne sais quoi te dire.
On pourrait modifier ces valeurs via le moniteur série en temps réel.

Laurent
On pourrait aisément intégrer un SECURITY GAP 4µs avant et après le CutOut avec Enable à l’état bas.
Le LMD18200 n’a pas de broche Enable, il faut mettre le PWM bas et Brake haut.
Voir les Datasheet.
Le LN298N a une broche Enable.

Bien à vous.
Titre: Re : Re : Re : RailCom: Générateur de CutOut
Posté par: trimarco232 le avril 28, 2024, 06:27:44 pm
(...)
3) décoder , à la manière d'un sniffer , le DCC issu de l'ESP32 , puis le reproduire
C’est un sujet sur lequel nous travaillons à quelques-uns. Si tu as déjà des choses réalisées, cela est très intéressant. L’objectif est de capter l’ensemble des trames DCC qui circulent sur le bus et, au travers d’une passerelle, envoyer ces trames sur un bus CAN pour par exemple permettre à un gestionnaire mais aussi tout autres appareils de connaitre « au plus près » l’état du réseau.
Je m’explique avec un exemple concret. La Box (basée sur DCC-Ex) sait retourner une confirmation de réception de commande, soit en CAN (les méthodes CAN revoyant un accusé de réception pour chaque commande) soit en TCP (WiFi, Ethernet) à l’appareil qui a envoyé cette commande. Mais dans ce dernier cas, seul l’appareil à l’origine de la commande reçoit la confirmation. Par exemple smartphone avec une appli Z21. Mais pas le gestionnaire de réseau par exemple. En cherchant à faire une diffusion CAN des commandes du bus DCC, on rend les informations accessibles à tous.
Donc si tu as déjà des solutions, je suis bien évidement très intéressé. Je vais créer un fil à part pour ne pas interférer avec celui-ci.
Christophe
Bonjour tous ,
non , je n'ai rien de tel (je suis accaparé par mon projet de centrale DCC/analogique combiné)
si je t'ai bien compris , il s'agit de récupérer auprès d'une centrale , les commandes qu'elle a accepté de la part des différents appareils qui lui sont raccordés , et de les retransmettre au(x) gestionnaire(s) , après avoir éventuellement éliminé les commandes redondantes
cela doit être relativement simple , je pense par exemple à un décodeur stationnaire , qui décoderait aussi les trames des décodeurs multifonctions , et qui au lieu de (en + de) sortir le résultat vers le moniteur de débogage , enverrait ce résultat dans un format ad hoc vers le gestionnaire ... (amha c'est un préalable , mais c'est de loin la partie la plus simple de la tâche qui vous attend ; de + , il me semble que le problème soit un peu pris à l'envers , mais c'est une autre histoire)
Titre: Re : Re : RailCom: Générateur de CutOut
Posté par: trimarco232 le avril 28, 2024, 06:37:45 pm
Bonjour
J ai une question qui va aider la recherche d éventuels autres pont H.
D'après OPEN DCC il faut insérer un "SECURITY GAP" de 4us  sur le L6203 entre son activation/désactivation ( broche ENABLE) et la commande de mise en "CC" ( via IN 1 et IN2)
Ceci est (fortement) recommandé notamment dans le cas de la présence de plusieurs (...)
Ltr
même si j'avoue que je n'ai pas trop compris ce que Wolfgang Kuffer veut protéger au juste avec ses security gap (je pense qu'il s'agit des décodeurs des locomotives) , je ne crois pas que cela soit nécessaire , dès que la source DCC est la même pour tous les boosters , et que les cutout sont générés par la même méthode , depuis cette source
Titre: Re : RailCom: Générateur de CutOut
Posté par: laurentr le avril 28, 2024, 09:12:00 pm
De ma compréhension le SECURITY GAP intervient lorsqu'un véhicule est à cheval entre sur 2 boosters. Ceux ci pilotant des électroniques qui peuvent avoir un temps de réaction "élastique" l'un vis à vis de l'autre pour être certain de ne pas être dans un cas où l'un a déjà fini et pas l'autre, on ajoute cette temporisation ce qui pourrait avoir des effets de bords sur les décodeurs mobiles.
C est donc un temps mort de sécurité mis à chaque bout du cutout.

(on pourrait aussi avoir en tête les fameux 4us de précision de delta des AVR avec micro?)

On pourra surement voir avec un L18200 et les ponts similaires comment traiter cela.

Ltr
Titre: Re : Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 29, 2024, 08:59:32 am
Bonjour à tous,

Retour d’expérience :

Avec l’Arduino Nano comme dit dans mes précédents messages, lecture parfaite des adresses de locomotives.

Configuration : Centrale DCC-Ex sur MEGA – Montage « leBelge » sur Nano – L298N

Malgré tout, de nombreuses trames sont hors des normes NMRA, 1/3 environ mais ça fonctionne. Voir copie d'écran en PJ.

Avec l’ATTiny45, je n’ai aucun résultat ! Les décodeurs refusent d’envoyer leur adresse. J’ai modifié les délais comme suggéré par lebelge mais les locos ne se laissent plus piloter ou refusent de bouger !!!

j’ai ajusté les tempos.
Dans la void CutOut()
Remplacer le delayMicroseconds(38) par 36 et le delayMicroseconds(20)  par 24

J’ai essayé d’autres valeurs mais cela ne change rien.

Je ne pense pas avoir de problème de câblage (lebelge ne m’a pas répondu sur ce point)

MEGA 6 -------> ATTiny PB2
MEGA 7 -------> L298 EN
ATTiny PB3 ---> L298 IN1
ATTiny PB4 ---> L298 IN2

J’ai réglé l’horloge à 8Mhz (internal), c'est normalement cela non ?

Quelqu’un a une idée du problème, ou mieux, de la solution ?

Christophe
Titre: Re : RailCom: Générateur de CutOut
Posté par: lebelge2 le avril 29, 2024, 11:23:36 am
Bonjour, ton cablage est correct.
Avec le L928N j'ai aussi parfois des très mauvaises mesures hors normes, je n'ais pas encore trouvé pourquoi, comportement étrange du module...
Je pense que à la sortie du nano, les signaux sont parfaits, c'est le module qui pose problème.
Pour en être certain, j'ai ajouter un test de diagnostique au programme.
Avec le moniteur série, il permet de faire des mesures et modifier les paramètres relatif au CutOut, test en cours...
Titre: Re : RailCom: Générateur de CutOut
Posté par: lebelge2 le avril 30, 2024, 12:22:48 am
BobyAndCo

Je crois avoir trouvé ton problème avec tes Attiny.
Le programme est fait pour mes Attiny que je programme avec une interface USB virtuelle et à 16,5 Mhz.

Je suppose que tu les programmes à  8 Mhz  avec un programmateur ordinaire, alors il
faut changer 2 paramètres :

TCCR1 à 3 au lieu de 4
TCNT1 à 102 au lieu de 112           (à 2 endroits)

J’ai fait ces modif et ça marche
Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 30, 2024, 08:27:17 am
@lebelge : Effectivement, avec cette modification ça fonctionne avec un ATTiny45 à 8Mhz.

Sur 3 locos testées, j'en ai juste une qui, à l'arret, ne se laisse pas lire 1/3 du temps environ. Bon ça veut dire que les 2/3 du temps, ça fonctionne. En roulage, la lecture est bonne. C'est un décodeur LENZ.

Les deux autres locos, ESU et Zimo envoient sans problème leur adresse en roulage et à l'arrêt.

Du coup, je publie le code ci-dessous dans lequel j'ai ajouté les modifications et le brochage sur l'ATTiny45.

J'ai ajouté un #define pour que le code soit compatible avec les autres versions d'µC. #define ATTiny45
   Cablage :

   MEGA 6 -------> ATTiny PB2
   MEGA 7 -------> L298 EN
   ATTiny PB3 ---> L298 IN1
   ATTiny PB4 ---> L298 IN2

// RailCom: Programme pour insérer un CutOut dans les trames DCC.  Arduino Uno, Nano, Micro.  Attiny85.(Betâ)
//
// V.1.0.   24-04-24  Testé avec Bridge Driver L6203 et L298N
// V.1.1.   27-04-24  Upgrade pour Attiny85 et polarité inverse.
//
// lebelge2@yahoo.fr
//--------------------------------- Sélectionné votre  Hardware Bridge Driver------------------------------------
#define L298N           // L6203
//#define LMD18200
//----------------------------------------------------------------------------------------------

/*
   Modifié 30/04/24 par christophe bobille

   Adapté pour ATTiny45 à 8Mhz (#define ATTiny45 ligne 27)
   DCC-Ex sur Arduino MEGA / booster LN298

   Cablage :

   MEGA 6 -------> ATTiny PB2
   MEGA 7 -------> L298 EN
   ATTiny PB3 ---> L298 IN1
   ATTiny PB4 ---> L298 IN2

*/

#define ATTiny45

volatile uint8_t dccrecState;
volatile uint8_t tempByte;
bool PolariteDcc = true;
bool Brake;
unsigned long currentTime = 0;
unsigned long previousTime = 0;
unsigned long Temps;
#define WAIT_PREAMBLE    0
#define WAIT_START_BIT   1
#define WAIT_DATA        2
#define WAIT_END_BIT     3
#define MaxDccSize 6                         // DCC messages can have a length upto this value

class DccMessage {
  public:
    volatile uint8_t size;
    volatile uint8_t data[MaxDccSize];        // The contents of the last dcc message received
} dccMessage;

struct {
  uint8_t bitCount;                           // Count number of preamble bits / if we have a byte
  volatile uint8_t tempMessage[MaxDccSize];   // Once we have a byte, we store it in the temp message
  volatile uint8_t tempMessageSize;           // Here we keep track of the size, including XOR
} dccrec;                                     // The received DCC message is assembled here

struct {
  uint8_t port;
  uint8_t bit;                                // Bitmask for reading the input Port
  volatile uint8_t *portRegister;
} dccIn;
//---------------------------------------------- SetUp -------------------------------------------------------
void setup() {
#ifdef LMD18200
  Brake = true;
#endif
#ifdef __AVR_ATmega328P__                    // --- Arduino AVR ---
#define PORTx PORTD                          // PORTD
  Serial.begin(250000);
  init_timer2();                             // Timer 2
#elif ARDUINO_AVR_ATTINYX5                   // --- ATTINY CORE ATTINY85 ---  (Micronucleus DigiSpark)
#define PORTx PORTB                          // PORTB
  init_timer1();                             // Timer 1
#else
#error "Unsupported CPU, you need to add another configuration section for your CPU"
#endif
#define PinIn PB2                               // Entrée trames Dcc sans CutOut
#define PinOut1 PB3                             // Sortie trames DCC avec CutOut           (LMD18200: Dir (sans CutOut))
#define PinOut2 PB4                             // Sortie trames DCC inversées avec CutOut (LMD18200: Brake (uniquement CutOut))
  pinMode(PinIn, INPUT);
  pinMode(PinOut1, OUTPUT);
  pinMode(PinOut2, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(PinIn), Dcc_Interrupt, CHANGE );
  dccIn.port = digitalPinToPort(PinIn);
  dccIn.bit = digitalPinToBitMask(PinIn);
  dccIn.portRegister = portInputRegister(dccIn.port);
}
//--------------------------------------------- Loop --------------------------------------------------------
void loop() {}
//------------------------------------------- Attiny 85 -----------------------------------------------------------
#ifdef ARDUINO_AVR_ATTINYX5
void init_timer1(void) {
  noInterrupts();              // disable all interrupts
  TCCR1 = 0;                   // B0 à B3: diviseur par 1, 2, 4, 8
#ifdef ATTiny45
  TCNT1 = 102;                 // preload the timer for ATTiny45 (8 Mhz)
#else
  TCNT1 = 112;                 // preload the timer
#endif
  TIMSK = 4;                   // the timer is used in overflow interrupt mode
  interrupts();                // enable all interrupts
}
//--------------------------------------------- AVR ---------------------------------------------------------
#elif __AVR_ATmega328P__
void init_timer2(void) {
  noInterrupts();              // disable all interrupts
  TCCR2A = 0;                  // should be zero for our purpose
  TCCR2B = 0;                  // 0 => timer is stopped
  TCNT2 = 102;                 // preload the timer
  TIMSK2 |= 1;                 // the timer is used in overflow interrupt mode
  interrupts();                // enable all interrupts
}
#endif
//----------------------------------------------------------------------------
void CutOut() {
  if (PolariteDcc == false) {        // Si DCC inversé,
    delayMicroseconds(38);           // retarder CutOut d'un demis-bit
    PORTx = PORTx | B00001000;
    PORTx = PORTx &= ~ B00010000;
    delayMicroseconds(20);
  }
  delayMicroseconds(8);
  PORTx = PORTx | B00010000;
  PORTx = PORTx | B00001000;
  delayMicroseconds(430);
  PORTx = PORTx &= ~ B00001000;
  PORTx = PORTx &= ~ B00010000;
}

//------------------------------------------ DCC Interrupt -------------------------------------------
void Dcc_Interrupt() {
#ifdef __AVR_ATmega328P__
  if ((bitRead(PIND, PinIn)) == 1) {                // bitRead en 3µs,    digitalRead en 7µs
    TCCR2B |= 2;                                    // Start Timer 2
#elif ARDUINO_AVR_ATTINYX5
  if ((bitRead(PINB, PinIn)) == 1) {
#ifdef ATTiny45
    TCCR1 |= 3;                                     // Start Timer 1
#else
    TCCR1 |= 4;                                     // Start Timer 1
#endif
#endif
    if (Brake == false)
      PORTx = PORTx &= ~ B00010000;                 // Temps d'exécution de ces instructions:
    PORTx = PORTx | B00001000;                      // 0,125µs en ASM,    0,170µs en C,      6µs en C++
  }
  else {
    if (Brake == false)
      PORTx = PORTx | B00010000;
    PORTx = PORTx &= ~ B00001000;
  }
  if (dccrecState == WAIT_START_BIT)  {           // L'intervalle de temps après le préambule est évalué,
    Temps = micros();
    previousTime = Temps - currentTime;
    currentTime = Temps;                          // si l'écart entre le dernier et le premier zéro est de 116 µs,
    if (previousTime > 90)                        // alors le signal est reconnu dans la bonne phase.
      PolariteDcc = true;                         // Polarité correcte
    else
      PolariteDcc = false;                        // Polarité inversée
  }
}

//------------------------------------------- ISR Timers ----------------------------------------------------
#ifdef __AVR_ATmega328P__
ISR(TIMER2_OVF_vect) {
  TCCR2B = 0;                                    // 0 => timer is stopped
  TCNT2 = 102;                                   // preload the timer. Fr. 16Mhz
#elif ARDUINO_AVR_ATTINYX5
ISR(TIMER1_OVF_vect) {
  TCCR1 = 0;                                     // 0 => timer is stopped
  #ifdef ATTiny45
  TCNT1 = 102;                 // preload the timer for ATTiny45 (8 Mhz)
#else
  TCNT1 = 112;                                   // preload the timer.  Fr. 16,5Mhz
#endif
#endif
  uint8_t DccBitVal;
  DccBitVal = !(*dccIn.portRegister & dccIn.bit);
  dccrec.bitCount++;
  switch (dccrecState) {
    case WAIT_PREAMBLE:
      if (DccBitVal) {                              // a "1" bit is received
        if (dccrec.bitCount >= 10)
          dccrecState = WAIT_START_BIT;
      }
      else
        dccrec.bitCount = 0;                        // not a valid preamble.
      break;
    case  WAIT_START_BIT:
      if ( !DccBitVal ) {                           // a "0" bit is received
        dccrecState = WAIT_DATA;
        dccrec.tempMessageSize = 0;
        uint8_t i;
        for (i = 0; i < MaxDccSize; i++ )
          dccrec.tempMessage[i] = 0;
        dccrec.bitCount = 0;
      }
      break;
    case WAIT_DATA:
      if ( dccrec.bitCount == 8 ) {                  // byte is complete
        if (dccrec.tempMessageSize == MaxDccSize ) { // Packet is too long - abort
          dccrecState = WAIT_PREAMBLE;
          dccrec.bitCount = 0;
        }
        else
          dccrecState = WAIT_END_BIT;                // Wait for next byte or end of packet
      }
      break;
    case WAIT_END_BIT:
      if ( DccBitVal ) {                             // End of packet?
        CutOut();
        dccrecState = WAIT_PREAMBLE;
      }
      else                                           // Get next Byte
        dccrecState = WAIT_DATA;
      dccrec.bitCount = 0;                           // prepare for the next byte
  }
}

Titre: Re : RailCom: Générateur de CutOut
Posté par: lebelge2 le avril 30, 2024, 04:21:19 pm
Arduino Mega et  Commande/Base Station.

Petite carte shield dotée seulement d’un Attiny85.
Insère la découpe RailCom dans la trame Dcc.

Signaux disponibles sur trois broches pour piloter tout type de Bridge Driver.

Fabriquée de façon artisanale mais suivant demande, je pourrais dessiner puis publier les fichiers Gerber/Kicad.

Bien à vous.
Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 30, 2024, 04:25:54 pm
Super ce petit montage. Oui ce serait sympa d'avoir les Gerber !

Est-ce que tu ne peux pas en profiter pour exposer les pins nécessaires à sa programmation ?

Merci par avance

Christophe
Titre: Re : RailCom: Générateur de CutOut
Posté par: laurentr le avril 30, 2024, 04:28:46 pm
Bonjour

@Christophe, l'adaptation pour le 328P ne devrait elle pas plutôt avoir PD2 comme entrée (INT0) pour le signal DCC ? et passer les broches de pilotage sur le PORTB ? ( PB2 ne gère pas des interruptions externes me semble t il?)

Je regarde de mon cote pour porter cela sur AVRx ( MAGTINY AVRx série 0 ou 1 (Atmegax08 x09, NANOEVERY et AVR Dx ( DA DB DD)...)
Je pense cependant plus à un hardware disposant de 2 timer B dans ce lots de candidats...

Ltr
Titre: Re : Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le avril 30, 2024, 04:32:55 pm
@Christophe, l'adaptation pour le 328P ne devrait elle pas plutôt avoir PD2 comme entrée (INT0) pour le signal DCC ? et passer les broches de pilotage sur le PORTB ? ( PB2 ne gère pas des interruptions externes me semble t il?)

Eheuuuu, excuse moi Laurent mais je ne vois pas du tout de quoi tu parles ! Je ne crois pas que l'on ait parler de 328 ici. Ce qui est amusant c'est l'ATTiny. Je trouve ce petit montage simple et ludique, c'est tout
Titre: Re : RailCom: Générateur de CutOut
Posté par: laurentr le avril 30, 2024, 05:46:08 pm
Y a bien une petit ligne qui le dit:

#ifdef __AVR_ATmega328P__                    // --- Arduino AVR --
Donc oui pour le TINY pas de sujet mais si 328P /MEGA ou autre le mapping brochage est à revoir pour être sur INT0 ( PD2) comme PB2 l'est sur le TINYx5

Ltr
Titre: Re : RailCom: Générateur de CutOut
Posté par: laurentr le mai 02, 2024, 06:00:50 am
Bonjour


J'ai comme prévu passe un peu de temps sur une implémentation "qui va bien" avec un cœur AVRx.

J ai retenu les TINY x6 de la serie 2: ATTINY426, 826 1626, 3226.
Le code est également 100% compatible avec leur dérivés x7 ATTINY 427,827,1627,3227.

Bien que désignés par le terme ATTINY il s agit de "MEGATINY" dont le cœur et les mécanismes sont plus proches des AVR Dx et ATMEGA serie 0 ( x08,x09) que des TINY historiques.

Ici on met à contribution la hard natif du chip :) ( TIMER B, LOGIC, COMPARATEUR pour la protection, EVENT pour lier le tout.)

Au final la boucle principale est ... vide de code!
 
Tout se gère via des mécanismes internes et il n'y a plus qu'à "profiter" du résultat pour piloter le pont H L6203 par ses broches ENABLE, IN1 et IN2.

Le MAPPING retenu est en commentaire dans le code ci joint avec un brochage SOIC20.

Le code va permettre de traiter 3 cas de figure:

SIMPLE BOOSTER: amplifie les signaux reçus sur les entrées PIN_PA1 et PIN_PA2, assure une protection contre les CC et surcharges.(avec sortie LED temoin,...)
Rm: Ce signal peut déjà comporter un CUTOUT RAILCOM, il sera alors reproduit.
BOOSTER GERANT LES TIMINGS SANS CUTOUT RAILCOM: on laisse ici le hard redécouper les trames pilotant le pont en H par les inputs sur PIN_PA1 et PIN_PA2.
BOOSTER AVEC CUTOUT RAILCOM: on insère le CUTOUT dans un signal en entrée sans CUTOUT sur PIN_PA1 et PIN_PA2.

A tester sans modération! :)

CODE PART1:
#include <Arduino.h>

/*
  author: LTR Laurent ROEKENS
  version v0.0.1
  last update date: 01/05/2024
*/


/*
  CUTOUT GENERATOR:

  Select mode:

  Simple BOOSTER (H bridge driven by input signals)
  BOOSTER without RAILCOM (H bridge drive by this CPU timings)
  BOOSTER WITH RAILCOM (H bridge drive by this CPU timings)

  In all modes CURRENT LIMITOR is activated (by measure on COMPARATOR AC0)

*/

/*
  HOW IS IT?:
  FROM DCC SIGNAL INPUTS IN PINS PA_PA2 & PIN_PA3:
  analyses DCC messages
  if required introduce CUTOUT at end of DCC messages with security GAP to drive L6203 H BRIDGE PINS

  use of EVENT software
  use of LOGIC hardware
  use of COMPARATOR for protection (overload/shorcut)
  use TCB0 counter in capture frequency mode
  L6203 IN1 and IN2 are driven by logic outputs 
  L6203_ENABLE_PIN is driven by logic output


// Connect the Attinyx26 as follows in SOIC20 SUPPORT:

//
//                            --------U--------
//     VCC (+5V)             -| 1 VCC   GND 20|- GND
//     LOGIC_CUTOUT_OUT_PIN  -| 2 PA4   PA3 19|- LOGIC_CUTOUT_IN_PIN
//     LOGIC_ENABLE_OUT_PIN  -| 3 PA5   PA2 18|- DCC_PIN_L
//     LOGIC_ENABLE_IN_PIN   -| 4 PA6   PA1 17|- DCC_PIN_R
//     L6203_ENABLE_PIN      -| 5 PA7   PA0 16|- UDPI
//     COMP_IN+              -| 6 PB5   PC3 15|- RAILCOM_MODE_SELECTOR_INPUT_PIN
//     COMP_IN-              -| 7 PB4   PC2 14|- LED_SHORCUT_OVERLOAD
//     L6203_IN2             -| 8 PB3   PC1 13|- LED_RAILCOM_ENABLE
//     L6203_IN1             -| 9 PB2   PC0 12|- LED_SIMPLE_AMPLIFICATOR_MODE
//     EXT_START_STOP_PIN    -|10 PB1   PB0 11|- SIMPLE_AMPLIFICATOR_MODE_INPUT_PIN
//                            -----------------

// CODE & PIN MAPPING FULY COMPATIBLE WITH ATTINYx27

*/

#include "Logic.h"
#include "Event.h"
#include "Comparator.h"

//RM: MILLIS_USES_TIMERB1

#define MaxDccSize 6                         // DCC messages can have a length upto this value

struct {
  uint8_t bitCount;                           // Count number of preamble bits / if we have a byte
  volatile uint8_t tempMessage[MaxDccSize];   // Once we have a byte, we store it in the temp message
  volatile uint8_t tempMessageSize;           // Here we keep track of the size, including XOR
} dccrec;                                     // The received DCC message is assembled here


#define dccrecState       GPIOR0              // fast: saves 3 clock cycli, but requires a free GPIOR
#define dccHalfBit        GPIOR1              // fast
#define RAILCOM           GPIOR2              // fast
#define AMPLIFICATOR_MODE GPIOR3              // fast


//******************************************************************************************************
// Defines, definitions and instantiation of local types and variables
//******************************************************************************************************
// Values for half bits from RCN 210, section 5: http://normen.railcommunity.de/RCN-210.pdf
#define ONE_BIT_MIN                             F_CPU / 1000000 * 52
#define ONE_BIT_MAX                             F_CPU / 1000000 * 64
#define ZERO_BIT_MIN                            F_CPU / 1000000 * 90
#define ZERO_BIT_MAX                            F_CPU / 1000000 * 119


// Possible values for dccrecState
#define WAIT_PREAMBLE                           (1<<0) //WAIT_PREAMBLE = 0;
#define WAIT_START_BIT                          (1<<1) //WAIT_START_BIT = 1;
#define WAIT_DATA                               (1<<2) //WAIT_DATA = 2;
#define WAIT_END_BIT                            (1<<3) //WAIT_END_BIT = 3;

// Possible values for dccHalfBit
#define EXPECT_ZERO                             (1<<0) //EXPECT_ZERO = 0;
#define EXPECT_ONE                              (1<<1) //EXPECT_ONE = 1;
#define EXPECT_ANYTHING                         (1<<2) //EXPECT_ANYTHING = 2;

// Values for half bits from RCN 217,
#define ENABLE_STOP_TIME                        F_CPU / 1000000 * 26    // CUTOUT MIN START VALUE
#define ENABLE_START_TIME                       F_CPU / 1000000 * 488  // CUTPOUT MAX STP¨VALUE

// Value according OPEN DCC best pracitces:
#define CUTOUT_START_TIME                       F_CPU / 1000000 * 30   //4us security gap cutout at start
#define CUTOUT_STOP_TIME                        F_CPU / 1000000 * 484   //4us secutity gap cutout at stop


#define DCC_TIMER                               TCB0

#define DCC_PIN_R                               PIN_PA1  //DCC FROM R INPUT SIGNAL DRIVEN BY LUT0 IN1
#define DCC_PIN_L                               PIN_PA2  //DCC FROM L INPUT SIGNAL DRIVEN BY LUT0 IN2

//PIN_PA3:
#define CUTOUT_LOGIC_IN_PIN                     PIN_PA3 //EVENT CHANNEL 1 IN

//PIN_PA4:

#define LOGIC_CUTOUT_OUT_PIN                    PIN_PA4
#define LOGIC_CUTOUT_OUT_PIN_ON                 PORTA.OUTSET = PIN4_bm
#define LOGIC_CUTOUT_OUT_PIN_OFF                PORTA.OUTCLR = PIN4_bm

//
//RM: HARDWARE SHOULD LINK PIN_PA3 WITH PIN_PA4
//

//PIN_PA5:

#define LOGIC_ENABLE_OUT_PIN                    PIN_PA5
#define LOGIC_ENABLE_OUT_PIN_ON                 PORTA.OUTSET = PIN5_bm
#define LOGIC_ENABLE_OUT_PIN_OFF                PORTA.OUTCLR = PIN5_bm

//PIN_PA6:

#define LOGIC_ENABLE_IN_PIN                     PIN_PA6

//
//RM: HARDWARE SHOULD LINK PIN_PA5 WITH PIN_PA6
//

//PIN_PA7:
#define L6203_ENABLE_OUT_PIN                    PIN_PA7 //L6203_ENABLE_PIN

//PIN_PB2:
#define L6203_IN1                               PIN_PB2 //DRIVEN BY EVOUTB

//PIN_PB3:
#define L6203_IN2                               PIN_PB3 //DRIVEN BY LUT2 OUTPUT

#define RAILCOM_MODE_SELECTOR_INPUT_PIN         PIN_PC3 //RAILCOM SWITCH ENABLE/DISABLE

#define LED_SHORCUT_OVERLOAD_PIN                PIN_PC2 //LED INDICATOR FOR OVERLOAD OR SHORCIRCUIT

#define LED_RAILCOM_ENABLE_PIN                  PIN_PC1
#define LED_RAILCOM_ENABLE_PIN_ON               PORTC.OUTSET = PIN1_bm
#define LED_RAILCOM_ENABLE_PIN_OFF              PORTC.OUTCLR = PIN1_bm

#define LED_SIMPLE_AMPLIFICATOR_MODE_PIN        PIN_PC0
#define LED_SIMPLE_AMPLIFICATOR_MODE_PIN_ON     PORTC.OUTSET = PIN0_bm
#define LED_SIMPLE_AMPLIFICATOR_MODE_PIN_OFF    PORTC.OUTCLR = PIN0_bm

#define EXT_STOP_START_INPUT_PIN                PIN_PB1 //STOP_START_INPUT_PIN: DRIVE EXTERNAL STOP /START

#define SIMPLE_AMPLIFICATOR_MODE_INPUT_PIN      PIN_PB0 //AMPLIFICATOR MODE: SIMPLE APLIFICATOR OR TIMINGS MANAGE BY THIS HARDWARE DRIVING L6203
             

void INIT_PINS(){

  //RAILCOM_MODE_SELECTOR_PIN:
  //pinMode(RAILCOM_MODE_SELECTOR_INPUT_PIN,INPUT); //INPUT slow
  PORTC.DIRCLR = PIN3_bm; //INTPUT fast
  //PORTC.PIN3CTRL |= PORT_PULLUPEN_bm; /// use the internal pullup resistor on PC3
  PORTC.PIN3CTRL &= ~PORT_PULLUPEN_bm;  // don't use the internal pullup resistor on PC3
 

  //AMPLIFICATOR_MODE_MODE_SELECTOR_INPUT_PIN:
  //pinMode(SIMPLE_AMPLIFICATOR_MODE_INPUT_PIN,INPUT); //INPUT slow
  PORTB.DIRCLR = PIN0_bm; //INTPUT fast
  //PORTC.PIN0CTRL |= PORT_PULLUPEN_bm; /// use the internal pullup resistor on PB0
  PORTC.PIN0CTRL &= ~PORT_PULLUPEN_bm;  // don't use the internal pullup resistor on PB0

  //STOP_START_PIN:
  //pinMode(EXT_STOP_START_INPUT_PIN,INPUT); //INPUT slow
  PORTB.DIRCLR = PIN1_bm;
  //PORTB.PIN1CTRL |= PORT_PULLUPEN_bm; /// use the internal pullup resistor on PC2
  PORTB.PIN1CTRL &= ~PORT_PULLUPEN_bm;  // don't use the internal pullup resistor on PC2

  //LED_RAILCOM_ENABLE
  //pinMode(LED_RAILCOM_ENABLE_PIN,OUTPUT); //OUTPUT slow
  PORTC.DIRSET = PIN1_bm; //OUTPUT fast
  PORTC.DIRCLR = PIN1_bm; //INIT STATE OFF

  //LED_SIMPLE_AMPLIFICATOR_MODE
  //pinMode(LED_SIMPLE_AMPLIFICATOR_MODE_PIN,OUTPUT); //OUTPUT slow
  PORTC.DIRSET = PIN0_bm; //OUTPUT fast
  PORTC.DIRCLR = PIN0_bm; //INIT STATE OFF

  //LED_SHORCUT_OVERLOAD_PIN PIN_PC2
  //pinMode(LED_SHORCUT_OVERLOAD_PIN,OUTPUT); //OUTPUT slow
  PORTC.DIRSET = PIN2_bm; //OUTPUT fast
  PORTC.DIRCLR = PIN2_bm; //INIT OFF

}

void INIT_ADDITIONNAL_PINS(){
  //INIT PINS DONT MANAGED BY LUT OR YET SET:

  //LOGIC_CUTOUT_OUT_PIN:
  //pinMode(LOGIC_CUTOUT_OUT_PIN,OUTPUT); //slow
  PORTA.DIRSET = PIN4_bm; //OUTPUT fast
  PORTA.OUTCLR = PIN4_bm; //INIT STATE OFF

  //LOGIC_CUTOUT_IN_PIN:
  //pinMode(CUTOUT_LOGIC_IN_PIN,INPUT); //slow
  PORTA.DIRCLR = PIN3_bm; //INPUT fast
  //PORTA.PIN3CTRL |= PORT_PULLUPEN_bm; // use the internal pullup resistor on PA3
  PORTA.PIN3CTRL &= ~PORT_PULLUPEN_bm;  // don't use the internal pullup resistor on PA3

  //LOGIC_ENABLE_OUT_PIN
  //pinMode(LOGIC_ENABLE_OUT_PIN,OUTPUT); //slow
  PORTA.DIRSET = PIN5_bm; //OUTPUT fast
  PORTA.OUTCLR = PIN5_bm; //INIT STATE OFF

  //LOGIC_ENABLE_IN_PIN:
  //pinMode(LOGIC_ENABLE_IN_PIN,INPUT); //slow
  PORTA.DIRCLR = PIN6_bm; //INPUT FAST
  //PORTA.PIN6CTRL |= PORT_PULLUPEN_bm; /// use the internal pullup resistor on PA6
  PORTA.PIN6CTRL &= ~PORT_PULLUPEN_bm;  // don't use the internal pullup resistor on PA6

  //L6203_IN1: PB2
  //pinMode(L6203_IN1,OUTPUT); //slow
  PORTB.DIRSET = PIN2_bm; //OUTPUT fast
  PORTB.OUTCLR = PIN2_bm; //INIT STATE OFF

  //L6203_IN2:PB3: already done by LOGIC block init
  //pinMode(L6203_IN2,OUTPUT);

  //L6203_ENABLE_PIN
  //pinMode(L6203_ENABLE_OUT_PIN,OUTPUT); //OUTPUT slow
  PORTA.DIRSET = PIN7_bm; //OUTPUT fast
  PORTA.OUTCLR = PIN7_bm; //INIT STATE OFF

}

void INIT_TCB(){
  // Reset registers. See the data sheets for details
    noInterrupts();
  // Clear the main timer control registers. Needed since the Arduino core creates some presets
      DCC_TIMER.CTRLA = 0;
      DCC_TIMER.CTRLB = 0;
      DCC_TIMER.EVCTRL = 0;
      DCC_TIMER.INTCTRL = 0;
      DCC_TIMER.CCMP = 0;
      DCC_TIMER.CNT = 0;
      DCC_TIMER.INTFLAGS = 0;

  // Set values:
      DCC_TIMER.CTRLA = TCB_ENABLE_bm;
      DCC_TIMER.CTRLB = TCB_CNTMODE_FRQ_gc;
      DCC_TIMER.EVCTRL = TCB_CAPTEI_bm | TCB_FILTER_bm;
      DCC_TIMER.INTCTRL |= TCB_CAPT_bm;

    interrupts();
}




Titre: Re : RailCom: Générateur de CutOut
Posté par: laurentr le mai 02, 2024, 06:01:40 am
CODE PART2:

void INIT_SYSTEM_WITH_RAILCOM(){

  //COMPARATOR:

  //when inverting > non inverting ==> AC OUT = high
  //measured current will be connected to inverting input (L6203 SENSE PIN trough a 10K resistor)
   
    Comparator.input_p = comparator::in_p::in1; //inverting PIN_PB5
    Comparator.input_n = comparator::in_n::in1; //non inverting PIN_PB4
    Comparator.reference = comparator::ref::disable;
    Comparator.hysteresis = comparator::hyst::disable;
    Comparator.output = comparator::out::disable; // No output pin, signal not inverted internally

    Comparator.init();
   
    Comparator.start(); // Start comparator
 
  //DEFINE CCL LUT3:
    //NEED EVENT CHANNEL FIRST TO ROUTE INSIDE LUT3: USE EVENT CHANNEL 3

    Event3.assign_generator_pin(gen3::pin_pa6);   //PIN_PA6 AS EVENT CH3 SOURCE
    Event3.set_user(user::ccl3_event_a);          //EVENT A OF LUT3

    Event3.start();

    Logic3.enable;                       //ENABLE LUT3
    Logic3.input0 = logic::in::ac;       //AC0 level OUT
    Logic3.input1 = logic::in::event_a;  //EVENT_A from EVENT CH3
    Logic3.input2 = logic::in::masked;   //not used
    Logic3.output = logic::out::disable; //NOT USED, use EVENT CH2 instead
    Logic3.truth = 0x04;     

    Logic3.init();


    Event2.assign_generator(gen::ccl3_out);
    Event2.set_user(user::evouta_pin_pa7);

    Event2.start();   
 
  //DEFINE CCL LUT0: 
    Logic0.enable;                       //ENABLE LUT0
    Logic0.input0 = logic::in::masked;   //UDPI NOT USED
    Logic0.input1 = logic::in::pin;      //PIN_PA1
    Logic0.input2 = logic::in::pin;      //PIN_PA2
    Logic0.output = logic::out::disable; //NOT USED, use EVENT CH5 instead
    Logic0.truth = 0x04;                 // Set truth table

    Logic0.init();

    Event5.assign_generator(gen::ccl0_out); //source LUT0 OUT
    Event5.set_user(user::tcb0_capt);       //route to TCB0
    Event5.set_user(user::ccl1_event_a);    //event A CCL1
    Event5.set_user(user::ccl2_event_a);    //event A CCL2

    Event5.start();

    Event1.assign_generator_pin(gen1::pin_pa3); //PA3 INPUT
    Event1.set_user(user::ccl1_event_b);        //event B CCL1
    Event1.set_user(user::ccl2_event_b);        //event B CCL2

    Event1.start();

    Logic1.enable;                        //ENABLE LUT1
    Logic1.input0 = logic::in::event_a;   //logic1 in0 event A
    Logic1.input1 = logic::in::event_b;   //logic1 in1 event B
    Logic1.input2 = logic::in::masked;    //not used
    Logic1.output = logic::out::disable;  //OUTPUT NOT USED, OUT STATE will go to EVENT CH0
    Logic1.truth = 0x01;                  // Set truth table

    Logic1.init();

    Event0.assign_generator(gen::ccl1_out);
    Event0.set_user(user::evoutb_pin_pb2);

    Event0.start();

    Logic2.enable;                        //ENABLE LUT2
    Logic2.input0 = logic::in::event_a;   //logic2 in0 event A
    Logic2.input1 = logic::in::event_b;   //logic2 in1 event B
    Logic2.input2 = logic::in::masked;    //not used
    Logic2.output = logic::out::enable;   //PIN_PB3 OUTPUT TO DRIVE L6203 IN
    Logic2.truth = 0x02;                  // Set truth table

    Logic2.init();

    Event4.set_generator(gen::ac0_out);    //source AC0 out
    Event4.set_user(user::evoutc_pin_pc2); //manage LED_SHORCUT_OVERLOAD

    Event4.start();
   
    Logic0.start();
    Logic1.start();
    Logic2.start();
    Logic3.start();

}

void INIT_SYSTEM_SIMPLE_AMPLIFICATOR(){

  //COMPARATOR:
   
    Comparator.input_p = comparator::in_p::in1;
    Comparator.input_n = comparator::in_n::in1;
    Comparator.reference = comparator::ref::disable;
    Comparator.hysteresis = comparator::hyst::disable;
    Comparator.output = comparator::out::disable_invert; // No output pin, signal inverted internally

    Comparator.init();
   
    Comparator.start(); // Start comparator

  //EVENT CH1:
    Event1.assign_generator_pin(gen1::pin_pa1);   //PIN_PA1 AS EVENT CH1 SOURCE
    Event1.set_user(user::ccl1_event_a);          //EVENT A OF LUT0
    Event1.set_user(user::ccl2_event_a);          //EVENT B OF LUT2

    Event1.start();

  //EVENT CH2:
    Event2.assign_generator_pin(gen2::pin_pa2);   //PIN_PA2 AS EVENT CH2 SOURCE
    Event2.set_user(user::ccl1_event_b);          //EVENT A OF LUT0
    Event2.set_user(user::ccl2_event_b);          //EVENT B OF LUT2

    Event2.start();

  //LOGIC LUT1:
    Logic1.enable;                       //ENABLE LUT3
    Logic1.input0 = logic::in::event_a;  //AC0 level OUT
    Logic1.input1 = logic::in::event_b;  //EVENT_A from EVENT CH3
    Logic1.input2 = logic::in::masked;   //not used
    Logic1.output = logic::out::disable; //NOT USED, use EVENT CH0 instead
    Logic1.truth = 0x04;   

    Logic1.init();

  //EVENT CH0:
    Event0.assign_generator(gen::ccl1_out);
    Event0.set_user(user::evoutb_pin_pb2);

    Event0.start();

  //LOGIC LUT2:
    Logic2.enable;                        //ENABLE LUT2
    Logic2.input0 = logic::in::event_a;   //logic1 in0 event A
    Logic2.input1 = logic::in::event_b;   //logic1 in1 event B
    Logic2.input2 = logic::in::masked;    //not used
    Logic2.output = logic::out::enable;   //OUTPUT PIN_PB3
    Logic2.truth = 0x02;                  // Set truth table

    Logic2.init();

  //LOGIC LUT3:
    Logic3.enable;                       //ENABLE LUT3
    Logic3.input0 = logic::in::ac;       //AC0 level OUT
    Logic3.input1 = logic::in::masked;   //not used
    Logic3.input2 = logic::in::masked;   //not used
    Logic3.output = logic::out::disable; //NOT USED, use EVENT CH2 instead
    Logic3.truth = 0x02;     

    Logic3.init();

  //EVENT CH3:
    Event3.assign_generator(gen::ccl3_out);
    Event3.set_user(user::evouta_pin_pa7); //L6203_ENABLE_PIN

    Event3.start();

  //EVENT CH4:
    Event4.assign_generator(gen::ac0_out);
    Event4.set_user(user::evoutc_pin_pc2);

    Event4.start();

  //START LOGIC NODES:
    Logic1.start();
    Logic2.start();
    Logic3.start();


}



CODE PART3:


void CUTOUT(bool RAILCOM_ACTIV_OR_NOT){
 
  if(RAILCOM_ACTIV_OR_NOT == false) //NO RAILCOM CUTOUT TO INSERT
  {
    //BY PASS CUTOUT: FORCE SOME PINS STATE ON:
    LOGIC_ENABLE_OUT_PIN_ON;
    LOGIC_CUTOUT_OUT_PIN_ON;

    return;
  }

  //RAILCOM ACTIV: NEED TO INSERT CUTOUT
 
  //TIMER B IS ALREADY STARTED AND CNT GO UP     
  if(TCB0.CNT < ENABLE_STOP_TIME) //< 26us
  {
    //ENABLE ON
    LOGIC_ENABLE_OUT_PIN_ON;
    while(TCB0.CNT < ENABLE_STOP_TIME) {;}; //WAIT TILL IS OVER
  }
  else if((TCB0.CNT < CUTOUT_START_TIME) && (TCB0.CNT >= ENABLE_STOP_TIME)) // >=26us & <30us
  {
    //ENABLE OFF
    LOGIC_ENABLE_OUT_PIN_OFF;
    while(TCB0.CNT < CUTOUT_START_TIME) {;}; //WAIT TILL IS OVER
  }
  else if((TCB0.CNT < CUTOUT_STOP_TIME) && (TCB0.CNT >= CUTOUT_START_TIME)) // >=30us & <484us
  {
    //MAKE CUTOUT:
    LOGIC_CUTOUT_OUT_PIN_ON;
    while(TCB0.CNT < CUTOUT_STOP_TIME) {;}; //WAIT TILL IS OVER
  }
  else if((TCB0.CNT < ENABLE_START_TIME) && (TCB0.CNT >= CUTOUT_STOP_TIME)) // >=484 us & < 488us
  {
    //STOP CUTOUT
    LOGIC_CUTOUT_OUT_PIN_OFF;
    while(TCB0.CNT < ENABLE_START_TIME) {;}; //WAIT TILL IS OVER
  }
  else if ((TCB0.CNT >= ENABLE_START_TIME)) // >=488us
  {
    //ENABLE PIN ON:
    LOGIC_ENABLE_OUT_PIN_ON;

    //CUTOUT IS OVER

  }
}


//------------------------------------------ DCC Interrupt -------------------------------------------


//------------------------------------------- ISR Timers ----------------------------------------------------
ISR(TCB0_INT_vect) {

  TCB0.EVCTRL ^= TCB_EDGE_bm;                          // Change the event edge at which we trigger
  uint16_t  delta = TCB0.CCMP;                        // Delta holds the time since the previous interrupt
  uint8_t DccBitVal;

  if ((delta >= ONE_BIT_MIN) && (delta <= ONE_BIT_MAX)) {
    if (dccHalfBit & EXPECT_ONE) {                     // This is the second part of the 1 bit
      dccHalfBit = EXPECT_ANYTHING;
      DccBitVal = 1;
    }
    else if (dccHalfBit & EXPECT_ANYTHING) {           // This is the first part of the 1 bit
      dccHalfBit = EXPECT_ONE;
      return;
    }
    else {                                             // We expected a 1, but received 0 => abort
      TCB0.EVCTRL ^= TCB_EDGE_bm;                     // Likely J/K should be changed
      dccHalfBit = EXPECT_ANYTHING;
      dccrecState = WAIT_PREAMBLE;
      dccrec.bitCount = 0;
      return;
    }
  }
  else if ((delta >= ZERO_BIT_MIN) && (delta <= ZERO_BIT_MAX)) {
    if (dccHalfBit & EXPECT_ZERO) {                    // This is the second part of the 0 bit
      dccHalfBit = EXPECT_ANYTHING;
      DccBitVal = 0;
      }
    else if (dccHalfBit & EXPECT_ANYTHING) {           // This is the first part of the 0 bit
      dccHalfBit = EXPECT_ZERO;
      return;
    }
    else {                                             // We expected a 0, but received 1 => abort
      dccHalfBit = EXPECT_ANYTHING;
      dccrecState = WAIT_PREAMBLE;
      dccrec.bitCount = 0;
      return;
    }
  }
  else {
    // We ignore other halfbits, to avoid interference with orther protocols.
    // In case railcom would be implemented, here we could detect the cutout start (26..32us)
    return;
  } 


  dccrec.bitCount++;

  switch( dccrecState )
  {
  // According to NMRA standard S9.2, a packet consists of:
  // - Preamble
  // - Packet Start Bit
  // - Address Data Byte:
  // - Data Byte Start Bit + Data Byte [0 or more times]
  // - Packet End Bit


  case WAIT_PREAMBLE:
      // The preamble to a packet consists of a sequence of "1" bits.
      // A digital decoder must not accept as a valid, any preamble
      // that has less then 10 complete one bits
      if( DccBitVal )                                   // a "1" bit is received
      {
        if( dccrec.bitCount > 10 )
          dccrecState = WAIT_START_BIT;
      }
      else
      {
        dccrec.bitCount = 0;                            // not a valid preamble.
      }
  break;

  case WAIT_START_BIT:
    // The packet start bit is the first bit with a value of "0"
    // that follows a valid preamble. The packet start bit terminates the preamble
    // and indicates that the next bits are an address data byte
    if( !DccBitVal )                                  // a "0" bit is received
    {
      dccrecState = WAIT_DATA;
      dccrec.tempMessageSize = 0;
      // Initialise all fields
      uint8_t i;
      for(i = 0; i< MaxDccSize; i++ )
      {
        dccrec.tempMessage[i] = 0;
      } 
      dccrec.bitCount = 0;     
    }

  break;

  case WAIT_DATA:
    //==================
     if( dccrec.bitCount == 8 )                        // byte is complete
    {
      if(dccrec.tempMessageSize == MaxDccSize )       // Packet is too long - abort
      {
        dccrecState = WAIT_PREAMBLE;
        dccrec.bitCount = 0;
      }
      else
      {
        dccrecState = WAIT_END_BIT;                  // Wait for next byte or end of packet
      }
    }

  break;

  case WAIT_END_BIT:
    // The next bit is either a Data Byte Start Bit or a Packet End Bit
    // Data Byte Start Bit: precedes a data byte and has the value of "0"
    // Packet End Bit: marks the termination of the packet and has a value of "1"
 
    if(DccBitVal) // End of packet?
    {
      // Complete packet received and no errors
      // we are in a CUTOUT AREA NOW
      noInterrupts(); //no new external event change the sequence actions queue above till queue is over, just add ~500ms latency on AC0 notifications
     
      CUTOUT(RAILCOM); //proceed cutout till end of it
     
      //cutout is over now, continue

      dccrecState = WAIT_PREAMBLE; //next step will be at this state

      interrupts();   
   
    }
    else  // Get next Byte   
    {
      dccrecState = WAIT_DATA;
      dccrec.bitCount = 0;                              // prepare for the next byte 
    }   
 
  break;
  }
}

ISR(AC0_AC_vect) {
 
    AC0.INTCTRL = 0; 

}




Ltr
Titre: Re : RailCom: Générateur de CutOut
Posté par: laurentr le mai 02, 2024, 06:02:49 am
CODE PART 4:


void setup()
{
  INIT_PINS(); //init first PINS

  //SIMPLE_AMPLIFICATOR = digitalRead(SIMPLE_AMPLIFICATOR_MODE_INPUT_PIN);
  AMPLIFICATOR_MODE = PORTC.IN & PIN4_bm; //faster

  if(AMPLIFICATOR_MODE == false)
  {   
    LED_SIMPLE_AMPLIFICATOR_MODE_PIN_OFF;

    INIT_TCB();

    INIT_ADDITIONNAL_PINS();

    //RAILCOM = digitalRead(RAILCOM_MODE_SELECTOR_INPUT_PIN);
    RAILCOM = PORTC.IN & PIN3_bm; //faster

    INIT_SYSTEM_WITH_RAILCOM();

    if (RAILCOM == false) //NO RAILCOM
    {
      //force these states out:
      LOGIC_ENABLE_OUT_PIN_ON;
      LOGIC_CUTOUT_OUT_PIN_ON;

      LED_RAILCOM_ENABLE_PIN_OFF;
    }
    else
    {
      LED_RAILCOM_ENABLE_PIN_ON;
    }
  }
  else
  {
    LED_RAILCOM_ENABLE_PIN_OFF;
    RAILCOM = false;
    LED_SIMPLE_AMPLIFICATOR_MODE_PIN_ON;
   
    INIT_SYSTEM_SIMPLE_AMPLIFICATOR();
  }

}


void loop(){

  //NOTHING TO DO! ALL IS AUTOMATIC :)

}





Plus qu'a se pencher sur un dessin de PCB pour donner vie à ce dispositif!

Ltr
Titre: Re : RailCom: Générateur de CutOut
Posté par: trimarco232 le mai 02, 2024, 03:37:01 pm
Laurent ,
les gens sont peu familiers avec la valorisation de tels hardwares , notamment les programmateurs , qui ont du mal avec l'électronique , et qui ont une culture de portabilité de leurs codes
si tu as du temps , tu peux faire une description didactique du projet , avec des dessins décrivant le principe général , et le rôle de chaque hardware particulier : cela permettra aux gens de voir les avantages des nouveaux microcontrôleurs , et de les comprendre , et de les approuver
Titre: Re : RailCom: Générateur de CutOut
Posté par: laurentr le mai 02, 2024, 05:09:45 pm
Hello Marc

Tu as raison la facilitera "l'adoption" et montrera la plus value de ces processeurs sur leur ainés dans le cas présent.

Je vais ouvrir un post dédié car cela sera utile en dehors de ce projet.

Je vais préparer ce qu'il faut pour mieux comprendre de quoi il retourne

J'ai profité de mes essais pour implémenter la commande externe avec gestion ad hoc de la led d'indication.
J ai du pour cela très légèrement revoir le mapping et ajouter quelques variables pour que "cela passe crème" sans voir besoin de ressources supplémentaires.

Ltr
Titre: Re : Re : RailCom: Générateur de CutOut
Posté par: Dominique le mai 03, 2024, 10:20:41 am

Je vais ouvrir un post dédié car cela sera utile en dehors de ce projet.


Peux-tu indiquer le lien vers ce sujet ?
Au passage, j'ai regroupé 2 sujets séparés (ça fait désordre !) et c'est peut-être celui-là : RailCom: Générateur de CutOut avec booster
https://forum.locoduino.org/index.php?topic=1709.0 (https://forum.locoduino.org/index.php?topic=1709.0)
Titre: Re : RailCom: Générateur de CutOut
Posté par: laurentr le mai 03, 2024, 01:12:03 pm
Merci Dominique


Voici le lien pour comprendre les apports de ces nouveaux CPU:

https://forum.locoduino.org/index.php?topic=1712.0 (https://forum.locoduino.org/index.php?topic=1712.0)
Titre: Re : RailCom: Générateur de CutOut
Posté par: CATPLUS le juin 06, 2024, 12:39:11 pm
@ Messieurs Lebelge2 & Christophe

Bonjour,
J'ai & je suis vos travaux avec attention, je viens vers vous (5 pages d'échanges)
Ma question va sembler un peu incongrue mais un schéma serait de bonne augure.
Ci-joint un montage assez grossier.

Pourriez-vous svp compléter, voir refaire un schéma en partant d'une centrale "x" jusqu'à la voie.
Merci d'indiquer les connections, le soft suivant l'appareil utiliser, le lcd, etc...

Cordialement
Marcel


Titre: Re : RailCom: Générateur de CutOut
Posté par: bobyAndCo le juin 06, 2024, 03:05:34 pm
Bonjour Marcel,

Si j’ai bien compris, tu parles ici d’utiliser un signal DCC, soit donc une alternance de +15/+18 V  -15/-18V et non pas un signal logique 0/1 comme il est question dans ce fil.

Le programme du belge ne fonctionnera pas dans ce cas, même avec un 6N137. En effet, le programme transforme un bit 1 en courant de puissance de 26µS en +15/+18v et de 26µS en -15/-18v et un bit 0 en courant de puissance > 52µS en +15/+18v et > 52µS en -15/-18v

Je vois bien ce que tu veux faire, mais il faudrait que le programme détecte des durées d’alternances pou en déduire s’il s’agit de bits 1 ou de bits 0

Christophe
Titre: Re : RailCom: Générateur de CutOut
Posté par: CATPLUS le juin 06, 2024, 03:29:12 pm
Merci Christophe

Donc, j'ai mal compris ce post.

A suivre.

Marcel