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

msport

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 2217
  • HO avec DCC++ en DIY Réseaux très éphémères
    • Voir le profil
Décodeur de locomotive multifonctions
« Réponse #30 le: décembre 04, 2023, 03:35:44 pm »
Bonjour,
https://forum.locoduino.org/index.php?topic=1579.msg17410#msg17410
comme le fait remarquer Marc : la présentation de lebelge2 est terminée et fonctionne , on est hors sujet depuis un moment.
On ouvre un nouveau topic :
https://forum.locoduino.org/index.php?topic=1628.0
Laurent s'est placé dans l'optique d'une fabrication industrielle.

« Modifié: décembre 04, 2023, 06:46:28 pm par msport »
Cordialement

laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #31 le: décembre 04, 2023, 06:35:23 pm »
Bonjour

Merci Michel pour te transfer sur ce topic dédié .

Je vais donc reposer les bases de ce projet ici ( que nous essayerons de placer en tête du topic) pour poser le cadre.

L’idée est bien de réaliser un décodeur DCC pour locomotive en mode semi DIY.

Quezako?
Et bien la fabrication est confiée à un industriel qui fabrique et assemble et il nous reste ensuite à y injecteur le code pour piloter ce hardware puis l’installer sur nos engins et procéder aux derniers réglages personnalisés.

L’optique reste de trouver un équilibre  prix fonctionnalité performance bien qu’il soit très probable que cela soit par une production en volume qu’un optimum soit plus avantageux.
Le cahier des charges est le suivant:

Gestion du mode DCC : pilotage, gestion des CV.
Gestion des unités multiples.
Fonctionnement en mode analogique
Fonctionnalité Railcom
Fonctionnalité ABC
8 sorties amplifiées
Fonctionnalité SUSI

Traitement du BEMF

Compacité (16mmx30mmx 3,6mm )du montage pour une intégration selon connecteur normalisé.( 21MTC et PLUX22)
( un portage et une déclinaison n est pas exclue non plus )

Extension complémentaire vers POWERPACK facilitée.

Idéalement sélection de composants dispo en volume à prix descents!


Pour ce qui est du PCB les choses sont déjà très avancées et quelques vues ont été produites.
Pour ce qui est du code il y an encore des choix et de nombreux tests à mener.

Plusieurs optiques sont possibles pour socle et il est également possible que le hardware puisse recevoir plusieurs sources de code différentes.

Certains se poseront alors la question du pourquoi de lancer dans un tel projet quand les décodeurs de grandes marques ou low cost sont dispo.
La réponse est multiple:
Tout d abord le goût de faire soit même et en groupe
Concevoir et concrétiser collectivement est un net plus. Locoduino est un formidable vecteur pour cela et ce terme n’a pas encore été traité aussi loin.
L’ aspect prix est aussi un facteur motivant à challenger.
Ensuite parcequ’il paraît plausible que sur un volume donné il soit plus avantageux de réaliser soit même ces décodeurs que de les acheter, possibilité de les décliner aussi selon un cahier des charges moins ambitieux. ( qui peut le plus peut le moins  :))
Une interface de gestion pourrait même être envisagée mais la gestion des CV se fera le plus normalement possible.


Quelques choix techniques sont opérés ( retours d expériences oblige!)
Le choix de confier à un régulateur de tension de type DC-DC va nous permettre de nous affranchir des problématique de chauffe excessive de l’étage d alimentation et offrir même le luxe de préparer le terrain à d autres utilisations ( ex POWERPACK)

Le point en H fait appel à un composant tout en un. Le choix fait jusque là est dicté par une volonté de supporter 1.5A pour le moins. ( 1.2A serait au seuil bas acceptable mais la granulométries des composants n’est pas aussi fine)
D autres solutions limitent cette intensité à 0,9A en pointe ce qui pour du N est suffisant s avèrerait pour le HO un peu juste surtout pour des engins à la performance moteur moindre.

Aussi pour optimiser celle ci le décodeur permettra de traiter le PWM de pilotage du moteur à une fréquence élevée évitant ainsi les sifflements désagréables.
Une valeur proche de 40kHz présente valeur communément présente sur les produits du marché.
Le question de savoir si en analogique le décodeur pilote aussi moteur selon cette fréquence reste posée. Pour moi c est un gage de qualité et d efficacité mais un mode purement linéaire est aussi possible.

Précisons que le décodeur aura besoin de 6V environ en entrée pour pouvoir fonctionner.
La gestion des CV pourra équilibrer les seuils des vitesses pour l’engin piloté.
La mesure et le traitement du BEMF ( FCEM ) compensation de charge sera aussi de la partie.

Le choix d intégrer des protocoles/fonctionnalités avancées fait partie du challenge
ABC
BEMF
RAILCOM


Et … le son…!?
Pas au programme pour le moment mais qui sait si des déclinaisons ne verront pas le jour par la suite.

Nous allons déjà nous pencher  sur cette première approche déjà ambitieuse et plutôt complète à laquelle vous êtes invités à participer et suivre les avancements.

Laurent





laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #32 le: décembre 08, 2023, 01:11:17 am »
Bonsoir

J ai apporté de légères corrections dans les attributions de broches sur le CPU afin d être sur le même port pour le traitement des signaux DCC en provenance de chacun des rails.

C est ainsi "arbitrairement"  ::) le PORTC qui reçoit sur PIN_PC0 et PIN_PC1 respectivement les entrées DCC_IN_R et DCC_IN_L.
Glissement aussi des attributions pour OUT5 OUT4 et OUT1 avec un routage aménagé.

Ces updates sont poussés sur les versions 21MTC et PLUX22.

La version PLUX22 reçoit elle un NMOS supplémentaire pour la gestion de AUX7 tandis que la PIN1 du connecteur PLUX  est éligible comme GPIO_A dans le respect de la norme. ( sait on jamais...)

Laurent




laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #33 le: décembre 09, 2023, 02:31:27 am »
Bonsoir

Support RAILCOM sur la lib d AIKO PRAS.

Aiko indique dans les commentaires de sa librairie une voie qui permettrait de traiter le signal RC avec ajout d un Timer supplémentaire et la combinaison d éléments sur le CCL.

Dans le code et dans le cas des usages avec MEGACOREX, DX_CORE & MEGATINICORE s'appuyant sur EVENT.H et dans la mesure des temps écoulés ( sur CCMP)  afin de déterminer s il s agit d un bit 1 ou 0 il est précisé une possibilité d ajouter un filtre pour détecter le debut du timout RAILCOM entre 26us et 32us selon la norme ( 29us +3us ou -3us) (RCN 217 dont le dernier update est du 23/11/2023)

A ce moment si on procède à une lecture des entrées de chaque pole DCC sur le CPU et si on obtient un digitalread(PIN_X) == 0 et digitalread(PIN_Y)== 0 alors on est en présence d un début du d un railcom cutout sinon ce n'est pas le cas. ( on est dans un bit 0 ou 1 en cours de géneration)

on aurait donc quelque chose de la forme:

if( (delta =>26us) && (delta <=32us)) //et EVCTRL == RISING ? ( EVCTRL == 0)
{
  if((digitalRead(PIN_PC0)==0) && digitalRead(PIN_PC1) ==0)
  {
   flag_raicom_cutout= true;
  }
  else
 {
  flag_railcom_cutout = false;
 }

}


Traduit littéralement on a un FLAG RAILCOM actif lorsque les conditions sont remplies tel que:
on mesure sur les deux poles des broches PIN_PC0 et PIN_PC1 sont à "0" dans la plage de temps comprise entre 26us et 32 us après une interruption portée par un front montant. ( EVTCTRL == 0 pour start du counter)


A partir de la on a le flag railcom de dispo et on doit pouvoir l exploiter

if (flag_railcom_cutout == false)
{
on traite les bits selon les timings pour des bit 0 et 1
}

if(flag_railcom_cutout) //on est ds le cutout donc on va procéder aux envois d info sur les CH1 et CH2 selon le temps écoulé)
{
if((delta =>75us) && (delta <177us)
{
on active TX
on peut émettre sur le CH1 via le TX
}
else if(delta =>177) && (delta <193))
{
 on coupe l émission sur TX
}
else if((delta=>193) && (delta<454)
{
on active TX
on peut emmètre sur le CH2
}
else if ((delta >454) && (delta <=488)
{
on coupe TX
flag_railcom_cutout = false; // fin cut out
}

si on a le flag_cutout_railcom == true alors on n'assemble pas de bit dans l étape suivante et on attend donc de nouveau que le flag repasse a false pour y procéder.

Cela me semble pouvoir fonctionner ainsi.

On peut être encore plus "efficace" avec l utilisation du CCL sur la lecture des PIN_PCx et exploiter ce résultat lorsque le flag est à false - d ou l intérêt d avoir placé les DCC_IN_R et DCC_IN_R sur les broches du LUT1 sur IN0 et IN1   :D

Cependant l ISR est activé a chaque changement de polarisation (TCB_EDGE) hors le changement d état sur la proche DCC_IN ( ici PIN_PC0)  ne risque t il pas d induire un biais lors du cutout?( le moment ou PIN_PC0 passe à "0" génère une interruption puisque c est un "FALLING" et introduit un reset du CNT par la fonction dcc_interupt()) et donc le temps de référence se trouve alors décalé ( et faux!) . D ou peut etre le besoin de cette seconde detection exclusive sur du front "RISING" comme point de départ ou d une conservation de la valeur CCMP dans une variable du timing qui s incrémente sur les  fronts montant "RISING" . Cette valeur devient pour le cutout le point "0" ( de depart) de mesure du temps delta.

Cette seconde mesure doit alors etre portee sur une configuration de type Input Capture Pulse-Width Measurement mode

On essaye alors via la meme broche DCC_IN ne prenant cette fois que les fronts "RISING" une mesure de "delta2"  et on a le flag cutout actif ou non

Je cherche donc the solution!

@Trimarco232 qu en penses tu?
Vois tu une autre approche? ( celle d AIKO?) ( il y  a aussi celle à laquelle tu faisais allusion via les interruptions)

Laurent
« Modifié: décembre 09, 2023, 02:33:58 am par laurentr »

laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #34 le: décembre 11, 2023, 11:55:23 pm »
Bonsoir

J ai un peu avancé malgré plusieurs tentatives infructueuses ( c est aussi bcp comme cela que l'on apprend)
Ce qui est en dessous pourra peut être paraitre abscond mais permet de mieux comprendre le cheminement. Les "sachants" zapperont, les autres pourront s'inspirer de la démarche et s approprier les mécanismes sous jacents associés. Pédagogie! Pédagogie!!

Je me suis "égaré" sur une voie que je pensais "bonne" au départ. L'idée était d'exploiter quelques unes des informations laissées par Aiok. Je m'y suis "mal pris" en ayant oublié que ce qui se passe dans une ISR est "isolé" de ce qui se passe dans la LOOP principale et non traité en parallèle. (au moins sur AVR)

L'écriture des conditions pour un déclanchement du FLAG_RAILCOM_CUTOUT se fait par mesure de l'état sur la( les) pin d'entrées du signal DCC sur le CPU lors de l'intervalle compris entre 26us et 32us ( digitalRead(PIN_PC0 et PC1) à défaut d exploiter la génération d un CCL avec un "&" sur ces deux I/O. ( ce qui serait en principe plus rapide en le liant à un EVENT).
Le temps requis pouvant être un peu long et nous faire sortir de la (très courte) période de 6us  on préfèrera un digitalReadFast pour gagner en efficacité. Et celui ci pourrait aussi être un peu long d où le bénéfice d utiliser le résultat de l'event qui analyse la combinaison des PIN en entrée...
On regardera comment exploiter cela un peu plus tard.

En fait ce "fait générateur" doit donner le START d'un simple compteur timerB en mode PERIODIC_CNT_MODE sur un timer libre dont la valeur d'une variable incrémentée à intervalle de temps fixe et régulier sera comparée aux seuils des timings des actions d'émission RAILCOM. On pourra alors enclencher les émissions sur TX des info "qui vont bien"!

Des que le fllag railcom est actif on sort de l'interruption d'assemblage des bits 0 ou 1 des messages. Celui ci sera libéré une fois le cycle railcom achevé.

J'avais pensé resté dans une ISR pour émettre des actualisations avec des FLAGS successifs que la boucle principale aurait alors traité. OUI MAIS si on est dans une ISR alors on est pas dans la LOOP et on ne passe de l un à l autre que lorsque l ISR est achevée ( sauf à se hasarder sur l'utilisation du complément NO_BLOCK comme paramètre du vecteur d'interruption.)
Ca aurait pu être une bonne piste!

Il faut donc reconstruire différemment.

La valeur CCMP du "timer DCC" est la représentation du "temps écoulé" entre deux interruptions du front montant au front descendant. ( et inversement)
La permutation de lecture se fait par inversion du bit "EDGE" sur timer_EVCTRL à chaque changement d'état sur la broche dccPin.
Si on compte dans le sens "montant" alors timer_EVCTRL aura la valeur == 0.

Le front montant représente le temps T0 servant de référentiel à nos mesures. La valeur CCMP pourra alors alors être ajoutée à la variable incrémentée par le TIMERBx ( ou soustraite des valeurs de temps à mesurer) afin de compenser l alignement temporel des éléments entre timers/compteurs. Il serait même juste d'ajouter alors le "delta" que represente l'incrementation du CNT du premier timer couvrant les mesures sur les PINS PC0 et PC1.
J ignore si l event serait aussi à compenser mais on serait la surement plus proche d"EPSILON*" que de delta pour les matheux qui suivent!  8) (*EPSILON quantité infime souvent négligeable)

Je pense aussi que de regrouper RIALCOM en une CLASSE dédiée sera un plus... ( je vais m appuyer sur les travaux de lebelge2 qui a mis en œuvre les fonctions d'encodage dans son projet  et y exploiter des infos)
On pourra alors y traiter des fonctions gérant l encodage des valeurs, leur émission, mesure des temps de référence...

Bon passons à la pratique à présent!

Laurent


« Modifié: décembre 12, 2023, 02:50:40 am par laurentr »

trimarco232

  • Sr. Member
  • ****
  • Messages: 283
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #35 le: décembre 16, 2023, 11:43:39 am »
Bonjour ,
je suis assez booké ces temps-ci , alors , pêle-mêle :
Citer
si on obtient un digitalread(PIN_X) == 0 et digitalread(PIN_Y)== 0
si les 2 entrés sont sur le même port , on peut faire : if (IN_portX & 0b00100100) , en plaçant les 1 sur les entrées ; on doit même avoir une option où IN_portX peut être lu en 1 cycle (pareil si on est pressé en écriture , utiliser SET_portX , ou CLEAR , ou TOGGLE)
utiliser EVCTRL est une autre option : (filter)CCL(NAND) -> EVCTRL(filter) -> CAPT ; ça aurait été mieux qu'Atmel mette un filtre programmable au niveau du CAPT , pour éliminer les parasites , mais aussi les dead-time du pont en H de la station , environ 1us où les 2 entrées peuvent êtres lues simultanément à LOW , en confusion avec le début d'un cutout
je me refuse d'indiquer une préférence entre le polling des entrées (on a tout le temps pour le faire) , et l'utilisation de CCL et des armes des nouveaux AVR : l'idéal serait de disposer d'un proto qui permet d'évaluer les 2 ...
concernant les ISR , tu découvres , c'est bien ; j'en profite pour faire noter une chose , qui a peut-être une certaine importance : avec les nouveaux AVR , on peut fixer une certaine priorité pour les ISR ; ce n'est certes pas une priorité de préemption comme pour les ARM , mais une priorité de queue ; cela signifie que si ton interruption prioritaire est déclenchée alors qu'une autre ISR est en cours , ton interruption ne pourra pas interrompre l'autre ISR (d'où  l'intérêt que les ISR soient le plus court possible) , mais elle sera prioritaire par rapport aux éventuelles autres interruptions en attente , c'est déjà ça
j'en suis réduit à la même réponse de Normand concernant l'utilisation du soft d'Aiko vs celui de lebelge2 (affaire interne au Benelux) ; par contre , je suis persuadé qu'il vaut mieux utiliser le hard de lebelge2 , beaucoup plus simple que celui , nippon (ni mauvais) de ton schéma ; donc en un 1er temps , concernant le cutout , pour simplifier et avancer , tu pourrais faire du lebelge2 intégral (un peu de rétroingénierie  sans doute) , puis quand ça marche , migrer progressivement vers la solution d'Aiko , afin d'utiliser les pouvoirs des nouveaux AVR ; en tous les cas je pense que la solution japonaise est à écarter , car trop compliquée et mal documentée
« Modifié: décembre 16, 2023, 01:25:52 pm par trimarco232 »

laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #36 le: décembre 17, 2023, 05:20:42 pm »
Bonjour Marc

Merci pour tes indications.

Pour ce qui est des priorisations intra ISR j ai en effet vu de quoi il retourne. Je n'étais pas très familier de leur usage directement. On peut en effet définir un niveau supérieur d ISR pour l un de entre eux et/ou préciser le "point de départ dans leur ordre d enchainement (Round Robin). Il reste aussi l option "NO_BLOCK" mais aux risques et périls associés de son usage.

Je me suis récemment offert une" bible": le livre de TOM ALMY qui est une source puissante de connaissances que je ne peux que recommander (mais c est en anglais!).

J ai un peu avancé et j'ai procédé de la façon suivante sur ce qu'il me reste à tester ( et les journées ne font que 24h afgh!!)

J ai gardé la Librairie d AIKO PRAS (AP_DCC_LIBRARY) que j ai enrichi d une CLASS_RAILCOM et d un fichier CONSTANTES.h qui contient des valeurs qui vont ensuite être utilisées en interne ( notamment des temps utilisés par RAILCOM)

j ai recomposé les timings de détection de la valeur CCMP pour isoler celui du CUTOUT et place un FLAG à l'exécution ou non de la gestion des  bits 0 et 1 si l on est dans un CUTOUT ou non.


Je choisi volontairement d'attribuer le décodage des trames du signal DCC sur le TIMERB1 (DCC_USES_TIMERB1)
J affecte le TIMERB0 à un fonction de comptage "basic" (TCB_CNTMODE_INT_gc).

Je place les valeurs adéquates pour que lors de l'ISR ce compteur lorsque il est démarré incrémente chaque 1us une variable de 16bits (uint16_t microseconds_ticks) qui va représenter le temps écoulé depuis le point temporel "T0" qui est utilisé ensuite pour les différentes mesures temporelles.
TCB0 au niveau interruption est en haut de liste des priorités d'exécution et peu d autres éléments peuvent luis "passer devant" assurant en principe une mesure efficace voir très précise du temps incrémenté.

Pourquoi alors ne pas utiliser micros()? Pas de micro dans un ISR!

Comme AIKO utilise la vitesse du CPU sans "prescaler" on procèdera de même sur le TIMERB0.

On peut donc écrire pour être sur la même échelle la valeur CCMP du TIMERB0.

On procède pour cela en écrivant TCB0.CCMP =(F_CPU/1000000);
Ainsi à 16Mhz toutes les 16 itérations il s est écoulé 1us (16 000 000 / 1000000 = 16) (16 * 1 /16000000 = 0.000001 seconds = 1us)
Pour 20Mhz on a une mise à l échelle automatique pour cette fois 20 itérations. (20 000 000 /1000000 = 20) (20 * 1/20000000 = 0.000001 seconds = 1us )
Même musique à 24Mhz.

IST(TCB0_int_vect)
{
   microseconds_tick++; //incrementation
   bitSet(TCB0.INTFLAGS,0); // reset de l ISR
}



Ce compteur est démarré sous condition lors de la détection du CUTOUT entre 26us et 32us dans les éléments du code d'AIKO.

J ajoute en effet la condition de lecture de la tension sur les entrées des signaux DCC pour confirmer le fait d être dans le CUTOUT. Dans le cas du CUTOUT il n y a aucune tension dans la voie ("0V"c e qui place les entrées CPU recevant les signaux DCC à "1".
Un digitalRead() étant trop long à s exécuter d autres solutions sont utilisées (voir plus bas)

On place alors un FLAG de type bool en état actif (FLAG_RAICOM_IN_CUTOUT = true;)
Juste avant de démarrer le compteur sur le TIMERB01 il faut donc passer le temps écoulé à la variable microseconds_ticks afin que cela soit le point de départ actualisé du comptage.
Pour cela j'ajoute le nombre microseconds_ticks+=(uint16_t) (TCB1.CCMP + TCB1.CNT)/(F_CPU/1000000);
Puis on peut démarrer le TIMERB0 via bitSet(TCB0.CTRLA,0); (mise à 1 du bit 0 du registre CTRLA du TIMERB0)

Dans une fonction void Process() de la CLASS_RAILCOM on établi alors les mesures entre les intervalles pour envoyer les bons messages sur le TX au fur et à mesure que le temps s écoule.
Une fois le timing du CUTOUT dépassé donc microseconds_ticks >454us et <488us (ou >488us au cas où)
on procède;
stop du compteur B0 ( BitClear(TCB0.CTRLA,0); ) (mise à 0 du bit 0 d u registre CTRLA du TIMBERB0)
reset de la valeur CNT du compteur B0 via  TCB0.CNT = 0
microseconds_ticks = 0;
FLAG_RAILOM_CUTOUT = false

rm: il me semble que le FLAG_RAILCOM_IN_CUTOUT pourrait être aussi stocke dans le GPIOR3.
On ne peut pas y placer la valeur microseconds_ticks qui est sur 16bits car le registre est sur 8! On devrait alors combiner avec un autre registre mais ceux ci sont déjà affectés à d autres usages.

En principe cela doit "rouler".

J avais utilisé la fonction (digitalWriteFast(PIN_PC0) && digitalWriteFast(PIN_PC1) pour avoir un temps d exécution le plus court possible pour confirmer la présence des conditions du CUTOUT (timing entre 26us et 32us ET pas de tension sur la voie donc sur une valeur à "1" sur chaque entree DCC.
Cependant cette fonction requiert que les valeurs en  entrée soient des CONSTANTES donc écrit " en dure"... question "portabilité" bof bof!
 La solution que propose Marc va être aussi efficace via le if (IN_portC & 0b00000011) avec une mise en alias sous un #define de cette variable pour supporter les PIN en entrées.
On pourra veiller à ce que les 2 PINS choisies soient idéalement sur le même port pour gagner en efficacité.

Encore quelques lignes à écrire donc puis à tester!

A ce stade ma crainte est que le serial.Write') soit altéré par l ISR de TCB0 et/ou à l inverse que l'émission sur TX face sauter des micocrseconds_ticks ce qui fausserait la précision et donc possiblement le résultat. Mais la c est plus costaud à construire ou à "compenser".

S il y a des idées je suis preneur...

Laurent

 



laurentr

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

J ai trouve la "commande qui va bien" sur les indications de Trimarco232.

La voici pour bien comprendre sa construction.
Ceux qui la maitrise trouveront cela trivial comme toujours mais il n'est jamais inutile de donner les clés pour bien comprendre chaque étape.

Le if(IN_portX & 0bRSTUVWYZ) == VAL) est en fait une syntaxe sur les AVRx des séries 0/1/2 (et consorts)* de la ligne suivante:

PORTx.IN & 0bQRSTUVWY

PORTx ou  x= A ou B ou C ou D ou E ou F ou G. ( selon ce qui est présent sur le MCU en fonction de son brochage)

QRSTUVWY sont les bits 0 à 7 des PINx de 0 à 7
VAL = 0 ou 1

Si on veut lire le statut de PIN_PA3 on effectue usuellement digitalRead(PIN_PA3)
Si on veut aller plus vite on peut utiliser digitalReadFast(PIN_PA3) car PIN_PA3 est une constante.

Si cette variable ne l'est pas ou "inconnue "on combine donc PORTx.IN & les numéros de()s pin(s) que l on veut lire sur le port en question.

Si dans notre cas on est sur le même port alors la lecture s'effectuant en 1 fois on gagne quelques précieux cycles. A défaut on pourra lire sur des ports différents dans le code proposé

Aussi pour notre cas toujours à titre d exemple sur PIN_PA3 on fait l'équivalent de:
 PORTA.IN & 0b00001000

Dans notre cas nous avons attribué DCC_PIN_RIGHT sur PIN_PC0 et DCC_PIN_LEFT sur PIN_PC1

on irait directement au résultat via la commande suivante afin de lire dans le même cycle les 2 broches 0 et 1 du port C:( rapide efficace mais ... codé en "dure")

PORTC.IN & 0b00000011

mis en forme on a donc in if(PORTC.IN & 0b00000011) ==1) comme condition.

On peut aussi écrire ceci:
if((PORTC.IN & 0b00000001) == 1) && (PORTC.IN & 0b00000010) == 1)) ( au prix de quelques cycles de traitement supplémentaires)

L'avantage de cette seconde structure va être de proposer possiblement des lectures sur des ports différenciés au besoin.

Dans l'initialisation de la CASS_RAILCOM on trouvera donc une fonction qui inclura les 2 PINS sur lesquelles établir la lecture indifféremment de leur port et de leur numéro de broche en entrée  sous réserve toutefois que ces 2 pins puissent être libre d'usage et être déclarées comme INPUT ( pinMode(PIN_PXn, INPUT) voir pinMode(PIN_PXn, INPUT_PULLUP) si nécessaire.

Avec ces éléments on assure (normalement) la portabilité selon les hardwares retenus pour le CPU.

*On parle toujours des familles ATMEGA x08 et x09 ( famille série 0) ( ex Atmega4808, Atmage4809 ( celui du NANO_EVERY), ...

Des AVR DA DB DD EA et EB ( famille des séries suivantes 1 et 2)
Mais aussi des CPU de type" MEGATINY" tels les ATTINY x06 & x07 ( ex Attiny1606 Attiny1607)

Notez toutefois que seuls ceux disposant à minima de 2 timers B pourront alors convenir donc ceux des séries 1 et 2 tels les  Attiny 1616 Attiny1626 ou encore bien encore Attiny 3216 et Attiny3617. Les Attiny de la serie0 ne pourront pas traiter le RAILCOM ( ex Attiny1606 Attiny 1607 ...)

Laurent

« Modifié: décembre 17, 2023, 09:53:35 pm par laurentr »

laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #38 le: décembre 18, 2023, 02:42:35 am »
Bonsoir

Voici ce que ce que cela donne en l'état.

https://we.tl/t-VhMtl2es2j

Précisons que ceci a été traite sous PLATFORMIO en ayant précisé les flags suivants dans le fichier. platformio.ini
build_flags = -D MILLIS_USE_TIMERB2 -D DCC_USES_TIMERB1 -D RAILCOM_USES_TIMERB0

Sous l IDE ARDUINO il faudra:
 A jouter dans le fichier  RAILCOM_CONSTANTES.h ceci:

#ifndef RAILCOM_USES_TIMERB0
    #define RAILCOM_USES_TIMERB0
#endif

#ifndef DCC_USES_TIMERB1
    #define DCC_USES_TIMERB1
#endif

 A noter que par défaut si ce n est pas ajouté cela doit "passer" sur les valeurs par defaut avec TCB0 pour traiter RAILCOM et TCB1 pour traiter le signal DCC.

Il faudra renommer le fichier main.cpp en main.ino.
Peut etre aussi ajuster les chemins des fichiers de la librairie ou copier celel ci en lieu et place de celle presente par defaut.

Puis essayer et voir ce que cela donne.

Laurent




laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #39 le: décembre 18, 2023, 03:34:04 pm »
Bonjour

Premier constat sur un test de "portabilité" inter CPU il a fallu compléter par des conditions les modalités d'usage selon définition des ports dans la fonction void RAILCOM::init_RAILCOM_pins(uint8_t dccPin_right, uint8_t dccPin_left)

En effet si certains port ne sont pas définis et présents on aura une erreur à la compilation.

Donc on "conditionne" le tout pour chacun d'entre eux.


trimarco232

  • Sr. Member
  • ****
  • Messages: 283
    • Voir le profil
Re : Re : Décodeur de locomotive multifonctions
« Réponse #40 le: décembre 18, 2023, 10:56:20 pm »
(...)
A ce stade ma crainte est que le serial.Write') soit altéré par l ISR de TCB0 et/ou à l inverse que l'émission sur TX face sauter des micocrseconds_ticks ce qui fausserait la précision et donc possiblement le résultat. Mais la c est plus costaud à construire ou à "compenser".
Laurent
amha ,
il ne faut plus utiliser TCB0 une fois que l'envoi des bytes railcom a commencé
il ne faut pas non plus utiliser de bibliothèque pour envoyer les bytes , mais le faire manuellement : on se prémunit ainsi des ennuis liés aux interruptions , qu'on aura désactivées au début ; j'imagine la séquence suivante
- désactiver les interruptions
- observer la fin de la tempo pour le début de l'émission railcom , via TIMB0
- écrire le 1er byte du canal 1 dans TXDATA
- écrire le 2ème byte du canal 1 dans TXDATA
- observer une tempo de 40+40+33us , via TIMB0
- écrire le 1er byte du canal 2 dans TXDATA
- écrire le 2ème byte du canal 2 dans TXDATA
- attendre DREIF par polling
- écrire le 3ème byte du canal 2 dans TXDATA
- attendre DREIF par polling
- écrire le 4ème byte du canal 2 dans TXDATA
- attendre DREIF par polling
- écrire le 5ème byte du canal 2 dans TXDATA
- réactiver les interruptions
- attendre TXCIF par polling
- reprendre le décodage des bits DCC

Citer
On pourra veiller à ce que les 2 PINS choisies soient idéalement sur le même port pour
il faudra aussi que ces broches puissent être lues par l'ADC pour les besoins de l'ABC

Edit : 40 + 40 + 33
« Modifié: décembre 19, 2023, 11:15:23 pm par trimarco232 »

laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #41 le: décembre 19, 2023, 01:28:30 am »
Bonsoir

Oui c est une bonne façon de faire un peu plus "costaude" à construire. JE crois que le code "nippon" construit cela.

Si on est en dehors de l ISR on à toujours une possibilité de s appuyer sur micro() avec les aléas qui vont avec.

J ai légèrement retouché la fonction indiquée plus haut à la suite de tests "debug" qui indiquaient bien une série de corrections à apporter.
A noter l'arrivée d une variable  combo_mask qui pourra servir au besoin.

void RAILCOM :: init_RAILCOM_pins(uint8_t dccPin_right, uint8_t dccPin_left)
{
    pinMode(dccPin_right,INPUT);
    pinMode(dccPin_left,INPUT);

    uint8_t dccpinright = dccPin_right;
    uint8_t dccpinleft = dccPin_left;
   
   
    uint8_t portNumber = 0;
    uint8_t portBitNumber = 0;

    uint8_t tmpval1 = 0;
   
    portNumber = digital_pin_to_port[dccpinright];
portBitNumber = digital_pin_to_bit_position[dccpinright];
     
    switch(portBitNumber)
    {
        case 0:
            tmpval1 = PIN0_bm; //0b00000001;
        break;

        case 1:
            tmpval1 = PIN1_bm; //0b00000010;
        break;

        case 2:
            tmpval1 = PIN2_bm; //0b00000100;
        break;

        case 3:
            tmpval1 = PIN3_bm; //0b00001000;
        break;

        case 4:
            tmpval1 = PIN4_bm; //0b00010000;
        break;

        case 5:
            tmpval1 = PIN5_bm; //0b00100000;
        break;

        case 6:
            tmpval1 = PIN6_bm; //0b01000000;
        break;

        case 7:
            tmpval1 = PIN7_bm; //0b10000000;
        break;

    }

    mask_combo = tmpval1;

   

    switch(portNumber)
    {
        #if defined(PA) && (PA != NOT_A_PORT)
            case PA:
                #define CHECK_IN_PART1 (PORTA.IN & tmpval1)
            break;
        #endif

        #if defined(PB) && (PB != NOT_A_PORT)
            case PB:
                #define CHECK_IN_PART1 (PORTB.IN & tmpval1)
            break;
        #endif

        #if defined(PC) && (PC != NOT_A_PORT)
            case PC:
                #define CHECK_IN_PART1 (PORTC.IN & tmpval1)
            break;
        #endif

        #if defined(PD) && (PD != NOT_A_PORT)
            case PD:
                #define CHECK_IN_PART1 (PORTD.IN & tmpval1)
            break;
        #endif

        #if defined(PE) && (PE != NOT_A_PORT)
            case PE:
                #define CHECK_IN_PART1 (PORTE.IN & tmpval1)
            break;
        #endif 

        #if defined(PF) && (PF != NOT_A_PORT)
            case PF:
                #define CHECK_IN_PART1 (PORTF.IN & tmpval1)
            break;
        #endif
    }

    RAILCOM_CHECK_PIN_RIGHT = CHECK_IN_PART1;

    tmpval1 = 0;

    portNumber = digital_pin_to_port[dccpinleft];
    portBitNumber = digital_pin_to_bit_position[dccpinleft];

    switch(portBitNumber)
    {
        case 0:
            tmpval1 = PIN0_bm; //0b00000001;
        break;

        case 1:
            tmpval1 = PIN1_bm; //0b00000010;
        break;

        case 2:
            tmpval1 = PIN2_bm; //0b00000100;
        break;

        case 3:
            tmpval1 = PIN3_bm; //0b00001000;
        break;

        case 4:
            tmpval1 = PIN4_bm; //0b00010000;
        break;

        case 5:
            tmpval1 = PIN5_bm; //0b00100000;
        break;

        case 6:
            tmpval1 = PIN6_bm; //0b01000000;
        break;

        case 7:
            tmpval1 = PIN7_bm; //0b10000000;
        break;

    }

    mask_combo |= tmpval1;

    switch(portNumber)
    {       
        #if defined(PA) && (PA != NOT_A_PORT)
            case PA:
                #define CHECK_IN_PART2 (PORTA.IN & tmpval1)
            break;
        #endif

        #if defined(PB) && (PB != NOT_A_PORT)
            case PB:
                #define CHECK_IN_PART2 (PORTB.IN & tmpval1)
            break;
        #endif

        #if defined(PC) && (PC != NOT_A_PORT)
            case PC:
                #define CHECK_IN_PART2 (PORTC.IN & tmpval1)
            break;
        #endif

        #if defined(PD) && (PD != NOT_A_PORT)
            case PD:
                #define CHECK_IN_PART2 (PORTD_IN & tmpval1)
            break;
        #endif 

        #if defined(PE) && (PE != NOT_A_PORT)
            case PE:
                #define CHECK_IN_PART2 (PORTE.IN & tmpval1)
            break;
        #endif 

        #if defined(PF) && (PF != NOT_A_PORT)
            case PF:
                #define CHECK_IN_PART2 (PORTF.IN & tmpval1)
            break;
        #endif
    }

    RAILCOM_CHECK_PIN_LEFT = CHECK_IN_PART2;

}

laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #42 le: décembre 19, 2023, 01:50:20 am »
@Trimarco

J ai lu que le nombre de cycles nécessaires/utilisés entre les bits en utilisant softwareserial est < 16cyles CPU ;

https://github.com/SpenceKonde/DxCore/blob/master/megaavr/libraries/SoftwareSerial/src/SoftwareSerial.cpp#L286

  // 12 (gcc 4.8.2) or 13 (gcc 4.3.2) cycles from start bit to first bit,
  // 15 (gcc 4.8.2) or 16 (gcc 4.3.2) cycles between bits,
  // 12 (gcc 4.8.2) or 14 (gcc 4.3.2) cycles from last bit to stop bit

C est une info précieuse même si on va "construire" surement différemment du coup que via une lib tout faite.

Pas (du tout) s aussi trivial que je l aurai pensé de prime abord mais "défi" intéressant et constructif.

On pourra noter que entre les bits d une trame 8N1 ( avec le start bit en plus donc au total 10bits émis!!)
1 start bit (+12 cycles)
8 bits (7 x 15 = +105 cycles)
1 stop bit (+12 cycles)

donc on a

139 cycles minimum requis entre les bits ( et on a pas les temps pour chacun d entre eux) ce qui vérifie mon hypothèse pessimiste origine entre le "tick" à la us et les multi interruptions liées.

On doit donc composer différèrent comme tu le proposes.
Je ne m étais pas encore aventuré jusque la, c est l'occasion d y plonger  8)

Laurent

laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Re : Re : Décodeur de locomotive multifonctions
« Réponse #43 le: décembre 19, 2023, 01:57:06 am »
Citer
On pourra veiller à ce que les 2 PINS choisies soient idéalement sur le même port pour
il faudra aussi que ces broches puissent être lues par l'ADC pour les besoins de l'ABC
[/quote]


A ce stade je sélectionne volontairement 2 pins différentes sur le modèle "NIPPON" pour gérer l'ABC tout comme j ai retenu aussi 1 entrée ANA pour la lecture de tension post pont de diodes.

Rien n empêchera de voir comment fusionner ces blocs ensuite mais pour le moment leur gestion différentiée sera plus "simple" à traiter unitairement et rend modulaire la chose puisque qu on peut aussi se passer d'ABC...

Ltr

laurentr

  • Hero Member
  • *****
  • Messages: 587
    • Voir le profil
Re : Décodeur de locomotive multifonctions
« Réponse #44 le: décembre 19, 2023, 02:13:21 am »
On va pouvoir aussi s appuyer sur ce doc:

https://ww1.microchip.com/downloads/en/AppNotes/TB3216-Getting-Started-with-USART-90003216A.pdf

Dans lequel on devrait trouver matière à construire.

Ltr