Auteur Sujet: Décodeur de locomotive multifonctions  (Lu 8932 fois)

laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #45 le: décembre 19, 2023, 03:05:18 am »
Pour ce qui est des timing indiqués je pense que tu as en tête cette source

https://www.opendcc.de/info/railcom/railcom_f.shtml

Par contre je ne savais pas  a quoi correspondaient les 40 + 40 + 13 ( ici 80+15)

La reponse ici :

"Durant la pause de transmission un décodeur peut émettre l'information de retour. La transmission de retour est effectuée en série, octet par octet. Ceux-ci sont codés en RS232, paramètres de transmission 250kBaud, 8 bits, 1 bit d'arrêt. A 250 kBaud un bit dure 4μs, et un octet 40μs y compris le bit de départ (= 0) et les bits d'arrêt (= 1)."

source:
https://www.opendcc.de/info/railcom/railcom_f.shtml

On est bien en format 8N1 de 10bits x4us = 40us. x 2 trames = 80us +15us? ( tempo?)



Finalement on aurait a attendre ce moment de 93/95us par déclanchement de l interruption du TIMERB0 pour "envoyer" ce que l on aura prépare avant (les 2 bytes de data du CH1) 

Cela se ferait par le passage dans la valeur TCB0.CCMP = F_CPU/1000000 * 93 (ou 95) ce qui déclenchera l interruption au moment venu. A ce moment on peut basculer une variable qui autorise l envoi.. ou quelque chose qui y ressemble.
Toutefois pour passer a 193+15 comme on aura eu une interruption qui remettraTCB0.CNT = 0. j imagine qu'on devra les déduire 80 des 193?( mais on a utilement le TCB1.CNT qui continue de compter de son coté a la même vitesse que TCB0... donc on a une valeur auto incrémenté a laquelle on ajoute le timing de l interruption entre 26 et 32...
On bascule alors la valeur CCMP avec ce nouveau nombre ajusté pour bis émission des autres bytes ds le CH2 si requis.

Bon encore un peu à moudre de ce cote la pour tout mettre au claire...

Ltr

trimarco232

  • Sr. Member
  • ****
  • Messages: 283
    • Voir le profil
Re : Re : Décodeur de locomotive multifonctions
« Réponse #46 le: décembre 19, 2023, 11:11:09 pm »
Pour ce qui est des timing indiqués je pense que tu as en tête cette source
https://www.opendcc.de/info/railcom/railcom_f.shtml
le mieux est toujours de partir du document officiel , mais ceci est en effet une source sérieuse
Par contre je ne savais pas  a quoi correspondaient les 40 + 40 + 13 ( ici 80+15)
je m'a gouré (pour constater que tu suis 8)) : on a , pour le canal 1 , une tempo de 80us , suivie d'une trame de 80us , ça fait 160us ; pour le canal 2 , la tempo est de 193 us : il faut donc attendre entre les 2 canaux , 193 - 160 = 33us (pas 13 ???)
La réponse ici :
"Durant la pause de transmission un décodeur peut émettre l'information de retour. La transmission de retour est effectuée en série, octet par octet. Ceux-ci sont codés en RS232, paramètres de transmission 250kBaud, 8 bits, 1 bit d'arrêt. A 250 kBaud un bit dure 4μs, et un octet 40μs y compris le bit de départ (= 0) et les bits d'arrêt (= 1)."
source:
https://www.opendcc.de/info/railcom/railcom_f.shtml

On est bien en format 8N1 de 10bits x4us = 40us. x 2 trames = 80us +15us? ( tempo?)
oui , comme on écrit les 2 octets ensembles , il faut attendre que l'USART les a transmis (40 + 40 = 80us) , tempo à laquelle on ajoute les les 33us

Finalement on aurait a attendre ce moment de 93/95us par déclenchement de l interruption du TIMERB0 pour "envoyer" ce que l'on aura prépare avant (les 2 bytes de data du CH1) (je pense que tu veux dire 193us) : non , car les interruptions doivent être coupées à ce stade , mais aussi oui , car on n'est pas obligé d'arrêter TIMB0 , on peut faire du polling pour voir s'il a atteint les 193us , puis envoyer le canal 2

Cela se ferait par le passage dans la valeur TCB0.CCMP = F_CPU/1000000 * 93 (ou 95) ce qui déclenchera l interruption au moment venu. A ce moment on peut basculer une variable qui autorise l envoi.. ou quelque chose qui y ressemble.
Toutefois pour passer a 193+15 comme on aura eu une interruption qui remettraTCB0.CNT = 0. j imagine qu'on devra les déduire 80 des 193?( mais on a utilement le TCB1.CNT qui continue de compter de son coté a la même vitesse que TCB0... donc on a une valeur auto incrémenté a laquelle on ajoute le timing de l interruption entre 26 et 32...
On bascule alors la valeur CCMP avec ce nouveau nombre ajusté pour bis émission des autres bytes ds le CH2 si requis. deviendra simple quand on aura compris

Bon encore un peu à moudre de ce cote la pour tout mettre au clair ...

Ltr

trimarco232

  • Sr. Member
  • ****
  • Messages: 283
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #47 le: décembre 20, 2023, 02:44:14 pm »
voilà comment je vois la chose :
1) au front montant de la 1ère impulsion du cutout (la 1ère partie du 1er bit du preamble , celle qui sera tronquée à 29us par la station) , TIMB0 provoque une interruption suite à la capture ; on fera toutes les opérations du cutout dans l'ISR correspondante , comme ça on ne sera pas gênés par d'improbables autres activités de l'arduino , pendant toute cette période ; de même , on ne fera appel à aucune instruction de type arduino , pour être certains que celles-ci ne perturbent pas notre timing , un peu critique : cela implique d'écrire directement dans les registres ;
la valeur capturée est mise en ram : uint16_t c = TCB0.CCMP0 ;
on fait aussi les opérations habituelles de décodage , qui permettent de vérifier qu'on est bien au début du cutout
2) il faut à présent attendre le début du cutout , cad. les entrées correspondant aux 2 rails à LOW ; il faut faire un double polling d'abord sur if (PORTx.IN & 0b00100100 == 0) , et aussi sur if ((TCB0.CNT - c) > 80) ;
il faudra aussi vérifier (TCB0.CNT - c) > (80+15) , auquel cas on arrête aussi les frais
si c'est ok , on envoie le canal 1 :
USART0.TXDATA = railcom_canal_1_byte_0 ;
USART0.TXDATA = railcom_canal_1_byte_1 ;
3) faire un polling sur if (PORTx.IN & 0b00100100 == 0) , et sur if ((TCB0.CNT - c) > 193) ;
il faut faire un double polling d'abord sur if (PORTx.IN & 0b00100100 == 0) , et aussi sur if ((TCB0.CNT - c) > 193) ;
il faudra aussi vérifier (TCB0.CNT - c) > (193+15) , auquel cas on arrête les frais
si c'est ok , on envoie le canal 1 :
USART0.TXDATA = railcom_canal_2_byte_0 ;
USART0.TXDATA = railcom_canal_2_byte_1 ;
puis on fait un polling sur la fin d'émission du 1er byte :
if (USART0.TXDATA & DREIF) USART0.TXDATA = railcom_canal_1_byte_2 ;
etc. , jusqu'à l'envoi des 6 bytes
là , les opérations du cutout sont terminées , on peut quitter l'ISR sans autre forme de procès !
« Modifié: décembre 20, 2023, 03:37:30 pm par trimarco232 »

laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #48 le: décembre 21, 2023, 12:45:40 am »
Bonsoir

Ca semble en effet la construction adéquate.

Je pèche en revanche sur ce que je "connais" et ce qui me "manque".

Ce qui "est connu":
La Lib d AIKO utilise le mode de capture du timer en charge du traitement du signal DCC en mode "Frequency measurement" avec un déclanchement sur "event".

Chaque "détection"  d'un front montant ou descendant introduit une interruption. Une bascule s'opère entre la "montée et la descente via  (EVENTCTRL ^= EDGE) Lors de celle ci et de façon automatique liée à cette implémentation la valeur du compteur du timer TIMERx.CNT est transférée dans le registre TIMERx.CCMP.

On effectue alors une "moulinette" en ayant mis à l échelle des timings avec la vitesse du CPU pour déterminer si on a un "demi 0" , un "demi 1" et bien aussi si on ne "serait pas" dans le cadre d un intervalle de timing de type RAILCOM CUTOUT ( typique 29us (intervalle de 26us à 32us selon les tolérances en vigueur de cette même norme) On a l'idée de vérifier les entrées des PIN DCC venant des deux pôles de la voie. ( 1 serait il suffisant?) ( utilisation de la commande PORTx.IN & "bytes" == "valeur*") (* celle du mask_combo évoquée dans mes précédents messages)

Si pas dans le cutout on assemble ensuite les "1" et les "0" après diagnostic de leur conformité pour constituer les messages DCC. ( ou pas! et alors "reset"!)

Pour RAILCOM on sait donc identifier le cas ou on est dans un CUTOUT:
on a un intervalle de détection conforme ET les entrées des pins DCC sont à "0" puisque il n y a pas de tension dans la voie ( sur les deux rails) ( les valeurs sont plutôt à 1 mais l'absence de tension est un état "0" = OFF) Comme le HARD inverse cette logique on cherchera alors à trouver une valeur 1 sur la lecture des pins

A tout moment on à alors les valeurs TIMERx.CNT et TIMERx.CCMP à dispo qui peuvent être exploitées. puisqu'il il n y aura pas d autre interruption (normalement) avant la reprise des trames DCC post cutout ( donc retour de tension et nouvelle mesure de temps entre 2 fronts de directions opposées) ( dans le cas contraire bye bye railcom et on rebascule en mode "nominal" de traitement des 0 et 1)

Dans mon "analyse" si j ai ces conditions de remplies je "déclenche" seulement le timer du railcom( TIMERy)  et je vais avec celui ci voir si je suis dans les intervalles d émission des canaux 1 et 2 ( si on a active RAILCOM via le bon bit du CV29 et préciser ce que l on émet à l aide des bits du CV28 ( canal 1 et ou canal 2)). Je peux rester dans l ISR générée par le signal DCC.( j ene vois pas comment faire le start du timer RC autrement en mode "periodic interupt")

Donc si je ne suis pas dans l erreur on ajoute le temps  écoulé sur le TIMERx à la valeur CNT du TIMERy utilisé pour la mesure de temps RAILCOM. ( le CCMP0 que tu indiquais avoir mis en mémoire)
Ce timer peut alors être simplement mis en mode "Periodic interrupt" dont on s arrangera pour ne jamais atteindre le seuil haut ou seulement à la fin du temps équivalent au max du timing cutout ( les fameuses 488us après le temps de référence T0 amorcé sur le TIMERx et incrémentés ici en sus à l origine du démarrage de ce TIMERy dédié à railcom.)
Dans le cas d une éventuelle ISR du timer Railcom on aurait alors STOP du timer RC, flag railcom = false, reset de la valeur CNT.

Lors des plages ad hoc (75us 177us) on émet manuellement les "2 bytes du CH1" une tempo puis ceux (les 3 bytes) du CH2 (de 193 à 454us) (CH2 dont il reste à construire aussi la création des contenus)

Ce que je n identifie pas c est "comment"/ "pourquoi" on établirait sur le "front montant"(oui c est le point de depart) l'interruption de démarrage du TIMERy pour RC de façon systématique quitte à faire un reset lors d un front descendant reçu sur le TIMERx si c est dans la plage ouverte et dans les conditions du cutout...

Il faudrait alors une condition qui fasse que uniquement sur les fronts montant on d'éclanche le compteur TIMERy pour RAILCOM depuis  l'interruption générée sur le traitement du signal DCC.( et "reset sur les front descendants)

Certes on s éviterait ainsi les passages de valeur(s) multiples inter timer comme j en avais l idée initialement... whynot!

En quittant l ISR on arrêterait aussi du coup le TIMERy du railcom qui serait en même temps réinitialisé ( TIMERy.CNT =0, TIMERy.CCMP= (valeur a maxima pour 488us), (bitClear(TIMERy.CTRLA,0) pour arreter le timer y.

Le code "NIPPON" formule l usage d envoi des messages sur le CH1 ( senduart(data)) et construit pour cela le paramétrage "complet" de l'uart.

Je ne suis pas du tout familier avec l UART et ses mécanismes mais l'inspiration "est là"

Je pense qu'une petite classe complémentaire RAILCOM_UART va devoir faire son apparition... 8)

Tout laisser dans l ISR parait presque "trivial" des lors que tout est construit "à la main". ( on contourne ainsi l émission sur TX DANS l ISR, ce qui est naturellement impossible avec la lib serial et les bons timings.)

L'autre solution alternative qui me vient en tête serait de rendre prioritaire les interruptions pour émettre les messages RAILCOM sans interruption extérieure dans les conditions requises avant de revenir à un niveau standard. C'est peut être un peu capillotracté et moins judicieux? /ou combinable) ou devenu inutile!... Sauf selon l effet contraire que toute nouvelle interruption sur le signal DCC viendrait obligatoirement faire un reset de "l encours", ce qui, si on est en émission railcom des bytes des CH1 ou 2 et en priorité LVL1 incompatible… On va donc oublier cette fausse bonne idée pour le moment.


Questions: quant tu parles de UART0.TXDATA tu veux parler de UART0.TXDATAL ?
Si j ai bien compris, ce registre contient précisément le Byte a envoyer donc si on n'a pas fini son émission complètement il ne faut pas le rafraichir par son successeur? ( supposition)
donc la close
if (USART0.TXDATA & DREIF) serait (plutôt)  à mettre entre chaque refresh des valeurs ds le registre TXDATAL pour émission. Une fois le byte envoyé refresh etc

Ou alors on stocke les valeurs à envoyer successivement dans un "POOL des BYTES RAILCOM A EMMETTRE" et on avance dans ce pool au fur et à mesure que chaque byte est envoyé un par un. Et ce pool est rafraichi périodiquement si valeurs dynamiques (ou pas si valeurs statiques ex l adresse ne change pas alors que la vitesse elle évolue plus régulièrement)...

A voir donc...

Laurent

Rm: pour ceux qui suivent ce fil et notre échange, ne soyez pas "timide" pour poser vos questions ou apporter votre pierre à l'édifice.






« Modifié: décembre 21, 2023, 05:14:02 am par laurentr »

trimarco232

  • Sr. Member
  • ****
  • Messages: 283
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #49 le: décembre 21, 2023, 12:30:19 pm »
on a un four , une horloge , alors on va cuire un gateau :
il faut le mettre 10mn à thermostat 7 , puis 20mn à thermostat 3
on le met au four à thermostat 7 , on regarde l'heure il est 11h05 , on s'en souvient
et là on surveille l'horloge de la cuisine :
ah , il est 11h15 (11h05 + 10mn) , on met thermostat 3
et on surveille l'heure , sans se laisser distraire
ah , il est 11h35 (11h05 + 10mn + 20mn) , on sort le gateau
il n'est pas beau mon gateau ? et pourtant je n'ai jamais eu besoin d'une seconde horloge !
ben là c'est pareil , TIMB0 continue de tourner , on fait du polling sur sa valeur pour savoir le temps écoulé depuis la capture (qu'on a sauvegardée) et ça suffit ; si certains parlent d'un second timer , c'est qu'ils n'ont pas compris un truc , où alors c'est moi ...

l'UART a 2 registres , un pour l'écriture TXDATA , et un , à décalage , qui transmet ; quand on écrit dans TXDATA , le byte est immédiatement transféré dans le registre à décalage, qui démarre immédiatement la transmission : on peut donc mettre de suite le 2ème byte dans TXDATA , qui ne sera automatiquement transféré dans le registre à décalage que quand ce dernier aura fini la transmission en cours
par contre , si on veut mettre un 3ème , il faut attendre que la transmission en cours soit finie
alors c'est peut être TXDATAL , ça dépend comment les registres sont définis , je n'ai aps vérifié ça

après relecture il semble bien que la priorité 1 soit une priorité de préemption, c'est à dire qu'elle peut interrompre une ISR en cours , et aussi qu'elle ne risque pas de se faire interrompre à son tour : on a donc une option qui permet(trait) d'éviter d'interdire les interruption pendant l'ISR du cutout ; la question sera aussi de voire comment se comporte le core vis à vis de ces options




laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #50 le: décembre 21, 2023, 05:19:22 pm »
Ca tombe bien j aime bien les gâteaux  ;D

Donc on conserve "exclusivement" la "minuterie du TIMER_DCC ( qui peut être du coup le TIMERB0) et nous allons donc suivre l'évolution de la valeur CNT.

Nous y ajouterons "delta" ( qui est la valeur enregistrée dans CCMP).
Dans notre usage nous ne risquons pas d avoir une valeur  CNT > 65535 ( max de uint16_t donc pas de risque de ce cote la d avoir un reset indésiré)

J ai relu la doc pour l'UART incluant aussi les info du livre de TOM ALMY.
On parle bien d un TXDATA en uint16_t qui est si j ai compris composé de 2 uint8_t ( byte)  TXDATAL et TXDATAH

TXDATAL contient bien nos 8 bits à transmettre
TXDATAH n est utilisé à priori qu'en mode émission sur 9 bits et ne contient qu'un bit de data. Il est a ignoré pour notre cas d usage puisque que nous voulons transmettre en "8N1"

On ne peut donc pas glisser 2 bytes dedans puisque c est un mécanisme de "FIFO" qui s opère avec la partie transmission. ( par morceaux de 8 bits maxi)
Une fois la transmission complète on peut seulement alors pousser un nouveau "byte" à transmettre etc.
C est la lecture des flags qui va orchestrer les séquences.

Ca "fait du monde " à coordonner.

Pour des question de "commodités" autant que de portabilité inter CPU ( AVR, MEGATINY) nous traiterons avec l'UART0 sur les broches par défauts ( ce qui au niveau hardware sera toujours à considérer dans le routage). ( ca tombe bien c est déjà dessiné comme cela d'origine)

Ltr







laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #51 le: décembre 21, 2023, 08:15:31 pm »
Voici le une partie du "squelette" pour ce qui est de la gestion des timings lors de l ISR

//ADD RAILCOM VALUES: LTR 20231210 follow RCN-217: https://normen.railcommunity.de/RCN-217.pdf last updated on 20231123

#define RAILCOM_CUTOUT_MIN_START_TIME   F_CPU/1000000 * 26        //MIN CUTOUT START TIME
#define RAILCOM_CUTOUT_MAX_START_TIME   F_CPU/1000000 * 32        //MAX CUTOUT START TIME

#define RAILCOM_CH1_START_TIME          F_CPU/1000000 * 75        ///MIN CH1 START TIME
#define RAILCOM_CH1_MAX_START_TIME      F_CPU/1000000 * (80+15)   //MAX CH1 SEND START TIME
#define RAILCOM_CH1_END_TIME            F_CPU/1000000 * 177       //MAX CH1 END TIME
#define RAILCOM_CH2_START_TIME          F_CPU/1000000 * 193       //MIN CH2 START TIME
#define RAILCOM_CH2_MAX_START_TIME      F_CPU/1000000 * (193+15)  //MAX CH2 SEND START TIME
#define RAILCOM_CH2_END_TIME            F_CPU/1000000 * 454       //END CH2
#define RAILCOM_CUTOUT_MAX_END_TIME     F_CPU/1000000 * 488       //MAX CUTOUT TIME



//******************************************************************************************************
// 6. The Timer ISR, which implements the DCC Receive Routine
//******************************************************************************************************
// Execution of this DCC Receive code typically takes between 3 and 8 microseconds.
// Select the corresponding ISR
#if (defined(DCC_USES_TIMERB0) && !defined(RAILCOM_USES_TIMERB0))
  ISR(TCB0_INT_vect) {
#elif (defined(DCC_USES_TIMERB1) && !defined(RAILCOM_USES_TIMERB1))
  ISR(TCB1_INT_vect) {
#elif (defined(DCC_USES_TIMERB2) && !defined(RAILCOM_USES_TIMERB2))
  ISR(TCB2_INT_vect) {
#elif (defined(DCC_USES_TIMERB3) && !defined(RAILCOM_USES_TIMERB3))
  ISR(TCB3_INT_vect) {
#else
  // fallback to TCB0 (selected platform must have it)
  ISR(TCB0_INT_vect) {
#endif
 
  TIMER_DCC_CONTROL.EVCTRL ^= TCB_EDGE_bm;                         // Change the event edge at which we trigger
  uint16_t  delta = TIMER_DCC_CONTROL.CCMP;                        // Delta holds the time since the previous interrupt
  uint8_t DccBitVal;

  if(delta < RAILCOM_CUTOUT_MIN_START_TIME)
  {
    //It may be a glitch so cancel elapsed time since last interupt < 26us
    FLAG_RAILCOM_IN_CUTOUT = false;   
    TIMER_DCC_CONTROL.CNT = 0;
    return; //it s a glitch so ignore it and leave ISR
  }

  if(FLAG_RAILCOM_IN_CUTOUT == false)
  { //check if we are in CUTOUT TIMINGS [26us;32us]:
    if( (delta <= RAILCOM_CUTOUT_MAX_START_TIME) && (delta >= RAILCOM_CUTOUT_MIN_START_TIME) )
    { //WE ARE IN CUTOUT START INTERVAL
      //READ PINS STATUS
      /*
      IF A TEMPORIZE TIME IS NEEEDED WE DONT USE delay() into the ISR but a line like "do {} WHILE (TIMER_DCC_CONTROL.CNT < WAIT_TIME);""
      WAIT_TIME is a 16bits variable where F_CPU/1000000 will give 1us ticks to adjust as delay expected
      WAIT_TIME MUST BE STITCLY LOWER THAN (75us-delta) due to match timing with CH1 emission interval, if not it would not be possible to send CH1!
      uint16_t TEMPOVAL;
      uint16_t WAITTIME = (F_CPU/1000000) * TEMPOVAL;
      do {} while (TIMER_DCC_CONTROL.CNT /(F_CPU/1000000) < WAITTIME);
      */
      if( ((railcom.RAILCOM_CHECK_PIN_RIGHT | railcom.mask_combo) == railcom.mask_combo) && ((railcom.RAILCOM_CHECK_PIN_LEFT | railcom.mask_combo) == railcom.mask_combo) )
      { //both CPU PINS ARE AT "1" states so WE ARE IN CUTOUT DUE TO NO VOLT DETECTED ON TRACK
        FLAG_RAILCOM_IN_CUTOUT = true;
      }   
    }
    else //we should receive O or 1 bits:
    {       
      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
          TIMER_DCC_CONTROL.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;
        }
      }   

      #include "sup_isr_assemble_packet.h"
   
    } //END BITS MANAGEMENT
  } //end FLAG RAILCOM == false


  if(FLAG_RAILCOM_IN_CUTOUT) //WE ARE IN RAILCOM CUTOUT
  {   
    /*
    IF A TEMPORIZE TIME IS NEEEDED WE DONT USE delay() into the ISR but a line like "do {} WHILE (TIMER_DCC_CONTROL.CNT < WAIT_TIME);""
    WAIT_TIME is a 16bits variable where F_CPU/1000000 will give 1us ticks to adjust as delay expected
    uint16_t TEMPOVAL;
    uint16_t WAITTIME = (F_CPU/1000000) * TEMPOVAL;
    do { } while (TIMER_DCC_CONTROL.CNT /(F_CPU/1000000) < WAITTIME); //in us
    WAITTIME_es_us = F_CPU/1000000 * TEMPOAL
    do { } while ((TIMER_DCC_CONTROL.CNT < WAITTIME_eq_us ); //general case
    */

    do {} while ((TIMER_DCC_CONTROL.CNT + delta) < RAILCOM_CH1_START_TIME); //WAIT TILL 75us ARRIVED TO START TO SEND BYTE ON UART
 
    if( ((TIMER_DCC_CONTROL.CNT + delta ) >= RAILCOM_CH1_START_TIME) && ((TIMER_DCC_CONTROL.CNT + delta) <= RAILCOM_CH1_END_TIME) ) //WE ARE IN THE CH1 PERIOD
    {
      if(TIMER_DCC_CONTROL.CNT + delta > RAILCOM_CH1_MAX_START_TIME) //ARE WE OVER THE TIME LIMIT TO START SENDING CH1 BYTES?
      {
        //TOO LATE TO START SENDING BYTES SO ABORT
        FLAG_RAILCOM_IN_CUTOUT = false;
        TIMER_DCC_CONTROL.CNT = 0;
        return; //LEAVE INTERUPT
      }
     
      //2 BYTES TO SEND IF CH1 AUTORIZED
      //SENDUART(data1); //BYTE0
      //SENDUART(data2); //BYTE1
    }
   
    do {} while ( ((TIMER_DCC_CONTROL.CNT + delta) > RAILCOM_CH1_END_TIME) && ((TIMER_DCC_CONTROL.CNT + delta) < RAILCOM_CH2_START_TIME) ); //WE ARE BETWEEN CH1 AND CH2 EMISSION INTERVAL SO WAIT
   
    if( ((TIMER_DCC_CONTROL.CNT + delta) >= RAILCOM_CH2_START_TIME) && ((TIMER_DCC_CONTROL.CNT + delta) <= RAILCOM_CH2_END_TIME) )
    {
      if(TIMER_DCC_CONTROL.CNT + delta > RAILCOM_CH2_MAX_START_TIME) //ARE WE OVER THE TIME LIMIT TO SEND CH2 BYTES?
      {
        //TOO LATE TO START SENDING CH2 SO ABORT
        FLAG_RAILCOM_IN_CUTOUT = false;       
        TIMER_DCC_CONTROL.CNT = 0; //RST TIMER_DCC CNT VALUE
        return; //LEAVE ISR
      }
     
      //3 BYTES TO SEND IF CH2 EMISSION AUTORISED
      //SENDUARTCH2BYTE0
      //SENDUARTCH2BYTE1
      //SENDUARTCH1BYTE2
   
     
      if(USART0.TXDATAL & bitRead(USART0.STATUS,5))
      {
        //LAST BYTE SENT
        //PROCES FINAL OPERATIONS:     
        TIMER_DCC_CONTROL.CNT = 0; //RESET TIMER_RC VALUE
        FLAG_RAILCOM_IN_CUTOUT = false; //UPDATE FLAG     
        return; //LEAVE ISR
      }
    }
   
    if((TIMER_DCC_CONTROL.CNT + delta) > RAILCOM_CUTOUT_MAX_END_TIME)
    {
      //IF SENDING BYTE RESET SENDIND OPERAITION   
      TIMER_DCC_CONTROL.CNT = 0; //RESET TIMER_RC VALUE
      FLAG_RAILCOM_IN_CUTOUT = false; //UPDATE FLAG   
      return; //LEAVE ISR
    }
   
  }

} //END ISR

Il reste à se pencher sur l'écriture des éléments pour la gestion de l'envoi des bytes sur TX.

Ltr

laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #52 le: décembre 22, 2023, 12:38:31 pm »
Bonjour

Quelques avancées supplémentaires avec la gestion de l émission de "datas" sur TX

Cela prend forme petit à petit.

#include "Arduino.h"

#ifndef _CLASS_RAILCOM_USART_H_
#define _CLASS_RAILCOM_USART_H_


#ifndef RAILCOM_USES_USART0
    #define RAILCOM_USES_USART0
#endif


class RAILCOM_USART
{
    public:

        static void init_RAILCOM_USART();         

        static void enable_USART_RC();

        static void disable_USART_RC();

        static void send_data_on_USART_RC(uint8_t data);


    private:

        static void init_USART_RC_PINS();

        static void init_USART_RC_MODE();

        static void USART_RC_PORTMUX_MAPPING(); 


};


#endif



#include "CLASS_RAILCOM_USART.h"


#define USART_DATABUF_SIZE 10
uint8_t dataBuf[USART_DATABUF_SIZE];
uint8_t dataBufSendPos = 0;
uint8_t dataBufInsertPos = 0;


#if defined(USART0)
    #if !defined(RAILCOM_USES_USART0)       
        #define USART_RC USART0
        #define USART_RC_NB 0
        #define USART_RC_VECTOR USART0_DRE_vect           
    #endif
    #if defined(RAILCOM_USES_USART0)   
        #define USART_RC USART0
        #define USART_RC_NB 0 
        #define USART_RC_VECTOR USART0_DRE_vect     
    #endif
#elif defined(USART1)
    #if defined(RAILCOM_USES_USART1)
        #define USART_RC USART1
        #define USART_RC_NB 1
        #define USART_RC_VECTOR USART1_DRE_vect 
    #endif
#elif defined(USART2)
    #if defined(RAILCOM_USES_UART2)
        #define USART_RC USART2
        #define USART_RC_NB 2
        #define USART_RC_VECTOR USART2_DRE_vect
    #endif
#elif defined(USART3)
    #if defined(RAILCOM_USES_USART3)
        #define UART_RC UART3
        #define USART_RC_NB 3
        #define USART_RC_VECTOR USART3_DRE_vect
    #endif
#else
    #define RAILCOM_USES_USART0
    #define USART_RC USART0
    #define USART_RC_NB 0
    #define USART_RC_VECTOR USART0_DRE_vect
#endif


#define USART_RC_BAUD_RATE(BAUD_RATE) ((float)(F_CPU * 64 / (16 * (float)BAUD_RATE)) + 0.5)


//ISR TO MANAGE TX EMISSION:
ISR(USART_RC_VECTOR)
{
    USART_RC.STATUS |= USART_DREIF_bm; //DATA REGISTER EMPTY FLAG STATE: SET IT EMPTY WITH VAL = 1
    //bitSet(USART_RC.STATUS,5);   

    noInterrupts(); //cli(); //SUSPEND ANY INTERRUPT BEHAVIOR

    USART_RC.TXDATAL = dataBuf[dataBufSendPos]; //PUT THE BYTE TO TRANSFERT INTO DATAL REGISTER
   
    dataBufSendPos++; //NEXT BUFFERED BYTE TO PROCESS AT NEXT INTERRUPT

    if (dataBufSendPos >= USART_DATABUF_SIZE) //ARRIVED AT BUFFER MAX VALUE, RETURN TO 1st POSITION
    {
        dataBufSendPos = 0;
    }

    if (dataBufSendPos == dataBufInsertPos) //TRANSFERT OF CURRENT BYTE IS OVER
    {
        USART_RC.CTRLA &= ~USART_DREIE_bm; //DISABLE DRE_INTERUPT
    }

    interrupts(); //sei(); //LET GLOBAL INTERRUPTS OCCURED

}


void RAILCOM_USART :: send_data_on_USART_RC(uint8_t data)
{
dataBuf[dataBufInsertPos] = data;

dataBufInsertPos++; //NEXT PLACE IN BUFFER FOR NEXT DATA

if (dataBufInsertPos >= USART_DATABUF_SIZE) //AT END OF BUFFER PLACE
    {
        dataBufInsertPos = 0; //START ON 1st BUFFER POOL POSITION
    }

USART_RC.CTRLA |= USART_DREIE_bm; //ENABLE DRE INTERUPT SO CALL ISR

interrupts(); //sei();

}


void RAILCOM_USART :: USART_RC_PORTMUX_MAPPING()
{
    //RAILCOM USART WOULD PREFER USE USART0 ON DEFAULT PORMUX MAPPING:
    switch(USART_RC_NB)
    {
        #if defined(USART0)
            case 0:
                #if defined(__AVR_TINY_0__) || defined(__AVR_TINY_1__) || defined(__AVR_TINY_2__)
                    bitClear(PORTMUX.CTRLB,0);               
                #else
                    bitClear(PORTMUX.USARTROUTEA,0);
                    bitClear(PORTMUX.USARTROUTEA,1);
                #endif         
            break;
        #endif

        #if defined(USART1)
            case 1:
                #if defined(__AVR_TINY_0__) || defined(__AVR_TINY_1__) || defined(__AVR_TINY_2__)
                  #error "ONLY 1 USART ON MEGATINY PARTS"                 
                #else
                    bitClear(PORTMUX.USARTROUTEA,2);
                    bitClear(PORTMUX.USARTROUTEA,3);
                #endif 
            break;
        #endif

        #if defined(USART2)
            case 2:
                #if defined(__AVR_TINY_0__) || defined(__AVR_TINY_1__) || defined(__AVR_TINY_2__)
                    #error "ONLY 1 USART ON MEGATINY PARTS"               
                #else
                    bitClear(PORTMUX.USARTROUTEA,4);
                    bitClear(PORTMUX.USARTROUTEA,5);
                #endif 
            break;
        #endif

        #if defined(USART3)
            case 3:
                #if defined(__AVR_TINY_0__) || defined(__AVR_TINY_1__) || defined(__AVR_TINY_2__)
                     #error "ONLY 1 USART ON MEGATINY PARTS"               
                #else
                    bitClear(PORTMUX.USARTROUTEA,6);
                    bitClear(PORTMUX.USARTROUTEA,7);
                #endif 
            break;
        #endif

        default:       
            #if defined(__AVR_TINY_0__) || defined(__AVR_TINY_1__) || defined(__AVR_TINY_2__)
                bitClear(PORTMUX.CTRLB,0);               
            #else
                bitClear(PORTMUX.USARTROUTEA,0);
                bitClear(PORTMUX.USARTROUTEA,1);
            #endif         
        break;
    }
}


void RAILCOM_USART :: init_USART_RC_PINS()
{
    #if defined(_AVR_FAMILY)

        #if defined(__AVR_DA__) || defined(__AVR_DB__) || defined(__AVR_DD__) || defined(__AVR_EA__) || defined(__AVR_EB__)             
            switch(USART_RC_NB)
            {   
                case 0:               
                    //PIN_PA0 AS RAILCOM TX OUTPUT
                    PORTA.DIRSET |= PIN0_bm;
                    //PIN_PA0 HIGH STATE
                    PORTA.OUTSET |= PIN0_bm;
                    #define PORT_TX_INIT_STATE (PORTA.OUTSET |= PIN0_bm)
                 
                break;
            }
        #endif

        #if defined(__AVR_TINY_0__) || defined(__AVR_TINY_1__) || defined(__AVR_TINY_2__)
            //ONLY MEGATINY
            switch(USART_RC_NB)
            {
                case 0:
                    //PIN_PB2 AS RAICLOM TX OUTPUT:
                    PORTB.DIRSET |= PIN2_bm;
                    //PIN_PB2 AS HIGH STATE
                    PORTB.OUTSET |= PIN2_bm;
                    #define PORT_TX_INIT_STATE (PORTB.OUTSET |= PIN2_bm)
                break;
            }
        #endif

    #endif

    #if defined(MEGACOREX) || defined(ARDUINO_AVR_NANO_EVERY)

        switch(USART_RC_NB)
        {
            case 0:   
                #if defined(__AVR_ATmegax08__) && defined(MEGACOREX_DEFAULT_28_PIN_PINOUT) //28PINS ITEMS
                    //PIN_PA0 AS RAILCOM TX OUTPUT
                    PORTA.DIRSET |= PIN0_bm;
                    //PIN_PA0 HIGHT STATE
                    PORTA.OUTSET |= PIN0_bm;
                    #define PORT_TX_INIT_STATE (PORTA.OUTSET |= PIN0_bm)
                #endif

                #if defined(__AVR_ATmegax08__) && defined(MEGACOREX_DEFAULT_32_PIN_PINOUT) //32PINS ITEMS
                    //PIN_PA0 AS RAILCOM TX OUTPUT
                    PORTA.DIRSET |= PIN0_bm;
                    //PIN_PA0 HIGHT STATE
                    PORTA.OUTSET |= PIN0_bm;
                    #define PORT_TX_INIT_STATE (PORTA.OUTSET |= PIN0_bm)
                #endif

                #if defined(__AVR_ATmegax09__) || defined(ARDUINO_AVR_NANO_EVERY) //48PINS ITEMS
                    //PIN_PA0 AS RAILCOM TX OUTPUT
                    PORTA.DIRSET |= PIN0_bm;
                    //PIN_PA0 HIGHT STATE
                    PORTA.OUTSET |= PIN0_bm;
                    #define PORT_TX_INIT_STATE (PORTA.OUTSET |= PIN0_bm)
                #endif

            break;

            default:

                //PIN_PA0 AS RAILCOM TX OUTPUT
                PORTA.DIRSET |= PIN0_bm;
                //PIN_PA0 HIGHT STATE
                PORTA.OUTSET |= PIN0_bm;
                #define PORT_TX_INIT_STATE (PORTA.OUTSET |= PIN0_bm)

            break;
           
        }

    #endif
}


void RAILCOM_USART :: init_USART_RC_MODE()
{
    //AVR ALL SERIES CPU PARTS + MEGATINY CPU PARTS

    //MANAGE PINS ACCORDING PARTS AND PORMUX:

    USART_RC_PORTMUX_MAPPING();

    init_USART_RC_PINS();

    //FIXE BAUD:
USART_RC.BAUD = (uint16_t)USART_RC_BAUD_RATE(250000); //FIXE BAUD

    //SET VALUES:   
    USART_RC.CTRLB =  0 << USART_MPCM_bp       // Multi-processor Communication Mode: disabled
                    | USART_RXMODE_NORMAL_gc // Normal mode
                    | 0 << USART_ODME_bp // Open Drain Mode Enable: disabled                
                    | 0 << USART_SFDEN_bp // Start Frame Detection Enable: disabled
                    | 1 << USART_TXEN_bp // Transmitter Enable: enabled
                    | 0 << USART_RXEN_bp; // Receiver Enable: disabled

    //bitSet(USART_RC.CTRLB,6); //EQUIVALENCE
   
    USART_RC.CTRLC = USART_CHSIZE_8BIT_gc;                // 8N1 form

    //bitSet(USART_RC.CTRLC,0);     //EQUIVALENCE
    //bitSet(USART_RC.CTRLC,1);     //EQUIVALENCE
    //bitClear(USART_RC.CTRLC,2);   //EQUIVALENCE

}



void RAILCOM_USART :: enable_USART_RC()
{
//ALL PARTS FOR AVR or MEGATINY
    USART_RC.CTRLB =  0 << USART_MPCM_bp                // Multi-processor Communication Mode: disabled
                    | USART_RXMODE_NORMAL_gc // Normal mode
                    | 0 << USART_ODME_bp // Open Drain Mode Enable: disabled   
                    | 0 << USART_SFDEN_bp // Start Frame Detection Enable: disabled
                    | 1 << USART_TXEN_bp // Transmitter Enable: enabled
                    | 0 << USART_RXEN_bp; // Receiver Enable: disabled
   
    //bitSet(USART_RC.CTRLB,6); //EQUIVALENCE

}



void RAILCOM_USART :: disable_USART_RC()
{
    //ALL PARTS:
USART0.CTRLB =    0 << USART_MPCM_bp                // Multi-processor Communication Mode: disabled
            | 0 << USART_ODME_bp // Open Drain Mode Enable: disabled
            | USART_RXMODE_NORMAL_gc // Normal mode
            | 0 << USART_SFDEN_bp // Start Frame Detection Enable: disabled
            | 0 << USART_TXEN_bp // Transmitter Enable: disabled
                    | 0 << USART_RXEN_bp; // Receiver Enable: disabled

    // bitClear(USART_RC.CTRLB,6); //EQUIVALENCE

//SET PORT IN READY STATE (HIGH LEVEL)
    PORT_TX_INIT_STATE;
}



void RAILCOM_USART :: init_RAILCOM_USART()
{
    init_USART_RC_MODE();
}


trimarco232

  • Sr. Member
  • ****
  • Messages: 283
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #53 le: décembre 25, 2023, 11:55:04 am »
il faut tenir compte du fait que la capture doit se faire sur le rail qui va bien : si elle se fait sur le mauvais rail , le bit DCC tronqué à 29us sera désespérément plat , on n'aura pas de top de référence pour les émissions railcom
voici comment je vois l'intérieur de l'AVR :
édit : PRINCIPE POUR LES ANCIENS AVR :
« Modifié: décembre 29, 2023, 11:35:46 am par trimarco232 »

trimarco232

  • Sr. Member
  • ****
  • Messages: 283
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #54 le: décembre 25, 2023, 12:12:38 pm »
(les 4 0402 amènent les tensions DCC dans le domaine mesurable)
grâce au MUX , l'ADC mesure alternativement le rail gauche et le rail droite , pour voir si on est sur un tronçon ABC
il faut tenir compte du fait que ces mesures perturbent la suite
l'ICP (input capture) utilise le MUX pour fonctionner sur le rail qui détient l'impulsion de 29us : ce rail est déterminé en observant les transitions bit DCC 0 <-> DCC 1 , à la mise sous tension , ou en cours de route , si l'engin passe par une boucle
compte tenu de la disparité des tension admise par la norme , il est possible que les signaux n'atteignent pas les 0.7*VCC nécessaires pour que l'ICP puisse voir un 1 logique : le comparateur y remédie , en employant la référence qui va bien ...
ne sont pas représentés , les différents filtres digitaux , qu'il faudra mettre en oeuvre en selon le MCU utilisé
 

laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #55 le: décembre 26, 2023, 09:17:02 pm »
Hello

Partie ABC:
Si on jette jette un œil sur le "code nippon" pour la lecture ABC, celui ci  se fonde sur le delta des impulsions perçues sur/entre les rails de droite et de gauche. Le hard associé y pourvoit par 2 entrées digital "classiques" en lecture. Les paramètres environnementaux indiquent si l'on compte ces impulsions sur le rail de droite, le rail de gauche ou si tout delta est considéré pour l'ABC. On exploite ensuite la mesure. Je trouve cela astucieux.
C'est donc traité " à part" du signal dcc exploité pour la transmission des ordres au prix de quelques broches supplémentaires utilisées. ( mais si on les a...)

RAILCOM:
En effet, comme Trimarco l'indique, le "cut" à 29us d'un bit de valeur 0 ou 1 est indifférencié lors de la capture sur 1 entrée (INPUT) en mode "event capture" et "frequence measurment" comme construit dans la lib d AIKO.
C'est pourquoi je fais ensuite la lecture des 2 ports ( 1 relié à chaque pole de rail) en entrée pour confirmer le statut d une boucle RAILCOM avec l absence de tensions dans les voies. (sinon on est pas dans un contexte railcom puisque toutes les conditions ne sont pas remplies) Il ne me semble pas important à ce stade alors de savoir de quel pole du rail provient cette détection.

Je ne me sans pas "concerné" par le soucis des 0.7v * Vcc pour le signal DCC puisque la lecture dans mon approche hardware se fait sur un pull_up piloté par un NMOSFET (ex BS138) dont la gate est pilotée par le signal DCC.
J ai donc de ce fait toujours un signal très "carré" (0 ou 1) sur l'entrée du CPU selon la bascule du NMOS.( approche similaire à être derrière un OPTO) Le "soucis de seuil" est alors un peu diffèrent car il doit alors être en capacité de faire basculer la gate du NMOSFET.( en DCC no soucis) ( en analogique de toute façon on ne tourne d'au delà de 6V, 6V5 à la voie)

Dans le cas par pont diviseur qu'indique Trimarco pour exploiter à la fois  sur les mêmes broches la lecture du signal DCC et la lecture des tensions présentes ( ou pas)  le contexte différent amène d autres points à considérer.
En ayant séparé les canaux selon les usages désirés (traitement du signal DCC, lecture tension , détection ABC, railcom, …)  je me prémunie ( au moins pour un temps) des effets de bords des uns et des autres en rendant "modulaire" l'association des éléments et à défaut cela permet de disposer d un hardware exploitable pour les fonctions de bases enrichies au fur et à mesure des blocs fonctionnels en question ( et des mises au point des codes associées).

La "fusion" sur un nombre réduit de broches est une ultime étape d optimisation que je n ai pas considéré dans cette approche initiale mais que suggère Trimarco. Elle n'est pas sans intérêt (choix judicieux à faire pour le hard entre autre et l exploitation du hard ensuite) au prix toutefois de quelques settings à combiner et éprouver.

Par ailleurs comme j'avais dans l'idée aussi de faire une lecture sur l'ADC après le pont de diode (donc le VIN type "fil bleu") ramené via un pont diviseur dans une plage compatible [0V;5V] pour être exploité par le CPU, peu m'importait le sens également.
Avec cette détection sur l'ADC je confirmais alors en cas de tension ou non sur la voie la lecture d une valeur pour les deux sens de circulation.( avantage d être derrière le pont)  On pouvait alors sur demande échantillonner la mesure de cette tension et l'exploiter.
Le sens de circulation en analogique étant alors déduit sur la présence ou non d'une tension sur la broche de capture après un timeout dcc. ( absence de trame DCC reçue) et aussi comparé par rapport à la mise sous tension du système ( lors du le setup)
On pourrait aussi au besoin, je pense, exploiter les broches utilisées pour la détection l'ABC sur le modèle que j'ai initialement proposé.

Comme indiqué précédemment il va nous falloir un "hard labo" pour éprouver ces approches. Idéalement le faire fabriquer avant la période du nouvel an chinois pour en disposer d'ici fin janvier au plus tard semble un objectif atteignable.

Qui serait intéressé pour en disposer?

Ce hard labo pourrait contenir sur un plaque proto:
un port de programmation UDPI
un support type SSOP28 pour un AVR (ATMEGA serie 0 ou AVRDx)
ponts diviseurs pour le signal et entrée ADC
circuiterie pour l ABC, le railcom, le signal DCC, la mesure BEMF
Quelques IO pour piloter des leds ou un half-bridge, une extension I2C
RX/TX pour le debug
des broches pour des liaisons filaires entre ces blocs afin de tester des implémentations différenciées

alim via DC DC? externe 5V? LDO type 7805 sur radiateur?


Qu en pensez vous?


Laurent






laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #56 le: décembre 27, 2023, 01:14:23 am »
Bonsoir

Je me suis un peu plus penché sur l'ACn (Analog Comparator n) indiqué précédemment par Trimarco et ce que l ont peut en obtenir.
On y voit donc de suite l'intérêt du pont diviseur en entrée pour les mesures.

A noter qu'à la différence du croquis de Trimarco il n'est pas possible de lier directement en interne ( by software) la sortie de l'AC (out)  et ADC(in).
Je base mon constat sur les vues et options offertes par ATMEL START (cas sur un ATMEGA4809)

https://start.atmel.com/#

(On ajoutera les composants DIGITAL GLUE LOGIC, EVENT, ANALOG COMPARATOR, ADC, ...) ce qui permettra d obtenir de quoi amorcer notre "setup" dans la parie "code review" une fois les configurations opérées.


La liaison devrait donc s opérer de façon externe pour la sortie de l'AC (PIN_PA7) (sur les AVR) vers le DAC (PIN_Pxy) en reliant ces 2 broches par une piste. ( si je ne me suis pas égaré?)


Le tableau ci joint dresse un aperçu pour des CPU de 20 24 et 32 broches
Les design hardware devront donc considérer ces info.

En vert les "optimums"

Laurent




trimarco232

  • Sr. Member
  • ****
  • Messages: 283
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #57 le: décembre 27, 2023, 11:54:28 am »
"A noter qu'à la différence du croquis de Trimarco il n'est pas possible de lier directement en interne ( by software) la sortie de l'AC (out)  et ADC(in)."
mon dessin est juste , si on le lit à l'endroit
« Modifié: décembre 27, 2023, 12:08:30 pm par trimarco232 »

laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #58 le: décembre 28, 2023, 01:50:28 pm »
Bonjour

Ce que je n arrive pas à comprendre sur le schéma proposé  c'est le fonctionnement de la bascule sur l'ADC PORMUX en entrée.

Sur les séries x08 x09  & MEGATINY A un instant T0 on ne rentre qu'une valeur à la fois de broche de l'ADC MUX car il n y a que des ADCMUXPOS

Donc je ne vois pas ce qui permet de faire cette bascule ( un interruption sur un changement de "Edge" créant un event mais pris à quel endroit? ( hors schéma?)

Seuls les AVR Dx proposent un canal POXMUXPOS et un PORMUXNEG qui permettent de traiter les entrées Positives et Négatives
Les nouveaux EA offre à présent aussi un PGA pour amplifier au besoin le signal en entrée.

Il y a donc une "magie que je n ai pas encore assimilée.

Quelques explications sont nécessaires pour nous éclairer.
Si tu peux nous apporter des précisions on prend :)

Pour les valeurs des résistances je propose 47K (R1 R3) et 12K (R2 R4) ce qui permet de couvrir la plage [0v; 25v] en entrée et ramener dans la plage [0v; 5V] nos lectures.

Laurent


laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #59 le: décembre 28, 2023, 06:16:32 pm »
Hello

Retour sur RAILCOM:

D'après les précieuses info lues ici:
https://www.opendcc.de/info/railcom/railcom_f.shtml

Il est précisé que le CUTOUT se produit à un moment TRES précis qui m'avait jusque la échappé: je cite"  Le signal de la station centrale est coupé 29μs (+ /-3μs) après l'envoi du bit de fin d'un message DCC , puis sur on à nouveau 16μs avant la fin. Ce temps de fonctionnement on/off permet au décodeur de détecter les fronts du signal DCC."

En effet nous mesurions alors uniquement les intervalles de 26 à 32 us sans nous soucier de savoir à quelle étape ils étaient positionnés dans les trames des bits reçus considérant que si une bascule correspondait à cette situation alors nous avions un CUTOUT. Hors pour être conforme on doit être placé au "bon endroit" pour traiter les messages.

Précision importante qui fait qu'il n y a pas que le simple fait d avoir un "timing" de 26 à 32us uniquement mais aussi que celui ci survienne bien à un endroit précis d émission de la centrale et donc du traitement des messages en entrée du décodeur en réception. Après le cutout on repart sur une émission de bits de préambule ( 10 bits mini à 1) pour ensuite "assembler" les bits et former les messages DCC.

Si le dernier bit reçu est bien un 1 qui forme la fin d un message alors sa valeur est de 1.

Si je fais la projection vers la lib d AIKO on sera alors normalement dans un cas ou :

dccrecState == WAIT_PREAMBLE; ( ce qui confirme bien que cela sera un préambule qui sera ensuite attendu une fois le cutout terminé)

alors quand on quitte l'SR en cas de cutout on ne devrait pas avoir à "forcer" le statut de dccrecState = WAIT_PREAMBLE; ( on y est encore) ( mais on peut toujours l'écraser par dccrecState = WAIT_PREAMBLE;)

Par corolaire l'autre option est de vérifier pour confirmer que nous sommes bien dans un CUTOUT "conforme" et placé tel qu'attendu en contrôlant que  dccrecState == WAIT_PREAMBLE avec le "delta" compris entre 26 et 32us.

A défaut on rejetterait le fait d entrer dans le CUTOUT.

A voir si cela affine qualitativement le traitement.

Laurent





« Modifié: décembre 28, 2023, 06:20:16 pm par laurentr »