Auteur Sujet: RailCom: Générateur de CutOut  (Lu 1684 fois)

laurentr

  • Hero Member
  • *****
  • Messages: 610
    • Voir le profil
Re : RailCom: Générateur de CutOut
« Réponse #45 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

lebelge2

  • Newbie
  • *
  • Messages: 31
    • Voir le profil
Re : RailCom: Générateur de CutOut
« Réponse #46 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.

trimarco232

  • Sr. Member
  • ****
  • Messages: 302
    • Voir le profil
Re : Re : Re : RailCom: Générateur de CutOut
« Réponse #47 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)

trimarco232

  • Sr. Member
  • ****
  • Messages: 302
    • Voir le profil
Re : Re : RailCom: Générateur de CutOut
« Réponse #48 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

laurentr

  • Hero Member
  • *****
  • Messages: 610
    • Voir le profil
Re : RailCom: Générateur de CutOut
« Réponse #49 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

bobyAndCo

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 947
  • HO avec DCC++
    • Voir le profil
Re : Re : RailCom: Générateur de CutOut
« Réponse #50 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

lebelge2

  • Newbie
  • *
  • Messages: 31
    • Voir le profil
Re : RailCom: Générateur de CutOut
« Réponse #51 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...

lebelge2

  • Newbie
  • *
  • Messages: 31
    • Voir le profil
Re : RailCom: Générateur de CutOut
« Réponse #52 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

bobyAndCo

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 947
  • HO avec DCC++
    • Voir le profil
Re : RailCom: Générateur de CutOut
« Réponse #53 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
  }
}

« Modifié: avril 30, 2024, 08:30:05 am par bobyAndCo »

lebelge2

  • Newbie
  • *
  • Messages: 31
    • Voir le profil
Re : RailCom: Générateur de CutOut
« Réponse #54 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.
« Modifié: avril 30, 2024, 04:25:14 pm par lebelge2 »

bobyAndCo

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 947
  • HO avec DCC++
    • Voir le profil
Re : RailCom: Générateur de CutOut
« Réponse #55 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

laurentr

  • Hero Member
  • *****
  • Messages: 610
    • Voir le profil
Re : RailCom: Générateur de CutOut
« Réponse #56 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

bobyAndCo

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 947
  • HO avec DCC++
    • Voir le profil
Re : Re : RailCom: Générateur de CutOut
« Réponse #57 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

laurentr

  • Hero Member
  • *****
  • Messages: 610
    • Voir le profil
Re : RailCom: Générateur de CutOut
« Réponse #58 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

laurentr

  • Hero Member
  • *****
  • Messages: 610
    • Voir le profil
Re : RailCom: Générateur de CutOut
« Réponse #59 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();
}




« Modifié: mai 02, 2024, 06:24:55 am par laurentr »