Décodeur pour signaux SNCF à base d'Arduino

Toutes les discussions sur l'Arduino !

Modérateur : MOD

Avatar du membre
jlb
Fécond
Messages : 686
Enregistré le : jeu. 04 oct. 2012, 15:38
Echelle pratiquée : N
Prénom : Jean-Luc
Site Internet : http://modelleisenbahn.triskell.org

Re: Décodeur pour signaux SNCF à base d'Arduino

Message par jlb » ven. 19 déc. 2014, 07:58

macsddau a écrit : Si c'est une question de goût, les goûts et les couleurs ça ne se discutent pas.
Ce n'est pas une question de goût.

L'élégance en programmation c'est concevoir un programme clair et lisible autant du point de vue de la présentation que du point de vue de la conception. C'est choisir les bonnes structures de données, les bonnes écritures (les [] au lieu des *() quand on accède à un tableau ce qui évite d'avoir à définir une macro pour retrouver une écriture simplifiée alors qu'elle existait déjà). C'est également d'éviter des codages alambiqués des données pour économiser de la RAM alors qu'il n'y a pas lieu de le faire.

Tout livre d'apprentissage d'un langage devrait guider son lecteur vers le choix des bonnes écritures, vers un style de programmation clair et bien structuré, vers les bonnes pratiques. Si le livre jaune, «Apprendre à programmer en C pour les nuls» t'amène au style d'écriture que je vois, ne t'apprend pas l'équivalence tableau/pointeur, t'encourage à l'utilisation des macros, alors c'est un bouquin médiocre.

(D'ailleurs il suffit d'aller voir la biographie de l'auteur pour s'en convaincre http://en.m.wikipedia.org/wiki/Dan_Gookin, il s'agit d'un paraphraseur de doc. comment peut-on prétendre écrire un bouquin sur un langage sans l'avoir soi même extensivement pratiqué ?)

macsddau
Papotier
Messages : 114
Enregistré le : jeu. 17 oct. 2013, 21:46
Echelle pratiquée : N

Re: Décodeur pour signaux SNCF à base d'Arduino

Message par macsddau » ven. 19 déc. 2014, 09:45

Il est vrai que je suis un amateur. Mon expérience en programmation est très limitée; quelques heures d'assembleur ou de fortran pendant mes études, quelques tentatives en ObjectiveC et le cours "Comprendre les micro-contrôleurs" (il y a un fil sur ce forum). Mon but est avant tout de faire quelque chose qui fonctionne et ce n'est pas facile. Quand je trouve une solution à un problème, je l'utilise même si ce n'est pas élégant.

Peux tu me confirmer que
(*(pmotif + m)) est équivalent à pmotif[m]
dans ce cas je pourrai supprimer les 2 macros. Il est vrai que cela simplifie la lecture du programme.
jbl a écrit :C'est également d'éviter des codages alambiqués des données pour économiser de la RAM alors qu'il n'y a pas lieu de le faire
Sur ce point je ne suis pas d'accord avec toi, car nous travaillons avec de micro-contrôleurs qui ne sont pas de PC avec quelques Go de RAM, mais quelques centaines voir milliers d'octet de mémoire.Il n'est pas évident pour une utilisateur comme moi de connaître la RAM utilisée surtout qu'Arduino et ses librairies en occupent une certaine partie et que la dimension de celle-ci n'est pas connue.
Comme tu peux le voir je souhaite porter ces programmes sur ATtiny et avec 512ko de RAM, j'aurai surement besoin de me poser la question "quelle quantité de mémoire j'utilise". Lors de la compilation Arduino me donne la taille du programme, donc là, je maitrise. Si j'ai besoin de table constante, je les place dans la zone programme const prog_type var[] {};, si ses constante sont juste des valeurs alors je fais des #define xxx (compilation dans la zone programme) et non const type var = xxx; (stocké dans la RAM). Avec cette méthode je suis sûr d'économiser la RAM.

Pour finir, je n'ai pas ouvert ce post pour parler de ces sujets. Son but est d'exposer mes réalisations. Si le besoin s'en fait sentir, rien n'empêche un utilisateur de créer un nouveau fil sur les bonnes pratiques de la programmation sur Arduino.
MS2 CC2 S-DEC-4-DC Rocrail

Avatar du membre
jlb
Fécond
Messages : 686
Enregistré le : jeu. 04 oct. 2012, 15:38
Echelle pratiquée : N
Prénom : Jean-Luc
Site Internet : http://modelleisenbahn.triskell.org

Re: Décodeur pour signaux SNCF à base d'Arduino

Message par jlb » ven. 19 déc. 2014, 14:21

macsddau a écrit : Peux tu me confirmer que
(*(pmotif + m)) est équivalent à pmotif[m]
dans ce cas je pourrai supprimer les 2 macros. Il est vrai que cela simplifie la lecture du programme.
Je te le confirme. Je confirme également que &tab[0] et tab sont équivalents. Concernant le fait que la notation tableau est du sucre syntaxique, essaye ceci :

Code : Tout sélectionner

int a[10];

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

    a[3] = 5;

    Serial.println(3[a]);
}
Oui, c'est bien 3[a] ! Le compilateur transforme l'expression en expression utilisant l'arithmétique des pointeurs. L'expression est donc *(3 + a). Comme l'addition est commutative, elle peut également s'écrire *(a + 3), ce qui est aussi a[3].
jlb a écrit :C'est également d'éviter des codages alambiqués des données pour économiser de la RAM alors qu'il n'y a pas lieu de le faire
Sur ce point je ne suis pas d'accord avec toi, car nous travaillons avec de micro-contrôleurs qui ne sont pas de PC avec quelques Go de RAM, mais quelques centaines voir milliers d'octet de mémoire.Il n'est pas évident pour une utilisateur comme moi de connaître la RAM utilisée surtout qu'Arduino et ses librairies en occupent une certaine partie et que la dimension de celle-ci n'est pas connue.
Comme tu peux le voir je souhaite porter ces programmes sur ATtiny et avec 512ko de RAM, j'aurai surement besoin de me poser la question "quelle quantité de mémoire j'utilise". Lors de la compilation Arduino me donne la taille du programme, donc là, je maitrise. Si j'ai besoin de table constante, je les place dans la zone programme const prog_type var[] {};, si ses constante sont juste des valeurs alors je fais des #define xxx (compilation dans la zone programme) et non const type var = xxx; (stocké dans la RAM). Avec cette méthode je suis sûr d'économiser la RAM.
Je ne conteste pas le fait d'utiliser des #define pour les constantes ni le qualificateur prog_<type> pour éviter que les constantes en flash soient recopiées en RAM au démarrage du programme. En effet, ça ne nuit pas à la lisibilité.

Les micro-contrôleurs ont moins de RAM qu'un PC (512 octets pour l'ATTiny) mais toute cette mémoire est disponible pour l'application contrairement à un PC où elle doit être partagée entre toutes les applications et l'OS. Par conséquent tant qu'il en reste, ce n'est pas la peine de se prendre la tête.

Mais, je n'ai rien contre la volonté d'économiser tant que ça ne nuit pas à la lisibilité du programme. Le fait que tu utilises des bits pour stocker tes informations rend le code particulièrement difficile à lire. Or, en C, on peut définir des struct dont les membres sont de taille inférieure à l'octet. Comme ceci :

Code : Tout sélectionner

typedef struct {
    int a : 2;
    int b : 1;
    int c : 3;
} element;
a occupera 2 bits, b, 1 et c, 3. l'ensemble occupant 6 bits soit un octet où deux bits sont perdus. Le compilateur se charge de tout.

Tu peux essayer ce code exemple :

Code : Tout sélectionner

typedef struct {
    unsigned int a : 2;
    unsigned int b : 1;
    unsigned int c : 3;
} element;

element tab[10];

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  
  element toto;
  
  Serial.print("taille element = ");
  Serial.println(sizeof(element));
  Serial.print("taille tab = ");
  Serial.println(sizeof(tab));
  
  toto.a = 2;
  toto.b = 1;
  toto.c = 4;
  
  Serial.print("a=");
  Serial.print(toto.a);
  Serial.print(" b=");
  Serial.print(toto.b);
  Serial.print(" c=");
  Serial.println(toto.c);
}

void loop() {
  // put your main code here, to run repeatedly:

}
Tu peux évidemment mettre une struct de ce type dans status au lieu d'un char. Ton code sera infiniment plus clair, quasiment auto-documenté Tu pourras faire sauter tous tes #define de masques.
Pour finir, je n'ai pas ouvert ce post pour parler de ces sujets. Son but est d'exposer mes réalisations. Si le besoin s'en fait sentir, rien n'empêche un utilisateur de créer un nouveau fil sur les bonnes pratiques de la programmation sur Arduino.
Certe mais à partir du moment où tu soumets ton code pour que l'on t'aide à découvrir les bugs, il faut s'attendre également à des commentaires qui remettent en cause la façon dont tu écris les programmes et recevoir des suggestions sur la façon dont il faudrait faire pour améliorer les choses. D'autant plus qu'il m'a fallu pas mal de temps pour comprendre ce que ça faisait afin de pouvoir t'aider. Comme je développe depuis maintenant 28 ans en C et C++ dans l'embarqué et sous Unix, et avec d'autres langages également et que j'ai écrit pas mal de code, comme j'enseigne également niveau ingénieur et Master et que je vois défiler pas mal d'étudiants avec toujours les mêmes défauts qui reviennent, j'ai pensé que mes suggestions pourraient être pertinentes. Ma démarche était purement altruiste et je suis un peu dépité de voir le résultat de mes efforts.

Avatar du membre
Arduino
Prolixe
Messages : 1698
Enregistré le : mer. 25 sept. 2013, 16:14

Re: Décodeur pour signaux SNCF à base d'Arduino

Message par Arduino » dim. 21 déc. 2014, 12:30

Voici une discussion extrêmement intéressante sur les macros, les pointeurs et l'optimisation des programmes.

Lorsque j'ai découvert Arduino, j'ai eu envie d'en parler pour notre hobby, le modélisme ferroviaire. La principale raison est que j'ai vite compris que ce concept permet de mettre le monde des microcontrôleurs à la portée des ferromodélistes, grâce à la simplicité de son langage. On peut d'ailleurs programmer Arduino, sans rien connaitre des pointeurs et des subtilités du C/C++.

Jean-Luc a raison ; les macros sont intéressantes si elles permettent d'apporter de la lisibilité dans un programme (et de le rendre indépendant du montage électronique qu'il y a derrière). L'utilisation systématique des macros peut parfois donner une usine à gaz ; il faut donc rester modéré.

Quant à l'optimisation du code, c'est louable de vouloir adopter les bons réflexes. Néanmoins, tant qu'on n'approche pas la taille de l'espace mémoire, ou bien la vitesse d'exécution limite du programme, est-ce vraiment nécessaire ? On peut allumer une LED avec digitalWrite, on peut aussi aller écrire directement sur le port de sortie. La première solution est triviale mais prend quelques octets et quelques dizaines de µs de plus, la deuxième solution engendre un risque de bugs plus élevé (il faut conditionner le port en sortie et écrire le bon octet) pour économiser ce que personne ne pourra voir.

Chacun fait bien sûr comme il lui plait, mais il ne faudrait surtout pas noyer le poisson, ni perdre de vue qu'Arduino a connu un succès planétaire grâce à sa simplicité de programmation. En disant cela, je pense surtout à tous ceux qui débutent : contentez vous du langage de base d'Arduino et vous verrez que vos applications fonctionneront. Pour les autres (et c'est aussi ce qui m'a motivé à m'inscrire au cours de microcontrôleurs), allez titiller les entrailles des microcontrôleurs, les registres cachés, les timers et les ports d'entrée sortie, c'est un autre genre de plaisir.

Car finalement, ce qui est important, c'est le plaisir qu'on a à faire les choses. J'ai pris du plaisir à faire découvrir Arduino, j'ai pris du plaisir à suivre le cours sur internet (conseillé par macsddau) et je prends du plaisir à consulter le blog de jlb (et maintenant Locoduino) car c'est un excellent pédagogue. Bref, tout ce qui fait la richesse d'un forum comme celui-ci, même si parfois nos avis divergent un peu. Vous ne croyez pas ? :wink:

macsddau
Papotier
Messages : 114
Enregistré le : jeu. 17 oct. 2013, 21:46
Echelle pratiquée : N

Re: Décodeur pour signaux SNCF à base d'Arduino

Message par macsddau » mer. 24 déc. 2014, 19:36

Suivant les derniers conseils de Jean-Luc, j'ai modifié la définition de motif_t.

Code : Tout sélectionner

typedef struct {
    unsigned char motif;
    struct {
        unsigned char groupe:2;
        unsigned char peutClignoter:1;
        unsigned char visible:1;
        unsigned char clignote:1;
    } statut;
} motif_t;
Les quelques lignes utilisant des variables de ce type ont été modifiées en conséquence. Le programme se compile sans erreur, il est légèrement plus volumineux (j'ai ajouté quelques ligne d'initialisation). Malheureusement, je n'ai pas mon matériel de test à portée de main. Vous aurez donc le résultat définitif plus tard.

Joyeux Noël à tous !
MS2 CC2 S-DEC-4-DC Rocrail

Avatar du membre
Trusty
Bavard
Messages : 66
Enregistré le : lun. 03 déc. 2012, 11:04
Echelle pratiquée : N
Prénom : Thierry
Localisation : Melun
Âge : 56

Re: Décodeur pour signaux SNCF à base d'Arduino

Message par Trusty » mer. 31 déc. 2014, 16:20

Juste une info pour signaler que dans la dernière version de ma librairie UAD (Universal Accessory Decoder) téléchargeable ici, la 4.0 Beta 4, j'ai ajouté un exemple SignalFrench qui pilote directement un feu type Français avec 2 à 12 leds, et avec précablés toutes les combinaisons officielles utilisées par la SNCF. C'est paramétrable, adaptable à une signalisation étrangère, et c'est gratuit !
Enfin pour être complet, une personnalisation de la librairie est possible manuellement pour ne pas dépenser de mémoire inutile, même si ce programme sur mon Mega ne prend que 21K en mémoire programme, et 1,7K en mémoire vive sans faire d'effort. On doit pouvoir descendre à 15K en supprimant les portions de code inutilisées.

macsddau
Papotier
Messages : 114
Enregistré le : jeu. 17 oct. 2013, 21:46
Echelle pratiquée : N

Re: Décodeur pour signaux SNCF à base d'Arduino

Message par macsddau » lun. 12 janv. 2015, 21:58

A ces tableaux !

J'ai réussi a passer un tableau à une dimension. Maintenant c'est un tableau à plusieurs dimension que je veux utiliser.

Voici ce que je fais :
Je défini mon tableau

Code : Tout sélectionner

const unsigned char PROGMEM monTableau[nbLignes][nbColonnes] = {
    {…},
    …,
    {…}
};
Jusque là rien de compliqué,

Je transmet l'adresse de mon tableau à mon objet en utilisant

Code : Tout sélectionner

(unsigned char*)&(monTableau[0][0])
Mon objet le range dans une variable

Code : Tout sélectionner

unsigned char* ptableau;
Mais lorsque mon objet veut utiliser le contenu du tableau

Code : Tout sélectionner

ptableau[uneLigne][uneColonne]
plus rien ne fonctionne.
Je ne vois pas ce qui cloche :(
MS2 CC2 S-DEC-4-DC Rocrail

Avatar du membre
Pierre59
Papotier
Messages : 147
Enregistré le : dim. 07 mars 2010, 09:17
Echelle pratiquée : HO
Club : Lille Modélisme
Localisation : Villeneuve d'Ascq (59650)
Âge : 75

Re: Décodeur pour signaux SNCF à base d'Arduino

Message par Pierre59 » mar. 13 janv. 2015, 08:44

Bonjour

Il faut mettre des char** pour les tableaux à deux dimensions.

Pierre

Avatar du membre
Trusty
Bavard
Messages : 66
Enregistré le : lun. 03 déc. 2012, 11:04
Echelle pratiquée : N
Prénom : Thierry
Localisation : Melun
Âge : 56

Re: Décodeur pour signaux SNCF à base d'Arduino

Message par Trusty » mar. 13 janv. 2015, 16:09

Attention, si tu utilises des tableaux déclarés en PROGMEM, alors tu dois faire appel à des fonctions spéciales pour y acceder:

// read back a char
myChar = pgm_read_byte_near(signMessage + k);

et je ne suis jamais arrivé à faire fonctionner ça sur un tableau à double entrée. J'ai été obligé de passer à un tableau à simple entrée en me fixant une taille fixe pour simuler les lignes... Mais je suis ouvert à toute proposition de solution !

Avatar du membre
Pierre59
Papotier
Messages : 147
Enregistré le : dim. 07 mars 2010, 09:17
Echelle pratiquée : HO
Club : Lille Modélisme
Localisation : Villeneuve d'Ascq (59650)
Âge : 75

Re: Décodeur pour signaux SNCF à base d'Arduino

Message par Pierre59 » mar. 13 janv. 2015, 16:58

Bonjour

Je proposerai quelque chose comme cela :

Code : Tout sélectionner

#include <avr/pgmspace.h>

byte mydata[11][10] PROGMEM =
{
{0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09},
{0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13},
{0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D},
{0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27},
{0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31},
{0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B},
{0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45},
{0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F},
{0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59},
{0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60,0x61,0x62,0x63},
{0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D}
};

void setup() {}

void loop() { byte i=5,j=4;

byte b = pgm_read_byte(&(mydata[i][j]));

}
On peut mettre n'importe quel type (reconnu par PROGMEM) à la place de byte.

Pierre

macsddau
Papotier
Messages : 114
Enregistré le : jeu. 17 oct. 2013, 21:46
Echelle pratiquée : N

Re: Décodeur pour signaux SNCF à base d'Arduino

Message par macsddau » mar. 13 janv. 2015, 23:10

Merci Pierre et Trusty.

voici ce que j'ai retiré de vos conseils :

Je crée la table

Code : Tout sélectionner

const unsigned char PROGMEM MOTIF[NOMBRE_MOTIFS][SIGNAL_TAILE_TABLE_MOTIF] = {
    //  composition                , groupe, masque (peut clignoter)
    { (1 << FEU_OE) | (1 << FEU_A) , 0     , 1 }, // A
    { (1 << FEU_OE) | (1 << FEU_S) , 0     , 1 }, // S
    { (1 << FEU_OE) | (1 << FEU_VL), 0     , 1 }, // VL
    { (1 << FEU_S)  | (1 << FEU_C) , 0     , 0 }, // CR
    { (1 << FEU_A)  | (1 << FEU_C) , 0     , 0 }, // PA
    { (1 << FEU_C)                 , 0     , 0 }, // DR
    { (1 << FEU_M)                 , 0     , 1 }, // M
    { (1 << FEU_C)                 , 0     , 0 }, // CV
    { (1 << FEU_R)                 , 1     , 1 }, // R
    { (1 << FEU_RR)                , 1     , 1 }  // RR
};
son adresse est transmise a l'objet SIGNAL_c

Code : Tout sélectionner

    Signal.definir((unsigned char*)FEU,  NOMBRE_FEUX,  (unsigned char*)MOTIF,  NOMBRE_MOTIFS,  statut);
Le contenu de la table est alors accessible de cette manière

Code : Tout sélectionner

void SIGNAL_c::fermer(unsigned char motif) {
    // fermer le motif
    pstatut[motif].visible = 0;
    feux &= ~(pgm_read_byte(&pmotif[MOTIF_COMPOSITION + SIGNAL_TAILE_TABLE_MOTIF * motif]));
}
dans cet exemple MOTIF_COMPOSITION correspond à la colonne 0.
MS2 CC2 S-DEC-4-DC Rocrail

macsddau
Papotier
Messages : 114
Enregistré le : jeu. 17 oct. 2013, 21:46
Echelle pratiquée : N

Re: Décodeur pour signaux SNCF à base d'Arduino

Message par macsddau » mer. 14 janv. 2015, 17:27

Une petite précision sur le dernier morceau de code
lorsque la table est définie de la sous la forme

Code : Tout sélectionner

const type PROGMEM table[nbLignes][nbColonnes] = {...};
type* ptable = &table;
pour accéder a une donnée de la table il faut utiliser

Code : Tout sélectionner

donnee = pgm_read_byte(&ptable[colonne + nbColonnes * sizeof(type) * ligne]);
MS2 CC2 S-DEC-4-DC Rocrail

Avatar du membre
Pierre59
Papotier
Messages : 147
Enregistré le : dim. 07 mars 2010, 09:17
Echelle pratiquée : HO
Club : Lille Modélisme
Localisation : Villeneuve d'Ascq (59650)
Âge : 75

Re: Décodeur pour signaux SNCF à base d'Arduino

Message par Pierre59 » mer. 14 janv. 2015, 18:12

Bonjour

Tu n'as pas essayé :

donnee = pgm_read_byte(&(table[ligne][colonne]));

Pierre

macsddau
Papotier
Messages : 114
Enregistré le : jeu. 17 oct. 2013, 21:46
Echelle pratiquée : N

Re: Décodeur pour signaux SNCF à base d'Arduino

Message par macsddau » mer. 14 janv. 2015, 18:34

Pierre59 a écrit :Bonjour

Tu n'as pas essayé :

donnee = pgm_read_byte(&(table[ligne][colonne]));

Pierre
Non en effet, car la table est définie dans le programme principal (.ino) et c'est ma librairie (.cpp) qui utilise son contenu. Elle ne connais la table que sous la forme de son pointeur et elle ne peut pas savoir que la table est à deux dimensions.
MS2 CC2 S-DEC-4-DC Rocrail

Avatar du membre
Pierre59
Papotier
Messages : 147
Enregistré le : dim. 07 mars 2010, 09:17
Echelle pratiquée : HO
Club : Lille Modélisme
Localisation : Villeneuve d'Ascq (59650)
Âge : 75

Re: Décodeur pour signaux SNCF à base d'Arduino

Message par Pierre59 » mer. 14 janv. 2015, 18:57

macsddau a écrit : Non en effet, car la table est définie dans le programme principal (.ino) et c'est ma librairie (.cpp) qui utilise son contenu. Elle ne connais la table que sous la forme de son pointeur et elle ne peut pas savoir que la table est à deux dimensions.
Mettre un cast (type entre parentheses) : ((byte°°)table)

Pierre

Répondre